annotate 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
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 }