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