Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_connect.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,544 @@ 1.4 +/* 1.5 + * $Id: gt_connect.c,v 1.55 2005/01/04 15:03:40 mkern Exp $ 1.6 + * 1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 +#include "gt_version.h" 1.22 + 1.23 +#include "gt_connect.h" 1.24 +#include "gt_accept.h" 1.25 +#include "gt_packet.h" 1.26 + 1.27 +#include "gt_node.h" 1.28 +#include "gt_node_list.h" 1.29 +#include "gt_utils.h" 1.30 + 1.31 +#include "gt_search.h" 1.32 +#include "gt_netorg.h" 1.33 + 1.34 +#include "gt_node_cache.h" 1.35 + 1.36 +#include "message/gt_message.h" /* gnutella_start_connection */ 1.37 + 1.38 +/*****************************************************************************/ 1.39 + 1.40 +static void send_connect (int fd, input_id id, TCPC *c); 1.41 +static void recv_headers (int fd, input_id id, TCPC *c); 1.42 +static void send_response (int fd, input_id id, TCPC *c); 1.43 +static BOOL send_final (TCPC *c); 1.44 + 1.45 +/*****************************************************************************/ 1.46 + 1.47 +static BOOL handshake_timeout (TCPC *c) 1.48 +{ 1.49 + GtNode *node = GT_NODE(c); 1.50 + 1.51 + node->handshake_timer = 0; 1.52 + 1.53 + if (!(node->state & GT_NODE_CONNECTED)) 1.54 + { 1.55 + gt_node_disconnect (c); 1.56 + return FALSE; 1.57 + } 1.58 + 1.59 + return FALSE; 1.60 +} 1.61 + 1.62 +void gnutella_set_handshake_timeout (TCPC *c, time_t delay) 1.63 +{ 1.64 + timer_remove (GT_NODE(c)->handshake_timer); 1.65 + 1.66 + GT_NODE(c)->handshake_timer = timer_add (delay, 1.67 + (TimerCallback)handshake_timeout, 1.68 + c); 1.69 +} 1.70 + 1.71 +int gt_connect (GtNode *node) 1.72 +{ 1.73 + TCPC *c; 1.74 + 1.75 + if (!node) 1.76 + return -1; 1.77 + 1.78 +#if 0 1.79 + if (GT_CONN(node) != NULL) 1.80 + { 1.81 + GT->dbg (GT, "duplicate connection?: %p", GT_CONN(node)); 1.82 + return -1; 1.83 + } 1.84 + 1.85 + if (node->state != GT_NODE_DISCONNECTED) 1.86 + { 1.87 + GT->dbg (GT, "state = %i??", node->state); 1.88 + return -1; 1.89 + } 1.90 +#endif 1.91 + 1.92 + /* this must be called only on disconnected nodes */ 1.93 + assert (GT_CONN(node) == NULL); 1.94 + assert (node->state == GT_NODE_DISCONNECTED); 1.95 + 1.96 +#if 0 1.97 + if (!conn_auth (c, TRUE)) 1.98 + return -1; 1.99 +#endif 1.100 + 1.101 + /* set this early: gt_netorg relies on this being set in order 1.102 + * to check if it should access the gwebcaches */ 1.103 + node->start_connect_time = time (NULL); 1.104 + 1.105 + /* make sure port is valid */ 1.106 + if (node->gt_port == 0) 1.107 + { 1.108 + GT->DBGFN (GT, "bad port on node %s", net_ip_str (node->ip)); 1.109 + return -1; 1.110 + } 1.111 + 1.112 + /* make outgoing connection */ 1.113 + if (!(c = tcp_open (node->ip, node->gt_port, FALSE))) 1.114 + return -1; 1.115 + 1.116 + gt_node_connect (node, c); 1.117 + 1.118 + gt_node_state_set (node, GT_NODE_CONNECTING_1); 1.119 + node->incoming = FALSE; 1.120 + 1.121 + /* set the connection timeout */ 1.122 + gnutella_set_handshake_timeout (c, TIMEOUT_1 * SECONDS); 1.123 + 1.124 + input_add (c->fd, c, INPUT_WRITE, 1.125 + (InputCallback)send_connect, 0); 1.126 + 1.127 + return c->fd; 1.128 +} 1.129 + 1.130 +static void send_connect (int fd, input_id id, TCPC *c) 1.131 +{ 1.132 + if (net_sock_error (c->fd)) 1.133 + { 1.134 + gt_node_disconnect (c); 1.135 + return; 1.136 + } 1.137 + 1.138 + /* Send the connect string along with our headers */ 1.139 + if (!gnutella_send_connection_headers (c, "GNUTELLA CONNECT/0.6")) 1.140 + { 1.141 + gt_node_error (c, NULL); 1.142 + gt_node_disconnect (c); 1.143 + return; 1.144 + } 1.145 + 1.146 + /* we connected ok, so give the peer some more time */ 1.147 + gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS); 1.148 + 1.149 + input_remove (id); 1.150 + input_add (fd, c, INPUT_READ, 1.151 + (InputCallback)recv_headers, 0); 1.152 +} 1.153 + 1.154 +BOOL gnutella_parse_response_headers (char *reply, Dataset **r_headers) 1.155 +{ 1.156 + int code; /* 200, 404, ... */ 1.157 + char *response; 1.158 + Dataset *headers = NULL; 1.159 + 1.160 + response = string_sep (&reply, "\r\n"); 1.161 + 1.162 + if (!response) 1.163 + return FALSE; 1.164 + 1.165 + /* */ string_sep (&response, " "); /* shift past HTTP/1.1 */ 1.166 + code = ATOI (string_sep (&response, " ")); /* shift past 200 */ 1.167 + 1.168 + /* parse the headers */ 1.169 + gt_http_header_parse (reply, &headers); 1.170 + 1.171 + if (r_headers) 1.172 + *r_headers = headers; 1.173 + else 1.174 + dataset_clear (headers); 1.175 + 1.176 + if (code >= 200 && code <= 299) 1.177 + return TRUE; 1.178 + 1.179 + return FALSE; 1.180 +} 1.181 + 1.182 +static time_t parse_uptime (Dataset *d) 1.183 +{ 1.184 + char *str; 1.185 + int days, hours, mins; 1.186 + int n; 1.187 + 1.188 + if (!(str = dataset_lookupstr (d, "uptime"))) 1.189 + return 0; 1.190 + 1.191 + string_lower (str); 1.192 + 1.193 + if ((n = sscanf (str, "%dd %dh %dm", &days, &hours, &mins)) != 3) 1.194 + return 0; 1.195 + 1.196 + if (HANDSHAKE_DEBUG) 1.197 + { 1.198 + GT->dbg (GT, "uptime parsed: %d days, %d hours, %d minutes", 1.199 + days, hours, mins); 1.200 + } 1.201 + 1.202 + return days * EDAYS + hours * EHOURS + mins * EMINUTES; 1.203 +} 1.204 + 1.205 +/* look in a header field for nodes, and register them */ 1.206 +static void extract_nodes (Dataset *d, in_addr_t src, 1.207 + const char *field, gt_node_class_t klass) 1.208 +{ 1.209 + char *str; 1.210 + char *value; 1.211 + time_t now; 1.212 + 1.213 + now = time (NULL); 1.214 + 1.215 + if (!(str = dataset_lookupstr (d, field))) 1.216 + return; 1.217 + 1.218 + while ((value = string_sep (&str, ","))) 1.219 + { 1.220 + in_addr_t ip; 1.221 + in_port_t port; 1.222 + 1.223 + ip = net_ip (string_sep (&value, ":")); 1.224 + port = ATOI (value); 1.225 + 1.226 + if (port == (in_port_t) -1 || port == 0) 1.227 + continue; 1.228 + 1.229 + if (ip == INADDR_NONE || ip == 0) 1.230 + continue; 1.231 + 1.232 + if (gt_is_local_ip (ip, src)) 1.233 + continue; 1.234 + 1.235 + gt_node_cache_add_ipv4 (ip, port, klass, now, 0, src); 1.236 + } 1.237 + 1.238 + gt_node_cache_trace (); 1.239 +} 1.240 + 1.241 +static void recv_headers (int fd, input_id id, TCPC *c) 1.242 +{ 1.243 + FDBuf *buf; 1.244 + char *response; 1.245 + size_t response_len = 0; 1.246 + int n; 1.247 + BOOL ok; 1.248 + time_t uptime; 1.249 + GtNode *node = GT_NODE(c); 1.250 + 1.251 + buf = tcp_readbuf (c); 1.252 + 1.253 + if ((n = fdbuf_delim (buf, "\n")) < 0) 1.254 + { 1.255 + GT->DBGFN (GT, "error reading headers: %s", GIFT_NETERROR ()); 1.256 + gt_node_disconnect (c); 1.257 + return; 1.258 + } 1.259 + 1.260 + if (gt_fdbuf_full (buf)) 1.261 + { 1.262 + gt_node_disconnect (c); 1.263 + return; 1.264 + } 1.265 + 1.266 + if (n > 0) 1.267 + return; 1.268 + 1.269 + response = fdbuf_data (buf, &response_len); 1.270 + if (!gt_http_header_terminated (response, response_len)) 1.271 + return; 1.272 + 1.273 + fdbuf_release (buf); 1.274 + 1.275 + if (HANDSHAKE_DEBUG) 1.276 + GT->DBGSOCK (GT, c, "node handshake response:\n%s", response); 1.277 + 1.278 + /* parse and store the response */ 1.279 + ok = gnutella_parse_response_headers (response, &node->hdr); 1.280 + 1.281 + /* extract nodes */ 1.282 + extract_nodes (node->hdr, node->ip, "x-try-ultrapeers", GT_NODE_ULTRA); 1.283 + extract_nodes (node->hdr, node->ip, "x-try", GT_NODE_NONE); 1.284 + 1.285 + /* grab the uptime from the "Uptime: " header and update this node */ 1.286 + if ((uptime = parse_uptime (node->hdr)) > 0) 1.287 + { 1.288 + gt_node_cache_add_ipv4 (node->ip, node->gt_port, 1.289 + GT_NODE_ULTRA, time (NULL), uptime, node->ip); 1.290 + 1.291 + /* XXX: remove the item immediately so we trigger the side effect of 1.292 + * adding this node to the stable list */ 1.293 + gt_node_cache_del_ipv4 (node->ip, node->gt_port); 1.294 + } 1.295 + 1.296 + if (!ok) 1.297 + { 1.298 + gt_node_disconnect (c); 1.299 + return; 1.300 + } 1.301 + 1.302 + input_remove (id); 1.303 + input_add (fd, c, INPUT_WRITE, 1.304 + (InputCallback)send_response, 0); 1.305 +} 1.306 + 1.307 +static void send_response (int fd, input_id id, TCPC *c) 1.308 +{ 1.309 + if (net_sock_error (c->fd)) 1.310 + { 1.311 + gt_node_error (c, NULL); 1.312 + gt_node_disconnect (c); 1.313 + return; 1.314 + } 1.315 + 1.316 + if (!gnutella_auth_connection (c)) 1.317 + { 1.318 + gt_node_error (c, "[outgoing] connection not authorized"); 1.319 + gt_node_disconnect (c); 1.320 + return; 1.321 + } 1.322 + 1.323 + if (!send_final (c)) 1.324 + { 1.325 + gt_node_error (c, NULL); 1.326 + GT->DBGSOCK (GT, c, "error at stage 3 of handshake"); 1.327 + gt_node_disconnect (c); 1.328 + return; 1.329 + } 1.330 + 1.331 + /* ok, startup this connection */ 1.332 + input_remove (id); 1.333 + input_add (fd, c, INPUT_WRITE, 1.334 + (InputCallback)gnutella_start_connection, 0); 1.335 +} 1.336 + 1.337 +/*****************************************************************************/ 1.338 + 1.339 +static GtNode *append_node (TCPC *c, GtNode *node, String *s) 1.340 +{ 1.341 + if (s->str[s->len - 1] != ' ') 1.342 + string_append (s, ","); 1.343 + 1.344 + string_appendf (s, "%s:%hu", net_ip_str (node->ip), node->gt_port); 1.345 + return NULL; 1.346 +} 1.347 + 1.348 +static void append_crawler_headers (String *msg) 1.349 +{ 1.350 + if (gt_conn_length (GT_NODE_ULTRA, GT_NODE_CONNECTED) > 0) 1.351 + { 1.352 + string_append (msg, "Peers: "); 1.353 + gt_conn_foreach (GT_CONN_FOREACH(append_node), msg, 1.354 + GT_NODE_ULTRA, GT_NODE_CONNECTED, 0); 1.355 + string_append (msg, "\r\n"); 1.356 + } 1.357 + 1.358 + if (GT_SELF->klass & GT_NODE_ULTRA && 1.359 + gt_conn_length (GT_NODE_LEAF, GT_NODE_CONNECTED) > 0) 1.360 + { 1.361 + string_append (msg, "Leaves: "); 1.362 + gt_conn_foreach (GT_CONN_FOREACH(append_node), msg, 1.363 + GT_NODE_LEAF, GT_NODE_CONNECTED, 0); 1.364 + string_append (msg, "\r\n"); 1.365 + } 1.366 +} 1.367 + 1.368 +BOOL gnutella_send_connection_headers (TCPC *c, const char *header) 1.369 +{ 1.370 + String *msg; 1.371 + 1.372 + if (!(msg = string_new (NULL, 0, 0, TRUE))) 1.373 + return FALSE; 1.374 + 1.375 + string_appendf (msg, "%s\r\n", header); 1.376 + 1.377 + string_append (msg, "X-Query-Routing: 0.1\r\n"); 1.378 + string_appendf (msg, "X-Ultrapeer: %s\r\n", 1.379 + (GT_SELF->klass & GT_NODE_ULTRA) ? "True" : "False"); 1.380 + 1.381 + /* append the client and version we are using */ 1.382 + string_appendf (msg, "User-Agent: %s\r\n", gt_version ()); 1.383 + 1.384 + /* Add a header describing the remote IP of the peer */ 1.385 + string_appendf (msg, "Remote-IP: %s\r\n", net_peer_ip (c->fd)); 1.386 + 1.387 + /* let remote end know it's ok to send vendor messages */ 1.388 + string_appendf (msg, "Vendor-Message: 0.1\r\n"); 1.389 + 1.390 + /* support transmission of pings/pongs with GGEP appended */ 1.391 + string_append (msg, "GGEP: 0.5\r\n"); 1.392 + 1.393 + /* If this is the limewire crawler, append "Peers: " and "Leaves: " 1.394 + * headers and close the connection */ 1.395 + if (!c->outgoing && dataset_lookupstr (GT_NODE(c)->hdr, "crawler")) 1.396 + append_crawler_headers (msg); 1.397 + 1.398 + /* append willingness to receive compressed data */ 1.399 + string_append (msg, "Accept-Encoding: deflate\r\n"); 1.400 + 1.401 + /* check whether the remote node sent us Accept-Encoding: deflate 1.402 + * already */ 1.403 + gnutella_mark_compression (GT_NODE(c)); 1.404 + 1.405 + /* compress data if we must */ 1.406 + if (GT_NODE(c)->tx_deflated) 1.407 + string_append (msg, "Content-Encoding: deflate\r\n"); 1.408 + 1.409 + /* Add message terminator */ 1.410 + string_append (msg, "\r\n"); 1.411 + 1.412 + if (HANDSHAKE_DEBUG) 1.413 + GT->DBGSOCK (GT, c, "sending node headers:\n%s", msg->str); 1.414 + 1.415 + if (tcp_send (c, msg->str, msg->len) <= 0) 1.416 + { 1.417 + string_free (msg); 1.418 + return FALSE; 1.419 + } 1.420 + 1.421 + string_free (msg); 1.422 + return TRUE; 1.423 +} 1.424 + 1.425 +static BOOL send_final (TCPC *c) 1.426 +{ 1.427 + String *s; 1.428 + int ret; 1.429 + int len; 1.430 + 1.431 + if (!(s = string_new (NULL, 0, 0, TRUE))) 1.432 + return FALSE; 1.433 + 1.434 + /* append header acceptance line */ 1.435 + string_append (s, "GNUTELLA/0.6 200 OK\r\n"); 1.436 + 1.437 + /* mark the connection as complete */ 1.438 + gnutella_mark_compression (GT_NODE(c)); 1.439 + 1.440 + if (GT_NODE(c)->tx_deflated) 1.441 + string_append (s, "Content-Encoding: deflate\r\n"); 1.442 + 1.443 + /* append msg terminator */ 1.444 + string_append (s, "\r\n"); 1.445 + 1.446 + if (HANDSHAKE_DEBUG) 1.447 + GT->DBGSOCK (GT, c, "sending final handshake:\n%s", s->str); 1.448 + 1.449 + len = s->len; 1.450 + ret = tcp_send (c, s->str, s->len); 1.451 + 1.452 + string_free (s); 1.453 + 1.454 + if (ret != len) 1.455 + return FALSE; 1.456 + 1.457 + return TRUE; 1.458 +} 1.459 + 1.460 +/*****************************************************************************/ 1.461 +/* CONNECTABILITY TESTING */ 1.462 + 1.463 +static void connect_test_result (GtNode *node, TCPC *c, BOOL success) 1.464 +{ 1.465 + GT->DBGFN (GT, "connect test to %s %s", net_ip_str (node->ip), 1.466 + (success ? "succeeded" : "failed")); 1.467 + 1.468 + node->firewalled = (success ? FALSE : TRUE); 1.469 + node->verified = TRUE; 1.470 + 1.471 + if (c) 1.472 + { 1.473 + tcp_close (c); 1.474 + node->gt_port_verify = NULL; 1.475 + } 1.476 +} 1.477 + 1.478 +static void test_connectable (int fd, input_id id, TCPC *c) 1.479 +{ 1.480 + GtNode *node; 1.481 + 1.482 + node = c->udata; 1.483 + 1.484 + if (net_sock_error (c->fd)) 1.485 + { 1.486 + connect_test_result (node, c, FALSE); 1.487 + return; 1.488 + } 1.489 + 1.490 + /* 1.491 + * Send two newlines, because some firewalls will let connections pass 1.492 + * through, but no data. 1.493 + */ 1.494 + tcp_send (c, "\n\n", 2); 1.495 + connect_test_result (node, c, TRUE); 1.496 +} 1.497 + 1.498 +/* 1.499 + * Test if the port of a peer we are connected to is connectable. This lets a 1.500 + * node know if it's firewalled. We could use this info to mangle the 'push' 1.501 + * flag on query hits from this node if it is a leaf. 1.502 + * 1.503 + * Mangling query hits would break any future checksum or signing algorithm on 1.504 + * query hits though, so that isn't done. 1.505 + */ 1.506 +void gt_connect_test (GtNode *node, in_port_t port) 1.507 +{ 1.508 + TCPC *new_c; 1.509 + 1.510 + if (!port) 1.511 + { 1.512 + node->firewalled = TRUE; 1.513 + return; 1.514 + } 1.515 + 1.516 + /* this needs some kind of local mode switch */ 1.517 +#if 0 1.518 + if (net_match_host (GT_NODE(c)->ip, "LOCAL")) 1.519 + { 1.520 + GT_NODE(c)->firewalled = TRUE; 1.521 + return; 1.522 + } 1.523 +#endif 1.524 + 1.525 + if (!node->incoming) 1.526 + return; 1.527 + 1.528 + GT->DBGFN (GT, "starting connect test on %s:%hu", 1.529 + net_ip_str (node->ip), port); 1.530 + 1.531 + if (!(new_c = tcp_open (node->ip, port, FALSE))) 1.532 + { 1.533 + GT->DBGFN (GT, "failed to open test connection to %s:%hu", 1.534 + net_ip_str (node->ip), node->gt_port); 1.535 + return; 1.536 + } 1.537 + 1.538 + if (node->gt_port_verify) 1.539 + tcp_close (node->gt_port_verify); 1.540 + 1.541 + /* keep track of this connection */ 1.542 + node->gt_port_verify = new_c; 1.543 + new_c->udata = node; 1.544 + 1.545 + input_add (new_c->fd, new_c, INPUT_WRITE, 1.546 + (InputCallback)test_connectable, TIMEOUT_DEF); 1.547 +}