Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff 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 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_node.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,459 @@ 1.4 +/* 1.5 + * $Id: gt_node.c,v 1.59 2005/01/04 15:00:51 mkern Exp $ 1.6 + * 1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 + 1.22 +#include "gt_node.h" 1.23 +#include "gt_node_list.h" 1.24 + 1.25 +#include "gt_utils.h" 1.26 + 1.27 +#include "gt_packet.h" 1.28 +#include "gt_query_route.h" 1.29 +#include "gt_share_state.h" 1.30 + 1.31 +#include "gt_node_cache.h" 1.32 + 1.33 +#include "io/rx_stack.h" /* gt_rx_stack_free */ 1.34 +#include "io/tx_stack.h" /* gt_tx_stack_free, gt_tx_stack_queue */ 1.35 + 1.36 +#include "transfer/push_proxy.h" 1.37 + 1.38 +/*****************************************************************************/ 1.39 + 1.40 +/* maps ids -> node, so we dont have to keep TCPC ptrs in 1.41 + * various data structures */ 1.42 +static Dataset *node_ids; 1.43 + 1.44 +/*****************************************************************************/ 1.45 + 1.46 +static void node_add (GtNode *node) 1.47 +{ 1.48 + if (!node_ids) 1.49 + node_ids = dataset_new (DATASET_HASH); 1.50 + 1.51 + if (!node->ip) 1.52 + return; 1.53 + 1.54 + dataset_insert (&node_ids, &node->ip, sizeof (node->ip), node, 0); 1.55 +} 1.56 + 1.57 +static void node_remove (GtNode *node) 1.58 +{ 1.59 + if (!node) 1.60 + return; 1.61 + 1.62 + if (!node->ip) 1.63 + return; 1.64 + 1.65 + dataset_remove (node_ids, &node->ip, sizeof (node->ip)); 1.66 +} 1.67 + 1.68 +/*****************************************************************************/ 1.69 + 1.70 +GtNode *gt_node_new () 1.71 +{ 1.72 + GtNode *node; 1.73 + 1.74 + if (!(node = MALLOC (sizeof (GtNode)))) 1.75 + return NULL; 1.76 + 1.77 + return node; 1.78 +} 1.79 + 1.80 +static void free_node (GtNode *node) 1.81 +{ 1.82 + if (!node) 1.83 + return; 1.84 + 1.85 + gt_node_disconnect (GT_CONN(node)); 1.86 + gt_conn_remove (node); 1.87 + 1.88 + free (node); 1.89 +} 1.90 + 1.91 +/* NOTE: this isnt safe to call at all times */ 1.92 +void gt_node_free (GtNode *node) 1.93 +{ 1.94 + node_remove (node); 1.95 + free_node (node); 1.96 +} 1.97 + 1.98 +/* set the node to use the TCP connection */ 1.99 +void gt_node_connect (GtNode *node, TCPC *c) 1.100 +{ 1.101 + assert (GT_CONN(node) == NULL); 1.102 + assert (GT_NODE(c) == NULL); 1.103 + 1.104 + node->c = c; 1.105 + c->udata = node; 1.106 +} 1.107 + 1.108 +/* put the node into some data structures to keep track of it */ 1.109 +static void track_node (GtNode *node, TCPC *c) 1.110 +{ 1.111 + if (node->ip) 1.112 + assert (node->ip == c->host); 1.113 + 1.114 + /* fill in peer info (in network byte order) */ 1.115 + node->ip = c->host; 1.116 + assert (node->ip != 0); 1.117 + 1.118 + gt_conn_add (node); 1.119 + node_add (node); 1.120 +} 1.121 + 1.122 +/* instantiate a node from an existing connection */ 1.123 +GtNode *gt_node_instantiate (TCPC *c) 1.124 +{ 1.125 + GtNode *node; 1.126 + BOOL existed = FALSE; 1.127 + 1.128 + if (!c || !c->host) 1.129 + return NULL; 1.130 + 1.131 + /* TODO: We should really lookup the port in Listen-IP header, right? */ 1.132 + node = gt_node_lookup (c->host, 0); 1.133 + 1.134 + if (node) 1.135 + { 1.136 + existed = TRUE; 1.137 + 1.138 + /* abort if already connected/connecting */ 1.139 + if (node->state != GT_NODE_DISCONNECTED) 1.140 + return NULL; 1.141 + } 1.142 + else 1.143 + { 1.144 + if (!(node = gt_node_new ())) 1.145 + return NULL; 1.146 + } 1.147 + 1.148 + assert (node->c == NULL); 1.149 + 1.150 + /* attach this node to the connection and vice-versa */ 1.151 + gt_node_connect (node, c); 1.152 + 1.153 + if (!existed) 1.154 + track_node (node, c); 1.155 + 1.156 + return node; 1.157 +} 1.158 + 1.159 +static int free_one (ds_data_t *key, ds_data_t *value, void *udata) 1.160 +{ 1.161 + GtNode *node = value->data; 1.162 + 1.163 + /* don't call gt_node_free here while iterating through the 1.164 + * Dataset because it will cause us to miss items when the 1.165 + * Dataset is resized */ 1.166 + free_node (node); 1.167 + 1.168 + return DS_CONTINUE | DS_REMOVE; 1.169 +} 1.170 + 1.171 +void gt_node_remove_all (void) 1.172 +{ 1.173 + dataset_foreach_ex (node_ids, DS_FOREACH_EX(free_one), NULL); 1.174 + dataset_clear (node_ids); 1.175 + node_ids = NULL; 1.176 +} 1.177 + 1.178 +BOOL gt_node_freeable (GtNode *node) 1.179 +{ 1.180 + time_t now; 1.181 + 1.182 + if (node->state != GT_NODE_DISCONNECTED) 1.183 + return FALSE; 1.184 + 1.185 + now = time (NULL); 1.186 + 1.187 + /* keep hosts with whom we've had a connection for a good while */ 1.188 + if (node->vitality > 0 && now - node->vitality <= 30 * EDAYS) 1.189 + return FALSE; 1.190 + 1.191 + if (now - node->start_connect_time <= 30 * EMINUTES) 1.192 + return FALSE; 1.193 + 1.194 + /* yeah, sure, free the node if you want */ 1.195 + return TRUE; 1.196 +} 1.197 + 1.198 +/*****************************************************************************/ 1.199 + 1.200 +/* 1.201 + * Check if this node supports the vendor message packet inside 'pkt', 1.202 + * and then send the vendor message if so. 1.203 + * 1.204 + * The 'version' field of the VMSG is mangled to be the minimum supported 1.205 + * by both this node and the remote end. 1.206 + */ 1.207 +BOOL gt_node_send_if_supported (GtNode *node, GtPacket *pkt) 1.208 +{ 1.209 + gt_vendor_msg_t vmsg; 1.210 + unsigned char *vendor; 1.211 + uint16_t id; 1.212 + uint16_t ver; 1.213 + uint16_t *send_ver; 1.214 + 1.215 + gt_packet_seek (pkt, GNUTELLA_HDR_LEN); 1.216 + vendor = gt_packet_get_ustr (pkt, 4); 1.217 + id = gt_packet_get_uint16 (pkt); 1.218 + ver = gt_packet_get_uint16 (pkt); 1.219 + 1.220 + if (gt_packet_error (pkt)) 1.221 + return FALSE; 1.222 + 1.223 + memset (&vmsg, 0, sizeof(vmsg)); 1.224 + memcpy (&vmsg.vendor_id, vendor, 4); 1.225 + vmsg.id = id; 1.226 + 1.227 + send_ver = dataset_lookup (node->vmsgs_supported, &vmsg, sizeof(vmsg)); 1.228 + if (!send_ver) 1.229 + return FALSE; 1.230 + 1.231 + /* XXX: we've no good facility for writing in the middle of the packet */ 1.232 + memcpy (&pkt->data[GNUTELLA_HDR_LEN + VMSG_HDR_LEN - 2], send_ver, 2); 1.233 + 1.234 + if (gt_packet_send (GT_CONN(node), pkt) < 0) 1.235 + return FALSE; 1.236 + 1.237 + return TRUE; 1.238 +} 1.239 + 1.240 +BOOL gt_node_send (GtNode *node, GtPacket *packet) 1.241 +{ 1.242 + /* don't queue the packet if the node isn't in a state to send it */ 1.243 + if (!(node->state & (GT_NODE_CONNECTED | GT_NODE_CONNECTING_2))) 1.244 + return FALSE; 1.245 + 1.246 + /* enable this at some point in the future */ 1.247 +#if 0 1.248 + assert (GT_CONN(node) != NULL); 1.249 +#endif 1.250 + if (!GT_CONN(node) || GT_CONN(node)->fd < 0) 1.251 + return FALSE; 1.252 + 1.253 + return gt_tx_stack_queue (node->tx_stack, packet->data, packet->len); 1.254 +} 1.255 + 1.256 +/*****************************************************************************/ 1.257 + 1.258 +GtNode *gt_node_lookup (in_addr_t ip, in_port_t port) 1.259 +{ 1.260 + return dataset_lookup (node_ids, &ip, sizeof (ip)); 1.261 +} 1.262 + 1.263 +GtNode *gt_node_register (in_addr_t ip, in_port_t port, 1.264 + gt_node_class_t klass) 1.265 +{ 1.266 + GtNode *node; 1.267 + 1.268 + if (GNUTELLA_LOCAL_MODE) 1.269 + { 1.270 + if (!net_match_host (ip, "LOCAL")) 1.271 + return NULL; 1.272 + } 1.273 + 1.274 + if (!ip) 1.275 + return NULL; 1.276 + 1.277 + /* TODO: there is probably a problem here if a node is already 1.278 + * connected and we're informed about it falsely some other way. */ 1.279 + if ((node = dataset_lookup (node_ids, &ip, sizeof (ip)))) 1.280 + { 1.281 + if (klass != GT_NODE_NONE) 1.282 + gt_node_class_set (node, klass); 1.283 + 1.284 + return node; 1.285 + } 1.286 + 1.287 + if (!(node = gt_node_new ())) 1.288 + return NULL; 1.289 + 1.290 + node->ip = ip; 1.291 + node->gt_port = port; 1.292 + 1.293 + node_add (node); 1.294 + gt_conn_add (node); 1.295 + 1.296 + if (klass != GT_NODE_NONE) 1.297 + gt_node_class_set (node, klass); 1.298 + 1.299 + /* remove this node from the node cache in order to keep the cache 1.300 + * conherent with the node list */ 1.301 + gt_node_cache_del_ipv4 (ip, port); 1.302 + 1.303 + return node; 1.304 +} 1.305 + 1.306 +void gt_node_error (TCPC *c, const char *fmt, ...) 1.307 +{ 1.308 + static char buf[4096]; 1.309 + va_list args; 1.310 + 1.311 + assert (GT_CONN(GT_NODE(c)) == c); 1.312 + 1.313 + if (!fmt) 1.314 + { 1.315 + GT->DBGSOCK (GT, c, "[%hu] error: %s", GT_NODE(c)->gt_port, 1.316 + GIFT_NETERROR ()); 1.317 + return; 1.318 + } 1.319 + 1.320 + va_start (args, fmt); 1.321 + vsnprintf (buf, sizeof (buf) - 1, fmt, args); 1.322 + va_end (args); 1.323 + 1.324 + GT->DBGSOCK (GT, c, "error: %s", buf); 1.325 +} 1.326 + 1.327 +void gt_node_disconnect (TCPC *c) 1.328 +{ 1.329 + GtNode *node; 1.330 + 1.331 + if (!c) 1.332 + return; 1.333 + 1.334 + node = GT_NODE(c); 1.335 + assert (node->c == c); 1.336 + 1.337 + /* remove node timers */ 1.338 + timer_remove_zero (&node->handshake_timer); 1.339 + timer_remove_zero (&node->search_timer); 1.340 + timer_remove_zero (&node->query_route_timer); 1.341 + 1.342 + /* destroy existing received buffers for this connection */ 1.343 + gt_rx_stack_free (node->rx_stack); 1.344 + node->rx_stack = NULL; 1.345 + 1.346 + /* destroy send buffers */ 1.347 + gt_tx_stack_free (node->tx_stack); 1.348 + node->tx_stack = NULL; 1.349 + 1.350 + /* remove the node from push proxy status */ 1.351 + gt_push_proxy_del (node); 1.352 + 1.353 + /* reset connection status flags */ 1.354 + node->verified = FALSE; 1.355 + node->firewalled = FALSE; 1.356 + node->incoming = FALSE; 1.357 + node->rx_inflated = FALSE; 1.358 + node->tx_deflated = FALSE; 1.359 + node->vmsgs_sent = FALSE; 1.360 + 1.361 + /* close the connection for this node, if any */ 1.362 + tcp_close_null (&node->c); 1.363 + 1.364 + node->pings_with_noreply = 0; 1.365 + 1.366 + /* clear verification connection */ 1.367 + tcp_close_null (&node->gt_port_verify); 1.368 + 1.369 + free (node->ping_guid); 1.370 + node->ping_guid = NULL; 1.371 + 1.372 + dataset_clear (node->hdr); 1.373 + node->hdr = NULL; 1.374 + 1.375 + dataset_clear (node->vmsgs_supported); 1.376 + node->vmsgs_supported = NULL; 1.377 + 1.378 + gt_share_state_free (node->share_state); 1.379 + node->share_state = NULL; 1.380 + 1.381 + gt_query_router_free (node->query_router); 1.382 + node->query_router = NULL; 1.383 + node->query_router_counter = 0; 1.384 + 1.385 + /* update the last time if this node was connected */ 1.386 + node->last_connect_duration = time (NULL) - node->start_connect_time; 1.387 + node->total_connect_duration += node->last_connect_duration; 1.388 + 1.389 + gt_node_state_set (node, GT_NODE_DISCONNECTED); 1.390 +} 1.391 + 1.392 +/*****************************************************************************/ 1.393 + 1.394 +char *gt_node_class_str (gt_node_class_t klass) 1.395 +{ 1.396 + switch (klass) 1.397 + { 1.398 + case GT_NODE_NONE: return "NONE"; 1.399 + case GT_NODE_LEAF: return "LEAF"; 1.400 + case GT_NODE_ULTRA: return "ULTRAPEER"; 1.401 + case GT_NODE_DEAD: return "DEAD (freeing node)"; 1.402 + default: return "<Unknown class>"; 1.403 + } 1.404 +} 1.405 + 1.406 +char *gt_node_state_str (gt_node_state_t state) 1.407 +{ 1.408 + switch (state) 1.409 + { 1.410 + case GT_NODE_DISCONNECTED: return "Disconnected"; 1.411 + case GT_NODE_CONNECTING_1: return "Connecting (handshaking)"; 1.412 + case GT_NODE_CONNECTING_2: return "Connecting (awaiting ping response)"; 1.413 + case GT_NODE_CONNECTED: return "Connected"; 1.414 + default: return "<Unknown state>"; 1.415 + } 1.416 +} 1.417 + 1.418 +char *gt_node_str (GtNode *node) 1.419 +{ 1.420 + static char buf[128]; 1.421 + 1.422 + snprintf (buf, sizeof (buf) - 1, "%s:%hu", net_ip_str (node->ip), 1.423 + node->gt_port); 1.424 + 1.425 + return buf; 1.426 +} 1.427 + 1.428 +void gt_node_state_set (GtNode *node, gt_node_state_t state) 1.429 +{ 1.430 + gt_node_state_t old_state = node->state; 1.431 + 1.432 + if (old_state != state) 1.433 + { 1.434 + node->state = state; 1.435 + gt_conn_set_state (node, old_state, state); 1.436 + } 1.437 +} 1.438 + 1.439 +void gt_node_class_set (GtNode *node, gt_node_class_t klass) 1.440 +{ 1.441 + gt_node_class_t old_class = node->klass; 1.442 + 1.443 + if (old_class != klass) 1.444 + { 1.445 + /* quiet, please */ 1.446 +#if 0 1.447 + if (old_class == GT_NODE_NONE) 1.448 + { 1.449 + GT->dbg (GT, "%-24s %s", gt_node_str (node), 1.450 + gt_node_class_str (klass)); 1.451 + } 1.452 + else 1.453 + { 1.454 + GT->dbg (GT, "%-24s %s (%s)", gt_node_str (node), 1.455 + gt_node_class_str (klass), gt_node_class_str (old_class)); 1.456 + } 1.457 +#endif 1.458 + 1.459 + node->klass = klass; 1.460 + gt_conn_set_class (node, old_class, klass); 1.461 + } 1.462 +}