paulo@0: /* paulo@0: * $Id: ping_reply.c,v 1.7 2005/01/04 15:00:52 mkern Exp $ paulo@0: * paulo@0: * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "message/msg_handler.h" paulo@0: paulo@0: #include "gt_node_cache.h" paulo@0: #include "gt_connect.h" paulo@0: #include "gt_bind.h" paulo@0: paulo@0: #include "gt_search.h" paulo@0: #include "gt_share_state.h" paulo@0: #include "gt_query_route.h" paulo@0: #include "gt_stats.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: extern BOOL gt_is_pow2 (uint32_t num); /* ping.c */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Update the port on the first pong or when the pong contains a different paulo@0: * pong. paulo@0: */ paulo@0: static void update_port (GtNode *node, in_port_t new_port) paulo@0: { paulo@0: /* update the port */ paulo@0: node->gt_port = new_port; paulo@0: paulo@0: /* paulo@0: * Test if the node is connectable. This will play with the node's paulo@0: * ->verified and ->firewalled bits. paulo@0: * paulo@0: * This is only important if this node is running as an ultrapeer, because paulo@0: * it lets us know whether we should route queries from firewalled peers paulo@0: * to the remote node. paulo@0: */ paulo@0: if (GT_SELF->klass & GT_NODE_ULTRA) paulo@0: gt_connect_test (node, node->gt_port); paulo@0: } paulo@0: paulo@0: /* paulo@0: * Transition the node into state 'connected', and do various things. This paulo@0: * has become a bit crufty and miscellaneous. TODO: change this to a callback paulo@0: * registration system in gt_node.c, register the callbacks in gt_gnutella.c paulo@0: */ paulo@0: static BOOL complete_connection (GtNode *node) paulo@0: { paulo@0: /* mark this node as now connected */ paulo@0: gt_node_state_set (node, GT_NODE_CONNECTED); paulo@0: paulo@0: /* submit the routing table */ paulo@0: if ((node->klass & GT_NODE_ULTRA) && paulo@0: !(GT_SELF->klass & GT_NODE_ULTRA)) paulo@0: { paulo@0: query_route_table_submit (GT_CONN(node)); paulo@0: } paulo@0: paulo@0: /* submit unfinished searches soon */ paulo@0: gt_searches_submit (GT_CONN(node), 30 * SECONDS); paulo@0: paulo@0: /* let the bind subsystem send a ConnectBack for tracking firewalled paulo@0: * status */ paulo@0: gt_bind_completed_connection (node); paulo@0: paulo@0: if (!(node->share_state = gt_share_state_new ())) paulo@0: return FALSE; paulo@0: paulo@0: gt_share_state_update (node); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: GT_MSG_HANDLER(gt_msg_ping_reply) paulo@0: { paulo@0: in_port_t port; paulo@0: in_addr_t ip; paulo@0: uint32_t files; paulo@0: uint32_t size_kb; paulo@0: gt_node_class_t klass; paulo@0: paulo@0: port = gt_packet_get_port (packet); paulo@0: ip = gt_packet_get_ip (packet); paulo@0: files = gt_packet_get_uint32 (packet); paulo@0: size_kb = gt_packet_get_uint32 (packet); paulo@0: paulo@0: /* this will keep the node from being disconnected by idle-check loop */ paulo@0: if (node->pings_with_noreply > 0) paulo@0: node->pings_with_noreply = 0; paulo@0: paulo@0: /* update stats and port */ paulo@0: if (gt_packet_ttl (packet) == 1 && gt_packet_hops (packet) == 0) paulo@0: { paulo@0: /* check if this is the first ping response on this connection */ paulo@0: if (node->state == GT_NODE_CONNECTING_2) paulo@0: { paulo@0: if (!complete_connection (node)) paulo@0: { paulo@0: gt_node_disconnect (c); paulo@0: return; paulo@0: } paulo@0: } paulo@0: paulo@0: if (ip == node->ip) paulo@0: { paulo@0: if (node->gt_port != port || !node->verified) paulo@0: update_port (node, port); paulo@0: paulo@0: /* update stats information */ paulo@0: node->size_kb = size_kb; paulo@0: node->files = files; paulo@0: paulo@0: /* don't add this node to the cache */ paulo@0: return; paulo@0: } paulo@0: paulo@0: /* paulo@0: * Morpheus nodes send pongs for other nodes with Hops=1. If paulo@0: * the IP doesn't equal the observed IP, then add the node to the paulo@0: * node cache. This may create problems with trying to connect twice paulo@0: * to some users, though. paulo@0: */ paulo@0: } paulo@0: paulo@0: /* add this node to the cache */ paulo@0: klass = GT_NODE_LEAF; paulo@0: paulo@0: /* LimeWire marks ultrapeer pongs by making files size a power of two */ paulo@0: if (size_kb >= 8 && gt_is_pow2 (size_kb)) paulo@0: klass = GT_NODE_ULTRA; paulo@0: paulo@0: /* don't register this node if its local and the peer isnt */ paulo@0: if (gt_is_local_ip (ip, node->ip)) paulo@0: return; paulo@0: paulo@0: /* keep track of stats from pongs */ paulo@0: gt_stats_accumulate (ip, port, node->ip, files, size_kb); paulo@0: paulo@0: /* TODO: check uptime GGEP extension and add it here */ paulo@0: gt_node_cache_add_ipv4 (ip, port, klass, time (NULL), 0, node->ip); paulo@0: gt_node_cache_trace (); paulo@0: }