paulo@0: /* paulo@0: * $Id: ping.c,v 1.4 2004/03/05 17:49:40 hipnod 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_list.h" paulo@0: #include "gt_bind.h" paulo@0: #include "gt_netorg.h" paulo@0: paulo@0: #include "gt_stats.h" paulo@0: #include "gt_share.h" paulo@0: paulo@0: /******************************************************************************/ paulo@0: paulo@0: BOOL gt_is_pow2 (uint32_t num) paulo@0: { paulo@0: return BOOL_EXPR (num > 0 && (num & (num-1)) == 0); paulo@0: } paulo@0: paulo@0: static uint32_t get_shared_size (unsigned long size_mb) paulo@0: { paulo@0: uint32_t size_kb; paulo@0: paulo@0: size_kb = size_mb * 1024; paulo@0: paulo@0: if (GT_SELF->klass & GT_NODE_ULTRA) paulo@0: /* TODO: round up to nearest power of two >= 8 here */; paulo@0: else if (gt_is_pow2 (size_kb)) paulo@0: size_kb += 5; /* unmakes all powers of two, including 1 */ paulo@0: paulo@0: return size_kb; paulo@0: } paulo@0: paulo@0: /* reply to a ping packet */ paulo@0: static void ping_reply_self (GtPacket *packet, TCPC *c) paulo@0: { paulo@0: unsigned long files, size_kb; paulo@0: double size_mb; paulo@0: GtPacket *reply; paulo@0: paulo@0: share_index (&files, &size_mb); paulo@0: size_kb = get_shared_size (size_mb); paulo@0: paulo@0: if (!(reply = gt_packet_reply (packet, GT_MSG_PING_REPLY))) paulo@0: return; paulo@0: paulo@0: gt_packet_put_port (reply, GT_SELF->gt_port); paulo@0: gt_packet_put_ip (reply, GT_NODE(c)->my_ip); paulo@0: gt_packet_put_uint32 (reply, (uint32_t)files); paulo@0: gt_packet_put_uint32 (reply, (uint32_t)size_kb); paulo@0: paulo@0: if (gt_packet_error (reply)) paulo@0: { paulo@0: gt_packet_free (reply); paulo@0: return; paulo@0: } paulo@0: paulo@0: gt_packet_send (c, reply); paulo@0: gt_packet_free (reply); paulo@0: } paulo@0: paulo@0: /* send info about node dst to node c */ paulo@0: static TCPC *send_status (TCPC *c, GtNode *node, void **data) paulo@0: { paulo@0: GtPacket *pkt = (GtPacket *) data[0]; paulo@0: TCPC *dst = (TCPC *) data[1]; paulo@0: GtPacket *reply; paulo@0: paulo@0: /* don't send a ping for the node itself */ paulo@0: if (c == dst) paulo@0: return NULL; paulo@0: paulo@0: if (!(reply = gt_packet_reply (pkt, GT_MSG_PING_REPLY))) paulo@0: return NULL; paulo@0: paulo@0: gt_packet_put_port (reply, node->gt_port); paulo@0: gt_packet_put_ip (reply, node->ip); paulo@0: gt_packet_put_uint32 (reply, node->files); paulo@0: gt_packet_put_uint32 (reply, node->size_kb); paulo@0: paulo@0: /* set the number of hops travelled to 1 */ paulo@0: gt_packet_set_hops (reply, 1); paulo@0: paulo@0: if (gt_packet_error (reply)) paulo@0: { paulo@0: gt_packet_free (reply); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: gt_packet_send (dst, reply); paulo@0: gt_packet_free (reply); paulo@0: paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: static void handle_crawler_ping (GtPacket *packet, TCPC *c) paulo@0: { paulo@0: void *data[2]; paulo@0: paulo@0: data[0] = packet; paulo@0: data[1] = c; paulo@0: paulo@0: /* reply ourselves */ paulo@0: ping_reply_self (packet, c); paulo@0: paulo@0: /* send pings from connected hosts */ paulo@0: gt_conn_foreach (GT_CONN_FOREACH(send_status), data, paulo@0: GT_NODE_NONE, GT_NODE_CONNECTED, 0); paulo@0: } paulo@0: paulo@0: static BOOL need_connections (void) paulo@0: { paulo@0: BOOL am_ultrapeer; paulo@0: paulo@0: am_ultrapeer = GT_SELF->klass & GT_NODE_ULTRA; paulo@0: paulo@0: /* send a pong if we need connections, but do this paulo@0: * only if this is a search node: leaves shouldnt send pongs */ paulo@0: if (gt_conn_need_connections (GT_NODE_ULTRA) > 0 && am_ultrapeer) paulo@0: return TRUE; paulo@0: paulo@0: /* pretend we need connections temporarily even if we don't in order to paulo@0: * figure out whether we are firewalled or not */ paulo@0: if (gt_uptime () < 10 * EMINUTES && GT_SELF->firewalled) paulo@0: return TRUE; paulo@0: paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: GT_MSG_HANDLER(gt_msg_ping) paulo@0: { paulo@0: time_t last_ping_time, now; paulo@0: uint8_t ttl, hops; paulo@0: paulo@0: now = time (NULL); paulo@0: paulo@0: ttl = gt_packet_ttl (packet); paulo@0: hops = gt_packet_hops (packet); paulo@0: paulo@0: last_ping_time = GT_NODE(c)->last_ping_time; paulo@0: GT_NODE(c)->last_ping_time = now; paulo@0: paulo@0: if ((ttl == 1 && (hops == 0 || hops == 1)) /* tests if host is up */ paulo@0: || GT_NODE(c)->state == GT_NODE_CONNECTING_2 /* need to reply */ paulo@0: || need_connections ()) /* we need connections */ paulo@0: { paulo@0: ping_reply_self (packet, c); paulo@0: paulo@0: if (ttl == 1) paulo@0: return; paulo@0: } paulo@0: else if (ttl == 2 && hops == 0) paulo@0: { paulo@0: /* crawler ping: respond with all connected nodes */ paulo@0: handle_crawler_ping (packet, c); paulo@0: return; paulo@0: } paulo@0: paulo@0: /* dont re-broadcast pings from search nodes if we are not one */ paulo@0: if ((GT_NODE(c)->klass & GT_NODE_ULTRA) && !(GT_SELF->klass & GT_NODE_ULTRA)) paulo@0: return; paulo@0: paulo@0: #if 0 paulo@0: /* notify this host when the pong cache gets full */ paulo@0: pong_cache_waiter_add (c, packet); paulo@0: #endif paulo@0: paulo@0: /* dont accept pings too often */ paulo@0: if (now - last_ping_time < 30 * ESECONDS) paulo@0: return; paulo@0: paulo@0: #if 0 paulo@0: if (!pong_cache_reply (c, packet)) paulo@0: { paulo@0: /* refill the pong cache */ paulo@0: pong_cache_refill (); paulo@0: return; paulo@0: } paulo@0: paulo@0: pong_cache_waiter_remove (c); paulo@0: #endif paulo@0: }