annotate src/gt_bind.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_bind.c,v 1.6 2004/04/17 06:08:14 hipnod Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2001-2004 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 #include "gt_node.h"
paulo@0 19 #include "gt_node_list.h"
paulo@0 20
paulo@0 21 #include "gt_accept.h"
paulo@0 22 #include "gt_bind.h"
paulo@0 23 #include "gt_packet.h"
paulo@0 24 #include "gt_share_state.h"
paulo@0 25
paulo@0 26 #include "transfer/push_proxy.h"
paulo@0 27
paulo@0 28 /*****************************************************************************/
paulo@0 29
paulo@0 30 /* global node pointer for this machine */
paulo@0 31 GtNode *GT_SELF;
paulo@0 32
paulo@0 33 /*****************************************************************************/
paulo@0 34
paulo@0 35 /* how often to retest the firewalled status of this node */
paulo@0 36 #define FW_RETEST_INTERVAL (60 * MINUTES)
paulo@0 37
paulo@0 38 /* maximum amount of time before last connect read from disk before
paulo@0 39 * reassuming this node is firewalled on startup */
paulo@0 40 #define FW_MAX_RETEST_TIME (7 * EDAYS)
paulo@0 41
paulo@0 42 /*****************************************************************************/
paulo@0 43
paulo@0 44 /* time at which this node started running */
paulo@0 45 static time_t start_time;
paulo@0 46
paulo@0 47 /* last time we got a connection on this host */
paulo@0 48 static time_t last_connect;
paulo@0 49
paulo@0 50 /* retest firewalled status every so often */
paulo@0 51 static timer_id fw_test_timer;
paulo@0 52
paulo@0 53 /*****************************************************************************/
paulo@0 54
paulo@0 55 static char *fw_file (void)
paulo@0 56 {
paulo@0 57 return gift_conf_path ("Gnutella/fwstatus");
paulo@0 58 }
paulo@0 59
paulo@0 60 static void save_fw_status (void)
paulo@0 61 {
paulo@0 62 FILE *f;
paulo@0 63
paulo@0 64 if (!(f = fopen (fw_file (), "w")))
paulo@0 65 return;
paulo@0 66
paulo@0 67 /*
paulo@0 68 * Store the last successful time of connect along with the port.
paulo@0 69 */
paulo@0 70 fprintf (f, "%lu %hu\n", last_connect, GT_SELF->gt_port);
paulo@0 71
paulo@0 72 fclose (f);
paulo@0 73 }
paulo@0 74
paulo@0 75 /* returns whether or not the node is firewalled */
paulo@0 76 static BOOL load_fw_status (in_port_t port)
paulo@0 77 {
paulo@0 78 FILE *f;
paulo@0 79 char buf[RW_BUFFER];
paulo@0 80 time_t last_time;
paulo@0 81 in_port_t last_port;
paulo@0 82
paulo@0 83 if (!(f = fopen (fw_file (), "r")))
paulo@0 84 return TRUE;
paulo@0 85
paulo@0 86 if (fgets (buf, sizeof (buf) - 1, f) == NULL)
paulo@0 87 {
paulo@0 88 fclose (f);
paulo@0 89 return TRUE;
paulo@0 90 }
paulo@0 91
paulo@0 92 fclose (f);
paulo@0 93
paulo@0 94 /* Store the time of the last successful connect, so
paulo@0 95 * > 0 means _not_ firewalled */
paulo@0 96 if (sscanf (buf, "%lu %hu", &last_time, &last_port) != 2)
paulo@0 97 return TRUE;
paulo@0 98
paulo@0 99 /* if we got a connection within a recent time at some point with this
paulo@0 100 * port, mark not firewalled */
paulo@0 101 if ((last_time > 0 && last_time < FW_MAX_RETEST_TIME) &&
paulo@0 102 last_port == port)
paulo@0 103 {
paulo@0 104 last_connect = last_time;
paulo@0 105 return FALSE;
paulo@0 106 }
paulo@0 107
paulo@0 108 return TRUE;
paulo@0 109 }
paulo@0 110
paulo@0 111 static gt_node_class_t read_class (void)
paulo@0 112 {
paulo@0 113 char *klass;
paulo@0 114
paulo@0 115 klass = gt_config_get_str ("main/class");
paulo@0 116
paulo@0 117 if (klass && strstr (klass, "ultra"))
paulo@0 118 return GT_NODE_ULTRA;
paulo@0 119
paulo@0 120 return GT_NODE_LEAF;
paulo@0 121 }
paulo@0 122
paulo@0 123 static void setup_self (GtNode *node, TCPC *c, in_port_t port)
paulo@0 124 {
paulo@0 125 /* load the firewalled status of this port */
paulo@0 126 node->firewalled = load_fw_status (port);
paulo@0 127
paulo@0 128 /* attach the connection to this node */
paulo@0 129 gt_node_connect (node, c);
paulo@0 130 node->gt_port = port;
paulo@0 131
paulo@0 132 /* load the class for this node */
paulo@0 133 node->klass = read_class ();
paulo@0 134
paulo@0 135 input_add (c->fd, c, INPUT_READ,
paulo@0 136 (InputCallback)gnutella_handle_incoming, FALSE);
paulo@0 137 }
paulo@0 138
paulo@0 139 static GtNode *bind_gnutella_port (in_port_t port)
paulo@0 140 {
paulo@0 141 GtNode *node;
paulo@0 142 TCPC *c;
paulo@0 143
paulo@0 144 GT->DBGFN (GT, "entered");
paulo@0 145
paulo@0 146 if (!(node = gt_node_new ()))
paulo@0 147 return NULL;
paulo@0 148
paulo@0 149 /* assume sane defaults in case the bind fails */
paulo@0 150 node->gt_port = 0;
paulo@0 151 node->firewalled = TRUE;
paulo@0 152 node->klass = GT_NODE_LEAF;
paulo@0 153
paulo@0 154 if (!port || !(c = tcp_bind (port, FALSE)))
paulo@0 155 {
paulo@0 156 GT->warn (GT, "Failed binding port %d, setting firewalled", port);
paulo@0 157 return node;
paulo@0 158 }
paulo@0 159
paulo@0 160 GT->dbg (GT, "bound to port %d", port);
paulo@0 161
paulo@0 162 /* setup what will become GT_SELF structure */
paulo@0 163 setup_self (node, c, port);
paulo@0 164
paulo@0 165 return node;
paulo@0 166 }
paulo@0 167
paulo@0 168 static void setup_listening_port (in_port_t port)
paulo@0 169 {
paulo@0 170 GT_SELF = bind_gnutella_port (port);
paulo@0 171
paulo@0 172 /*
paulo@0 173 * If running in local mode, let the user set firewalled status in
paulo@0 174 * GNUTELLA_LOCAL_FW. Not sure if this is a good idea, but it makes
paulo@0 175 * testing easier.
paulo@0 176 */
paulo@0 177 if (GNUTELLA_LOCAL_MODE)
paulo@0 178 {
paulo@0 179 if (GNUTELLA_LOCAL_FW)
paulo@0 180 GT_SELF->firewalled = TRUE;
paulo@0 181 else
paulo@0 182 GT_SELF->firewalled = FALSE;
paulo@0 183 }
paulo@0 184 }
paulo@0 185
paulo@0 186 /*****************************************************************************/
paulo@0 187
paulo@0 188 BOOL gt_bind_is_firewalled (void)
paulo@0 189 {
paulo@0 190 if (!GT_SELF->firewalled)
paulo@0 191 return FALSE;
paulo@0 192
paulo@0 193 /*
paulo@0 194 * Pretend we are not firewalled at the beginning in order
paulo@0 195 * to possibly get more connections, to prove we are not firewalled.
paulo@0 196 */
paulo@0 197 if (gt_uptime () < 10 * EMINUTES)
paulo@0 198 return FALSE;
paulo@0 199
paulo@0 200 /* we are firewalled */
paulo@0 201 return TRUE;
paulo@0 202 }
paulo@0 203
paulo@0 204 void gt_bind_clear_firewalled (void)
paulo@0 205 {
paulo@0 206 time (&last_connect);
paulo@0 207 GT_SELF->firewalled = FALSE;
paulo@0 208 }
paulo@0 209
paulo@0 210 static BOOL fwtest_node (GtNode *node)
paulo@0 211 {
paulo@0 212 GtPacket *pkt;
paulo@0 213 BOOL ret;
paulo@0 214
paulo@0 215 if (!GT_SELF->firewalled)
paulo@0 216 return FALSE;
paulo@0 217
paulo@0 218 if (!(pkt = gt_packet_vendor (GT_VMSG_TCP_CONNECT_BACK)))
paulo@0 219 return FALSE;
paulo@0 220
paulo@0 221 gt_packet_put_port (pkt, GT_SELF->gt_port);
paulo@0 222 GT->DBGSOCK (GT, GT_CONN(node), "fwtesting");
paulo@0 223
paulo@0 224 ret = gt_node_send_if_supported (node, pkt);
paulo@0 225 gt_packet_free (pkt);
paulo@0 226
paulo@0 227 return ret;
paulo@0 228 }
paulo@0 229
paulo@0 230 /*****************************************************************************/
paulo@0 231
paulo@0 232 static void push_proxy_request (GtNode *node)
paulo@0 233 {
paulo@0 234 GtPacket *pkt;
paulo@0 235
paulo@0 236 if (!(pkt = gt_packet_vendor (GT_VMSG_PUSH_PROXY_REQ)))
paulo@0 237 return;
paulo@0 238
paulo@0 239 /* the GUID of the PushProxyRequest must be our client identifier */
paulo@0 240 gt_packet_set_guid (pkt, GT_SELF_GUID);
paulo@0 241
paulo@0 242 gt_node_send_if_supported (node, pkt);
paulo@0 243
paulo@0 244 gt_packet_free (pkt);
paulo@0 245 }
paulo@0 246
paulo@0 247 /*****************************************************************************/
paulo@0 248
paulo@0 249 /*
paulo@0 250 * Called when a new connection to a node has completed.
paulo@0 251 */
paulo@0 252 void gt_bind_completed_connection (GtNode *node)
paulo@0 253 {
paulo@0 254 if (node->vmsgs_sent && dataset_length (node->vmsgs_supported) > 0)
paulo@0 255 return;
paulo@0 256
paulo@0 257 node->vmsgs_sent = TRUE;
paulo@0 258
paulo@0 259 fwtest_node (node);
paulo@0 260 push_proxy_request (node);
paulo@0 261 }
paulo@0 262
paulo@0 263 /*****************************************************************************/
paulo@0 264
paulo@0 265 static GtNode *retest (TCPC *c, GtNode *node, void *udata)
paulo@0 266 {
paulo@0 267 /*
paulo@0 268 * Only clear firewalled status once and if the node supports
paulo@0 269 * the TcpConnectBack message.
paulo@0 270 */
paulo@0 271 if (fwtest_node (node) && GT_SELF->firewalled == FALSE)
paulo@0 272 {
paulo@0 273 GT->DBGFN (GT, "clearing firewalled status");
paulo@0 274 GT_SELF->firewalled = TRUE;
paulo@0 275 }
paulo@0 276
paulo@0 277 return NULL;
paulo@0 278 }
paulo@0 279
paulo@0 280 static BOOL fw_test (void *udata)
paulo@0 281 {
paulo@0 282 gt_conn_foreach (retest, NULL,
paulo@0 283 GT_NODE_NONE, GT_NODE_CONNECTED, 0);
paulo@0 284
paulo@0 285 return TRUE;
paulo@0 286 }
paulo@0 287
paulo@0 288 /*****************************************************************************/
paulo@0 289
paulo@0 290 time_t gt_uptime (void)
paulo@0 291 {
paulo@0 292 return start_time;
paulo@0 293 }
paulo@0 294
paulo@0 295 /*****************************************************************************/
paulo@0 296
paulo@0 297 void gt_bind_init (void)
paulo@0 298 {
paulo@0 299 int port;
paulo@0 300
paulo@0 301 port = gt_config_get_int ("main/port=6346");
paulo@0 302 setup_listening_port (port);
paulo@0 303
paulo@0 304 time (&start_time);
paulo@0 305
paulo@0 306 fw_test_timer = timer_add (FW_RETEST_INTERVAL, fw_test, NULL);
paulo@0 307 }
paulo@0 308
paulo@0 309 void gt_bind_cleanup (void)
paulo@0 310 {
paulo@0 311 save_fw_status ();
paulo@0 312
paulo@0 313 /* gt_node_free() will remove the listening input callback */
paulo@0 314 gt_node_free (GT_SELF);
paulo@0 315 GT_SELF = NULL;
paulo@0 316
paulo@0 317 start_time = 0;
paulo@0 318 last_connect = 0;
paulo@0 319
paulo@0 320 timer_remove_zero (&fw_test_timer);
paulo@0 321 }