view src/gt_connect.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
line source
1 /*
2 * $Id: gt_connect.c,v 1.55 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 */
17 #include "gt_gnutella.h"
18 #include "gt_version.h"
20 #include "gt_connect.h"
21 #include "gt_accept.h"
22 #include "gt_packet.h"
24 #include "gt_node.h"
25 #include "gt_node_list.h"
26 #include "gt_utils.h"
28 #include "gt_search.h"
29 #include "gt_netorg.h"
31 #include "gt_node_cache.h"
33 #include "message/gt_message.h" /* gnutella_start_connection */
35 /*****************************************************************************/
37 static void send_connect (int fd, input_id id, TCPC *c);
38 static void recv_headers (int fd, input_id id, TCPC *c);
39 static void send_response (int fd, input_id id, TCPC *c);
40 static BOOL send_final (TCPC *c);
42 /*****************************************************************************/
44 static BOOL handshake_timeout (TCPC *c)
45 {
46 GtNode *node = GT_NODE(c);
48 node->handshake_timer = 0;
50 if (!(node->state & GT_NODE_CONNECTED))
51 {
52 gt_node_disconnect (c);
53 return FALSE;
54 }
56 return FALSE;
57 }
59 void gnutella_set_handshake_timeout (TCPC *c, time_t delay)
60 {
61 timer_remove (GT_NODE(c)->handshake_timer);
63 GT_NODE(c)->handshake_timer = timer_add (delay,
64 (TimerCallback)handshake_timeout,
65 c);
66 }
68 int gt_connect (GtNode *node)
69 {
70 TCPC *c;
72 if (!node)
73 return -1;
75 #if 0
76 if (GT_CONN(node) != NULL)
77 {
78 GT->dbg (GT, "duplicate connection?: %p", GT_CONN(node));
79 return -1;
80 }
82 if (node->state != GT_NODE_DISCONNECTED)
83 {
84 GT->dbg (GT, "state = %i??", node->state);
85 return -1;
86 }
87 #endif
89 /* this must be called only on disconnected nodes */
90 assert (GT_CONN(node) == NULL);
91 assert (node->state == GT_NODE_DISCONNECTED);
93 #if 0
94 if (!conn_auth (c, TRUE))
95 return -1;
96 #endif
98 /* set this early: gt_netorg relies on this being set in order
99 * to check if it should access the gwebcaches */
100 node->start_connect_time = time (NULL);
102 /* make sure port is valid */
103 if (node->gt_port == 0)
104 {
105 GT->DBGFN (GT, "bad port on node %s", net_ip_str (node->ip));
106 return -1;
107 }
109 /* make outgoing connection */
110 if (!(c = tcp_open (node->ip, node->gt_port, FALSE)))
111 return -1;
113 gt_node_connect (node, c);
115 gt_node_state_set (node, GT_NODE_CONNECTING_1);
116 node->incoming = FALSE;
118 /* set the connection timeout */
119 gnutella_set_handshake_timeout (c, TIMEOUT_1 * SECONDS);
121 input_add (c->fd, c, INPUT_WRITE,
122 (InputCallback)send_connect, 0);
124 return c->fd;
125 }
127 static void send_connect (int fd, input_id id, TCPC *c)
128 {
129 if (net_sock_error (c->fd))
130 {
131 gt_node_disconnect (c);
132 return;
133 }
135 /* Send the connect string along with our headers */
136 if (!gnutella_send_connection_headers (c, "GNUTELLA CONNECT/0.6"))
137 {
138 gt_node_error (c, NULL);
139 gt_node_disconnect (c);
140 return;
141 }
143 /* we connected ok, so give the peer some more time */
144 gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS);
146 input_remove (id);
147 input_add (fd, c, INPUT_READ,
148 (InputCallback)recv_headers, 0);
149 }
151 BOOL gnutella_parse_response_headers (char *reply, Dataset **r_headers)
152 {
153 int code; /* 200, 404, ... */
154 char *response;
155 Dataset *headers = NULL;
157 response = string_sep (&reply, "\r\n");
159 if (!response)
160 return FALSE;
162 /* */ string_sep (&response, " "); /* shift past HTTP/1.1 */
163 code = ATOI (string_sep (&response, " ")); /* shift past 200 */
165 /* parse the headers */
166 gt_http_header_parse (reply, &headers);
168 if (r_headers)
169 *r_headers = headers;
170 else
171 dataset_clear (headers);
173 if (code >= 200 && code <= 299)
174 return TRUE;
176 return FALSE;
177 }
179 static time_t parse_uptime (Dataset *d)
180 {
181 char *str;
182 int days, hours, mins;
183 int n;
185 if (!(str = dataset_lookupstr (d, "uptime")))
186 return 0;
188 string_lower (str);
190 if ((n = sscanf (str, "%dd %dh %dm", &days, &hours, &mins)) != 3)
191 return 0;
193 if (HANDSHAKE_DEBUG)
194 {
195 GT->dbg (GT, "uptime parsed: %d days, %d hours, %d minutes",
196 days, hours, mins);
197 }
199 return days * EDAYS + hours * EHOURS + mins * EMINUTES;
200 }
202 /* look in a header field for nodes, and register them */
203 static void extract_nodes (Dataset *d, in_addr_t src,
204 const char *field, gt_node_class_t klass)
205 {
206 char *str;
207 char *value;
208 time_t now;
210 now = time (NULL);
212 if (!(str = dataset_lookupstr (d, field)))
213 return;
215 while ((value = string_sep (&str, ",")))
216 {
217 in_addr_t ip;
218 in_port_t port;
220 ip = net_ip (string_sep (&value, ":"));
221 port = ATOI (value);
223 if (port == (in_port_t) -1 || port == 0)
224 continue;
226 if (ip == INADDR_NONE || ip == 0)
227 continue;
229 if (gt_is_local_ip (ip, src))
230 continue;
232 gt_node_cache_add_ipv4 (ip, port, klass, now, 0, src);
233 }
235 gt_node_cache_trace ();
236 }
238 static void recv_headers (int fd, input_id id, TCPC *c)
239 {
240 FDBuf *buf;
241 char *response;
242 size_t response_len = 0;
243 int n;
244 BOOL ok;
245 time_t uptime;
246 GtNode *node = GT_NODE(c);
248 buf = tcp_readbuf (c);
250 if ((n = fdbuf_delim (buf, "\n")) < 0)
251 {
252 GT->DBGFN (GT, "error reading headers: %s", GIFT_NETERROR ());
253 gt_node_disconnect (c);
254 return;
255 }
257 if (gt_fdbuf_full (buf))
258 {
259 gt_node_disconnect (c);
260 return;
261 }
263 if (n > 0)
264 return;
266 response = fdbuf_data (buf, &response_len);
267 if (!gt_http_header_terminated (response, response_len))
268 return;
270 fdbuf_release (buf);
272 if (HANDSHAKE_DEBUG)
273 GT->DBGSOCK (GT, c, "node handshake response:\n%s", response);
275 /* parse and store the response */
276 ok = gnutella_parse_response_headers (response, &node->hdr);
278 /* extract nodes */
279 extract_nodes (node->hdr, node->ip, "x-try-ultrapeers", GT_NODE_ULTRA);
280 extract_nodes (node->hdr, node->ip, "x-try", GT_NODE_NONE);
282 /* grab the uptime from the "Uptime: " header and update this node */
283 if ((uptime = parse_uptime (node->hdr)) > 0)
284 {
285 gt_node_cache_add_ipv4 (node->ip, node->gt_port,
286 GT_NODE_ULTRA, time (NULL), uptime, node->ip);
288 /* XXX: remove the item immediately so we trigger the side effect of
289 * adding this node to the stable list */
290 gt_node_cache_del_ipv4 (node->ip, node->gt_port);
291 }
293 if (!ok)
294 {
295 gt_node_disconnect (c);
296 return;
297 }
299 input_remove (id);
300 input_add (fd, c, INPUT_WRITE,
301 (InputCallback)send_response, 0);
302 }
304 static void send_response (int fd, input_id id, TCPC *c)
305 {
306 if (net_sock_error (c->fd))
307 {
308 gt_node_error (c, NULL);
309 gt_node_disconnect (c);
310 return;
311 }
313 if (!gnutella_auth_connection (c))
314 {
315 gt_node_error (c, "[outgoing] connection not authorized");
316 gt_node_disconnect (c);
317 return;
318 }
320 if (!send_final (c))
321 {
322 gt_node_error (c, NULL);
323 GT->DBGSOCK (GT, c, "error at stage 3 of handshake");
324 gt_node_disconnect (c);
325 return;
326 }
328 /* ok, startup this connection */
329 input_remove (id);
330 input_add (fd, c, INPUT_WRITE,
331 (InputCallback)gnutella_start_connection, 0);
332 }
334 /*****************************************************************************/
336 static GtNode *append_node (TCPC *c, GtNode *node, String *s)
337 {
338 if (s->str[s->len - 1] != ' ')
339 string_append (s, ",");
341 string_appendf (s, "%s:%hu", net_ip_str (node->ip), node->gt_port);
342 return NULL;
343 }
345 static void append_crawler_headers (String *msg)
346 {
347 if (gt_conn_length (GT_NODE_ULTRA, GT_NODE_CONNECTED) > 0)
348 {
349 string_append (msg, "Peers: ");
350 gt_conn_foreach (GT_CONN_FOREACH(append_node), msg,
351 GT_NODE_ULTRA, GT_NODE_CONNECTED, 0);
352 string_append (msg, "\r\n");
353 }
355 if (GT_SELF->klass & GT_NODE_ULTRA &&
356 gt_conn_length (GT_NODE_LEAF, GT_NODE_CONNECTED) > 0)
357 {
358 string_append (msg, "Leaves: ");
359 gt_conn_foreach (GT_CONN_FOREACH(append_node), msg,
360 GT_NODE_LEAF, GT_NODE_CONNECTED, 0);
361 string_append (msg, "\r\n");
362 }
363 }
365 BOOL gnutella_send_connection_headers (TCPC *c, const char *header)
366 {
367 String *msg;
369 if (!(msg = string_new (NULL, 0, 0, TRUE)))
370 return FALSE;
372 string_appendf (msg, "%s\r\n", header);
374 string_append (msg, "X-Query-Routing: 0.1\r\n");
375 string_appendf (msg, "X-Ultrapeer: %s\r\n",
376 (GT_SELF->klass & GT_NODE_ULTRA) ? "True" : "False");
378 /* append the client and version we are using */
379 string_appendf (msg, "User-Agent: %s\r\n", gt_version ());
381 /* Add a header describing the remote IP of the peer */
382 string_appendf (msg, "Remote-IP: %s\r\n", net_peer_ip (c->fd));
384 /* let remote end know it's ok to send vendor messages */
385 string_appendf (msg, "Vendor-Message: 0.1\r\n");
387 /* support transmission of pings/pongs with GGEP appended */
388 string_append (msg, "GGEP: 0.5\r\n");
390 /* If this is the limewire crawler, append "Peers: " and "Leaves: "
391 * headers and close the connection */
392 if (!c->outgoing && dataset_lookupstr (GT_NODE(c)->hdr, "crawler"))
393 append_crawler_headers (msg);
395 /* append willingness to receive compressed data */
396 string_append (msg, "Accept-Encoding: deflate\r\n");
398 /* check whether the remote node sent us Accept-Encoding: deflate
399 * already */
400 gnutella_mark_compression (GT_NODE(c));
402 /* compress data if we must */
403 if (GT_NODE(c)->tx_deflated)
404 string_append (msg, "Content-Encoding: deflate\r\n");
406 /* Add message terminator */
407 string_append (msg, "\r\n");
409 if (HANDSHAKE_DEBUG)
410 GT->DBGSOCK (GT, c, "sending node headers:\n%s", msg->str);
412 if (tcp_send (c, msg->str, msg->len) <= 0)
413 {
414 string_free (msg);
415 return FALSE;
416 }
418 string_free (msg);
419 return TRUE;
420 }
422 static BOOL send_final (TCPC *c)
423 {
424 String *s;
425 int ret;
426 int len;
428 if (!(s = string_new (NULL, 0, 0, TRUE)))
429 return FALSE;
431 /* append header acceptance line */
432 string_append (s, "GNUTELLA/0.6 200 OK\r\n");
434 /* mark the connection as complete */
435 gnutella_mark_compression (GT_NODE(c));
437 if (GT_NODE(c)->tx_deflated)
438 string_append (s, "Content-Encoding: deflate\r\n");
440 /* append msg terminator */
441 string_append (s, "\r\n");
443 if (HANDSHAKE_DEBUG)
444 GT->DBGSOCK (GT, c, "sending final handshake:\n%s", s->str);
446 len = s->len;
447 ret = tcp_send (c, s->str, s->len);
449 string_free (s);
451 if (ret != len)
452 return FALSE;
454 return TRUE;
455 }
457 /*****************************************************************************/
458 /* CONNECTABILITY TESTING */
460 static void connect_test_result (GtNode *node, TCPC *c, BOOL success)
461 {
462 GT->DBGFN (GT, "connect test to %s %s", net_ip_str (node->ip),
463 (success ? "succeeded" : "failed"));
465 node->firewalled = (success ? FALSE : TRUE);
466 node->verified = TRUE;
468 if (c)
469 {
470 tcp_close (c);
471 node->gt_port_verify = NULL;
472 }
473 }
475 static void test_connectable (int fd, input_id id, TCPC *c)
476 {
477 GtNode *node;
479 node = c->udata;
481 if (net_sock_error (c->fd))
482 {
483 connect_test_result (node, c, FALSE);
484 return;
485 }
487 /*
488 * Send two newlines, because some firewalls will let connections pass
489 * through, but no data.
490 */
491 tcp_send (c, "\n\n", 2);
492 connect_test_result (node, c, TRUE);
493 }
495 /*
496 * Test if the port of a peer we are connected to is connectable. This lets a
497 * node know if it's firewalled. We could use this info to mangle the 'push'
498 * flag on query hits from this node if it is a leaf.
499 *
500 * Mangling query hits would break any future checksum or signing algorithm on
501 * query hits though, so that isn't done.
502 */
503 void gt_connect_test (GtNode *node, in_port_t port)
504 {
505 TCPC *new_c;
507 if (!port)
508 {
509 node->firewalled = TRUE;
510 return;
511 }
513 /* this needs some kind of local mode switch */
514 #if 0
515 if (net_match_host (GT_NODE(c)->ip, "LOCAL"))
516 {
517 GT_NODE(c)->firewalled = TRUE;
518 return;
519 }
520 #endif
522 if (!node->incoming)
523 return;
525 GT->DBGFN (GT, "starting connect test on %s:%hu",
526 net_ip_str (node->ip), port);
528 if (!(new_c = tcp_open (node->ip, port, FALSE)))
529 {
530 GT->DBGFN (GT, "failed to open test connection to %s:%hu",
531 net_ip_str (node->ip), node->gt_port);
532 return;
533 }
535 if (node->gt_port_verify)
536 tcp_close (node->gt_port_verify);
538 /* keep track of this connection */
539 node->gt_port_verify = new_c;
540 new_c->udata = node;
542 input_add (new_c->fd, new_c, INPUT_WRITE,
543 (InputCallback)test_connectable, TIMEOUT_DEF);
544 }