rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * $Id: gt_accept.c,v 1.64 2005/01/04 15:03:40 mkern Exp $
|
paulo@0
|
3 *
|
paulo@0
|
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
|
paulo@0
|
5 *
|
paulo@0
|
6 * This program is free software; you can redistribute it and/or modify it
|
paulo@0
|
7 * under the terms of the GNU General Public License as published by the
|
paulo@0
|
8 * Free Software Foundation; either version 2, or (at your option) any
|
paulo@0
|
9 * later version.
|
paulo@0
|
10 *
|
paulo@0
|
11 * This program is distributed in the hope that it will be useful, but
|
paulo@0
|
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
paulo@0
|
14 * General Public License for more details.
|
paulo@0
|
15 */
|
paulo@0
|
16
|
paulo@0
|
17 #include "gt_gnutella.h"
|
paulo@0
|
18
|
paulo@0
|
19 #include "gt_packet.h"
|
paulo@0
|
20 #include "gt_utils.h"
|
paulo@0
|
21
|
paulo@0
|
22 #include "gt_connect.h"
|
paulo@0
|
23
|
paulo@0
|
24 #include "gt_node.h"
|
paulo@0
|
25 #include "gt_node_cache.h"
|
paulo@0
|
26 #include "gt_netorg.h"
|
paulo@0
|
27
|
paulo@0
|
28 #include "gt_xfer_obj.h"
|
paulo@0
|
29 #include "gt_xfer.h"
|
paulo@0
|
30 #include "gt_http_server.h"
|
paulo@0
|
31
|
paulo@0
|
32 #include "gt_accept.h"
|
paulo@0
|
33 #include "gt_bind.h"
|
paulo@0
|
34 #include "gt_ban.h"
|
paulo@0
|
35 #include "gt_version.h"
|
paulo@0
|
36
|
paulo@0
|
37 #include "message/gt_message.h" /* gnutella_start_connection */
|
paulo@0
|
38
|
paulo@0
|
39 /*****************************************************************************/
|
paulo@0
|
40
|
paulo@0
|
41 #define MAX_FDBUF_SIZE 16384
|
paulo@0
|
42
|
paulo@0
|
43 #define INCOMING_TIMEOUT (1 * MINUTES)
|
paulo@0
|
44
|
paulo@0
|
45 /*****************************************************************************/
|
paulo@0
|
46 /* Handle incoming connections to this node */
|
paulo@0
|
47
|
paulo@0
|
48 /*
|
paulo@0
|
49 * Wrap TCPC and associated timeout for incoming connections in order to limit
|
paulo@0
|
50 * the number of incoming connections.
|
paulo@0
|
51 */
|
paulo@0
|
52 struct incoming_conn
|
paulo@0
|
53 {
|
paulo@0
|
54 TCPC *c;
|
paulo@0
|
55 timer_id timer;
|
paulo@0
|
56 };
|
paulo@0
|
57
|
paulo@0
|
58 #define GT_METHOD(func) void func (int fd, input_id id, \
|
paulo@0
|
59 struct incoming_conn *conn)
|
paulo@0
|
60 typedef void (*GtAcceptFunc) (int fd, input_id id, struct incoming_conn *conn);
|
paulo@0
|
61
|
paulo@0
|
62 GT_METHOD(gt_server_accept);
|
paulo@0
|
63 GT_METHOD(gt_server_get);
|
paulo@0
|
64 GT_METHOD(gt_server_giv);
|
paulo@0
|
65
|
paulo@0
|
66 static struct server_cmd
|
paulo@0
|
67 {
|
paulo@0
|
68 char *name;
|
paulo@0
|
69 GtAcceptFunc callback;
|
paulo@0
|
70 }
|
paulo@0
|
71 server_commands[] =
|
paulo@0
|
72 {
|
paulo@0
|
73 { "GNUTELLA", gt_server_accept },
|
paulo@0
|
74 { "GET", gt_server_get },
|
paulo@0
|
75 { "HEAD", gt_server_get },
|
paulo@0
|
76 { "GIV", gt_server_giv },
|
paulo@0
|
77 { NULL, NULL }
|
paulo@0
|
78 };
|
paulo@0
|
79
|
paulo@0
|
80 /*****************************************************************************/
|
paulo@0
|
81
|
paulo@0
|
82 static void send_node_headers (int fd, input_id id, TCPC *c);
|
paulo@0
|
83 static void recv_final_handshake (int fd, input_id id, TCPC *c);
|
paulo@0
|
84 static void determine_method (int fd, input_id id,
|
paulo@0
|
85 struct incoming_conn *conn);
|
paulo@0
|
86
|
paulo@0
|
87 /*****************************************************************************/
|
paulo@0
|
88
|
paulo@0
|
89 static void incoming_conn_free (struct incoming_conn *conn)
|
paulo@0
|
90 {
|
paulo@0
|
91 timer_remove (conn->timer);
|
paulo@0
|
92 free (conn);
|
paulo@0
|
93 }
|
paulo@0
|
94
|
paulo@0
|
95 static void incoming_conn_close (struct incoming_conn *conn)
|
paulo@0
|
96 {
|
paulo@0
|
97 tcp_close (conn->c);
|
paulo@0
|
98 incoming_conn_free (conn);
|
paulo@0
|
99 }
|
paulo@0
|
100
|
paulo@0
|
101 static BOOL conn_timeout (struct incoming_conn *conn)
|
paulo@0
|
102 {
|
paulo@0
|
103 incoming_conn_close (conn);
|
paulo@0
|
104 return FALSE;
|
paulo@0
|
105 }
|
paulo@0
|
106
|
paulo@0
|
107 static struct incoming_conn *incoming_conn_alloc (TCPC *c)
|
paulo@0
|
108 {
|
paulo@0
|
109 struct incoming_conn *conn;
|
paulo@0
|
110
|
paulo@0
|
111 conn = malloc (sizeof (struct incoming_conn));
|
paulo@0
|
112 if (!conn)
|
paulo@0
|
113 return NULL;
|
paulo@0
|
114
|
paulo@0
|
115 conn->c = c;
|
paulo@0
|
116 conn->timer = timer_add (INCOMING_TIMEOUT, (TimerCallback)conn_timeout,
|
paulo@0
|
117 conn);
|
paulo@0
|
118
|
paulo@0
|
119 return conn;
|
paulo@0
|
120 }
|
paulo@0
|
121
|
paulo@0
|
122 /*****************************************************************************/
|
paulo@0
|
123
|
paulo@0
|
124 BOOL gt_fdbuf_full (FDBuf *buf)
|
paulo@0
|
125 {
|
paulo@0
|
126 size_t len = MAX_FDBUF_SIZE;
|
paulo@0
|
127
|
paulo@0
|
128 if (fdbuf_data (buf, &len) == NULL)
|
paulo@0
|
129 return TRUE;
|
paulo@0
|
130
|
paulo@0
|
131 return len >= MAX_FDBUF_SIZE;
|
paulo@0
|
132 }
|
paulo@0
|
133
|
paulo@0
|
134 /*****************************************************************************/
|
paulo@0
|
135
|
paulo@0
|
136 /*
|
paulo@0
|
137 * Receive an incoming connection.
|
paulo@0
|
138 */
|
paulo@0
|
139 void gnutella_handle_incoming (int fd, input_id id, TCPC *listen)
|
paulo@0
|
140 {
|
paulo@0
|
141 TCPC *c;
|
paulo@0
|
142
|
paulo@0
|
143 if (!(c = tcp_accept (listen, FALSE)))
|
paulo@0
|
144 return;
|
paulo@0
|
145
|
paulo@0
|
146 if (HANDSHAKE_DEBUG)
|
paulo@0
|
147 GT->DBGSOCK (GT, c, "got a new connection");
|
paulo@0
|
148
|
paulo@0
|
149 id = INPUT_NONE;
|
paulo@0
|
150 gt_handshake_dispatch_incoming (fd, id, c);
|
paulo@0
|
151 }
|
paulo@0
|
152
|
paulo@0
|
153 /*
|
paulo@0
|
154 * Mark not firewalled if the other end isn't local.
|
paulo@0
|
155 */
|
paulo@0
|
156 static void fw_status_update (TCPC *c)
|
paulo@0
|
157 {
|
paulo@0
|
158 if (!c->outgoing && !net_match_host (c->host, "LOCAL"))
|
paulo@0
|
159 {
|
paulo@0
|
160 if (GT_SELF->firewalled)
|
paulo@0
|
161 GT->DBGSOCK (GT, c, "connected, clearing firewalled status");
|
paulo@0
|
162
|
paulo@0
|
163 gt_bind_clear_firewalled ();
|
paulo@0
|
164 }
|
paulo@0
|
165 }
|
paulo@0
|
166
|
paulo@0
|
167 /*
|
paulo@0
|
168 * Allocate a structure to track this connection and continue the dispatching
|
paulo@0
|
169 * process.
|
paulo@0
|
170 */
|
paulo@0
|
171 void gt_handshake_dispatch_incoming (int fd, input_id id, TCPC *c)
|
paulo@0
|
172 {
|
paulo@0
|
173 struct incoming_conn *conn;
|
paulo@0
|
174 in_addr_t peer_ip;
|
paulo@0
|
175
|
paulo@0
|
176 /*
|
paulo@0
|
177 * This will trigger if the connection was closed on the remote end
|
paulo@0
|
178 * or if there's a input timer setup to timeout this connection. In the
|
paulo@0
|
179 * latter case, we have to avoid adding any inputs for the connection.
|
paulo@0
|
180 */
|
paulo@0
|
181 if (net_sock_error (c->fd))
|
paulo@0
|
182 {
|
paulo@0
|
183 tcp_close (c);
|
paulo@0
|
184 return;
|
paulo@0
|
185 }
|
paulo@0
|
186
|
paulo@0
|
187 peer_ip = net_peer (c->fd);
|
paulo@0
|
188
|
paulo@0
|
189 /*
|
paulo@0
|
190 * While this might be nice for the banner, it's not nice to the ban-ee,
|
paulo@0
|
191 * and I'm sure there's some bugs in the banning code, so we should
|
paulo@0
|
192 * send back an error instead.
|
paulo@0
|
193 */
|
paulo@0
|
194 #if 0
|
paulo@0
|
195 if (gt_ban_ipv4_is_banned (peer_ip))
|
paulo@0
|
196 {
|
paulo@0
|
197 if (HANDSHAKE_DEBUG)
|
paulo@0
|
198 GT->DBGSOCK (GT, c, "not accepting connection [address banned]");
|
paulo@0
|
199
|
paulo@0
|
200 tcp_close (c);
|
paulo@0
|
201 return;
|
paulo@0
|
202 }
|
paulo@0
|
203 #endif
|
paulo@0
|
204
|
paulo@0
|
205 /*
|
paulo@0
|
206 * If there are too many HTTP connections for this IP, close the
|
paulo@0
|
207 * connection now before investing any more resources servicing it.
|
paulo@0
|
208 */
|
paulo@0
|
209 if (gt_http_connection_length (GT_TRANSFER_UPLOAD, peer_ip) >=
|
paulo@0
|
210 HTTP_MAX_PERUSER_UPLOAD_CONNS)
|
paulo@0
|
211 {
|
paulo@0
|
212 if (HTTP_DEBUG)
|
paulo@0
|
213 GT->DBGSOCK (GT, c, "too many connections for this user, closing");
|
paulo@0
|
214
|
paulo@0
|
215 tcp_close (c);
|
paulo@0
|
216 return;
|
paulo@0
|
217 }
|
paulo@0
|
218
|
paulo@0
|
219 /* local hosts_allow may need to be evaluated to keep outside sources
|
paulo@0
|
220 * away */
|
paulo@0
|
221 if (GNUTELLA_LOCAL_MODE)
|
paulo@0
|
222 {
|
paulo@0
|
223 if (!net_match_host (peer_ip, GNUTELLA_LOCAL_ALLOW))
|
paulo@0
|
224 {
|
paulo@0
|
225 if (HANDSHAKE_DEBUG)
|
paulo@0
|
226 GT->DBGSOCK (GT, c, "non-local connection, closing");
|
paulo@0
|
227
|
paulo@0
|
228 tcp_close (c);
|
paulo@0
|
229 return;
|
paulo@0
|
230 }
|
paulo@0
|
231 }
|
paulo@0
|
232
|
paulo@0
|
233 if (!(conn = incoming_conn_alloc (c)))
|
paulo@0
|
234 {
|
paulo@0
|
235 tcp_close (c);
|
paulo@0
|
236 return;
|
paulo@0
|
237 }
|
paulo@0
|
238
|
paulo@0
|
239 input_remove (id);
|
paulo@0
|
240 input_add (c->fd, conn, INPUT_READ,
|
paulo@0
|
241 (InputCallback)determine_method, 0);
|
paulo@0
|
242 }
|
paulo@0
|
243
|
paulo@0
|
244 /*
|
paulo@0
|
245 * Dispatch incoming connections to the proper subsystem.
|
paulo@0
|
246 */
|
paulo@0
|
247 static void determine_method (int fd, input_id id, struct incoming_conn *conn)
|
paulo@0
|
248 {
|
paulo@0
|
249 struct server_cmd *command;
|
paulo@0
|
250 FDBuf *fdbuf;
|
paulo@0
|
251 int ret;
|
paulo@0
|
252 char *request;
|
paulo@0
|
253 TCPC *c = conn->c;
|
paulo@0
|
254
|
paulo@0
|
255 fdbuf = tcp_readbuf (c);
|
paulo@0
|
256
|
paulo@0
|
257 if ((ret = fdbuf_delim (fdbuf, "\n")) < 0)
|
paulo@0
|
258 {
|
paulo@0
|
259 if (HANDSHAKE_DEBUG)
|
paulo@0
|
260 GT->DBGSOCK (GT, c, "error: %s", GIFT_NETERROR ());
|
paulo@0
|
261
|
paulo@0
|
262 incoming_conn_close (conn);
|
paulo@0
|
263 return;
|
paulo@0
|
264 }
|
paulo@0
|
265
|
paulo@0
|
266 /* some data was read: update fw status */
|
paulo@0
|
267 fw_status_update (c);
|
paulo@0
|
268
|
paulo@0
|
269 if (gt_fdbuf_full (fdbuf))
|
paulo@0
|
270 {
|
paulo@0
|
271 incoming_conn_close (conn);
|
paulo@0
|
272 return;
|
paulo@0
|
273 }
|
paulo@0
|
274
|
paulo@0
|
275 if (ret > 0)
|
paulo@0
|
276 return;
|
paulo@0
|
277
|
paulo@0
|
278 /*
|
paulo@0
|
279 * NOTE: fdbuf_release() is not called here, so the first line is left on
|
paulo@0
|
280 * the FDBuf.
|
paulo@0
|
281 */
|
paulo@0
|
282 request = fdbuf_data (fdbuf, NULL);
|
paulo@0
|
283
|
paulo@0
|
284 for (command = server_commands; command->name != NULL; command++)
|
paulo@0
|
285 {
|
paulo@0
|
286 if (!strncasecmp (command->name, request, strlen (command->name)))
|
paulo@0
|
287 {
|
paulo@0
|
288 input_remove (id);
|
paulo@0
|
289 input_add (fd, conn, INPUT_READ,
|
paulo@0
|
290 (InputCallback)command->callback, 0);
|
paulo@0
|
291
|
paulo@0
|
292 return;
|
paulo@0
|
293 }
|
paulo@0
|
294 }
|
paulo@0
|
295
|
paulo@0
|
296 if (HANDSHAKE_DEBUG)
|
paulo@0
|
297 GT->DBGFN (GT, "bad command: %s", request);
|
paulo@0
|
298
|
paulo@0
|
299 incoming_conn_close (conn);
|
paulo@0
|
300 }
|
paulo@0
|
301
|
paulo@0
|
302 /*****************************************************************************/
|
paulo@0
|
303
|
paulo@0
|
304 /*
|
paulo@0
|
305 * Main connection acceptance routine. This begins the connection's
|
paulo@0
|
306 * journey through the rest of the handshaking code.
|
paulo@0
|
307 */
|
paulo@0
|
308 GT_METHOD(gt_server_accept)
|
paulo@0
|
309 {
|
paulo@0
|
310 char *request;
|
paulo@0
|
311 char *version_str;
|
paulo@0
|
312 size_t request_len = 0;
|
paulo@0
|
313 int n;
|
paulo@0
|
314 GtNode *node;
|
paulo@0
|
315 TCPC *c = conn->c;
|
paulo@0
|
316 FDBuf *buf;
|
paulo@0
|
317
|
paulo@0
|
318 if (HANDSHAKE_DEBUG)
|
paulo@0
|
319 GT->DBGFN (GT, "entered");
|
paulo@0
|
320
|
paulo@0
|
321 buf = tcp_readbuf (c);
|
paulo@0
|
322
|
paulo@0
|
323 if ((n = fdbuf_delim (buf, "\n")) < 0)
|
paulo@0
|
324 {
|
paulo@0
|
325 if (HANDSHAKE_DEBUG)
|
paulo@0
|
326 GT->DBGSOCK (GT, c, "error on recv: %s", GIFT_NETERROR ());
|
paulo@0
|
327
|
paulo@0
|
328 incoming_conn_close (conn);
|
paulo@0
|
329 return;
|
paulo@0
|
330 }
|
paulo@0
|
331
|
paulo@0
|
332 if (gt_fdbuf_full (buf))
|
paulo@0
|
333 {
|
paulo@0
|
334 incoming_conn_close (conn);
|
paulo@0
|
335 return;
|
paulo@0
|
336 }
|
paulo@0
|
337
|
paulo@0
|
338 if (n > 0)
|
paulo@0
|
339 return;
|
paulo@0
|
340
|
paulo@0
|
341 request = fdbuf_data (buf, &request_len);
|
paulo@0
|
342
|
paulo@0
|
343 if (!gt_http_header_terminated (request, request_len))
|
paulo@0
|
344 return;
|
paulo@0
|
345
|
paulo@0
|
346 fdbuf_release (buf);
|
paulo@0
|
347
|
paulo@0
|
348 if (HANDSHAKE_DEBUG)
|
paulo@0
|
349 GT->DBGSOCK (GT, c, "accepted headers:\n%s", request);
|
paulo@0
|
350
|
paulo@0
|
351 version_str = strchr (request, '/');
|
paulo@0
|
352 if (version_str)
|
paulo@0
|
353 version_str++;
|
paulo@0
|
354
|
paulo@0
|
355 if (strncasecmp ("GNUTELLA CONNECT/", request,
|
paulo@0
|
356 sizeof ("GNUTELLA CONNECT/") - 1) != 0)
|
paulo@0
|
357 {
|
paulo@0
|
358 if (HANDSHAKE_DEBUG)
|
paulo@0
|
359 GT->DBGSOCK (GT, c, "bad handshake header");
|
paulo@0
|
360
|
paulo@0
|
361 incoming_conn_close (conn);
|
paulo@0
|
362 return;
|
paulo@0
|
363 }
|
paulo@0
|
364
|
paulo@0
|
365 /* deny 0.4 connections */
|
paulo@0
|
366 if (!version_str || strncasecmp (version_str, "0.4", 3) == 0)
|
paulo@0
|
367 {
|
paulo@0
|
368 if (HANDSHAKE_DEBUG)
|
paulo@0
|
369 GT->DBGSOCK (GT, c, "closing 0.4 connection");
|
paulo@0
|
370
|
paulo@0
|
371 incoming_conn_close (conn);
|
paulo@0
|
372 return;
|
paulo@0
|
373 }
|
paulo@0
|
374
|
paulo@0
|
375 /* make a node out of this connection */
|
paulo@0
|
376 if (!(node = gt_node_instantiate (c)))
|
paulo@0
|
377 {
|
paulo@0
|
378 if (HANDSHAKE_DEBUG)
|
paulo@0
|
379 GT->DBGFN (GT, "node_instantiate failed");
|
paulo@0
|
380
|
paulo@0
|
381 incoming_conn_close (conn);
|
paulo@0
|
382 return;
|
paulo@0
|
383 }
|
paulo@0
|
384
|
paulo@0
|
385 /*
|
paulo@0
|
386 * Update the start_connect_time so the node will stick around for a while
|
paulo@0
|
387 * even when the node list gets trimmed.
|
paulo@0
|
388 */
|
paulo@0
|
389 node->start_connect_time = time (NULL);
|
paulo@0
|
390
|
paulo@0
|
391 gt_node_state_set (node, GT_NODE_CONNECTING_1);
|
paulo@0
|
392 node->incoming = TRUE;
|
paulo@0
|
393
|
paulo@0
|
394 /*
|
paulo@0
|
395 * Store the http header.
|
paulo@0
|
396 *
|
paulo@0
|
397 * NOTE: We don't check the return code here. This function expects a
|
paulo@0
|
398 * properly formatted HTTP response, but we are handing it a
|
paulo@0
|
399 * Gnutella connection request instead, so it will return failure.
|
paulo@0
|
400 */
|
paulo@0
|
401 gnutella_parse_response_headers (request, &node->hdr);
|
paulo@0
|
402
|
paulo@0
|
403 /*
|
paulo@0
|
404 * Use the node handshake timeout timer now, and get rid of the
|
paulo@0
|
405 * generic incoming connection timeout timer.
|
paulo@0
|
406 */
|
paulo@0
|
407 gnutella_set_handshake_timeout (c, TIMEOUT_2 * SECONDS);
|
paulo@0
|
408 incoming_conn_free (conn);
|
paulo@0
|
409
|
paulo@0
|
410 input_remove (id);
|
paulo@0
|
411 input_add (fd, c, INPUT_WRITE,
|
paulo@0
|
412 (InputCallback)send_node_headers, TIMEOUT_DEF);
|
paulo@0
|
413 }
|
paulo@0
|
414
|
paulo@0
|
415 GT_METHOD(gt_server_giv)
|
paulo@0
|
416 {
|
paulo@0
|
417 FDBuf *buf;
|
paulo@0
|
418 int n;
|
paulo@0
|
419 in_addr_t peer_ip;
|
paulo@0
|
420 char *response;
|
paulo@0
|
421 size_t response_len = 0;
|
paulo@0
|
422 char *client_id;
|
paulo@0
|
423 gt_guid_t *guid;
|
paulo@0
|
424 TCPC *c = conn->c;
|
paulo@0
|
425
|
paulo@0
|
426 if (HTTP_DEBUG || HANDSHAKE_DEBUG)
|
paulo@0
|
427 GT->DBGFN (GT, "entered");
|
paulo@0
|
428
|
paulo@0
|
429 buf = tcp_readbuf (c);
|
paulo@0
|
430
|
paulo@0
|
431 if ((n = fdbuf_delim (buf, "\n")) < 0)
|
paulo@0
|
432 {
|
paulo@0
|
433 incoming_conn_close (conn);
|
paulo@0
|
434 return;
|
paulo@0
|
435 }
|
paulo@0
|
436
|
paulo@0
|
437 if (gt_fdbuf_full (buf))
|
paulo@0
|
438 {
|
paulo@0
|
439 incoming_conn_close (conn);
|
paulo@0
|
440 return;
|
paulo@0
|
441 }
|
paulo@0
|
442
|
paulo@0
|
443 if (n > 0)
|
paulo@0
|
444 return;
|
paulo@0
|
445
|
paulo@0
|
446 response = fdbuf_data (buf, &response_len);
|
paulo@0
|
447
|
paulo@0
|
448 if (!gt_http_header_terminated (response, response_len))
|
paulo@0
|
449 return;
|
paulo@0
|
450
|
paulo@0
|
451 fdbuf_release (buf);
|
paulo@0
|
452
|
paulo@0
|
453 if (HTTP_DEBUG || HANDSHAKE_DEBUG)
|
paulo@0
|
454 GT->DBGSOCK (GT, c, "giv response=%s", response);
|
paulo@0
|
455
|
paulo@0
|
456 string_sep (&response, " "); /* Skip "GIV " */
|
paulo@0
|
457 string_sep (&response, ":"); /* Skip the file index */
|
paulo@0
|
458
|
paulo@0
|
459 client_id = string_sep (&response, "/");
|
paulo@0
|
460 string_lower (client_id);
|
paulo@0
|
461
|
paulo@0
|
462 if (!(guid = gt_guid_bin (client_id)))
|
paulo@0
|
463 {
|
paulo@0
|
464 incoming_conn_close (conn);
|
paulo@0
|
465 return;
|
paulo@0
|
466 }
|
paulo@0
|
467
|
paulo@0
|
468 peer_ip = net_peer (c->fd);
|
paulo@0
|
469
|
paulo@0
|
470 /*
|
paulo@0
|
471 * This will continue the GtTransfer if one is found, and store
|
paulo@0
|
472 * the connection if one is not. The connection will be resumed
|
paulo@0
|
473 * when a chunk for this source is reissued.
|
paulo@0
|
474 */
|
paulo@0
|
475 gt_push_source_add_conn (guid, peer_ip, c);
|
paulo@0
|
476
|
paulo@0
|
477 incoming_conn_free (conn);
|
paulo@0
|
478 free (guid);
|
paulo@0
|
479 }
|
paulo@0
|
480
|
paulo@0
|
481 /*****************************************************************************/
|
paulo@0
|
482
|
paulo@0
|
483 GT_METHOD(gt_server_get)
|
paulo@0
|
484 {
|
paulo@0
|
485 if (HTTP_DEBUG)
|
paulo@0
|
486 GT->DBGFN (GT, "entered");
|
paulo@0
|
487
|
paulo@0
|
488 /* dispatch to the HTTP server code */
|
paulo@0
|
489 gt_http_server_dispatch (fd, id, conn->c);
|
paulo@0
|
490
|
paulo@0
|
491 /* get rid of the tracking information for the connection in this
|
paulo@0
|
492 * subsystem */
|
paulo@0
|
493 incoming_conn_free (conn);
|
paulo@0
|
494 }
|
paulo@0
|
495
|
paulo@0
|
496 /*****************************************************************************/
|
paulo@0
|
497 /* GNUTELLA/0.6 CONNECTIONS */
|
paulo@0
|
498
|
paulo@0
|
499 BOOL gt_http_header_terminated (char *data, size_t len)
|
paulo@0
|
500 {
|
paulo@0
|
501 int cnt;
|
paulo@0
|
502
|
paulo@0
|
503 assert (len > 0);
|
paulo@0
|
504 len--;
|
paulo@0
|
505
|
paulo@0
|
506 for (cnt = 0; len > 0 && cnt < 2; cnt++)
|
paulo@0
|
507 {
|
paulo@0
|
508 if (data[len--] != '\n')
|
paulo@0
|
509 break;
|
paulo@0
|
510
|
paulo@0
|
511 /* treat CRLF as LF */
|
paulo@0
|
512 if (data[len] == '\r')
|
paulo@0
|
513 len--;
|
paulo@0
|
514 }
|
paulo@0
|
515
|
paulo@0
|
516 return (cnt == 2);
|
paulo@0
|
517 }
|
paulo@0
|
518
|
paulo@0
|
519 /* TODO: header continuation and joining of multiple occurrences */
|
paulo@0
|
520 void gt_http_header_parse (char *headers, Dataset **d)
|
paulo@0
|
521 {
|
paulo@0
|
522 char *line, *key;
|
paulo@0
|
523
|
paulo@0
|
524 while ((line = string_sep_set (&headers, "\r\n")))
|
paulo@0
|
525 {
|
paulo@0
|
526 key = string_sep (&line, ":");
|
paulo@0
|
527
|
paulo@0
|
528 if (!key || !line)
|
paulo@0
|
529 continue;
|
paulo@0
|
530
|
paulo@0
|
531 string_trim (key);
|
paulo@0
|
532 string_trim (line);
|
paulo@0
|
533
|
paulo@0
|
534 /* dont allow empty key-values, need to check this too */
|
paulo@0
|
535 if (string_isempty (line))
|
paulo@0
|
536 continue;
|
paulo@0
|
537
|
paulo@0
|
538 dataset_insertstr (d, string_lower (key), line);
|
paulo@0
|
539 }
|
paulo@0
|
540 }
|
paulo@0
|
541
|
paulo@0
|
542 static void send_nodes (struct cached_node *node, String *s)
|
paulo@0
|
543 {
|
paulo@0
|
544 if (s->str[s->len - 1] != ':')
|
paulo@0
|
545 string_append (s, ",");
|
paulo@0
|
546 else
|
paulo@0
|
547 string_append (s, " ");
|
paulo@0
|
548
|
paulo@0
|
549 string_appendf (s, "%s:%hu", net_ip_str (node->addr.ip), node->addr.port);
|
paulo@0
|
550 }
|
paulo@0
|
551
|
paulo@0
|
552 static void deny_connection (TCPC *c, int code, char *msg)
|
paulo@0
|
553 {
|
paulo@0
|
554 String *s;
|
paulo@0
|
555 List *nodes;
|
paulo@0
|
556 in_addr_t ip;
|
paulo@0
|
557
|
paulo@0
|
558 if (!(s = string_new (NULL, 0, 0, TRUE)))
|
paulo@0
|
559 return;
|
paulo@0
|
560
|
paulo@0
|
561 string_appendf (s, "GNUTELLA/0.6 %d %s\r\n", code, msg);
|
paulo@0
|
562 string_appendf (s, "User-Agent: %s\r\n", gt_version());
|
paulo@0
|
563
|
paulo@0
|
564 ip = net_peer (c->fd);
|
paulo@0
|
565 if (!gt_is_local_ip (ip, 0))
|
paulo@0
|
566 string_appendf (s, "Remote-IP: %s\r\n", net_ip_str (ip));
|
paulo@0
|
567
|
paulo@0
|
568 /* append some different nodes to try */
|
paulo@0
|
569 nodes = gt_node_cache_get (10);
|
paulo@0
|
570
|
paulo@0
|
571 if (nodes)
|
paulo@0
|
572 {
|
paulo@0
|
573 string_append (s, "X-Try-Ultrapeers:");
|
paulo@0
|
574
|
paulo@0
|
575 list_foreach (nodes, (ListForeachFunc)send_nodes, s);
|
paulo@0
|
576 list_free (nodes);
|
paulo@0
|
577
|
paulo@0
|
578 string_append (s, "\r\n");
|
paulo@0
|
579 }
|
paulo@0
|
580
|
paulo@0
|
581 /* append message terminator */
|
paulo@0
|
582 string_append (s, "\r\n");
|
paulo@0
|
583
|
paulo@0
|
584 /* we will be closing the connection after this, so we don't
|
paulo@0
|
585 * care if the send fails or not. */
|
paulo@0
|
586 tcp_send (c, s->str, s->len);
|
paulo@0
|
587
|
paulo@0
|
588 if (HANDSHAKE_DEBUG)
|
paulo@0
|
589 GT->DBGSOCK (GT, c, "connection denied response:\n%s", s->str);
|
paulo@0
|
590
|
paulo@0
|
591 string_free (s);
|
paulo@0
|
592 }
|
paulo@0
|
593
|
paulo@0
|
594 static void setup_node_class (GtNode *node)
|
paulo@0
|
595 {
|
paulo@0
|
596 char *ultrapeer;
|
paulo@0
|
597 char *qrp;
|
paulo@0
|
598
|
paulo@0
|
599 ultrapeer = dataset_lookupstr (node->hdr, "x-ultrapeer");
|
paulo@0
|
600 qrp = dataset_lookupstr (node->hdr, "x-query-routing");
|
paulo@0
|
601
|
paulo@0
|
602 if (ultrapeer && !strcasecmp (ultrapeer, "true") && qrp != NULL)
|
paulo@0
|
603 gt_node_class_set (node, GT_NODE_ULTRA);
|
paulo@0
|
604 else
|
paulo@0
|
605 gt_node_class_set (node, GT_NODE_LEAF);
|
paulo@0
|
606 }
|
paulo@0
|
607
|
paulo@0
|
608 BOOL gnutella_auth_connection (TCPC *c)
|
paulo@0
|
609 {
|
paulo@0
|
610 GtNode *node;
|
paulo@0
|
611
|
paulo@0
|
612 node = GT_NODE(c);
|
paulo@0
|
613 assert (GT_NODE(c) == node && GT_CONN(node) == c);
|
paulo@0
|
614
|
paulo@0
|
615 /* set the class of this node based on the headers sent */
|
paulo@0
|
616 setup_node_class (node);
|
paulo@0
|
617
|
paulo@0
|
618 /*
|
paulo@0
|
619 * If the remote node is only crawling us, accept the connection
|
paulo@0
|
620 * no matter what.
|
paulo@0
|
621 */
|
paulo@0
|
622 if (dataset_lookupstr (node->hdr, "crawler"))
|
paulo@0
|
623 return TRUE;
|
paulo@0
|
624
|
paulo@0
|
625 /*
|
paulo@0
|
626 * If we are a leaf node, and so is this node, deny the connection,
|
paulo@0
|
627 * but send 'X-Try-Ultrapeer:' headers with the ultrapeers we
|
paulo@0
|
628 * are connected to in order to try to bootstrap the remote client.
|
paulo@0
|
629 */
|
paulo@0
|
630 if (!(GT_SELF->klass & GT_NODE_ULTRA) && (node->klass & GT_NODE_LEAF))
|
paulo@0
|
631 {
|
paulo@0
|
632 deny_connection (c, 503, "I am a shielded leaf node");
|
paulo@0
|
633 return FALSE;
|
paulo@0
|
634 }
|
paulo@0
|
635
|
paulo@0
|
636 if (gt_conn_need_connections (node->klass) <= 0)
|
paulo@0
|
637 {
|
paulo@0
|
638 deny_connection (c, 503, "Too many connections");
|
paulo@0
|
639 return FALSE;
|
paulo@0
|
640 }
|
paulo@0
|
641
|
paulo@0
|
642 if (gt_ban_ipv4_is_banned (node->ip))
|
paulo@0
|
643 {
|
paulo@0
|
644 deny_connection (c, 403, "Unauthorized");
|
paulo@0
|
645 return FALSE;
|
paulo@0
|
646 }
|
paulo@0
|
647
|
paulo@0
|
648 return TRUE;
|
paulo@0
|
649 }
|
paulo@0
|
650
|
paulo@0
|
651 static void send_node_headers (int fd, input_id id, TCPC *c)
|
paulo@0
|
652 {
|
paulo@0
|
653 if (net_sock_error (c->fd))
|
paulo@0
|
654 {
|
paulo@0
|
655 gt_node_error (c, NULL);
|
paulo@0
|
656 gt_node_disconnect (c);
|
paulo@0
|
657 return;
|
paulo@0
|
658 }
|
paulo@0
|
659
|
paulo@0
|
660 if (!gnutella_auth_connection (c))
|
paulo@0
|
661 {
|
paulo@0
|
662 gt_node_error (c, "[incoming] connection not authorized");
|
paulo@0
|
663 gt_node_disconnect (c);
|
paulo@0
|
664 return;
|
paulo@0
|
665 }
|
paulo@0
|
666
|
paulo@0
|
667 /* send OK response to the peer and send headers for this node also */
|
paulo@0
|
668 if (!gnutella_send_connection_headers (c, "GNUTELLA/0.6 200 OK"))
|
paulo@0
|
669 {
|
paulo@0
|
670 gt_node_error (c, NULL);
|
paulo@0
|
671 gt_node_disconnect (c);
|
paulo@0
|
672 return;
|
paulo@0
|
673 }
|
paulo@0
|
674
|
paulo@0
|
675 /* reset the timeout for this connection */
|
paulo@0
|
676 gnutella_set_handshake_timeout (c, TIMEOUT_3 * SECONDS);
|
paulo@0
|
677
|
paulo@0
|
678 input_remove (id);
|
paulo@0
|
679 input_add (fd, c, INPUT_READ,
|
paulo@0
|
680 (InputCallback)recv_final_handshake, 0);
|
paulo@0
|
681 }
|
paulo@0
|
682
|
paulo@0
|
683 /* TODO: this should be abstracted */
|
paulo@0
|
684 static char *field_has_value (Dataset *d, const char *key, const char *val)
|
paulo@0
|
685 {
|
paulo@0
|
686 char *value;
|
paulo@0
|
687 char *str;
|
paulo@0
|
688
|
paulo@0
|
689 if (!(value = dataset_lookupstr (d, key)))
|
paulo@0
|
690 return NULL;
|
paulo@0
|
691
|
paulo@0
|
692 if ((str = strstr (value, val)))
|
paulo@0
|
693 return str;
|
paulo@0
|
694
|
paulo@0
|
695 return NULL;
|
paulo@0
|
696 }
|
paulo@0
|
697
|
paulo@0
|
698 static BOOL need_inflate (GtNode *node)
|
paulo@0
|
699 {
|
paulo@0
|
700 if (!(field_has_value (node->hdr, "content-encoding", "deflate")))
|
paulo@0
|
701 return FALSE;
|
paulo@0
|
702
|
paulo@0
|
703 return TRUE;
|
paulo@0
|
704 }
|
paulo@0
|
705
|
paulo@0
|
706 static BOOL need_deflate (GtNode *node)
|
paulo@0
|
707 {
|
paulo@0
|
708 if (!(field_has_value (node->hdr, "accept-encoding", "deflate")))
|
paulo@0
|
709 return FALSE;
|
paulo@0
|
710
|
paulo@0
|
711 return TRUE;
|
paulo@0
|
712 }
|
paulo@0
|
713
|
paulo@0
|
714 void gnutella_mark_compression (GtNode *node)
|
paulo@0
|
715 {
|
paulo@0
|
716 if (need_inflate (node))
|
paulo@0
|
717 node->rx_inflated = TRUE;
|
paulo@0
|
718
|
paulo@0
|
719 if (need_deflate (node))
|
paulo@0
|
720 node->tx_deflated = TRUE;
|
paulo@0
|
721 }
|
paulo@0
|
722
|
paulo@0
|
723 static void add_key (ds_data_t *key, ds_data_t *value, Dataset **d)
|
paulo@0
|
724 {
|
paulo@0
|
725 char *hdr = key->data;
|
paulo@0
|
726 char *val = value->data;
|
paulo@0
|
727
|
paulo@0
|
728 dataset_insertstr (d, hdr, val);
|
paulo@0
|
729 }
|
paulo@0
|
730
|
paulo@0
|
731 static void recv_final_handshake (int fd, input_id id, TCPC *c)
|
paulo@0
|
732 {
|
paulo@0
|
733 FDBuf *buf;
|
paulo@0
|
734 int n;
|
paulo@0
|
735 char *response;
|
paulo@0
|
736 size_t response_len = 0;
|
paulo@0
|
737 Dataset *additional = NULL;
|
paulo@0
|
738
|
paulo@0
|
739 buf = tcp_readbuf (c);
|
paulo@0
|
740
|
paulo@0
|
741 if ((n = fdbuf_delim (buf, "\n")) < 0)
|
paulo@0
|
742 {
|
paulo@0
|
743 if (HANDSHAKE_DEBUG)
|
paulo@0
|
744 GT->DBGSOCK (GT, c, "fdbuf_delim: error %s", GIFT_NETERROR ());
|
paulo@0
|
745
|
paulo@0
|
746 gt_node_disconnect (c);
|
paulo@0
|
747 return;
|
paulo@0
|
748 }
|
paulo@0
|
749
|
paulo@0
|
750 if (gt_fdbuf_full (buf))
|
paulo@0
|
751 {
|
paulo@0
|
752 gt_node_disconnect (c);
|
paulo@0
|
753 return;
|
paulo@0
|
754 }
|
paulo@0
|
755
|
paulo@0
|
756 if (n > 0)
|
paulo@0
|
757 return;
|
paulo@0
|
758
|
paulo@0
|
759 response = fdbuf_data (buf, &response_len);
|
paulo@0
|
760
|
paulo@0
|
761 if (!gt_http_header_terminated (response, response_len))
|
paulo@0
|
762 return;
|
paulo@0
|
763
|
paulo@0
|
764 fdbuf_release (buf);
|
paulo@0
|
765
|
paulo@0
|
766 if (HANDSHAKE_DEBUG)
|
paulo@0
|
767 GT->DBGSOCK (GT, c, "stage3 response:\n%s", response);
|
paulo@0
|
768
|
paulo@0
|
769 if (!gnutella_parse_response_headers (response, &additional))
|
paulo@0
|
770 {
|
paulo@0
|
771 if (HANDSHAKE_DEBUG)
|
paulo@0
|
772 gt_node_error (c, "node denied us in stage3 of handshake");
|
paulo@0
|
773
|
paulo@0
|
774 gt_node_disconnect (c);
|
paulo@0
|
775 dataset_clear (additional);
|
paulo@0
|
776 return;
|
paulo@0
|
777 }
|
paulo@0
|
778
|
paulo@0
|
779 /*
|
paulo@0
|
780 * Append or replace the fields the node sent into the header for this
|
paulo@0
|
781 * node.
|
paulo@0
|
782 *
|
paulo@0
|
783 * TODO: should probably change the interface to parse_response_headers so
|
paulo@0
|
784 * we can just pass in the header list of the node.
|
paulo@0
|
785 */
|
paulo@0
|
786 dataset_foreach (additional, DS_FOREACH(add_key), >_NODE(c)->hdr);
|
paulo@0
|
787 dataset_clear (additional);
|
paulo@0
|
788
|
paulo@0
|
789 /* mark the compression flags on this GtNode */
|
paulo@0
|
790 gnutella_mark_compression (GT_NODE(c));
|
paulo@0
|
791
|
paulo@0
|
792 input_remove (id);
|
paulo@0
|
793 input_add (fd, c, INPUT_WRITE,
|
paulo@0
|
794 (InputCallback)gnutella_start_connection, TIMEOUT_DEF);
|
paulo@0
|
795 }
|