comparison src/gt_accept.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:8abade8ddb2c
1 /*
2 * $Id: gt_accept.c,v 1.64 2005/01/04 15:03:40 mkern Exp $
3 *
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17 #include "gt_gnutella.h"
18
19 #include "gt_packet.h"
20 #include "gt_utils.h"
21
22 #include "gt_connect.h"
23
24 #include "gt_node.h"
25 #include "gt_node_cache.h"
26 #include "gt_netorg.h"
27
28 #include "gt_xfer_obj.h"
29 #include "gt_xfer.h"
30 #include "gt_http_server.h"
31
32 #include "gt_accept.h"
33 #include "gt_bind.h"
34 #include "gt_ban.h"
35 #include "gt_version.h"
36
37 #include "message/gt_message.h" /* gnutella_start_connection */
38
39 /*****************************************************************************/
40
41 #define MAX_FDBUF_SIZE 16384
42
43 #define INCOMING_TIMEOUT (1 * MINUTES)
44
45 /*****************************************************************************/
46 /* Handle incoming connections to this node */
47
48 /*
49 * Wrap TCPC and associated timeout for incoming connections in order to limit
50 * the number of incoming connections.
51 */
52 struct incoming_conn
53 {
54 TCPC *c;
55 timer_id timer;
56 };
57
58 #define GT_METHOD(func) void func (int fd, input_id id, \
59 struct incoming_conn *conn)
60 typedef void (*GtAcceptFunc) (int fd, input_id id, struct incoming_conn *conn);
61
62 GT_METHOD(gt_server_accept);
63 GT_METHOD(gt_server_get);
64 GT_METHOD(gt_server_giv);
65
66 static struct server_cmd
67 {
68 char *name;
69 GtAcceptFunc callback;
70 }
71 server_commands[] =
72 {
73 { "GNUTELLA", gt_server_accept },
74 { "GET", gt_server_get },
75 { "HEAD", gt_server_get },
76 { "GIV", gt_server_giv },
77 { NULL, NULL }
78 };
79
80 /*****************************************************************************/
81
82 static void send_node_headers (int fd, input_id id, TCPC *c);
83 static void recv_final_handshake (int fd, input_id id, TCPC *c);
84 static void determine_method (int fd, input_id id,
85 struct incoming_conn *conn);
86
87 /*****************************************************************************/
88
89 static void incoming_conn_free (struct incoming_conn *conn)
90 {
91 timer_remove (conn->timer);
92 free (conn);
93 }
94
95 static void incoming_conn_close (struct incoming_conn *conn)
96 {
97 tcp_close (conn->c);
98 incoming_conn_free (conn);
99 }
100
101 static BOOL conn_timeout (struct incoming_conn *conn)
102 {
103 incoming_conn_close (conn);
104 return FALSE;
105 }
106
107 static struct incoming_conn *incoming_conn_alloc (TCPC *c)
108 {
109 struct incoming_conn *conn;
110
111 conn = malloc (sizeof (struct incoming_conn));
112 if (!conn)
113 return NULL;
114
115 conn->c = c;
116 conn->timer = timer_add (INCOMING_TIMEOUT, (TimerCallback)conn_timeout,
117 conn);
118
119 return conn;
120 }
121
122 /*****************************************************************************/
123
124 BOOL gt_fdbuf_full (FDBuf *buf)
125 {
126 size_t len = MAX_FDBUF_SIZE;
127
128 if (fdbuf_data (buf, &len) == NULL)
129 return TRUE;
130
131 return len >= MAX_FDBUF_SIZE;
132 }
133
134 /*****************************************************************************/
135
136 /*
137 * Receive an incoming connection.
138 */
139 void gnutella_handle_incoming (int fd, input_id id, TCPC *listen)
140 {
141 TCPC *c;
142
143 if (!(c = tcp_accept (listen, FALSE)))
144 return;
145
146 if (HANDSHAKE_DEBUG)
147 GT->DBGSOCK (GT, c, "got a new connection");
148
149 id = INPUT_NONE;
150 gt_handshake_dispatch_incoming (fd, id, c);
151 }
152
153 /*
154 * Mark not firewalled if the other end isn't local.
155 */
156 static void fw_status_update (TCPC *c)
157 {
158 if (!c->outgoing && !net_match_host (c->host, "LOCAL"))
159 {
160 if (GT_SELF->firewalled)
161 GT->DBGSOCK (GT, c, "connected, clearing firewalled status");
162
163 gt_bind_clear_firewalled ();
164 }
165 }
166
167 /*
168 * Allocate a structure to track this connection and continue the dispatching
169 * process.
170 */
171 void gt_handshake_dispatch_incoming (int fd, input_id id, TCPC *c)
172 {
173 struct incoming_conn *conn;
174 in_addr_t peer_ip;
175
176 /*
177 * This will trigger if the connection was closed on the remote end
178 * or if there's a input timer setup to timeout this connection. In the
179 * latter case, we have to avoid adding any inputs for the connection.
180 */
181 if (net_sock_error (c->fd))
182 {
183 tcp_close (c);
184 return;
185 }
186
187 peer_ip = net_peer (c->fd);
188
189 /*
190 * While this might be nice for the banner, it's not nice to the ban-ee,
191 * and I'm sure there's some bugs in the banning code, so we should
192 * send back an error instead.
193 */
194 #if 0
195 if (gt_ban_ipv4_is_banned (peer_ip))
196 {
197 if (HANDSHAKE_DEBUG)
198 GT->DBGSOCK (GT, c, "not accepting connection [address banned]");
199
200 tcp_close (c);
201 return;
202 }
203 #endif
204
205 /*
206 * If there are too many HTTP connections for this IP, close the
207 * connection now before investing any more resources servicing it.
208 */
209 if (gt_http_connection_length (GT_TRANSFER_UPLOAD, peer_ip) >=
210 HTTP_MAX_PERUSER_UPLOAD_CONNS)
211 {
212 if (HTTP_DEBUG)
213 GT->DBGSOCK (GT, c, "too many connections for this user, closing");
214
215 tcp_close (c);
216 return;
217 }
218
219 /* local hosts_allow may need to be evaluated to keep outside sources
220 * away */
221 if (GNUTELLA_LOCAL_MODE)
222 {
223 if (!net_match_host (peer_ip, GNUTELLA_LOCAL_ALLOW))
224 {
225 if (HANDSHAKE_DEBUG)
226 GT->DBGSOCK (GT, c, "non-local connection, closing");
227
228 tcp_close (c);
229 return;
230 }
231 }
232
233 if (!(conn = incoming_conn_alloc (c)))
234 {
235 tcp_close (c);
236 return;
237 }
238
239 input_remove (id);
240 input_add (c->fd, conn, INPUT_READ,
241 (InputCallback)determine_method, 0);
242 }
243
244 /*
245 * Dispatch incoming connections to the proper subsystem.
246 */
247 static void determine_method (int fd, input_id id, struct incoming_conn *conn)
248 {
249 struct server_cmd *command;
250 FDBuf *fdbuf;
251 int ret;
252 char *request;
253 TCPC *c = conn->c;
254
255 fdbuf = tcp_readbuf (c);
256
257 if ((ret = fdbuf_delim (fdbuf, "\n")) < 0)
258 {
259 if (HANDSHAKE_DEBUG)
260 GT->DBGSOCK (GT, c, "error: %s", GIFT_NETERROR ());
261
262 incoming_conn_close (conn);
263 return;
264 }
265
266 /* some data was read: update fw status */
267 fw_status_update (c);
268
269 if (gt_fdbuf_full (fdbuf))
270 {
271 incoming_conn_close (conn);
272 return;
273 }
274
275 if (ret > 0)
276 return;
277
278 /*
279 * NOTE: fdbuf_release() is not called here, so the first line is left on
280 * the FDBuf.
281 */
282 request = fdbuf_data (fdbuf, NULL);
283
284 for (command = server_commands; command->name != NULL; command++)
285 {
286 if (!strncasecmp (command->name, request, strlen (command->name)))
287 {
288 input_remove (id);
289 input_add (fd, conn, INPUT_READ,
290 (InputCallback)command->callback, 0);
291
292 return;
293 }
294 }
295
296 if (HANDSHAKE_DEBUG)
297 GT->DBGFN (GT, "bad command: %s", request);
298
299 incoming_conn_close (conn);
300 }
301
302 /*****************************************************************************/
303
304 /*
305 * Main connection acceptance routine. This begins the connection's
306 * journey through the rest of the handshaking code.
307 */
308 GT_METHOD(gt_server_accept)
309 {
310 char *request;
311 char *version_str;
312 size_t request_len = 0;
313 int n;
314 GtNode *node;
315 TCPC *c = conn->c;
316 FDBuf *buf;
317
318 if (HANDSHAKE_DEBUG)
319 GT->DBGFN (GT, "entered");
320
321 buf = tcp_readbuf (c);
322
323 if ((n = fdbuf_delim (buf, "\n")) < 0)
324 {
325 if (HANDSHAKE_DEBUG)
326 GT->DBGSOCK (GT, c, "error on recv: %s", GIFT_NETERROR ());
327
328 incoming_conn_close (conn);
329 return;
330 }
331
332 if (gt_fdbuf_full (buf))
333 {
334 incoming_conn_close (conn);
335 return;
336 }
337
338 if (n > 0)
339 return;
340
341 request = fdbuf_data (buf, &request_len);
342
343 if (!gt_http_header_terminated (request, request_len))
344 return;
345
346 fdbuf_release (buf);
347
348 if (HANDSHAKE_DEBUG)
349 GT->DBGSOCK (GT, c, "accepted headers:\n%s", request);
350
351 version_str = strchr (request, '/');
352 if (version_str)
353 version_str++;
354
355 if (strncasecmp ("GNUTELLA CONNECT/", request,
356 sizeof ("GNUTELLA CONNECT/") - 1) != 0)
357 {
358 if (HANDSHAKE_DEBUG)
359 GT->DBGSOCK (GT, c, "bad handshake header");
360
361 incoming_conn_close (conn);
362 return;
363 }
364
365 /* deny 0.4 connections */
366 if (!version_str || strncasecmp (version_str, "0.4", 3) == 0)
367 {
368 if (HANDSHAKE_DEBUG)
369 GT->DBGSOCK (GT, c, "closing 0.4 connection");
370
371 incoming_conn_close (conn);
372 return;
373 }
374
375 /* make a node out of this connection */
376 if (!(node = gt_node_instantiate (c)))
377 {
378 if (HANDSHAKE_DEBUG)
379 GT->DBGFN (GT, "node_instantiate failed");
380
381 incoming_conn_close (conn);
382 return;
383 }
384
385 /*
386 * Update the start_connect_time so the node will stick around for a while
387 * even when the node list gets trimmed.
388 */
389 node->start_connect_time = time (NULL);
390
391 gt_node_state_set (node, GT_NODE_CONNECTING_1);
392 node->incoming = TRUE;
393
394 /*
395 * Store the http header.
396 *
397 * NOTE: We don't check the return code here. This function expects a
398 * properly formatted HTTP response, but we are handing it a
399 * Gnutella connection request instead, so it will return failure.
400 */
401 gnutella_parse_response_headers (request, &node->hdr);
402
403 /*
404 * Use the node handshake timeout timer now, and get rid of the
405 * generic incoming connection timeout timer.
406 */
407 gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS);
408 incoming_conn_free (conn);
409
410 input_remove (id);
411 input_add (fd, c, INPUT_WRITE,
412 (InputCallback)send_node_headers, TIMEOUT_DEF);
413 }
414
415 GT_METHOD(gt_server_giv)
416 {
417 FDBuf *buf;
418 int n;
419 in_addr_t peer_ip;
420 char *response;
421 size_t response_len = 0;
422 char *client_id;
423 gt_guid_t *guid;
424 TCPC *c = conn->c;
425
426 if (HTTP_DEBUG || HANDSHAKE_DEBUG)
427 GT->DBGFN (GT, "entered");
428
429 buf = tcp_readbuf (c);
430
431 if ((n = fdbuf_delim (buf, "\n")) < 0)
432 {
433 incoming_conn_close (conn);
434 return;
435 }
436
437 if (gt_fdbuf_full (buf))
438 {
439 incoming_conn_close (conn);
440 return;
441 }
442
443 if (n > 0)
444 return;
445
446 response = fdbuf_data (buf, &response_len);
447
448 if (!gt_http_header_terminated (response, response_len))
449 return;
450
451 fdbuf_release (buf);
452
453 if (HTTP_DEBUG || HANDSHAKE_DEBUG)
454 GT->DBGSOCK (GT, c, "giv response=%s", response);
455
456 string_sep (&response, " "); /* Skip "GIV " */
457 string_sep (&response, ":"); /* Skip the file index */
458
459 client_id = string_sep (&response, "/");
460 string_lower (client_id);
461
462 if (!(guid = gt_guid_bin (client_id)))
463 {
464 incoming_conn_close (conn);
465 return;
466 }
467
468 peer_ip = net_peer (c->fd);
469
470 /*
471 * This will continue the GtTransfer if one is found, and store
472 * the connection if one is not. The connection will be resumed
473 * when a chunk for this source is reissued.
474 */
475 gt_push_source_add_conn (guid, peer_ip, c);
476
477 incoming_conn_free (conn);
478 free (guid);
479 }
480
481 /*****************************************************************************/
482
483 GT_METHOD(gt_server_get)
484 {
485 if (HTTP_DEBUG)
486 GT->DBGFN (GT, "entered");
487
488 /* dispatch to the HTTP server code */
489 gt_http_server_dispatch (fd, id, conn->c);
490
491 /* get rid of the tracking information for the connection in this
492 * subsystem */
493 incoming_conn_free (conn);
494 }
495
496 /*****************************************************************************/
497 /* GNUTELLA/0.6 CONNECTIONS */
498
499 BOOL gt_http_header_terminated (char *data, size_t len)
500 {
501 int cnt;
502
503 assert (len > 0);
504 len--;
505
506 for (cnt = 0; len > 0 && cnt < 2; cnt++)
507 {
508 if (data[len--] != '\n')
509 break;
510
511 /* treat CRLF as LF */
512 if (data[len] == '\r')
513 len--;
514 }
515
516 return (cnt == 2);
517 }
518
519 /* TODO: header continuation and joining of multiple occurrences */
520 void gt_http_header_parse (char *headers, Dataset **d)
521 {
522 char *line, *key;
523
524 while ((line = string_sep_set (&headers, "\r\n")))
525 {
526 key = string_sep (&line, ":");
527
528 if (!key || !line)
529 continue;
530
531 string_trim (key);
532 string_trim (line);
533
534 /* dont allow empty key-values, need to check this too */
535 if (string_isempty (line))
536 continue;
537
538 dataset_insertstr (d, string_lower (key), line);
539 }
540 }
541
542 static void send_nodes (struct cached_node *node, String *s)
543 {
544 if (s->str[s->len - 1] != ':')
545 string_append (s, ",");
546 else
547 string_append (s, " ");
548
549 string_appendf (s, "%s:%hu", net_ip_str (node->addr.ip), node->addr.port);
550 }
551
552 static void deny_connection (TCPC *c, int code, char *msg)
553 {
554 String *s;
555 List *nodes;
556 in_addr_t ip;
557
558 if (!(s = string_new (NULL, 0, 0, TRUE)))
559 return;
560
561 string_appendf (s, "GNUTELLA/0.6 %d %s\r\n", code, msg);
562 string_appendf (s, "User-Agent: %s\r\n", gt_version());
563
564 ip = net_peer (c->fd);
565 if (!gt_is_local_ip (ip, 0))
566 string_appendf (s, "Remote-IP: %s\r\n", net_ip_str (ip));
567
568 /* append some different nodes to try */
569 nodes = gt_node_cache_get (10);
570
571 if (nodes)
572 {
573 string_append (s, "X-Try-Ultrapeers:");
574
575 list_foreach (nodes, (ListForeachFunc)send_nodes, s);
576 list_free (nodes);
577
578 string_append (s, "\r\n");
579 }
580
581 /* append message terminator */
582 string_append (s, "\r\n");
583
584 /* we will be closing the connection after this, so we don't
585 * care if the send fails or not. */
586 tcp_send (c, s->str, s->len);
587
588 if (HANDSHAKE_DEBUG)
589 GT->DBGSOCK (GT, c, "connection denied response:\n%s", s->str);
590
591 string_free (s);
592 }
593
594 static void setup_node_class (GtNode *node)
595 {
596 char *ultrapeer;
597 char *qrp;
598
599 ultrapeer = dataset_lookupstr (node->hdr, "x-ultrapeer");
600 qrp = dataset_lookupstr (node->hdr, "x-query-routing");
601
602 if (ultrapeer && !strcasecmp (ultrapeer, "true") && qrp != NULL)
603 gt_node_class_set (node, GT_NODE_ULTRA);
604 else
605 gt_node_class_set (node, GT_NODE_LEAF);
606 }
607
608 BOOL gnutella_auth_connection (TCPC *c)
609 {
610 GtNode *node;
611
612 node = GT_NODE(c);
613 assert (GT_NODE(c) == node && GT_CONN(node) == c);
614
615 /* set the class of this node based on the headers sent */
616 setup_node_class (node);
617
618 /*
619 * If the remote node is only crawling us, accept the connection
620 * no matter what.
621 */
622 if (dataset_lookupstr (node->hdr, "crawler"))
623 return TRUE;
624
625 /*
626 * If we are a leaf node, and so is this node, deny the connection,
627 * but send 'X-Try-Ultrapeer:' headers with the ultrapeers we
628 * are connected to in order to try to bootstrap the remote client.
629 */
630 if (!(GT_SELF->klass & GT_NODE_ULTRA) && (node->klass & GT_NODE_LEAF))
631 {
632 deny_connection (c, 503, "I am a shielded leaf node");
633 return FALSE;
634 }
635
636 if (gt_conn_need_connections (node->klass) <= 0)
637 {
638 deny_connection (c, 503, "Too many connections");
639 return FALSE;
640 }
641
642 if (gt_ban_ipv4_is_banned (node->ip))
643 {
644 deny_connection (c, 403, "Unauthorized");
645 return FALSE;
646 }
647
648 return TRUE;
649 }
650
651 static void send_node_headers (int fd, input_id id, TCPC *c)
652 {
653 if (net_sock_error (c->fd))
654 {
655 gt_node_error (c, NULL);
656 gt_node_disconnect (c);
657 return;
658 }
659
660 if (!gnutella_auth_connection (c))
661 {
662 gt_node_error (c, "[incoming] connection not authorized");
663 gt_node_disconnect (c);
664 return;
665 }
666
667 /* send OK response to the peer and send headers for this node also */
668 if (!gnutella_send_connection_headers (c, "GNUTELLA/0.6 200 OK"))
669 {
670 gt_node_error (c, NULL);
671 gt_node_disconnect (c);
672 return;
673 }
674
675 /* reset the timeout for this connection */
676 gnutella_set_handshake_timeout (c, TIMEOUT_3 * SECONDS);
677
678 input_remove (id);
679 input_add (fd, c, INPUT_READ,
680 (InputCallback)recv_final_handshake, 0);
681 }
682
683 /* TODO: this should be abstracted */
684 static char *field_has_value (Dataset *d, const char *key, const char *val)
685 {
686 char *value;
687 char *str;
688
689 if (!(value = dataset_lookupstr (d, key)))
690 return NULL;
691
692 if ((str = strstr (value, val)))
693 return str;
694
695 return NULL;
696 }
697
698 static BOOL need_inflate (GtNode *node)
699 {
700 if (!(field_has_value (node->hdr, "content-encoding", "deflate")))
701 return FALSE;
702
703 return TRUE;
704 }
705
706 static BOOL need_deflate (GtNode *node)
707 {
708 if (!(field_has_value (node->hdr, "accept-encoding", "deflate")))
709 return FALSE;
710
711 return TRUE;
712 }
713
714 void gnutella_mark_compression (GtNode *node)
715 {
716 if (need_inflate (node))
717 node->rx_inflated = TRUE;
718
719 if (need_deflate (node))
720 node->tx_deflated = TRUE;
721 }
722
723 static void add_key (ds_data_t *key, ds_data_t *value, Dataset **d)
724 {
725 char *hdr = key->data;
726 char *val = value->data;
727
728 dataset_insertstr (d, hdr, val);
729 }
730
731 static void recv_final_handshake (int fd, input_id id, TCPC *c)
732 {
733 FDBuf *buf;
734 int n;
735 char *response;
736 size_t response_len = 0;
737 Dataset *additional = NULL;
738
739 buf = tcp_readbuf (c);
740
741 if ((n = fdbuf_delim (buf, "\n")) < 0)
742 {
743 if (HANDSHAKE_DEBUG)
744 GT->DBGSOCK (GT, c, "fdbuf_delim: error %s", GIFT_NETERROR ());
745
746 gt_node_disconnect (c);
747 return;
748 }
749
750 if (gt_fdbuf_full (buf))
751 {
752 gt_node_disconnect (c);
753 return;
754 }
755
756 if (n > 0)
757 return;
758
759 response = fdbuf_data (buf, &response_len);
760
761 if (!gt_http_header_terminated (response, response_len))
762 return;
763
764 fdbuf_release (buf);
765
766 if (HANDSHAKE_DEBUG)
767 GT->DBGSOCK (GT, c, "stage3 response:\n%s", response);
768
769 if (!gnutella_parse_response_headers (response, &additional))
770 {
771 if (HANDSHAKE_DEBUG)
772 gt_node_error (c, "node denied us in stage3 of handshake");
773
774 gt_node_disconnect (c);
775 dataset_clear (additional);
776 return;
777 }
778
779 /*
780 * Append or replace the fields the node sent into the header for this
781 * node.
782 *
783 * TODO: should probably change the interface to parse_response_headers so
784 * we can just pass in the header list of the node.
785 */
786 dataset_foreach (additional, DS_FOREACH(add_key), &GT_NODE(c)->hdr);
787 dataset_clear (additional);
788
789 /* mark the compression flags on this GtNode */
790 gnutella_mark_compression (GT_NODE(c));
791
792 input_remove (id);
793 input_add (fd, c, INPUT_WRITE,
794 (InputCallback)gnutella_start_connection, TIMEOUT_DEF);
795 }