paulo@0: /* paulo@0: * $Id: gt_bind.c,v 1.6 2004/04/17 06:08:14 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2001-2004 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 "gt_node.h" paulo@0: #include "gt_node_list.h" paulo@0: paulo@0: #include "gt_accept.h" paulo@0: #include "gt_bind.h" paulo@0: #include "gt_packet.h" paulo@0: #include "gt_share_state.h" paulo@0: paulo@0: #include "transfer/push_proxy.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* global node pointer for this machine */ paulo@0: GtNode *GT_SELF; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* how often to retest the firewalled status of this node */ paulo@0: #define FW_RETEST_INTERVAL (60 * MINUTES) paulo@0: paulo@0: /* maximum amount of time before last connect read from disk before paulo@0: * reassuming this node is firewalled on startup */ paulo@0: #define FW_MAX_RETEST_TIME (7 * EDAYS) paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* time at which this node started running */ paulo@0: static time_t start_time; paulo@0: paulo@0: /* last time we got a connection on this host */ paulo@0: static time_t last_connect; paulo@0: paulo@0: /* retest firewalled status every so often */ paulo@0: static timer_id fw_test_timer; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static char *fw_file (void) paulo@0: { paulo@0: return gift_conf_path ("Gnutella/fwstatus"); paulo@0: } paulo@0: paulo@0: static void save_fw_status (void) paulo@0: { paulo@0: FILE *f; paulo@0: paulo@0: if (!(f = fopen (fw_file (), "w"))) paulo@0: return; paulo@0: paulo@0: /* paulo@0: * Store the last successful time of connect along with the port. paulo@0: */ paulo@0: fprintf (f, "%lu %hu\n", last_connect, GT_SELF->gt_port); paulo@0: paulo@0: fclose (f); paulo@0: } paulo@0: paulo@0: /* returns whether or not the node is firewalled */ paulo@0: static BOOL load_fw_status (in_port_t port) paulo@0: { paulo@0: FILE *f; paulo@0: char buf[RW_BUFFER]; paulo@0: time_t last_time; paulo@0: in_port_t last_port; paulo@0: paulo@0: if (!(f = fopen (fw_file (), "r"))) paulo@0: return TRUE; paulo@0: paulo@0: if (fgets (buf, sizeof (buf) - 1, f) == NULL) paulo@0: { paulo@0: fclose (f); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: fclose (f); paulo@0: paulo@0: /* Store the time of the last successful connect, so paulo@0: * > 0 means _not_ firewalled */ paulo@0: if (sscanf (buf, "%lu %hu", &last_time, &last_port) != 2) paulo@0: return TRUE; paulo@0: paulo@0: /* if we got a connection within a recent time at some point with this paulo@0: * port, mark not firewalled */ paulo@0: if ((last_time > 0 && last_time < FW_MAX_RETEST_TIME) && paulo@0: last_port == port) paulo@0: { paulo@0: last_connect = last_time; paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static gt_node_class_t read_class (void) paulo@0: { paulo@0: char *klass; paulo@0: paulo@0: klass = gt_config_get_str ("main/class"); paulo@0: paulo@0: if (klass && strstr (klass, "ultra")) paulo@0: return GT_NODE_ULTRA; paulo@0: paulo@0: return GT_NODE_LEAF; paulo@0: } paulo@0: paulo@0: static void setup_self (GtNode *node, TCPC *c, in_port_t port) paulo@0: { paulo@0: /* load the firewalled status of this port */ paulo@0: node->firewalled = load_fw_status (port); paulo@0: paulo@0: /* attach the connection to this node */ paulo@0: gt_node_connect (node, c); paulo@0: node->gt_port = port; paulo@0: paulo@0: /* load the class for this node */ paulo@0: node->klass = read_class (); paulo@0: paulo@0: input_add (c->fd, c, INPUT_READ, paulo@0: (InputCallback)gnutella_handle_incoming, FALSE); paulo@0: } paulo@0: paulo@0: static GtNode *bind_gnutella_port (in_port_t port) paulo@0: { paulo@0: GtNode *node; paulo@0: TCPC *c; paulo@0: paulo@0: GT->DBGFN (GT, "entered"); paulo@0: paulo@0: if (!(node = gt_node_new ())) paulo@0: return NULL; paulo@0: paulo@0: /* assume sane defaults in case the bind fails */ paulo@0: node->gt_port = 0; paulo@0: node->firewalled = TRUE; paulo@0: node->klass = GT_NODE_LEAF; paulo@0: paulo@0: if (!port || !(c = tcp_bind (port, FALSE))) paulo@0: { paulo@0: GT->warn (GT, "Failed binding port %d, setting firewalled", port); paulo@0: return node; paulo@0: } paulo@0: paulo@0: GT->dbg (GT, "bound to port %d", port); paulo@0: paulo@0: /* setup what will become GT_SELF structure */ paulo@0: setup_self (node, c, port); paulo@0: paulo@0: return node; paulo@0: } paulo@0: paulo@0: static void setup_listening_port (in_port_t port) paulo@0: { paulo@0: GT_SELF = bind_gnutella_port (port); paulo@0: paulo@0: /* paulo@0: * If running in local mode, let the user set firewalled status in paulo@0: * GNUTELLA_LOCAL_FW. Not sure if this is a good idea, but it makes paulo@0: * testing easier. paulo@0: */ paulo@0: if (GNUTELLA_LOCAL_MODE) paulo@0: { paulo@0: if (GNUTELLA_LOCAL_FW) paulo@0: GT_SELF->firewalled = TRUE; paulo@0: else paulo@0: GT_SELF->firewalled = FALSE; paulo@0: } paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: BOOL gt_bind_is_firewalled (void) paulo@0: { paulo@0: if (!GT_SELF->firewalled) paulo@0: return FALSE; paulo@0: paulo@0: /* paulo@0: * Pretend we are not firewalled at the beginning in order paulo@0: * to possibly get more connections, to prove we are not firewalled. paulo@0: */ paulo@0: if (gt_uptime () < 10 * EMINUTES) paulo@0: return FALSE; paulo@0: paulo@0: /* we are firewalled */ paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: void gt_bind_clear_firewalled (void) paulo@0: { paulo@0: time (&last_connect); paulo@0: GT_SELF->firewalled = FALSE; paulo@0: } paulo@0: paulo@0: static BOOL fwtest_node (GtNode *node) paulo@0: { paulo@0: GtPacket *pkt; paulo@0: BOOL ret; paulo@0: paulo@0: if (!GT_SELF->firewalled) paulo@0: return FALSE; paulo@0: paulo@0: if (!(pkt = gt_packet_vendor (GT_VMSG_TCP_CONNECT_BACK))) paulo@0: return FALSE; paulo@0: paulo@0: gt_packet_put_port (pkt, GT_SELF->gt_port); paulo@0: GT->DBGSOCK (GT, GT_CONN(node), "fwtesting"); paulo@0: paulo@0: ret = gt_node_send_if_supported (node, pkt); paulo@0: gt_packet_free (pkt); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void push_proxy_request (GtNode *node) paulo@0: { paulo@0: GtPacket *pkt; paulo@0: paulo@0: if (!(pkt = gt_packet_vendor (GT_VMSG_PUSH_PROXY_REQ))) paulo@0: return; paulo@0: paulo@0: /* the GUID of the PushProxyRequest must be our client identifier */ paulo@0: gt_packet_set_guid (pkt, GT_SELF_GUID); paulo@0: paulo@0: gt_node_send_if_supported (node, pkt); paulo@0: paulo@0: gt_packet_free (pkt); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Called when a new connection to a node has completed. paulo@0: */ paulo@0: void gt_bind_completed_connection (GtNode *node) paulo@0: { paulo@0: if (node->vmsgs_sent && dataset_length (node->vmsgs_supported) > 0) paulo@0: return; paulo@0: paulo@0: node->vmsgs_sent = TRUE; paulo@0: paulo@0: fwtest_node (node); paulo@0: push_proxy_request (node); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static GtNode *retest (TCPC *c, GtNode *node, void *udata) paulo@0: { paulo@0: /* paulo@0: * Only clear firewalled status once and if the node supports paulo@0: * the TcpConnectBack message. paulo@0: */ paulo@0: if (fwtest_node (node) && GT_SELF->firewalled == FALSE) paulo@0: { paulo@0: GT->DBGFN (GT, "clearing firewalled status"); paulo@0: GT_SELF->firewalled = TRUE; paulo@0: } paulo@0: paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: static BOOL fw_test (void *udata) paulo@0: { paulo@0: gt_conn_foreach (retest, NULL, paulo@0: GT_NODE_NONE, GT_NODE_CONNECTED, 0); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: time_t gt_uptime (void) paulo@0: { paulo@0: return start_time; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_bind_init (void) paulo@0: { paulo@0: int port; paulo@0: paulo@0: port = gt_config_get_int ("main/port=6346"); paulo@0: setup_listening_port (port); paulo@0: paulo@0: time (&start_time); paulo@0: paulo@0: fw_test_timer = timer_add (FW_RETEST_INTERVAL, fw_test, NULL); paulo@0: } paulo@0: paulo@0: void gt_bind_cleanup (void) paulo@0: { paulo@0: save_fw_status (); paulo@0: paulo@0: /* gt_node_free() will remove the listening input callback */ paulo@0: gt_node_free (GT_SELF); paulo@0: GT_SELF = NULL; paulo@0: paulo@0: start_time = 0; paulo@0: last_connect = 0; paulo@0: paulo@0: timer_remove_zero (&fw_test_timer); paulo@0: }