Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
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), >_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 } |