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