rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * $Id: gt_node.c,v 1.59 2005/01/04 15:00:51 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_node.h"
|
paulo@0
|
20 #include "gt_node_list.h"
|
paulo@0
|
21
|
paulo@0
|
22 #include "gt_utils.h"
|
paulo@0
|
23
|
paulo@0
|
24 #include "gt_packet.h"
|
paulo@0
|
25 #include "gt_query_route.h"
|
paulo@0
|
26 #include "gt_share_state.h"
|
paulo@0
|
27
|
paulo@0
|
28 #include "gt_node_cache.h"
|
paulo@0
|
29
|
paulo@0
|
30 #include "io/rx_stack.h" /* gt_rx_stack_free */
|
paulo@0
|
31 #include "io/tx_stack.h" /* gt_tx_stack_free, gt_tx_stack_queue */
|
paulo@0
|
32
|
paulo@0
|
33 #include "transfer/push_proxy.h"
|
paulo@0
|
34
|
paulo@0
|
35 /*****************************************************************************/
|
paulo@0
|
36
|
paulo@0
|
37 /* maps ids -> node, so we dont have to keep TCPC ptrs in
|
paulo@0
|
38 * various data structures */
|
paulo@0
|
39 static Dataset *node_ids;
|
paulo@0
|
40
|
paulo@0
|
41 /*****************************************************************************/
|
paulo@0
|
42
|
paulo@0
|
43 static void node_add (GtNode *node)
|
paulo@0
|
44 {
|
paulo@0
|
45 if (!node_ids)
|
paulo@0
|
46 node_ids = dataset_new (DATASET_HASH);
|
paulo@0
|
47
|
paulo@0
|
48 if (!node->ip)
|
paulo@0
|
49 return;
|
paulo@0
|
50
|
paulo@0
|
51 dataset_insert (&node_ids, &node->ip, sizeof (node->ip), node, 0);
|
paulo@0
|
52 }
|
paulo@0
|
53
|
paulo@0
|
54 static void node_remove (GtNode *node)
|
paulo@0
|
55 {
|
paulo@0
|
56 if (!node)
|
paulo@0
|
57 return;
|
paulo@0
|
58
|
paulo@0
|
59 if (!node->ip)
|
paulo@0
|
60 return;
|
paulo@0
|
61
|
paulo@0
|
62 dataset_remove (node_ids, &node->ip, sizeof (node->ip));
|
paulo@0
|
63 }
|
paulo@0
|
64
|
paulo@0
|
65 /*****************************************************************************/
|
paulo@0
|
66
|
paulo@0
|
67 GtNode *gt_node_new ()
|
paulo@0
|
68 {
|
paulo@0
|
69 GtNode *node;
|
paulo@0
|
70
|
paulo@0
|
71 if (!(node = MALLOC (sizeof (GtNode))))
|
paulo@0
|
72 return NULL;
|
paulo@0
|
73
|
paulo@0
|
74 return node;
|
paulo@0
|
75 }
|
paulo@0
|
76
|
paulo@0
|
77 static void free_node (GtNode *node)
|
paulo@0
|
78 {
|
paulo@0
|
79 if (!node)
|
paulo@0
|
80 return;
|
paulo@0
|
81
|
paulo@0
|
82 gt_node_disconnect (GT_CONN(node));
|
paulo@0
|
83 gt_conn_remove (node);
|
paulo@0
|
84
|
paulo@0
|
85 free (node);
|
paulo@0
|
86 }
|
paulo@0
|
87
|
paulo@0
|
88 /* NOTE: this isnt safe to call at all times */
|
paulo@0
|
89 void gt_node_free (GtNode *node)
|
paulo@0
|
90 {
|
paulo@0
|
91 node_remove (node);
|
paulo@0
|
92 free_node (node);
|
paulo@0
|
93 }
|
paulo@0
|
94
|
paulo@0
|
95 /* set the node to use the TCP connection */
|
paulo@0
|
96 void gt_node_connect (GtNode *node, TCPC *c)
|
paulo@0
|
97 {
|
paulo@0
|
98 assert (GT_CONN(node) == NULL);
|
paulo@0
|
99 assert (GT_NODE(c) == NULL);
|
paulo@0
|
100
|
paulo@0
|
101 node->c = c;
|
paulo@0
|
102 c->udata = node;
|
paulo@0
|
103 }
|
paulo@0
|
104
|
paulo@0
|
105 /* put the node into some data structures to keep track of it */
|
paulo@0
|
106 static void track_node (GtNode *node, TCPC *c)
|
paulo@0
|
107 {
|
paulo@0
|
108 if (node->ip)
|
paulo@0
|
109 assert (node->ip == c->host);
|
paulo@0
|
110
|
paulo@0
|
111 /* fill in peer info (in network byte order) */
|
paulo@0
|
112 node->ip = c->host;
|
paulo@0
|
113 assert (node->ip != 0);
|
paulo@0
|
114
|
paulo@0
|
115 gt_conn_add (node);
|
paulo@0
|
116 node_add (node);
|
paulo@0
|
117 }
|
paulo@0
|
118
|
paulo@0
|
119 /* instantiate a node from an existing connection */
|
paulo@0
|
120 GtNode *gt_node_instantiate (TCPC *c)
|
paulo@0
|
121 {
|
paulo@0
|
122 GtNode *node;
|
paulo@0
|
123 BOOL existed = FALSE;
|
paulo@0
|
124
|
paulo@0
|
125 if (!c || !c->host)
|
paulo@0
|
126 return NULL;
|
paulo@0
|
127
|
paulo@0
|
128 /* TODO: We should really lookup the port in Listen-IP header, right? */
|
paulo@0
|
129 node = gt_node_lookup (c->host, 0);
|
paulo@0
|
130
|
paulo@0
|
131 if (node)
|
paulo@0
|
132 {
|
paulo@0
|
133 existed = TRUE;
|
paulo@0
|
134
|
paulo@0
|
135 /* abort if already connected/connecting */
|
paulo@0
|
136 if (node->state != GT_NODE_DISCONNECTED)
|
paulo@0
|
137 return NULL;
|
paulo@0
|
138 }
|
paulo@0
|
139 else
|
paulo@0
|
140 {
|
paulo@0
|
141 if (!(node = gt_node_new ()))
|
paulo@0
|
142 return NULL;
|
paulo@0
|
143 }
|
paulo@0
|
144
|
paulo@0
|
145 assert (node->c == NULL);
|
paulo@0
|
146
|
paulo@0
|
147 /* attach this node to the connection and vice-versa */
|
paulo@0
|
148 gt_node_connect (node, c);
|
paulo@0
|
149
|
paulo@0
|
150 if (!existed)
|
paulo@0
|
151 track_node (node, c);
|
paulo@0
|
152
|
paulo@0
|
153 return node;
|
paulo@0
|
154 }
|
paulo@0
|
155
|
paulo@0
|
156 static int free_one (ds_data_t *key, ds_data_t *value, void *udata)
|
paulo@0
|
157 {
|
paulo@0
|
158 GtNode *node = value->data;
|
paulo@0
|
159
|
paulo@0
|
160 /* don't call gt_node_free here while iterating through the
|
paulo@0
|
161 * Dataset because it will cause us to miss items when the
|
paulo@0
|
162 * Dataset is resized */
|
paulo@0
|
163 free_node (node);
|
paulo@0
|
164
|
paulo@0
|
165 return DS_CONTINUE | DS_REMOVE;
|
paulo@0
|
166 }
|
paulo@0
|
167
|
paulo@0
|
168 void gt_node_remove_all (void)
|
paulo@0
|
169 {
|
paulo@0
|
170 dataset_foreach_ex (node_ids, DS_FOREACH_EX(free_one), NULL);
|
paulo@0
|
171 dataset_clear (node_ids);
|
paulo@0
|
172 node_ids = NULL;
|
paulo@0
|
173 }
|
paulo@0
|
174
|
paulo@0
|
175 BOOL gt_node_freeable (GtNode *node)
|
paulo@0
|
176 {
|
paulo@0
|
177 time_t now;
|
paulo@0
|
178
|
paulo@0
|
179 if (node->state != GT_NODE_DISCONNECTED)
|
paulo@0
|
180 return FALSE;
|
paulo@0
|
181
|
paulo@0
|
182 now = time (NULL);
|
paulo@0
|
183
|
paulo@0
|
184 /* keep hosts with whom we've had a connection for a good while */
|
paulo@0
|
185 if (node->vitality > 0 && now - node->vitality <= 30 * EDAYS)
|
paulo@0
|
186 return FALSE;
|
paulo@0
|
187
|
paulo@0
|
188 if (now - node->start_connect_time <= 30 * EMINUTES)
|
paulo@0
|
189 return FALSE;
|
paulo@0
|
190
|
paulo@0
|
191 /* yeah, sure, free the node if you want */
|
paulo@0
|
192 return TRUE;
|
paulo@0
|
193 }
|
paulo@0
|
194
|
paulo@0
|
195 /*****************************************************************************/
|
paulo@0
|
196
|
paulo@0
|
197 /*
|
paulo@0
|
198 * Check if this node supports the vendor message packet inside 'pkt',
|
paulo@0
|
199 * and then send the vendor message if so.
|
paulo@0
|
200 *
|
paulo@0
|
201 * The 'version' field of the VMSG is mangled to be the minimum supported
|
paulo@0
|
202 * by both this node and the remote end.
|
paulo@0
|
203 */
|
paulo@0
|
204 BOOL gt_node_send_if_supported (GtNode *node, GtPacket *pkt)
|
paulo@0
|
205 {
|
paulo@0
|
206 gt_vendor_msg_t vmsg;
|
paulo@0
|
207 unsigned char *vendor;
|
paulo@0
|
208 uint16_t id;
|
paulo@0
|
209 uint16_t ver;
|
paulo@0
|
210 uint16_t *send_ver;
|
paulo@0
|
211
|
paulo@0
|
212 gt_packet_seek (pkt, GNUTELLA_HDR_LEN);
|
paulo@0
|
213 vendor = gt_packet_get_ustr (pkt, 4);
|
paulo@0
|
214 id = gt_packet_get_uint16 (pkt);
|
paulo@0
|
215 ver = gt_packet_get_uint16 (pkt);
|
paulo@0
|
216
|
paulo@0
|
217 if (gt_packet_error (pkt))
|
paulo@0
|
218 return FALSE;
|
paulo@0
|
219
|
paulo@0
|
220 memset (&vmsg, 0, sizeof(vmsg));
|
paulo@0
|
221 memcpy (&vmsg.vendor_id, vendor, 4);
|
paulo@0
|
222 vmsg.id = id;
|
paulo@0
|
223
|
paulo@0
|
224 send_ver = dataset_lookup (node->vmsgs_supported, &vmsg, sizeof(vmsg));
|
paulo@0
|
225 if (!send_ver)
|
paulo@0
|
226 return FALSE;
|
paulo@0
|
227
|
paulo@0
|
228 /* XXX: we've no good facility for writing in the middle of the packet */
|
paulo@0
|
229 memcpy (&pkt->data[GNUTELLA_HDR_LEN + VMSG_HDR_LEN - 2], send_ver, 2);
|
paulo@0
|
230
|
paulo@0
|
231 if (gt_packet_send (GT_CONN(node), pkt) < 0)
|
paulo@0
|
232 return FALSE;
|
paulo@0
|
233
|
paulo@0
|
234 return TRUE;
|
paulo@0
|
235 }
|
paulo@0
|
236
|
paulo@0
|
237 BOOL gt_node_send (GtNode *node, GtPacket *packet)
|
paulo@0
|
238 {
|
paulo@0
|
239 /* don't queue the packet if the node isn't in a state to send it */
|
paulo@0
|
240 if (!(node->state & (GT_NODE_CONNECTED | GT_NODE_CONNECTING_2)))
|
paulo@0
|
241 return FALSE;
|
paulo@0
|
242
|
paulo@0
|
243 /* enable this at some point in the future */
|
paulo@0
|
244 #if 0
|
paulo@0
|
245 assert (GT_CONN(node) != NULL);
|
paulo@0
|
246 #endif
|
paulo@0
|
247 if (!GT_CONN(node) || GT_CONN(node)->fd < 0)
|
paulo@0
|
248 return FALSE;
|
paulo@0
|
249
|
paulo@0
|
250 return gt_tx_stack_queue (node->tx_stack, packet->data, packet->len);
|
paulo@0
|
251 }
|
paulo@0
|
252
|
paulo@0
|
253 /*****************************************************************************/
|
paulo@0
|
254
|
paulo@0
|
255 GtNode *gt_node_lookup (in_addr_t ip, in_port_t port)
|
paulo@0
|
256 {
|
paulo@0
|
257 return dataset_lookup (node_ids, &ip, sizeof (ip));
|
paulo@0
|
258 }
|
paulo@0
|
259
|
paulo@0
|
260 GtNode *gt_node_register (in_addr_t ip, in_port_t port,
|
paulo@0
|
261 gt_node_class_t klass)
|
paulo@0
|
262 {
|
paulo@0
|
263 GtNode *node;
|
paulo@0
|
264
|
paulo@0
|
265 if (GNUTELLA_LOCAL_MODE)
|
paulo@0
|
266 {
|
paulo@0
|
267 if (!net_match_host (ip, "LOCAL"))
|
paulo@0
|
268 return NULL;
|
paulo@0
|
269 }
|
paulo@0
|
270
|
paulo@0
|
271 if (!ip)
|
paulo@0
|
272 return NULL;
|
paulo@0
|
273
|
paulo@0
|
274 /* TODO: there is probably a problem here if a node is already
|
paulo@0
|
275 * connected and we're informed about it falsely some other way. */
|
paulo@0
|
276 if ((node = dataset_lookup (node_ids, &ip, sizeof (ip))))
|
paulo@0
|
277 {
|
paulo@0
|
278 if (klass != GT_NODE_NONE)
|
paulo@0
|
279 gt_node_class_set (node, klass);
|
paulo@0
|
280
|
paulo@0
|
281 return node;
|
paulo@0
|
282 }
|
paulo@0
|
283
|
paulo@0
|
284 if (!(node = gt_node_new ()))
|
paulo@0
|
285 return NULL;
|
paulo@0
|
286
|
paulo@0
|
287 node->ip = ip;
|
paulo@0
|
288 node->gt_port = port;
|
paulo@0
|
289
|
paulo@0
|
290 node_add (node);
|
paulo@0
|
291 gt_conn_add (node);
|
paulo@0
|
292
|
paulo@0
|
293 if (klass != GT_NODE_NONE)
|
paulo@0
|
294 gt_node_class_set (node, klass);
|
paulo@0
|
295
|
paulo@0
|
296 /* remove this node from the node cache in order to keep the cache
|
paulo@0
|
297 * conherent with the node list */
|
paulo@0
|
298 gt_node_cache_del_ipv4 (ip, port);
|
paulo@0
|
299
|
paulo@0
|
300 return node;
|
paulo@0
|
301 }
|
paulo@0
|
302
|
paulo@0
|
303 void gt_node_error (TCPC *c, const char *fmt, ...)
|
paulo@0
|
304 {
|
paulo@0
|
305 static char buf[4096];
|
paulo@0
|
306 va_list args;
|
paulo@0
|
307
|
paulo@0
|
308 assert (GT_CONN(GT_NODE(c)) == c);
|
paulo@0
|
309
|
paulo@0
|
310 if (!fmt)
|
paulo@0
|
311 {
|
paulo@0
|
312 GT->DBGSOCK (GT, c, "[%hu] error: %s", GT_NODE(c)->gt_port,
|
paulo@0
|
313 GIFT_NETERROR ());
|
paulo@0
|
314 return;
|
paulo@0
|
315 }
|
paulo@0
|
316
|
paulo@0
|
317 va_start (args, fmt);
|
paulo@0
|
318 vsnprintf (buf, sizeof (buf) - 1, fmt, args);
|
paulo@0
|
319 va_end (args);
|
paulo@0
|
320
|
paulo@0
|
321 GT->DBGSOCK (GT, c, "error: %s", buf);
|
paulo@0
|
322 }
|
paulo@0
|
323
|
paulo@0
|
324 void gt_node_disconnect (TCPC *c)
|
paulo@0
|
325 {
|
paulo@0
|
326 GtNode *node;
|
paulo@0
|
327
|
paulo@0
|
328 if (!c)
|
paulo@0
|
329 return;
|
paulo@0
|
330
|
paulo@0
|
331 node = GT_NODE(c);
|
paulo@0
|
332 assert (node->c == c);
|
paulo@0
|
333
|
paulo@0
|
334 /* remove node timers */
|
paulo@0
|
335 timer_remove_zero (&node->handshake_timer);
|
paulo@0
|
336 timer_remove_zero (&node->search_timer);
|
paulo@0
|
337 timer_remove_zero (&node->query_route_timer);
|
paulo@0
|
338
|
paulo@0
|
339 /* destroy existing received buffers for this connection */
|
paulo@0
|
340 gt_rx_stack_free (node->rx_stack);
|
paulo@0
|
341 node->rx_stack = NULL;
|
paulo@0
|
342
|
paulo@0
|
343 /* destroy send buffers */
|
paulo@0
|
344 gt_tx_stack_free (node->tx_stack);
|
paulo@0
|
345 node->tx_stack = NULL;
|
paulo@0
|
346
|
paulo@0
|
347 /* remove the node from push proxy status */
|
paulo@0
|
348 gt_push_proxy_del (node);
|
paulo@0
|
349
|
paulo@0
|
350 /* reset connection status flags */
|
paulo@0
|
351 node->verified = FALSE;
|
paulo@0
|
352 node->firewalled = FALSE;
|
paulo@0
|
353 node->incoming = FALSE;
|
paulo@0
|
354 node->rx_inflated = FALSE;
|
paulo@0
|
355 node->tx_deflated = FALSE;
|
paulo@0
|
356 node->vmsgs_sent = FALSE;
|
paulo@0
|
357
|
paulo@0
|
358 /* close the connection for this node, if any */
|
paulo@0
|
359 tcp_close_null (&node->c);
|
paulo@0
|
360
|
paulo@0
|
361 node->pings_with_noreply = 0;
|
paulo@0
|
362
|
paulo@0
|
363 /* clear verification connection */
|
paulo@0
|
364 tcp_close_null (&node->gt_port_verify);
|
paulo@0
|
365
|
paulo@0
|
366 free (node->ping_guid);
|
paulo@0
|
367 node->ping_guid = NULL;
|
paulo@0
|
368
|
paulo@0
|
369 dataset_clear (node->hdr);
|
paulo@0
|
370 node->hdr = NULL;
|
paulo@0
|
371
|
paulo@0
|
372 dataset_clear (node->vmsgs_supported);
|
paulo@0
|
373 node->vmsgs_supported = NULL;
|
paulo@0
|
374
|
paulo@0
|
375 gt_share_state_free (node->share_state);
|
paulo@0
|
376 node->share_state = NULL;
|
paulo@0
|
377
|
paulo@0
|
378 gt_query_router_free (node->query_router);
|
paulo@0
|
379 node->query_router = NULL;
|
paulo@0
|
380 node->query_router_counter = 0;
|
paulo@0
|
381
|
paulo@0
|
382 /* update the last time if this node was connected */
|
paulo@0
|
383 node->last_connect_duration = time (NULL) - node->start_connect_time;
|
paulo@0
|
384 node->total_connect_duration += node->last_connect_duration;
|
paulo@0
|
385
|
paulo@0
|
386 gt_node_state_set (node, GT_NODE_DISCONNECTED);
|
paulo@0
|
387 }
|
paulo@0
|
388
|
paulo@0
|
389 /*****************************************************************************/
|
paulo@0
|
390
|
paulo@0
|
391 char *gt_node_class_str (gt_node_class_t klass)
|
paulo@0
|
392 {
|
paulo@0
|
393 switch (klass)
|
paulo@0
|
394 {
|
paulo@0
|
395 case GT_NODE_NONE: return "NONE";
|
paulo@0
|
396 case GT_NODE_LEAF: return "LEAF";
|
paulo@0
|
397 case GT_NODE_ULTRA: return "ULTRAPEER";
|
paulo@0
|
398 case GT_NODE_DEAD: return "DEAD (freeing node)";
|
paulo@0
|
399 default: return "<Unknown class>";
|
paulo@0
|
400 }
|
paulo@0
|
401 }
|
paulo@0
|
402
|
paulo@0
|
403 char *gt_node_state_str (gt_node_state_t state)
|
paulo@0
|
404 {
|
paulo@0
|
405 switch (state)
|
paulo@0
|
406 {
|
paulo@0
|
407 case GT_NODE_DISCONNECTED: return "Disconnected";
|
paulo@0
|
408 case GT_NODE_CONNECTING_1: return "Connecting (handshaking)";
|
paulo@0
|
409 case GT_NODE_CONNECTING_2: return "Connecting (awaiting ping response)";
|
paulo@0
|
410 case GT_NODE_CONNECTED: return "Connected";
|
paulo@0
|
411 default: return "<Unknown state>";
|
paulo@0
|
412 }
|
paulo@0
|
413 }
|
paulo@0
|
414
|
paulo@0
|
415 char *gt_node_str (GtNode *node)
|
paulo@0
|
416 {
|
paulo@0
|
417 static char buf[128];
|
paulo@0
|
418
|
paulo@0
|
419 snprintf (buf, sizeof (buf) - 1, "%s:%hu", net_ip_str (node->ip),
|
paulo@0
|
420 node->gt_port);
|
paulo@0
|
421
|
paulo@0
|
422 return buf;
|
paulo@0
|
423 }
|
paulo@0
|
424
|
paulo@0
|
425 void gt_node_state_set (GtNode *node, gt_node_state_t state)
|
paulo@0
|
426 {
|
paulo@0
|
427 gt_node_state_t old_state = node->state;
|
paulo@0
|
428
|
paulo@0
|
429 if (old_state != state)
|
paulo@0
|
430 {
|
paulo@0
|
431 node->state = state;
|
paulo@0
|
432 gt_conn_set_state (node, old_state, state);
|
paulo@0
|
433 }
|
paulo@0
|
434 }
|
paulo@0
|
435
|
paulo@0
|
436 void gt_node_class_set (GtNode *node, gt_node_class_t klass)
|
paulo@0
|
437 {
|
paulo@0
|
438 gt_node_class_t old_class = node->klass;
|
paulo@0
|
439
|
paulo@0
|
440 if (old_class != klass)
|
paulo@0
|
441 {
|
paulo@0
|
442 /* quiet, please */
|
paulo@0
|
443 #if 0
|
paulo@0
|
444 if (old_class == GT_NODE_NONE)
|
paulo@0
|
445 {
|
paulo@0
|
446 GT->dbg (GT, "%-24s %s", gt_node_str (node),
|
paulo@0
|
447 gt_node_class_str (klass));
|
paulo@0
|
448 }
|
paulo@0
|
449 else
|
paulo@0
|
450 {
|
paulo@0
|
451 GT->dbg (GT, "%-24s %s (%s)", gt_node_str (node),
|
paulo@0
|
452 gt_node_class_str (klass), gt_node_class_str (old_class));
|
paulo@0
|
453 }
|
paulo@0
|
454 #endif
|
paulo@0
|
455
|
paulo@0
|
456 node->klass = klass;
|
paulo@0
|
457 gt_conn_set_class (node, old_class, klass);
|
paulo@0
|
458 }
|
paulo@0
|
459 }
|