Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_accept.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,795 @@ 1.4 +/* 1.5 + * $Id: gt_accept.c,v 1.64 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 + 1.22 +#include "gt_packet.h" 1.23 +#include "gt_utils.h" 1.24 + 1.25 +#include "gt_connect.h" 1.26 + 1.27 +#include "gt_node.h" 1.28 +#include "gt_node_cache.h" 1.29 +#include "gt_netorg.h" 1.30 + 1.31 +#include "gt_xfer_obj.h" 1.32 +#include "gt_xfer.h" 1.33 +#include "gt_http_server.h" 1.34 + 1.35 +#include "gt_accept.h" 1.36 +#include "gt_bind.h" 1.37 +#include "gt_ban.h" 1.38 +#include "gt_version.h" 1.39 + 1.40 +#include "message/gt_message.h" /* gnutella_start_connection */ 1.41 + 1.42 +/*****************************************************************************/ 1.43 + 1.44 +#define MAX_FDBUF_SIZE 16384 1.45 + 1.46 +#define INCOMING_TIMEOUT (1 * MINUTES) 1.47 + 1.48 +/*****************************************************************************/ 1.49 +/* Handle incoming connections to this node */ 1.50 + 1.51 +/* 1.52 + * Wrap TCPC and associated timeout for incoming connections in order to limit 1.53 + * the number of incoming connections. 1.54 + */ 1.55 +struct incoming_conn 1.56 +{ 1.57 + TCPC *c; 1.58 + timer_id timer; 1.59 +}; 1.60 + 1.61 +#define GT_METHOD(func) void func (int fd, input_id id, \ 1.62 + struct incoming_conn *conn) 1.63 +typedef void (*GtAcceptFunc) (int fd, input_id id, struct incoming_conn *conn); 1.64 + 1.65 +GT_METHOD(gt_server_accept); 1.66 +GT_METHOD(gt_server_get); 1.67 +GT_METHOD(gt_server_giv); 1.68 + 1.69 +static struct server_cmd 1.70 +{ 1.71 + char *name; 1.72 + GtAcceptFunc callback; 1.73 +} 1.74 +server_commands[] = 1.75 +{ 1.76 + { "GNUTELLA", gt_server_accept }, 1.77 + { "GET", gt_server_get }, 1.78 + { "HEAD", gt_server_get }, 1.79 + { "GIV", gt_server_giv }, 1.80 + { NULL, NULL } 1.81 +}; 1.82 + 1.83 +/*****************************************************************************/ 1.84 + 1.85 +static void send_node_headers (int fd, input_id id, TCPC *c); 1.86 +static void recv_final_handshake (int fd, input_id id, TCPC *c); 1.87 +static void determine_method (int fd, input_id id, 1.88 + struct incoming_conn *conn); 1.89 + 1.90 +/*****************************************************************************/ 1.91 + 1.92 +static void incoming_conn_free (struct incoming_conn *conn) 1.93 +{ 1.94 + timer_remove (conn->timer); 1.95 + free (conn); 1.96 +} 1.97 + 1.98 +static void incoming_conn_close (struct incoming_conn *conn) 1.99 +{ 1.100 + tcp_close (conn->c); 1.101 + incoming_conn_free (conn); 1.102 +} 1.103 + 1.104 +static BOOL conn_timeout (struct incoming_conn *conn) 1.105 +{ 1.106 + incoming_conn_close (conn); 1.107 + return FALSE; 1.108 +} 1.109 + 1.110 +static struct incoming_conn *incoming_conn_alloc (TCPC *c) 1.111 +{ 1.112 + struct incoming_conn *conn; 1.113 + 1.114 + conn = malloc (sizeof (struct incoming_conn)); 1.115 + if (!conn) 1.116 + return NULL; 1.117 + 1.118 + conn->c = c; 1.119 + conn->timer = timer_add (INCOMING_TIMEOUT, (TimerCallback)conn_timeout, 1.120 + conn); 1.121 + 1.122 + return conn; 1.123 +} 1.124 + 1.125 +/*****************************************************************************/ 1.126 + 1.127 +BOOL gt_fdbuf_full (FDBuf *buf) 1.128 +{ 1.129 + size_t len = MAX_FDBUF_SIZE; 1.130 + 1.131 + if (fdbuf_data (buf, &len) == NULL) 1.132 + return TRUE; 1.133 + 1.134 + return len >= MAX_FDBUF_SIZE; 1.135 +} 1.136 + 1.137 +/*****************************************************************************/ 1.138 + 1.139 +/* 1.140 + * Receive an incoming connection. 1.141 + */ 1.142 +void gnutella_handle_incoming (int fd, input_id id, TCPC *listen) 1.143 +{ 1.144 + TCPC *c; 1.145 + 1.146 + if (!(c = tcp_accept (listen, FALSE))) 1.147 + return; 1.148 + 1.149 + if (HANDSHAKE_DEBUG) 1.150 + GT->DBGSOCK (GT, c, "got a new connection"); 1.151 + 1.152 + id = INPUT_NONE; 1.153 + gt_handshake_dispatch_incoming (fd, id, c); 1.154 +} 1.155 + 1.156 +/* 1.157 + * Mark not firewalled if the other end isn't local. 1.158 + */ 1.159 +static void fw_status_update (TCPC *c) 1.160 +{ 1.161 + if (!c->outgoing && !net_match_host (c->host, "LOCAL")) 1.162 + { 1.163 + if (GT_SELF->firewalled) 1.164 + GT->DBGSOCK (GT, c, "connected, clearing firewalled status"); 1.165 + 1.166 + gt_bind_clear_firewalled (); 1.167 + } 1.168 +} 1.169 + 1.170 +/* 1.171 + * Allocate a structure to track this connection and continue the dispatching 1.172 + * process. 1.173 + */ 1.174 +void gt_handshake_dispatch_incoming (int fd, input_id id, TCPC *c) 1.175 +{ 1.176 + struct incoming_conn *conn; 1.177 + in_addr_t peer_ip; 1.178 + 1.179 + /* 1.180 + * This will trigger if the connection was closed on the remote end 1.181 + * or if there's a input timer setup to timeout this connection. In the 1.182 + * latter case, we have to avoid adding any inputs for the connection. 1.183 + */ 1.184 + if (net_sock_error (c->fd)) 1.185 + { 1.186 + tcp_close (c); 1.187 + return; 1.188 + } 1.189 + 1.190 + peer_ip = net_peer (c->fd); 1.191 + 1.192 + /* 1.193 + * While this might be nice for the banner, it's not nice to the ban-ee, 1.194 + * and I'm sure there's some bugs in the banning code, so we should 1.195 + * send back an error instead. 1.196 + */ 1.197 +#if 0 1.198 + if (gt_ban_ipv4_is_banned (peer_ip)) 1.199 + { 1.200 + if (HANDSHAKE_DEBUG) 1.201 + GT->DBGSOCK (GT, c, "not accepting connection [address banned]"); 1.202 + 1.203 + tcp_close (c); 1.204 + return; 1.205 + } 1.206 +#endif 1.207 + 1.208 + /* 1.209 + * If there are too many HTTP connections for this IP, close the 1.210 + * connection now before investing any more resources servicing it. 1.211 + */ 1.212 + if (gt_http_connection_length (GT_TRANSFER_UPLOAD, peer_ip) >= 1.213 + HTTP_MAX_PERUSER_UPLOAD_CONNS) 1.214 + { 1.215 + if (HTTP_DEBUG) 1.216 + GT->DBGSOCK (GT, c, "too many connections for this user, closing"); 1.217 + 1.218 + tcp_close (c); 1.219 + return; 1.220 + } 1.221 + 1.222 + /* local hosts_allow may need to be evaluated to keep outside sources 1.223 + * away */ 1.224 + if (GNUTELLA_LOCAL_MODE) 1.225 + { 1.226 + if (!net_match_host (peer_ip, GNUTELLA_LOCAL_ALLOW)) 1.227 + { 1.228 + if (HANDSHAKE_DEBUG) 1.229 + GT->DBGSOCK (GT, c, "non-local connection, closing"); 1.230 + 1.231 + tcp_close (c); 1.232 + return; 1.233 + } 1.234 + } 1.235 + 1.236 + if (!(conn = incoming_conn_alloc (c))) 1.237 + { 1.238 + tcp_close (c); 1.239 + return; 1.240 + } 1.241 + 1.242 + input_remove (id); 1.243 + input_add (c->fd, conn, INPUT_READ, 1.244 + (InputCallback)determine_method, 0); 1.245 +} 1.246 + 1.247 +/* 1.248 + * Dispatch incoming connections to the proper subsystem. 1.249 + */ 1.250 +static void determine_method (int fd, input_id id, struct incoming_conn *conn) 1.251 +{ 1.252 + struct server_cmd *command; 1.253 + FDBuf *fdbuf; 1.254 + int ret; 1.255 + char *request; 1.256 + TCPC *c = conn->c; 1.257 + 1.258 + fdbuf = tcp_readbuf (c); 1.259 + 1.260 + if ((ret = fdbuf_delim (fdbuf, "\n")) < 0) 1.261 + { 1.262 + if (HANDSHAKE_DEBUG) 1.263 + GT->DBGSOCK (GT, c, "error: %s", GIFT_NETERROR ()); 1.264 + 1.265 + incoming_conn_close (conn); 1.266 + return; 1.267 + } 1.268 + 1.269 + /* some data was read: update fw status */ 1.270 + fw_status_update (c); 1.271 + 1.272 + if (gt_fdbuf_full (fdbuf)) 1.273 + { 1.274 + incoming_conn_close (conn); 1.275 + return; 1.276 + } 1.277 + 1.278 + if (ret > 0) 1.279 + return; 1.280 + 1.281 + /* 1.282 + * NOTE: fdbuf_release() is not called here, so the first line is left on 1.283 + * the FDBuf. 1.284 + */ 1.285 + request = fdbuf_data (fdbuf, NULL); 1.286 + 1.287 + for (command = server_commands; command->name != NULL; command++) 1.288 + { 1.289 + if (!strncasecmp (command->name, request, strlen (command->name))) 1.290 + { 1.291 + input_remove (id); 1.292 + input_add (fd, conn, INPUT_READ, 1.293 + (InputCallback)command->callback, 0); 1.294 + 1.295 + return; 1.296 + } 1.297 + } 1.298 + 1.299 + if (HANDSHAKE_DEBUG) 1.300 + GT->DBGFN (GT, "bad command: %s", request); 1.301 + 1.302 + incoming_conn_close (conn); 1.303 +} 1.304 + 1.305 +/*****************************************************************************/ 1.306 + 1.307 +/* 1.308 + * Main connection acceptance routine. This begins the connection's 1.309 + * journey through the rest of the handshaking code. 1.310 + */ 1.311 +GT_METHOD(gt_server_accept) 1.312 +{ 1.313 + char *request; 1.314 + char *version_str; 1.315 + size_t request_len = 0; 1.316 + int n; 1.317 + GtNode *node; 1.318 + TCPC *c = conn->c; 1.319 + FDBuf *buf; 1.320 + 1.321 + if (HANDSHAKE_DEBUG) 1.322 + GT->DBGFN (GT, "entered"); 1.323 + 1.324 + buf = tcp_readbuf (c); 1.325 + 1.326 + if ((n = fdbuf_delim (buf, "\n")) < 0) 1.327 + { 1.328 + if (HANDSHAKE_DEBUG) 1.329 + GT->DBGSOCK (GT, c, "error on recv: %s", GIFT_NETERROR ()); 1.330 + 1.331 + incoming_conn_close (conn); 1.332 + return; 1.333 + } 1.334 + 1.335 + if (gt_fdbuf_full (buf)) 1.336 + { 1.337 + incoming_conn_close (conn); 1.338 + return; 1.339 + } 1.340 + 1.341 + if (n > 0) 1.342 + return; 1.343 + 1.344 + request = fdbuf_data (buf, &request_len); 1.345 + 1.346 + if (!gt_http_header_terminated (request, request_len)) 1.347 + return; 1.348 + 1.349 + fdbuf_release (buf); 1.350 + 1.351 + if (HANDSHAKE_DEBUG) 1.352 + GT->DBGSOCK (GT, c, "accepted headers:\n%s", request); 1.353 + 1.354 + version_str = strchr (request, '/'); 1.355 + if (version_str) 1.356 + version_str++; 1.357 + 1.358 + if (strncasecmp ("GNUTELLA CONNECT/", request, 1.359 + sizeof ("GNUTELLA CONNECT/") - 1) != 0) 1.360 + { 1.361 + if (HANDSHAKE_DEBUG) 1.362 + GT->DBGSOCK (GT, c, "bad handshake header"); 1.363 + 1.364 + incoming_conn_close (conn); 1.365 + return; 1.366 + } 1.367 + 1.368 + /* deny 0.4 connections */ 1.369 + if (!version_str || strncasecmp (version_str, "0.4", 3) == 0) 1.370 + { 1.371 + if (HANDSHAKE_DEBUG) 1.372 + GT->DBGSOCK (GT, c, "closing 0.4 connection"); 1.373 + 1.374 + incoming_conn_close (conn); 1.375 + return; 1.376 + } 1.377 + 1.378 + /* make a node out of this connection */ 1.379 + if (!(node = gt_node_instantiate (c))) 1.380 + { 1.381 + if (HANDSHAKE_DEBUG) 1.382 + GT->DBGFN (GT, "node_instantiate failed"); 1.383 + 1.384 + incoming_conn_close (conn); 1.385 + return; 1.386 + } 1.387 + 1.388 + /* 1.389 + * Update the start_connect_time so the node will stick around for a while 1.390 + * even when the node list gets trimmed. 1.391 + */ 1.392 + node->start_connect_time = time (NULL); 1.393 + 1.394 + gt_node_state_set (node, GT_NODE_CONNECTING_1); 1.395 + node->incoming = TRUE; 1.396 + 1.397 + /* 1.398 + * Store the http header. 1.399 + * 1.400 + * NOTE: We don't check the return code here. This function expects a 1.401 + * properly formatted HTTP response, but we are handing it a 1.402 + * Gnutella connection request instead, so it will return failure. 1.403 + */ 1.404 + gnutella_parse_response_headers (request, &node->hdr); 1.405 + 1.406 + /* 1.407 + * Use the node handshake timeout timer now, and get rid of the 1.408 + * generic incoming connection timeout timer. 1.409 + */ 1.410 + gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS); 1.411 + incoming_conn_free (conn); 1.412 + 1.413 + input_remove (id); 1.414 + input_add (fd, c, INPUT_WRITE, 1.415 + (InputCallback)send_node_headers, TIMEOUT_DEF); 1.416 +} 1.417 + 1.418 +GT_METHOD(gt_server_giv) 1.419 +{ 1.420 + FDBuf *buf; 1.421 + int n; 1.422 + in_addr_t peer_ip; 1.423 + char *response; 1.424 + size_t response_len = 0; 1.425 + char *client_id; 1.426 + gt_guid_t *guid; 1.427 + TCPC *c = conn->c; 1.428 + 1.429 + if (HTTP_DEBUG || HANDSHAKE_DEBUG) 1.430 + GT->DBGFN (GT, "entered"); 1.431 + 1.432 + buf = tcp_readbuf (c); 1.433 + 1.434 + if ((n = fdbuf_delim (buf, "\n")) < 0) 1.435 + { 1.436 + incoming_conn_close (conn); 1.437 + return; 1.438 + } 1.439 + 1.440 + if (gt_fdbuf_full (buf)) 1.441 + { 1.442 + incoming_conn_close (conn); 1.443 + return; 1.444 + } 1.445 + 1.446 + if (n > 0) 1.447 + return; 1.448 + 1.449 + response = fdbuf_data (buf, &response_len); 1.450 + 1.451 + if (!gt_http_header_terminated (response, response_len)) 1.452 + return; 1.453 + 1.454 + fdbuf_release (buf); 1.455 + 1.456 + if (HTTP_DEBUG || HANDSHAKE_DEBUG) 1.457 + GT->DBGSOCK (GT, c, "giv response=%s", response); 1.458 + 1.459 + string_sep (&response, " "); /* Skip "GIV " */ 1.460 + string_sep (&response, ":"); /* Skip the file index */ 1.461 + 1.462 + client_id = string_sep (&response, "/"); 1.463 + string_lower (client_id); 1.464 + 1.465 + if (!(guid = gt_guid_bin (client_id))) 1.466 + { 1.467 + incoming_conn_close (conn); 1.468 + return; 1.469 + } 1.470 + 1.471 + peer_ip = net_peer (c->fd); 1.472 + 1.473 + /* 1.474 + * This will continue the GtTransfer if one is found, and store 1.475 + * the connection if one is not. The connection will be resumed 1.476 + * when a chunk for this source is reissued. 1.477 + */ 1.478 + gt_push_source_add_conn (guid, peer_ip, c); 1.479 + 1.480 + incoming_conn_free (conn); 1.481 + free (guid); 1.482 +} 1.483 + 1.484 +/*****************************************************************************/ 1.485 + 1.486 +GT_METHOD(gt_server_get) 1.487 +{ 1.488 + if (HTTP_DEBUG) 1.489 + GT->DBGFN (GT, "entered"); 1.490 + 1.491 + /* dispatch to the HTTP server code */ 1.492 + gt_http_server_dispatch (fd, id, conn->c); 1.493 + 1.494 + /* get rid of the tracking information for the connection in this 1.495 + * subsystem */ 1.496 + incoming_conn_free (conn); 1.497 +} 1.498 + 1.499 +/*****************************************************************************/ 1.500 +/* GNUTELLA/0.6 CONNECTIONS */ 1.501 + 1.502 +BOOL gt_http_header_terminated (char *data, size_t len) 1.503 +{ 1.504 + int cnt; 1.505 + 1.506 + assert (len > 0); 1.507 + len--; 1.508 + 1.509 + for (cnt = 0; len > 0 && cnt < 2; cnt++) 1.510 + { 1.511 + if (data[len--] != '\n') 1.512 + break; 1.513 + 1.514 + /* treat CRLF as LF */ 1.515 + if (data[len] == '\r') 1.516 + len--; 1.517 + } 1.518 + 1.519 + return (cnt == 2); 1.520 +} 1.521 + 1.522 +/* TODO: header continuation and joining of multiple occurrences */ 1.523 +void gt_http_header_parse (char *headers, Dataset **d) 1.524 +{ 1.525 + char *line, *key; 1.526 + 1.527 + while ((line = string_sep_set (&headers, "\r\n"))) 1.528 + { 1.529 + key = string_sep (&line, ":"); 1.530 + 1.531 + if (!key || !line) 1.532 + continue; 1.533 + 1.534 + string_trim (key); 1.535 + string_trim (line); 1.536 + 1.537 + /* dont allow empty key-values, need to check this too */ 1.538 + if (string_isempty (line)) 1.539 + continue; 1.540 + 1.541 + dataset_insertstr (d, string_lower (key), line); 1.542 + } 1.543 +} 1.544 + 1.545 +static void send_nodes (struct cached_node *node, String *s) 1.546 +{ 1.547 + if (s->str[s->len - 1] != ':') 1.548 + string_append (s, ","); 1.549 + else 1.550 + string_append (s, " "); 1.551 + 1.552 + string_appendf (s, "%s:%hu", net_ip_str (node->addr.ip), node->addr.port); 1.553 +} 1.554 + 1.555 +static void deny_connection (TCPC *c, int code, char *msg) 1.556 +{ 1.557 + String *s; 1.558 + List *nodes; 1.559 + in_addr_t ip; 1.560 + 1.561 + if (!(s = string_new (NULL, 0, 0, TRUE))) 1.562 + return; 1.563 + 1.564 + string_appendf (s, "GNUTELLA/0.6 %d %s\r\n", code, msg); 1.565 + string_appendf (s, "User-Agent: %s\r\n", gt_version()); 1.566 + 1.567 + ip = net_peer (c->fd); 1.568 + if (!gt_is_local_ip (ip, 0)) 1.569 + string_appendf (s, "Remote-IP: %s\r\n", net_ip_str (ip)); 1.570 + 1.571 + /* append some different nodes to try */ 1.572 + nodes = gt_node_cache_get (10); 1.573 + 1.574 + if (nodes) 1.575 + { 1.576 + string_append (s, "X-Try-Ultrapeers:"); 1.577 + 1.578 + list_foreach (nodes, (ListForeachFunc)send_nodes, s); 1.579 + list_free (nodes); 1.580 + 1.581 + string_append (s, "\r\n"); 1.582 + } 1.583 + 1.584 + /* append message terminator */ 1.585 + string_append (s, "\r\n"); 1.586 + 1.587 + /* we will be closing the connection after this, so we don't 1.588 + * care if the send fails or not. */ 1.589 + tcp_send (c, s->str, s->len); 1.590 + 1.591 + if (HANDSHAKE_DEBUG) 1.592 + GT->DBGSOCK (GT, c, "connection denied response:\n%s", s->str); 1.593 + 1.594 + string_free (s); 1.595 +} 1.596 + 1.597 +static void setup_node_class (GtNode *node) 1.598 +{ 1.599 + char *ultrapeer; 1.600 + char *qrp; 1.601 + 1.602 + ultrapeer = dataset_lookupstr (node->hdr, "x-ultrapeer"); 1.603 + qrp = dataset_lookupstr (node->hdr, "x-query-routing"); 1.604 + 1.605 + if (ultrapeer && !strcasecmp (ultrapeer, "true") && qrp != NULL) 1.606 + gt_node_class_set (node, GT_NODE_ULTRA); 1.607 + else 1.608 + gt_node_class_set (node, GT_NODE_LEAF); 1.609 +} 1.610 + 1.611 +BOOL gnutella_auth_connection (TCPC *c) 1.612 +{ 1.613 + GtNode *node; 1.614 + 1.615 + node = GT_NODE(c); 1.616 + assert (GT_NODE(c) == node && GT_CONN(node) == c); 1.617 + 1.618 + /* set the class of this node based on the headers sent */ 1.619 + setup_node_class (node); 1.620 + 1.621 + /* 1.622 + * If the remote node is only crawling us, accept the connection 1.623 + * no matter what. 1.624 + */ 1.625 + if (dataset_lookupstr (node->hdr, "crawler")) 1.626 + return TRUE; 1.627 + 1.628 + /* 1.629 + * If we are a leaf node, and so is this node, deny the connection, 1.630 + * but send 'X-Try-Ultrapeer:' headers with the ultrapeers we 1.631 + * are connected to in order to try to bootstrap the remote client. 1.632 + */ 1.633 + if (!(GT_SELF->klass & GT_NODE_ULTRA) && (node->klass & GT_NODE_LEAF)) 1.634 + { 1.635 + deny_connection (c, 503, "I am a shielded leaf node"); 1.636 + return FALSE; 1.637 + } 1.638 + 1.639 + if (gt_conn_need_connections (node->klass) <= 0) 1.640 + { 1.641 + deny_connection (c, 503, "Too many connections"); 1.642 + return FALSE; 1.643 + } 1.644 + 1.645 + if (gt_ban_ipv4_is_banned (node->ip)) 1.646 + { 1.647 + deny_connection (c, 403, "Unauthorized"); 1.648 + return FALSE; 1.649 + } 1.650 + 1.651 + return TRUE; 1.652 +} 1.653 + 1.654 +static void send_node_headers (int fd, input_id id, TCPC *c) 1.655 +{ 1.656 + if (net_sock_error (c->fd)) 1.657 + { 1.658 + gt_node_error (c, NULL); 1.659 + gt_node_disconnect (c); 1.660 + return; 1.661 + } 1.662 + 1.663 + if (!gnutella_auth_connection (c)) 1.664 + { 1.665 + gt_node_error (c, "[incoming] connection not authorized"); 1.666 + gt_node_disconnect (c); 1.667 + return; 1.668 + } 1.669 + 1.670 + /* send OK response to the peer and send headers for this node also */ 1.671 + if (!gnutella_send_connection_headers (c, "GNUTELLA/0.6 200 OK")) 1.672 + { 1.673 + gt_node_error (c, NULL); 1.674 + gt_node_disconnect (c); 1.675 + return; 1.676 + } 1.677 + 1.678 + /* reset the timeout for this connection */ 1.679 + gnutella_set_handshake_timeout (c, TIMEOUT_3 * SECONDS); 1.680 + 1.681 + input_remove (id); 1.682 + input_add (fd, c, INPUT_READ, 1.683 + (InputCallback)recv_final_handshake, 0); 1.684 +} 1.685 + 1.686 +/* TODO: this should be abstracted */ 1.687 +static char *field_has_value (Dataset *d, const char *key, const char *val) 1.688 +{ 1.689 + char *value; 1.690 + char *str; 1.691 + 1.692 + if (!(value = dataset_lookupstr (d, key))) 1.693 + return NULL; 1.694 + 1.695 + if ((str = strstr (value, val))) 1.696 + return str; 1.697 + 1.698 + return NULL; 1.699 +} 1.700 + 1.701 +static BOOL need_inflate (GtNode *node) 1.702 +{ 1.703 + if (!(field_has_value (node->hdr, "content-encoding", "deflate"))) 1.704 + return FALSE; 1.705 + 1.706 + return TRUE; 1.707 +} 1.708 + 1.709 +static BOOL need_deflate (GtNode *node) 1.710 +{ 1.711 + if (!(field_has_value (node->hdr, "accept-encoding", "deflate"))) 1.712 + return FALSE; 1.713 + 1.714 + return TRUE; 1.715 +} 1.716 + 1.717 +void gnutella_mark_compression (GtNode *node) 1.718 +{ 1.719 + if (need_inflate (node)) 1.720 + node->rx_inflated = TRUE; 1.721 + 1.722 + if (need_deflate (node)) 1.723 + node->tx_deflated = TRUE; 1.724 +} 1.725 + 1.726 +static void add_key (ds_data_t *key, ds_data_t *value, Dataset **d) 1.727 +{ 1.728 + char *hdr = key->data; 1.729 + char *val = value->data; 1.730 + 1.731 + dataset_insertstr (d, hdr, val); 1.732 +} 1.733 + 1.734 +static void recv_final_handshake (int fd, input_id id, TCPC *c) 1.735 +{ 1.736 + FDBuf *buf; 1.737 + int n; 1.738 + char *response; 1.739 + size_t response_len = 0; 1.740 + Dataset *additional = NULL; 1.741 + 1.742 + buf = tcp_readbuf (c); 1.743 + 1.744 + if ((n = fdbuf_delim (buf, "\n")) < 0) 1.745 + { 1.746 + if (HANDSHAKE_DEBUG) 1.747 + GT->DBGSOCK (GT, c, "fdbuf_delim: error %s", GIFT_NETERROR ()); 1.748 + 1.749 + gt_node_disconnect (c); 1.750 + return; 1.751 + } 1.752 + 1.753 + if (gt_fdbuf_full (buf)) 1.754 + { 1.755 + gt_node_disconnect (c); 1.756 + return; 1.757 + } 1.758 + 1.759 + if (n > 0) 1.760 + return; 1.761 + 1.762 + response = fdbuf_data (buf, &response_len); 1.763 + 1.764 + if (!gt_http_header_terminated (response, response_len)) 1.765 + return; 1.766 + 1.767 + fdbuf_release (buf); 1.768 + 1.769 + if (HANDSHAKE_DEBUG) 1.770 + GT->DBGSOCK (GT, c, "stage3 response:\n%s", response); 1.771 + 1.772 + if (!gnutella_parse_response_headers (response, &additional)) 1.773 + { 1.774 + if (HANDSHAKE_DEBUG) 1.775 + gt_node_error (c, "node denied us in stage3 of handshake"); 1.776 + 1.777 + gt_node_disconnect (c); 1.778 + dataset_clear (additional); 1.779 + return; 1.780 + } 1.781 + 1.782 + /* 1.783 + * Append or replace the fields the node sent into the header for this 1.784 + * node. 1.785 + * 1.786 + * TODO: should probably change the interface to parse_response_headers so 1.787 + * we can just pass in the header list of the node. 1.788 + */ 1.789 + dataset_foreach (additional, DS_FOREACH(add_key), >_NODE(c)->hdr); 1.790 + dataset_clear (additional); 1.791 + 1.792 + /* mark the compression flags on this GtNode */ 1.793 + gnutella_mark_compression (GT_NODE(c)); 1.794 + 1.795 + input_remove (id); 1.796 + input_add (fd, c, INPUT_WRITE, 1.797 + (InputCallback)gnutella_start_connection, TIMEOUT_DEF); 1.798 +}