Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e4f6bbec341e |
---|---|
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 */ | |
16 | |
17 #include "gt_gnutella.h" | |
18 #include "gt_version.h" | |
19 | |
20 #include "gt_connect.h" | |
21 #include "gt_accept.h" | |
22 #include "gt_packet.h" | |
23 | |
24 #include "gt_node.h" | |
25 #include "gt_node_list.h" | |
26 #include "gt_utils.h" | |
27 | |
28 #include "gt_search.h" | |
29 #include "gt_netorg.h" | |
30 | |
31 #include "gt_node_cache.h" | |
32 | |
33 #include "message/gt_message.h" /* gnutella_start_connection */ | |
34 | |
35 /*****************************************************************************/ | |
36 | |
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); | |
41 | |
42 /*****************************************************************************/ | |
43 | |
44 static BOOL handshake_timeout (TCPC *c) | |
45 { | |
46 GtNode *node = GT_NODE(c); | |
47 | |
48 node->handshake_timer = 0; | |
49 | |
50 if (!(node->state & GT_NODE_CONNECTED)) | |
51 { | |
52 gt_node_disconnect (c); | |
53 return FALSE; | |
54 } | |
55 | |
56 return FALSE; | |
57 } | |
58 | |
59 void gnutella_set_handshake_timeout (TCPC *c, time_t delay) | |
60 { | |
61 timer_remove (GT_NODE(c)->handshake_timer); | |
62 | |
63 GT_NODE(c)->handshake_timer = timer_add (delay, | |
64 (TimerCallback)handshake_timeout, | |
65 c); | |
66 } | |
67 | |
68 int gt_connect (GtNode *node) | |
69 { | |
70 TCPC *c; | |
71 | |
72 if (!node) | |
73 return -1; | |
74 | |
75 #if 0 | |
76 if (GT_CONN(node) != NULL) | |
77 { | |
78 GT->dbg (GT, "duplicate connection?: %p", GT_CONN(node)); | |
79 return -1; | |
80 } | |
81 | |
82 if (node->state != GT_NODE_DISCONNECTED) | |
83 { | |
84 GT->dbg (GT, "state = %i??", node->state); | |
85 return -1; | |
86 } | |
87 #endif | |
88 | |
89 /* this must be called only on disconnected nodes */ | |
90 assert (GT_CONN(node) == NULL); | |
91 assert (node->state == GT_NODE_DISCONNECTED); | |
92 | |
93 #if 0 | |
94 if (!conn_auth (c, TRUE)) | |
95 return -1; | |
96 #endif | |
97 | |
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); | |
101 | |
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 } | |
108 | |
109 /* make outgoing connection */ | |
110 if (!(c = tcp_open (node->ip, node->gt_port, FALSE))) | |
111 return -1; | |
112 | |
113 gt_node_connect (node, c); | |
114 | |
115 gt_node_state_set (node, GT_NODE_CONNECTING_1); | |
116 node->incoming = FALSE; | |
117 | |
118 /* set the connection timeout */ | |
119 gnutella_set_handshake_timeout (c, TIMEOUT_1 * SECONDS); | |
120 | |
121 input_add (c->fd, c, INPUT_WRITE, | |
122 (InputCallback)send_connect, 0); | |
123 | |
124 return c->fd; | |
125 } | |
126 | |
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 } | |
134 | |
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 } | |
142 | |
143 /* we connected ok, so give the peer some more time */ | |
144 gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS); | |
145 | |
146 input_remove (id); | |
147 input_add (fd, c, INPUT_READ, | |
148 (InputCallback)recv_headers, 0); | |
149 } | |
150 | |
151 BOOL gnutella_parse_response_headers (char *reply, Dataset **r_headers) | |
152 { | |
153 int code; /* 200, 404, ... */ | |
154 char *response; | |
155 Dataset *headers = NULL; | |
156 | |
157 response = string_sep (&reply, "\r\n"); | |
158 | |
159 if (!response) | |
160 return FALSE; | |
161 | |
162 /* */ string_sep (&response, " "); /* shift past HTTP/1.1 */ | |
163 code = ATOI (string_sep (&response, " ")); /* shift past 200 */ | |
164 | |
165 /* parse the headers */ | |
166 gt_http_header_parse (reply, &headers); | |
167 | |
168 if (r_headers) | |
169 *r_headers = headers; | |
170 else | |
171 dataset_clear (headers); | |
172 | |
173 if (code >= 200 && code <= 299) | |
174 return TRUE; | |
175 | |
176 return FALSE; | |
177 } | |
178 | |
179 static time_t parse_uptime (Dataset *d) | |
180 { | |
181 char *str; | |
182 int days, hours, mins; | |
183 int n; | |
184 | |
185 if (!(str = dataset_lookupstr (d, "uptime"))) | |
186 return 0; | |
187 | |
188 string_lower (str); | |
189 | |
190 if ((n = sscanf (str, "%dd %dh %dm", &days, &hours, &mins)) != 3) | |
191 return 0; | |
192 | |
193 if (HANDSHAKE_DEBUG) | |
194 { | |
195 GT->dbg (GT, "uptime parsed: %d days, %d hours, %d minutes", | |
196 days, hours, mins); | |
197 } | |
198 | |
199 return days * EDAYS + hours * EHOURS + mins * EMINUTES; | |
200 } | |
201 | |
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; | |
209 | |
210 now = time (NULL); | |
211 | |
212 if (!(str = dataset_lookupstr (d, field))) | |
213 return; | |
214 | |
215 while ((value = string_sep (&str, ","))) | |
216 { | |
217 in_addr_t ip; | |
218 in_port_t port; | |
219 | |
220 ip = net_ip (string_sep (&value, ":")); | |
221 port = ATOI (value); | |
222 | |
223 if (port == (in_port_t) -1 || port == 0) | |
224 continue; | |
225 | |
226 if (ip == INADDR_NONE || ip == 0) | |
227 continue; | |
228 | |
229 if (gt_is_local_ip (ip, src)) | |
230 continue; | |
231 | |
232 gt_node_cache_add_ipv4 (ip, port, klass, now, 0, src); | |
233 } | |
234 | |
235 gt_node_cache_trace (); | |
236 } | |
237 | |
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); | |
247 | |
248 buf = tcp_readbuf (c); | |
249 | |
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 } | |
256 | |
257 if (gt_fdbuf_full (buf)) | |
258 { | |
259 gt_node_disconnect (c); | |
260 return; | |
261 } | |
262 | |
263 if (n > 0) | |
264 return; | |
265 | |
266 response = fdbuf_data (buf, &response_len); | |
267 if (!gt_http_header_terminated (response, response_len)) | |
268 return; | |
269 | |
270 fdbuf_release (buf); | |
271 | |
272 if (HANDSHAKE_DEBUG) | |
273 GT->DBGSOCK (GT, c, "node handshake response:\n%s", response); | |
274 | |
275 /* parse and store the response */ | |
276 ok = gnutella_parse_response_headers (response, &node->hdr); | |
277 | |
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); | |
281 | |
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); | |
287 | |
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 } | |
292 | |
293 if (!ok) | |
294 { | |
295 gt_node_disconnect (c); | |
296 return; | |
297 } | |
298 | |
299 input_remove (id); | |
300 input_add (fd, c, INPUT_WRITE, | |
301 (InputCallback)send_response, 0); | |
302 } | |
303 | |
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 } | |
312 | |
313 if (!gnutella_auth_connection (c)) | |
314 { | |
315 gt_node_error (c, "[outgoing] connection not authorized"); | |
316 gt_node_disconnect (c); | |
317 return; | |
318 } | |
319 | |
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 } | |
327 | |
328 /* ok, startup this connection */ | |
329 input_remove (id); | |
330 input_add (fd, c, INPUT_WRITE, | |
331 (InputCallback)gnutella_start_connection, 0); | |
332 } | |
333 | |
334 /*****************************************************************************/ | |
335 | |
336 static GtNode *append_node (TCPC *c, GtNode *node, String *s) | |
337 { | |
338 if (s->str[s->len - 1] != ' ') | |
339 string_append (s, ","); | |
340 | |
341 string_appendf (s, "%s:%hu", net_ip_str (node->ip), node->gt_port); | |
342 return NULL; | |
343 } | |
344 | |
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 } | |
354 | |
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 } | |
364 | |
365 BOOL gnutella_send_connection_headers (TCPC *c, const char *header) | |
366 { | |
367 String *msg; | |
368 | |
369 if (!(msg = string_new (NULL, 0, 0, TRUE))) | |
370 return FALSE; | |
371 | |
372 string_appendf (msg, "%s\r\n", header); | |
373 | |
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"); | |
377 | |
378 /* append the client and version we are using */ | |
379 string_appendf (msg, "User-Agent: %s\r\n", gt_version ()); | |
380 | |
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)); | |
383 | |
384 /* let remote end know it's ok to send vendor messages */ | |
385 string_appendf (msg, "Vendor-Message: 0.1\r\n"); | |
386 | |
387 /* support transmission of pings/pongs with GGEP appended */ | |
388 string_append (msg, "GGEP: 0.5\r\n"); | |
389 | |
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); | |
394 | |
395 /* append willingness to receive compressed data */ | |
396 string_append (msg, "Accept-Encoding: deflate\r\n"); | |
397 | |
398 /* check whether the remote node sent us Accept-Encoding: deflate | |
399 * already */ | |
400 gnutella_mark_compression (GT_NODE(c)); | |
401 | |
402 /* compress data if we must */ | |
403 if (GT_NODE(c)->tx_deflated) | |
404 string_append (msg, "Content-Encoding: deflate\r\n"); | |
405 | |
406 /* Add message terminator */ | |
407 string_append (msg, "\r\n"); | |
408 | |
409 if (HANDSHAKE_DEBUG) | |
410 GT->DBGSOCK (GT, c, "sending node headers:\n%s", msg->str); | |
411 | |
412 if (tcp_send (c, msg->str, msg->len) <= 0) | |
413 { | |
414 string_free (msg); | |
415 return FALSE; | |
416 } | |
417 | |
418 string_free (msg); | |
419 return TRUE; | |
420 } | |
421 | |
422 static BOOL send_final (TCPC *c) | |
423 { | |
424 String *s; | |
425 int ret; | |
426 int len; | |
427 | |
428 if (!(s = string_new (NULL, 0, 0, TRUE))) | |
429 return FALSE; | |
430 | |
431 /* append header acceptance line */ | |
432 string_append (s, "GNUTELLA/0.6 200 OK\r\n"); | |
433 | |
434 /* mark the connection as complete */ | |
435 gnutella_mark_compression (GT_NODE(c)); | |
436 | |
437 if (GT_NODE(c)->tx_deflated) | |
438 string_append (s, "Content-Encoding: deflate\r\n"); | |
439 | |
440 /* append msg terminator */ | |
441 string_append (s, "\r\n"); | |
442 | |
443 if (HANDSHAKE_DEBUG) | |
444 GT->DBGSOCK (GT, c, "sending final handshake:\n%s", s->str); | |
445 | |
446 len = s->len; | |
447 ret = tcp_send (c, s->str, s->len); | |
448 | |
449 string_free (s); | |
450 | |
451 if (ret != len) | |
452 return FALSE; | |
453 | |
454 return TRUE; | |
455 } | |
456 | |
457 /*****************************************************************************/ | |
458 /* CONNECTABILITY TESTING */ | |
459 | |
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")); | |
464 | |
465 node->firewalled = (success ? FALSE : TRUE); | |
466 node->verified = TRUE; | |
467 | |
468 if (c) | |
469 { | |
470 tcp_close (c); | |
471 node->gt_port_verify = NULL; | |
472 } | |
473 } | |
474 | |
475 static void test_connectable (int fd, input_id id, TCPC *c) | |
476 { | |
477 GtNode *node; | |
478 | |
479 node = c->udata; | |
480 | |
481 if (net_sock_error (c->fd)) | |
482 { | |
483 connect_test_result (node, c, FALSE); | |
484 return; | |
485 } | |
486 | |
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 } | |
494 | |
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; | |
506 | |
507 if (!port) | |
508 { | |
509 node->firewalled = TRUE; | |
510 return; | |
511 } | |
512 | |
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 | |
521 | |
522 if (!node->incoming) | |
523 return; | |
524 | |
525 GT->DBGFN (GT, "starting connect test on %s:%hu", | |
526 net_ip_str (node->ip), port); | |
527 | |
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 } | |
534 | |
535 if (node->gt_port_verify) | |
536 tcp_close (node->gt_port_verify); | |
537 | |
538 /* keep track of this connection */ | |
539 node->gt_port_verify = new_c; | |
540 new_c->udata = node; | |
541 | |
542 input_add (new_c->fd, new_c, INPUT_WRITE, | |
543 (InputCallback)test_connectable, TIMEOUT_DEF); | |
544 } |