paulo@0: /* paulo@0: * $Id: push_proxy.c,v 1.2 2004/06/02 07:13:02 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 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: paulo@0: #include "gt_node.h" paulo@0: #include "gt_packet.h" /* htovs() */ paulo@0: paulo@0: #include "transfer/push_proxy.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * All this GGEP stuff will move somewhere else soon. Just haven't decided paulo@0: * where to put it. paulo@0: */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #define PROXY_DEBUG gt_config_get_int("push_proxy/debug=0") paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #define GGEP_HDR_LEN (1) /* 0xc3 */ paulo@0: #define GGEP_EXT_MAX_LEN (63) /* use only a single len chunk for now */ paulo@0: paulo@0: enum ggep_length_flags paulo@0: { paulo@0: GGEP_LEN_NOT_LAST = 0x80, /* not the last chunk */ paulo@0: GGEP_LEN_LAST = 0x40, /* last length chunk flag */ paulo@0: }; paulo@0: paulo@0: enum ggep_extension_flags paulo@0: { paulo@0: GGEP_EXTF_LAST = 0x80, /* last extension */ paulo@0: GGEP_EXTF_COBS_ENCODED = 0x40, /* encoded with COBS-encoding */ paulo@0: GGEP_EXTF_COMPRESSED = 0x20, /* compressed */ paulo@0: GGEP_EXTF_RESERVED = 0x10, /* reserved */ paulo@0: paulo@0: /* lower 4 bits is identifier length */ paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: struct proxy_addr paulo@0: { paulo@0: in_addr_t ipv4; paulo@0: in_port_t port; paulo@0: }; paulo@0: paulo@0: typedef struct ggep paulo@0: { paulo@0: uint8_t *block; paulo@0: size_t block_len; paulo@0: size_t offset; paulo@0: size_t last_ext_offset; paulo@0: BOOL error; paulo@0: } ggep_t; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static ggep_t proxy_block; paulo@0: static Dataset *proxies; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static BOOL ggep_grow (ggep_t *ggep, size_t sz) paulo@0: { paulo@0: uint8_t *new_block; paulo@0: size_t new_size; paulo@0: paulo@0: new_size = ggep->block_len + sz; paulo@0: if (!(new_block = realloc (ggep->block, new_size))) paulo@0: return FALSE; paulo@0: paulo@0: ggep->block = new_block; paulo@0: ggep->block_len = new_size; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static BOOL ggep_init (ggep_t *ggep) paulo@0: { paulo@0: ggep->block_len = 1; paulo@0: ggep->offset = 1; paulo@0: ggep->last_ext_offset = 0; paulo@0: ggep->error = FALSE; paulo@0: paulo@0: if (!(ggep->block = malloc (1))) paulo@0: return FALSE; paulo@0: paulo@0: /* append magic byte */ paulo@0: ggep->block[0] = 0xc3; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static void ggep_append (ggep_t *ggep, const void *data, size_t data_size) paulo@0: { paulo@0: if (ggep_grow (ggep, data_size) == FALSE) paulo@0: { paulo@0: ggep->error = TRUE; paulo@0: return; paulo@0: } paulo@0: paulo@0: assert (ggep->offset + data_size <= ggep->block_len); paulo@0: memcpy (&ggep->block[ggep->offset], data, data_size); paulo@0: ggep->offset += data_size; paulo@0: } paulo@0: paulo@0: /* TODO: this should use a writev()-like interface */ paulo@0: static BOOL ggep_append_extension (ggep_t *ggep, const char *id, paulo@0: const uint8_t *data, size_t data_len) paulo@0: { paulo@0: uint8_t id_len; paulo@0: uint8_t ext_flags; paulo@0: uint8_t ext_data_len; paulo@0: paulo@0: id_len = strlen (id) & 0x0f; paulo@0: paulo@0: /* disable Encoding, Compression, LastExtension bits, len in low 4 bits */ paulo@0: ext_flags = id_len; paulo@0: paulo@0: /* track position of last extension for setting LastExtension bit */ paulo@0: ggep->last_ext_offset = ggep->offset; paulo@0: paulo@0: /* extension flag header */ paulo@0: ggep_append (ggep, &ext_flags, 1); paulo@0: paulo@0: /* extension identifier */ paulo@0: ggep_append (ggep, id, id_len); paulo@0: paulo@0: assert (data_len <= GGEP_EXT_MAX_LEN); paulo@0: ext_data_len = data_len | GGEP_LEN_LAST; /* add last length chunk flag */ paulo@0: paulo@0: /* the extension length */ paulo@0: ggep_append (ggep, &ext_data_len, 1); paulo@0: paulo@0: /* the extension data */ paulo@0: ggep_append (ggep, data, data_len); paulo@0: paulo@0: if (ggep->error) paulo@0: return FALSE; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static BOOL ggep_seal (ggep_t *ggep) paulo@0: { paulo@0: if (ggep->last_ext_offset == 0) paulo@0: return FALSE; paulo@0: paulo@0: /* set the LastExtension bit on the last extension */ paulo@0: ggep->block[ggep->last_ext_offset] |= GGEP_EXTF_LAST; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static void ggep_finish (ggep_t *ggep) paulo@0: { paulo@0: free (ggep->block); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void ds_add_proxy (ds_data_t *key, ds_data_t *value, void **cmp) paulo@0: { paulo@0: uint8_t *push_ext = cmp[0]; paulo@0: size_t *push_ext_len = cmp[1]; paulo@0: in_port_t port; paulo@0: struct proxy_addr *proxy = value->data; paulo@0: paulo@0: port = htovs (proxy->port); paulo@0: paulo@0: if (*push_ext_len + 6 >= GGEP_EXT_MAX_LEN) paulo@0: return; paulo@0: paulo@0: /* build the PUSH extension */ paulo@0: memcpy (&push_ext[*push_ext_len], &proxy->ipv4, 4); *push_ext_len += 4; paulo@0: memcpy (&push_ext[*push_ext_len], &port, 2); *push_ext_len += 2; paulo@0: } paulo@0: paulo@0: static void update_block (ggep_t *ggep) paulo@0: { paulo@0: uint8_t push_ext[GGEP_EXT_MAX_LEN]; /* ugh */ paulo@0: size_t push_ext_len; /* double ugh */ paulo@0: void *cmp[2]; paulo@0: paulo@0: ggep_finish (ggep); paulo@0: paulo@0: if (ggep_init (ggep) == FALSE) paulo@0: return; paulo@0: paulo@0: cmp[0] = push_ext; paulo@0: cmp[1] = &push_ext_len; paulo@0: paulo@0: push_ext_len = 0; paulo@0: dataset_foreach (proxies, DS_FOREACH(ds_add_proxy), cmp); paulo@0: assert (push_ext_len <= GGEP_EXT_MAX_LEN); paulo@0: paulo@0: if (ggep_append_extension (ggep, "PUSH", push_ext, push_ext_len) == FALSE) paulo@0: return; paulo@0: paulo@0: ggep_seal (ggep); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void push_proxy_change (GtNode *node, in_addr_t ipv4, paulo@0: in_port_t port, BOOL add) paulo@0: { paulo@0: struct proxy_addr addr; paulo@0: struct proxy_addr *stored; paulo@0: paulo@0: addr.ipv4 = ipv4; paulo@0: addr.port = port; paulo@0: paulo@0: stored = dataset_lookup (proxies, &node, sizeof(node)); paulo@0: if (PROXY_DEBUG) paulo@0: { paulo@0: if (add && !stored) paulo@0: GT->DBGFN (GT, "adding push proxy %s:%hu", net_ip_str (ipv4), port); paulo@0: else if (!add && stored) paulo@0: GT->DBGFN (GT, "rming push proxy %s:%hu", net_ip_str (ipv4), port); paulo@0: } paulo@0: paulo@0: if (add) paulo@0: dataset_insert (&proxies, &node, sizeof(node), &addr, sizeof(addr)); paulo@0: else paulo@0: dataset_remove (proxies, &node, sizeof(node)); paulo@0: paulo@0: update_block (&proxy_block); paulo@0: } paulo@0: paulo@0: void gt_push_proxy_add (GtNode *node, in_addr_t ipv4, in_port_t port) paulo@0: { paulo@0: assert (node->push_proxy_ip == 0); paulo@0: assert (node->push_proxy_port == 0); paulo@0: paulo@0: push_proxy_change (node, ipv4, port, TRUE); paulo@0: node->push_proxy_ip = ipv4; paulo@0: node->push_proxy_port = port; paulo@0: } paulo@0: paulo@0: /* paulo@0: * This must be called if the port changes, or if the proxy disconnects. paulo@0: */ paulo@0: void gt_push_proxy_del (GtNode *node) paulo@0: { paulo@0: push_proxy_change (node, node->push_proxy_ip, paulo@0: node->push_proxy_port, FALSE); paulo@0: node->push_proxy_ip = 0; paulo@0: node->push_proxy_port = 0; paulo@0: } paulo@0: paulo@0: BOOL gt_push_proxy_get_ggep_block (uint8_t **block, size_t *block_len) paulo@0: { paulo@0: if (dataset_length (proxies) == 0) paulo@0: return FALSE; paulo@0: paulo@0: *block = proxy_block.block; paulo@0: *block_len = proxy_block.block_len; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_push_proxy_init (void) paulo@0: { paulo@0: ggep_init (&proxy_block); paulo@0: } paulo@0: paulo@0: void gt_push_proxy_cleanup (void) paulo@0: { paulo@0: dataset_clear (proxies); paulo@0: proxies = NULL; paulo@0: paulo@0: ggep_finish (&proxy_block); paulo@0: }