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