Пример #1
0
/**
 * Notification from the socket layer that we got a new datagram.
 * If `truncated' is true, then the message was too large for the
 * socket buffer.
 */
static void
urpc_received(struct gnutella_socket *s, bool truncated)
{
	struct urpc_cb *ucb;

	inet_udp_got_incoming(s->addr);

	ucb = htable_lookup(pending, s);

	if (NULL == ucb) {
		g_warning("UDP got unexpected %s%zu-byte RPC reply from %s",
			truncated ? "truncated " : "", s->pos,
			host_addr_port_to_string(s->addr, s->port));
		return;
	}

	if (GNET_PROPERTY(udp_debug) > 1) {
		g_debug("UDP [%s] got %s%zu-byte RPC reply from %s",
			ucb->what, truncated ? "truncated " : "", s->pos,
			host_addr_port_to_string(s->addr, s->port));
	}

	/*
	 * Invoke user callback so that reply can be processed.
	 * Then discard the socket.
	 */

	(*ucb->cb)(URPC_REPLY, s->addr, s->port, s->buf, s->pos, ucb->arg);
	urpc_cb_free(ucb, FALSE);
}
Пример #2
0
static void
host_lookup_callback(const gchar *hostname, gpointer key)
{
	const struct nid *node_id = key;
	gnet_node_info_t info;
	struct node_data *data;
	host_addr_t addr;
	guint16 port;

	if (!ht_pending_lookups)
		goto finish;

	if (!remove_item(ht_pending_lookups, node_id))
		goto finish;

	data = find_node(node_id);
	if (!data)
		goto finish;

	guc_node_fill_info(node_id, &info);
	g_assert(node_id == info.node_id);
	
	addr = info.addr;
	port = info.port;
	guc_node_clear_info(&info);

	WFREE_NULL(data->host, data->host_size);
	
	if (hostname) {
		const gchar *host;
		gchar *to_free;

		if (utf8_is_valid_string(hostname)) {
			to_free = NULL;
			host = hostname;
		} else {
			to_free = locale_to_utf8_normalized(hostname, UNI_NORM_GUI);
			host = to_free;
		}
		
		data->host_size = w_concat_strings(&data->host,
							host, " (",
							host_addr_port_to_string(addr, port), ")",
							(void *) 0);

		G_FREE_NULL(to_free);
	} else {
		statusbar_gui_warning(10,
			_("Reverse lookup for %s failed"), host_addr_to_string(addr));
		data->host_size = w_concat_strings(&data->host,
							host_addr_port_to_string(addr, port),
							(void *) 0);
	}

finish:
	nid_unref(node_id);
}
Пример #3
0
/**
 * Log whitelist item.
 */
static void
log_whitelist_item(const struct whitelist *item, const char *what)
{
	const char *host;
	uint8 bits;

	if (item->host != NULL) {
		host = 0 == item->port ? item->host->name :
			host_port_to_string(item->host->name, ipv4_unspecified, item->port);
	} else {
		host = 0 == item->port ? host_addr_to_string(item->addr) :
			host_addr_port_to_string(item->addr, item->port);
	}

	if (is_host_addr(item->addr)) {
		bits = item->bits == addr_default_mask(item->addr) ? 0 : item->bits;
	} else {
		bits = 0;
	}

	g_debug("WLIST %s %s%s%s%s", what,
		item->use_tls ? "tls:" : "", host,
		bits == 0 ? "" : "/",
		bits == 0 ? "" : uint32_to_string(bits));
}
Пример #4
0
static ssize_t
tls_write_intern(struct wrap_io *wio, const void *buf, size_t size)
{
    struct gnutella_socket *s = wio->ctx;
    ssize_t ret;

    g_assert((0 == s->tls.snarf) ^ (NULL == buf));
    g_assert((0 == s->tls.snarf) ^ (0 == size));

    size = tls_adjust_send_size(s, size);
    ret = gnutls_record_send(tls_socket_get_session(s), buf, size);
    if (ret < 0) {
        switch (ret) {
        case GNUTLS_E_INTERRUPTED:
        case GNUTLS_E_AGAIN:
            if (0 == s->tls.snarf) {
                s->tls.snarf = size;
                ret = size;
            } else {
                errno = VAL_EAGAIN;
                ret = -1;
            }
            break;
        case GNUTLS_E_PULL_ERROR:
        case GNUTLS_E_PUSH_ERROR:
            /* Logging already done by tls_transport_debug() */
            errno = (SOCK_F_CONNRESET & s->flags) ? ECONNRESET : EIO;
            ret = -1;
            goto finish;

        default:
            if (GNET_PROPERTY(tls_debug)) {
                g_carp("tls_write(): gnutls_record_send(fd=%d) failed: "
                       "host=%s snarf=%zu error=\"%s\"",
                       s->file_desc, host_addr_port_to_string(s->addr, s->port),
                       s->tls.snarf, gnutls_strerror(ret));
            }
            errno = EIO;
            ret = -1;
            goto finish;
        }
    } else {

        if (s->tls.snarf) {
            g_assert(s->tls.snarf >= (size_t) ret);
            s->tls.snarf -= ret;
            errno = VAL_EAGAIN;
            ret = -1;
            goto finish;
        }
    }

    if (s->tls.snarf) {
        tls_socket_evt_change(s, INPUT_EVENT_WX);
    }

finish:
    g_assert(ret == (ssize_t) -1 || (size_t) ret <= size);
    return ret;
}
Пример #5
0
static void
tls_print_session_info(const host_addr_t addr, uint16 port,
                       gnutls_session session, bool incoming)
{
    const char *proto, *cert, *kx, *ciph, *mac, *comp;

    g_return_if_fail(session);

    proto = gnutls_protocol_get_name(gnutls_protocol_get_version(session));
    cert = gnutls_certificate_type_get_name(
               gnutls_certificate_type_get(session));
    kx = gnutls_kx_get_name(gnutls_kx_get(session));
    comp = gnutls_compression_get_name(gnutls_compression_get(session));
    ciph = gnutls_cipher_get_name(gnutls_cipher_get(session));
    mac = gnutls_mac_get_name(gnutls_mac_get (session));

    g_debug(
        "TLS session info (%s):\n"
        "    Host:         %s\n"
        "    Protocol:     %s\n"
        "    Certificate:  %s\n"
        "    Key Exchange: %s\n"
        "    Cipher:       %s\n"
        "    MAC:          %s\n"
        "    Compression:  %s",
        incoming ? "incoming" : "outgoing",
        host_addr_port_to_string(addr, port),
        NULL_STRING(proto),
        NULL_STRING(cert),
        NULL_STRING(kx),
        NULL_STRING(ciph),
        NULL_STRING(mac),
        NULL_STRING(comp)
    );
}
Пример #6
0
void
hsep_connection_init(struct gnutella_node *n, uint8 major, uint8 minor)
{
	static const hsep_ctx_t zero_hsep;
	time_t now = tm_time();
	uint i;

	g_assert(n);

	if (GNET_PROPERTY(hsep_debug) > 1)
		printf("HSEP: Initializing node %s\n",
			host_addr_port_to_string(n->addr, n->port));

	WALLOC(n->hsep);
	*n->hsep = zero_hsep; /* Initializes everything to 0 */
	n->hsep->last_sent = now;
	n->hsep->major = major;
	n->hsep->minor = minor;

	/* this is what we know before receiving the first message */

	for (i = 1; i < G_N_ELEMENTS(hsep_global_table); i++) {
		n->hsep->table[i][HSEP_IDX_NODES] = 1;
		hsep_global_table[i][HSEP_IDX_NODES]++;
	}


	hsep_sanity_check();

	hsep_fire_global_table_changed(now);
}
Пример #7
0
/**
 * Adds the given node to the gui.
 */
void
nodes_gui_add_node(gnet_node_info_t *n)
{
    GtkCList *clist_nodes;
	const gchar *titles[c_gnet_num];
	gchar proto_tmp[32];
    gint row;

    g_assert(n != NULL);

   	str_bprintf(proto_tmp, sizeof proto_tmp, "%d.%d",
		n->proto_major, n->proto_minor);

    titles[c_gnet_host]       = host_addr_port_to_string(n->addr, n->port);
    titles[c_gnet_flags]      = "...";
    titles[c_gnet_user_agent] = n->vendor
									? lazy_utf8_to_locale(n->vendor)
									: "...";
    titles[c_gnet_loc]        = iso3166_country_cc(n->country);
    titles[c_gnet_version]    = proto_tmp;
    titles[c_gnet_connected]  = "...";
    titles[c_gnet_uptime]     = "...";
    titles[c_gnet_info]       = "...";

    clist_nodes = GTK_CLIST(gui_main_window_lookup("clist_nodes"));

    row = gtk_clist_append(clist_nodes, (gchar **) titles); /* override const */
    gtk_clist_set_row_data(clist_nodes, row,
		deconstify_gpointer(nid_ref(n->node_id)));
}
Пример #8
0
static inline void
nodes_gui_reverse_lookup_selected_helper(GtkTreeModel *model,
		GtkTreePath *unused_path, GtkTreeIter *iter, gpointer unused_data)
{
	struct node_data *data;
	gnet_node_info_t info;

	(void) unused_path;
	(void) unused_data;

	gtk_tree_model_get(model, iter, 0, &data, (-1));
	g_assert(NULL != find_node(data->node_id));

	if (hset_contains(ht_pending_lookups, data->node_id))
		return;

	guc_node_fill_info(data->node_id, &info);
	g_assert(data->node_id == info.node_id);

	if (!info.is_pseudo) {
		const struct nid *key = nid_ref(data->node_id);

		WFREE_NULL(data->host, data->host_size);
		data->host_size = w_concat_strings(&data->host,
				_("Reverse lookup in progress..."),
				" (", host_addr_port_to_string(info.addr, info.port), ")",
				(void *) 0);

		hset_insert(ht_pending_lookups, key);
		adns_reverse_lookup(info.addr, host_lookup_callback,
			deconstify_gpointer(nid_ref(key)));
	}
	guc_node_clear_info(&info);
}
Пример #9
0
/**
 * Adds the given node to the gui.
 */
void
nodes_gui_add_node(gnet_node_info_t *info)
{
	static const struct node_data zero_data;
	struct node_data *data;
	gnet_node_flags_t flags;

    g_return_if_fail(info);
	g_return_if_fail(!htable_contains(nodes_handles, info->node_id));

	WALLOC(data);
	*data = zero_data;

	data->node_id = nid_ref(info->node_id);
	data->user_agent = info->vendor ? atom_str_get(info->vendor) : NULL;
	data->country = info->country;
	data->host_size = w_concat_strings(&data->host,
						host_addr_port_to_string(info->addr, info->port),
						(void *) 0);
	str_bprintf(data->version, sizeof data->version, "%u.%u",
		info->proto_major, info->proto_minor);

	guc_node_fill_flags(data->node_id, &flags);
	nodes_gui_update_node_flags(data, &flags);

	htable_insert(nodes_handles, data->node_id, data);

    gtk_list_store_append(nodes_model, &data->iter);
    gtk_list_store_set(nodes_model, &data->iter, 0, data, (-1));

}
Пример #10
0
/**
 * Send a Gnutella ping to the specified host via UDP, using the
 * specified MUID.
 */
void
udp_connect_back(const host_addr_t addr, uint16 port, const struct guid *muid)
{
	if (udp_send_ping(muid, addr, port, FALSE)) {
		if (GNET_PROPERTY(udp_debug) > 19)
			g_debug("UDP queued connect-back PING #%s to %s\n",
				guid_hex_str(muid), host_addr_port_to_string(addr, port));
	}
}
Пример #11
0
/**
 * Convert magnet source to a string representation.
 *
 * @return A newly allocated string.
 */
char *
magnet_source_to_string(const struct magnet_source *s)
{
	char *url;

	g_return_val_if_fail(s, NULL);

	if (s->url) {
		url = g_strdup(s->url);
	} else {
		char *proxies = NULL;
		const char *host, *prefix;
		char prefix_buf[256];
		char port_buf[16];

		if (s->guid) {
			char guid_buf[GUID_HEX_SIZE + 1];
			
			guid_to_string_buf(s->guid, guid_buf, sizeof guid_buf);
			concat_strings(prefix_buf, sizeof prefix_buf,
				"push://", guid_buf, (void *) 0);
			prefix = prefix_buf;
		} else {
			prefix = "http://";
		}
		
		port_buf[0] = '\0';
		if (s->hostname) {
			host = s->hostname;
			if (80 != s->port) {
				str_bprintf(port_buf, sizeof port_buf, ":%u",
					(unsigned) s->port);
			}
		} else if (s->guid) {
			proxies = proxies_to_string(s->proxies);
			host = proxies;
		} else {
			host = host_addr_port_to_string(s->addr, s->port);
		}
		if (s->path) {
			url = g_strconcat(prefix, host, port_buf, s->path, (void *) 0);
		} else if (s->sha1) {
			url = g_strconcat(prefix, host, port_buf,
					"/uri-res/N2R?", bitprint_to_urn_string(s->sha1, s->tth),
					(void *) 0);
		} else {
			url = g_strconcat(prefix, host, port_buf, "/", (void *) 0);
		}

		HFREE_NULL(proxies);
	}

	return url;
}
Пример #12
0
static inline void
tls_transport_debug(const char *op, const struct gnutella_socket *s,
                    size_t size, ssize_t ret)
{
    if ((ssize_t) -1 == ret) {
        unsigned level = is_temporary_error(errno) ? 2 : 0;

        if (GNET_PROPERTY(tls_debug) > level) {
            g_debug("%s(): fd=%d size=%zu host=%s ret=-1 errno=%m",
                    op, s->file_desc, size,
                    host_addr_port_to_string(s->addr, s->port));
        }
    } else {
        if (GNET_PROPERTY(tls_debug) > 2) {
            g_debug("%s(): fd=%d size=%zu host=%s ret=%zu",
                    op, s->file_desc, size,
                    host_addr_port_to_string(s->addr, s->port), ret);
        }
    }
}
Пример #13
0
/**
 * Main RPC iteration loop.
 */
static void
natpmp_rpc_iterate(cqueue_t *unused_cq, void *obj)
{
	struct natpmp_rpc *rd = obj;
	int ret;

	natpmp_rpc_check(rd);
	(void) unused_cq;

	if (rd->count++ > rd->retries)
		goto finished;

	ret = urpc_send("NAT-PMP", rd->gateway, NATPMP_SRV_PORT,
			pmsg_start(rd->mb), pmsg_size(rd->mb), rd->timeout,
			natpmp_rpc_reply, rd);

	if (0 != ret) {
		if (GNET_PROPERTY(natpmp_debug)) {
			g_warning("NATPMP could not send \"%s\" #%u to %s: %m",
				natpmp_op_to_string(rd->op), rd->count,
				host_addr_port_to_string(rd->gateway, NATPMP_SRV_PORT));
		}
		goto finished;
	} else {
		if (GNET_PROPERTY(natpmp_debug) > 4) {
			g_debug("NATPMP sent \"%s\" #%u to %s, with %u ms timeout",
				natpmp_op_to_string(rd->op), rd->count,
				host_addr_port_to_string(rd->gateway, NATPMP_SRV_PORT),
				rd->timeout);
		}
	}

	rd->timeout = uint_saturate_mult(rd->timeout, 2);	/* For next time */
	return;

finished:
	natpmp_rpc_error(rd);
}
Пример #14
0
/**
 * Change node's version
 */
void
knode_change_version(knode_t *kn, uint8 major, uint8 minor)
{
	knode_check(kn);

	if (GNET_PROPERTY(dht_debug))
		g_warning("DHT node %s at %s changed from v%u.%u to v%u.%u",
			kuid_to_hex_string(kn->id),
			host_addr_port_to_string(kn->addr, kn->port),
			kn->major, kn->minor, major, minor);

	kn->major = major;
	kn->minor = minor;
}
Пример #15
0
static void
search_stats_notify_routed(query_type_t unused_type, const char *unused_search,
                           const host_addr_t addr, guint16 port)
{
    word_vec_t wovec;

    (void) unused_type;
    (void) unused_search;

    wovec.word = deconstify_char(host_addr_port_to_string(addr, port));
    wovec.len = strlen(wovec.word);
    wovec.amount = 1;

    search_stats_tally(&wovec);
}
Пример #16
0
/**
 * Callback invoked when the HTTP data of the request have been sent.
 */
static void
soap_sent_data(const struct http_async *ha,
	const struct gnutella_socket *s, const char *data, size_t len,
	gboolean deferred)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);

	soap_rpc_check(sr);

	if (GNET_PROPERTY(soap_trace) & SOCK_TRACE_OUT) {
		g_debug("----Sent SOAP HTTP data%s to %s (%u bytes):",
			deferred ? " completely" : "",
			host_addr_port_to_string(s->addr, s->port), (unsigned) len);
		dump_string(stderr, data, len, "----");
	}
}
Пример #17
0
/**
 * RPC timed out.
 */
static void
urpc_timed_out(cqueue_t *unused_cq, void *obj)
{
	struct urpc_cb *ucb = obj;

	urpc_cb_check(ucb);
	(void) unused_cq;

	ucb->timeout_ev = NULL;

	if (GNET_PROPERTY(udp_debug)) {
		g_message("UDP [%s] RPC to %s timed out",
			ucb->what, host_addr_port_to_string(ucb->addr, ucb->port));
	}

	(*ucb->cb)(URPC_TIMEOUT, ucb->addr, ucb->port, NULL, 0, ucb->arg);
	urpc_cb_free(ucb, FALSE);
}
Пример #18
0
static int
tls_flush(struct wrap_io *wio)
{
    struct gnutella_socket *s = wio->ctx;

    socket_check(s);

    if (s->tls.snarf) {
        if (GNET_PROPERTY(tls_debug > 1)) {
            g_debug("tls_flush: snarf=%zu host=%s fd=%d",
                    s->tls.snarf,
                    host_addr_port_to_string(s->addr, s->port), s->file_desc);
        }
        (void ) tls_write_intern(wio, NULL, 0);
        if (s->tls.snarf)
            return -1;
    }
    return 0;
}
Пример #19
0
/**
 * Change node's vendor code.
 */
void
knode_change_vendor(knode_t *kn, vendor_code_t vcode)
{
	knode_check(kn);

	if (GNET_PROPERTY(dht_debug)) {
		char vc_old[VENDOR_CODE_BUFLEN];
		char vc_new[VENDOR_CODE_BUFLEN];

		vendor_code_to_string_buf(kn->vcode.u32, vc_old, sizeof vc_old);
		vendor_code_to_string_buf(vcode.u32, vc_new, sizeof vc_new);

		g_warning("DHT node %s at %s changed vendor from %s to %s",
			kuid_to_hex_string(kn->id),
			host_addr_port_to_string(kn->addr, kn->port),
			vc_old, vc_new);
	}

	kn->vcode = vcode;
}
Пример #20
0
/**
 * Send an UDP ping to the host cache.
 */
static void
uhc_send_ping(void)
{
	g_assert(uhc_connecting);

	guid_random_muid(&uhc_ctx.muid);

	if (udp_send_ping(&uhc_ctx.muid, uhc_ctx.addr, uhc_ctx.port, TRUE)) {

		if (GNET_PROPERTY(bootstrap_debug) || GNET_PROPERTY(log_uhc_pings_tx)) {
			g_debug("BOOT sent UDP SCP ping #%s to %s:%u",
				guid_hex_str(&uhc_ctx.muid), uhc_ctx.host, uhc_ctx.port);
		}

		/*
		 * Give GUI feedback.
		 */
		{
			char msg[256];

			str_bprintf(msg, sizeof msg,
				_("Sent ping to UDP host cache %s:%u"),
				uhc_ctx.host, uhc_ctx.port);
			gcu_statusbar_message(msg);
		}

		/*
		 * Arm a timer to see whether we should not try to ping another
		 * host cache if we don't get a timely reply.
		 */

		g_assert(uhc_ctx.timeout_ev == NULL);

		uhc_ctx.timeout_ev = cq_main_insert(UHC_TIMEOUT,
			uhc_ping_timeout, NULL);
	} else {
		g_warning("BOOT failed to send UDP SCP to %s",
			host_addr_port_to_string(uhc_ctx.addr, uhc_ctx.port));
		uhc_try_next();
	}
}
Пример #21
0
/**
 * @return A pointer to a static buffer holding the host address as string.
 */
const gchar * 
uploads_gui_host_string(const gnet_upload_info_t *u)
{
	static gchar buf[1024];
	const gchar *peer;

	if (u->gnet_port && is_host_addr(u->gnet_addr)) {
		peer = host_addr_port_to_string(u->gnet_addr, u->gnet_port);
	} else {
		peer = NULL;
	}

	concat_strings(buf, sizeof buf,
		host_addr_to_string(u->addr),
		u->encrypted ? " (E) " : " ",
		peer ? " <" : "",
		peer ? peer : "",
		peer ? ">"  : "",
		(void *) 0);

	return buf;
}
Пример #22
0
/**
 * Updates the global HSEP table when a connection is about
 * to be closed. The connection's HSEP data is restored to
 * zero and the CAN_HSEP attribute is cleared.
 */
void
hsep_connection_close(struct gnutella_node *n, bool in_shutdown)
{
	unsigned int i, j;

	g_assert(n);
	g_assert(n->hsep);

	if (GNET_PROPERTY(hsep_debug) > 1)
		printf("HSEP: Deinitializing node %s\n",
			host_addr_port_to_string(n->addr, n->port));

	if (in_shutdown)
		goto cleanup;

	for (i = 1; i < G_N_ELEMENTS(hsep_global_table); i++) {

		for (j = 0; j < G_N_ELEMENTS(hsep_global_table[0]); j++) {
			hsep_global_table[i][j] -= n->hsep->table[i][j];
			n->hsep->table[i][j] = 0;
		}
	}

	if (GNET_PROPERTY(hsep_debug) > 1)
		hsep_dump_table();

	hsep_fire_global_table_changed(tm_time());

	/*
	 * Clear CAN_HSEP attribute so that the HSEP code
	 * will not use the node any longer.
	 */

cleanup:
	n->attrs &= ~NODE_A_CAN_HSEP;
	WFREE(n->hsep);
	n->hsep = NULL;
}
Пример #23
0
void
tls_bye(struct gnutella_socket *s)
{
    int ret;

    socket_check(s);
    g_return_if_fail(s->tls.ctx);
    g_return_if_fail(s->tls.ctx->session);

    if ((SOCK_F_EOF | SOCK_F_SHUTDOWN) & s->flags)
        return;

    if (tls_flush(&s->wio) && GNET_PROPERTY(tls_debug)) {
        g_warning("%s(): tls_flush(fd=%d) failed", G_STRFUNC, s->file_desc);
    }

    ret = gnutls_bye(s->tls.ctx->session,
                     SOCK_CONN_INCOMING != s->direction
                     ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);

    if (ret < 0) {
        switch (ret) {
        case GNUTLS_E_INTERRUPTED:
        case GNUTLS_E_AGAIN:
            break;
        case GNUTLS_E_PULL_ERROR:
        case GNUTLS_E_PUSH_ERROR:
            /* Logging already done by tls_transport_debug() */
            break;
        default:
            if (GNET_PROPERTY(tls_debug)) {
                g_carp("gnutls_bye() failed: host=%s error=%m",
                       host_addr_port_to_string(s->addr, s->port));
            }
        }
    }
}
Пример #24
0
/**
 * Called when a pong with an "IPP" extension was received.
 */
void
uhc_ipp_extract(gnutella_node_t *n, const char *payload, int paylen,
	enum net_type type)
{
	int i, cnt;
	int len = NET_TYPE_IPV6 == type ? 18 : 6;
	const void *p;

	g_assert(0 == paylen % len);

	cnt = paylen / len;

	if (GNET_PROPERTY(bootstrap_debug))
		g_debug("extracting %d host%s in UDP IPP pong #%s from %s",
			cnt, plural(cnt),
			guid_hex_str(gnutella_header_get_muid(&n->header)), node_addr(n));

	for (i = 0, p = payload; i < cnt; i++, p = const_ptr_add_offset(p, len)) {
		host_addr_t ha;
		uint16 port;

		host_ip_port_peek(p, type, &ha, &port);
		hcache_add_caught(HOST_ULTRA, ha, port, "UDP-HC");

		if (GNET_PROPERTY(bootstrap_debug) > 2)
			g_debug("BOOT collected %s from UDP IPP pong from %s",
				host_addr_port_to_string(ha, port), node_addr(n));
	}

	if (!uhc_connecting)
		return;

	/*
	 * Check whether this was a reply from our request.
	 *
	 * The reply could come well after we decided it timed out and picked
	 * another UDP host cache, which ended-up replying, so we must really
	 * check whether we're still in a probing cycle.
	 */

	if (!guid_eq(&uhc_ctx.muid, gnutella_header_get_muid(&n->header)))
		return;

	if (GNET_PROPERTY(bootstrap_debug)) {
		g_debug("BOOT UDP cache \"%s\" replied: got %d host%s from %s",
			uhc_ctx.host, cnt, plural(cnt), node_addr(n));
	}

	/*
	 * Terminate the probing cycle if we got hosts.
	 */

	if (cnt > 0) {
		char msg[256];

		cq_cancel(&uhc_ctx.timeout_ev);
		uhc_connecting = FALSE;

		str_bprintf(msg, sizeof(msg),
			NG_("Got %d host from UDP host cache %s",
				"Got %d hosts from UDP host cache %s",
				cnt),
			cnt, uhc_ctx.host);

		gcu_statusbar_message(msg);
	} else {
		uhc_try_next();
	}
}
Пример #25
0
/**
 * Callback for adns_resolve(), invoked when the resolution is complete.
 */
static void
uhc_host_resolved(const host_addr_t *addrs, size_t n, void *uu_udata)
{
	(void) uu_udata;
	g_assert(addrs);

	/*
	 * If resolution failed, try again if possible.
	 */

	if (0 == n) {
		if (GNET_PROPERTY(bootstrap_debug))
			g_warning("could not resolve UDP host cache \"%s\"",
				uhc_ctx.host);

		uhc_try_next();
		return;
	}

	if (n > 1) {
		size_t i;
		host_addr_t *hav;
		/* Current UHC was moved to tail by uhc_get_next() */
		struct uhc *uhc = hash_list_tail(uhc_list);

		/*
		 * UHC resolved to multiple endpoints. Could be roundrobbin or
		 * IPv4 and IPv6 addresss. Adding them as seperate entries: if the
		 * IPv6 is unreachable we have an opportunity to skip it.
		 * 		-- JA 24/7/2011
		 *
		 * Shuffle the address array before appending them to the UHC list.
		 *		--RAM, 2015-10-01
		 */

		hav = HCOPY_ARRAY(addrs, n);
		SHUFFLE_ARRAY_N(hav, n);

		for (i = 0; i < n; i++) {
			const char *host = host_addr_port_to_string(hav[i], uhc_ctx.port);
			g_debug("BOOT UDP host cache \"%s\" resolved to %s (#%zu)",
				uhc_ctx.host, host, i + 1);

			uhc_list_append(host);
		}

		hash_list_remove(uhc_list, uhc);	/* Replaced by IP address list */
		uhc_free(&uhc);

		/*
		 * We're going to continue and process the first address (in our
		 * shuffled array).  Make sure it is put at the end of the list
		 * and marked as being used, mimicing what uhc_get_next() would do.
		 *		--RAM, 2015-10-01
		 */

		{
			struct uhc key;

			key.host = host_addr_port_to_string(hav[0], uhc_ctx.port);
			uhc = hash_list_lookup(uhc_list, &key);
			g_assert(uhc != NULL);	/* We added the entry above! */
			uhc->stamp = tm_time();
			uhc->used++;
			hash_list_moveto_tail(uhc_list, uhc);
		}

		uhc_ctx.addr = hav[0];		/* Struct copy */
		HFREE_NULL(hav);
	} else {
		uhc_ctx.addr = addrs[0];
	}

	if (GNET_PROPERTY(bootstrap_debug))
		g_debug("BOOT UDP host cache \"%s\" resolved to %s",
			uhc_ctx.host, host_addr_to_string(uhc_ctx.addr));


	/*
	 * Now send the ping.
	 */

	uhc_send_ping();
}
Пример #26
0
/**
 * Notification from the socket layer that we got a new datagram.
 *
 * @param s				the receiving socket (with s->addr and s->port set)
 * @param data			start of received data (not necessarily s->buf)
 * @param len			length of received data (not necessarily s->pos)
 * @param truncated		whether received datagram was truncated
 *
 * If `truncated' is true, then the message was too large for the
 * socket buffer.
 */
void
udp_received(const gnutella_socket_t *s,
	const void *data, size_t len, bool truncated)
{
	gnutella_node_t *n;
	bool bogus = FALSE;
	bool dht = FALSE;
	bool rudp = FALSE;

	/*
	 * This must be regular Gnutella / DHT traffic.
	 */

	inet_udp_got_incoming(s->addr);

	/*
	 * We need to identify semi-reliable UDP traffic early, because that
	 * traffic needs to go through the RX stack to reassemble the final
	 * payload out of the many fragments, or to process the acknowledgments.
	 *
	 * We have to apply heuristics however because the leading 8 bytes could
	 * be just a part of gnutella message (the first 8 bytes of a GUID).
	 * One thing is certain though: if the size is less than that of a a
	 * Gnutella header, it has to be semi-reliable UDP traffic...
	 *
	 * Because semi-reliable UDP uses small payloads, much smaller than our
	 * socket buffer, the datagram cannot be truncated.
	 */

	if (!truncated) {
		enum udp_traffic utp;
		rxdrv_t *rx;

		utp = udp_intuit_traffic_type(s, data, len);

		switch (utp) {
		case GNUTELLA:
			goto unreliable;
		case RUDP:
			rudp = TRUE;
			gnet_stats_count_general(GNR_RUDP_RX_BYTES, len);
			goto rudp;		/* Don't account this message in UDP statistics */
		case DHT:
			dht = TRUE;
			goto unreliable;
		case UNKNOWN:
			goto unknown;
		case SEMI_RELIABLE_GTA:
		case SEMI_RELIABLE_GND:
			break;
		}

		/*
		 * We are going to treat this message a a semi-reliable UDP fragment.
		 *
		 * Account the size of the payload for traffic purposes, then redirect
		 * the message to the RX layer that reassembles and dispatches these
		 * messages.
		 */

		bws_udp_count_read(len, FALSE);	/* We know it's not DHT traffic */

		rx = udp_get_rx_semi_reliable(utp, s->addr, len);

		if (rx != NULL) {
			gnet_host_t from;

			gnet_host_set(&from, s->addr, s->port);
			ut_got_message(rx, data, len, &from);
		}

		return;
	}

unknown:
	/*
	 * Discriminate between Gnutella UDP and DHT messages, so that we
	 * can account received data with the proper bandwidth scheduler.
	 */

	if (len >= GTA_HEADER_SIZE)
		dht = GTA_MSG_DHT == gnutella_header_get_function(data);

	/* FALL THROUGH */

unreliable:
	/*
	 * Account for Gnutella / DHT incoming UDP traffic.
	 */

	bws_udp_count_read(len, dht);

	/* FALL THROUGH */

rudp:
	/*
	 * The RUDP layer is used to implement firewalled-to-firewalled transfers
	 * via a mini TCP-like layer built on top of UDP.  Therefore, it is used
	 * as the basis for higher-level connections (HTTP) and will have to be
	 * accounted for once the type of traffic is known, by upper layers, as
	 * part of the upload/download traffic.
	 *
	 * Of course, the higher levels will never see all the bytes that pass
	 * through, such as acknowledgments or retransmissions, but that is also
	 * the case for TCP-based sockets.
	 *		--RAM, 2012-11-02.
	 */

	/*
	 * If we get traffic from a bogus IP (unroutable), warn, for now.
	 */

	if (bogons_check(s->addr)) {
		bogus = TRUE;

		if (GNET_PROPERTY(udp_debug)) {
			g_warning("UDP %sdatagram (%zu byte%s) received from bogus IP %s",
				truncated ? "truncated " : "",
				len, 1 == len ? "" : "s",
				host_addr_to_string(s->addr));
		}
		gnet_stats_inc_general(GNR_UDP_BOGUS_SOURCE_IP);
	}

	/*
	 * Get proper pseudo-node.
	 *
	 * These routines can return NULL if the address/port combination is
	 * not correct, but this will be handled by udp_is_valid_gnet().
	 */

	n = dht ? node_dht_get_addr_port(s->addr, s->port) :
		node_udp_get_addr_port(s->addr, s->port);

	if (!udp_is_valid_gnet(n, s, truncated, data, len))
		return;

	/*
	 * RUDP traffic does not go to the upper Gnutella processing layers.
	 */

	if (rudp) {
		/* Not ready for prime time */
#if 0
		rudp_handle_packet(s->addr, s->port. data, len);
#endif
		return;
	}

	/*
	 * Process message as if it had been received from regular Gnet by
	 * another node, only we'll use a special "pseudo UDP node" as origin.
	 */

	if (GNET_PROPERTY(udp_debug) > 19 || (bogus && GNET_PROPERTY(udp_debug)))
		g_debug("UDP got %s from %s%s", gmsg_infostr_full(data, len),
			bogus ? "BOGUS " : "", host_addr_port_to_string(s->addr, s->port));

	node_udp_process(n, s, data, len);
}
Пример #27
0
/**
 * Identify the traffic type received on the UDP socket.
 *
 * This routine uses simple heuristics that ensure we're properly discriminating
 * incoming traffic on the UDP socket between regular Gnutella traffic and
 * semi-reliable UDP traffic (which adds a small header before its actual
 * payload).
 *
 * Most messages will be un-ambiguous, and the probabilty of misclassifying
 * an ambiguous message (one that look like valid for both types, based on
 * header inspections) is brought down to less than 1 in a billion, making
 * it perfectly safe in practice.
 *
 * @return intuited type
 */
static enum udp_traffic
udp_intuit_traffic_type(const gnutella_socket_t *s,
	const void *data, size_t len)
{
	enum udp_traffic utp;

	utp = udp_check_semi_reliable(data, len);

	if (len >= GTA_HEADER_SIZE) {
		uint16 size;			/* Payload size, from the Gnutella message */
		gmsg_valid_t valid;

		valid = gmsg_size_valid(data, &size);

		switch (valid) {
		case GMSG_VALID:
		case GMSG_VALID_MARKED:
			if ((size_t) size + GTA_HEADER_SIZE == len) {
				uint8 function, hops, ttl;

				function = gnutella_header_get_function(data);

				/*
				 * If the header cannot be that of a known semi-reliable
				 * UDP protocol, there is no ambiguity.
				 */

				if (UNKNOWN == utp) {
					return GTA_MSG_DHT == function ?
						DHT : GTA_MSG_RUDP == function ?
						RUDP : GNUTELLA;
				}

				/*
				 * Message is ambiguous: its leading header appears to be
				 * both a legitimate Gnutella message and a semi-reliable UDP
				 * header.
				 *
				 * We have to apply some heuristics to decide whether to handle
				 * the message as a Gnutella one or as a semi-reliable UDP one,
				 * knowing that if we improperly classify it, the message will
				 * not be handled correctly.
				 *
				 * Note that this is highly unlikely.  There is about 1 chance
				 * in 10 millions (1 / 2^23 exactly) to mis-interpret a random
				 * Gnutella MUID as the start of one of the semi-reliable
				 * protocols we support.  Our discriminating logic probes a
				 * few more bytes (say 2 at least) which are going to let us
				 * decide with about 99% certainety.  So mis-classification
				 * will occur only once per billion -- a ratio which is OK.
				 *
				 * We could also mistakenely handle a semi-reliable UDP message
				 * as a Gnutella one.  For that to happen, the payload must
				 * contain a field that will be exactly the message size,
				 * a 1 / 2^32 event (since the size is 4 bytes in Gnutella).
				 * However, if message flags are put to use for Gnutella UDP,
				 * this ratio could lower to 1 / 2^16 and that is too large
				 * a chance (about 1.5 in 100,000).
				 *
				 * So when we think an ambiguous message could be a valid
				 * Gnutella message, we also check whether the message could
				 * not be interpreted as a valid semi-reliable UDP one, and
				 * we give priority to that classification if we have a match:
				 * correct sequence number, consistent count and emitting host.
				 * This checks roughly 3 more bytes in the message, yielding
				 * a misclassification for about 1 / 2^(16+24) random cases.
				 */

				hops = gnutella_header_get_hops(data);
				ttl = gnutella_header_get_ttl(data);

				gnet_stats_inc_general(GNR_UDP_AMBIGUOUS);

				if (GNET_PROPERTY(udp_debug)) {
					g_debug("UDP ambiguous datagram from %s: "
						"%zu bytes (%u-byte payload), "
						"function=%u, hops=%u, TTL=%u, size=%u",
						host_addr_port_to_string(s->addr, s->port),
						len, size, function, hops, ttl,
						gnutella_header_get_size(data));
					dump_hex(stderr, "UDP ambiguous datagram", data, len);
				}

				switch (function) {
				case GTA_MSG_DHT:
					/*
					 * A DHT message must be larger than KDA_HEADER_SIZE bytes.
					 */

					if (len < KDA_HEADER_SIZE)
						break;		/* Not a DHT message */

					/*
					 * DHT messages have no bits defined in the size field
					 * to mark them.
					 */

					if (valid != GMSG_VALID)
						break;		/* Higest bit set, not a DHT message */

					/*
					 * If it is a DHT message, it must have a valid opcode.
					 */

					function = kademlia_header_get_function(data);

					if (function > KDA_MSG_MAX_ID)
						break;		/* Not a valid DHT opcode */

					/*
					 * Check the contact address length: it must be 4 in the
					 * header, because there is only room for an IPv4 address.
					 */

					if (!kademlia_header_constants_ok(data))
						break;		/* Not a valid Kademlia header */

					/*
					 * Make sure we're not mistaking a valid semi-reliable UDP
					 * message as a DHT message.
					 */

					if (udp_is_valid_semi_reliable(utp, s, data, len))
						break;		/* Validated it as semi-reliable UDP */

					g_warning("UDP ambiguous message from %s (%zu bytes total),"
						" DHT function is %s",
						host_addr_port_to_string(s->addr, s->port),
						len, kmsg_name(function));

					return DHT;

				case GTA_MSG_INIT:
				case GTA_MSG_PUSH_REQUEST:
				case GTA_MSG_SEARCH:
					/*
					 * No incoming messages of this type can have a TTL
					 * indicating a deflated payload, since there is no
					 * guarantee the host would be able to read it (deflated
					 * UDP is negotiated and can therefore only come from a
					 * response).
					 */

					if (ttl & GTA_UDP_DEFLATED)
						break;			/* Not Gnutella, we're positive */

					/* FALL THROUGH */

				case GTA_MSG_INIT_RESPONSE:
				case GTA_MSG_VENDOR:
				case GTA_MSG_SEARCH_RESULTS:
					/*
					 * To further discriminate, look at the hop count.
					 * Over UDP, the hop count will be low (0 or 1 mostly)
					 * and definitely less than 3 since the only UDP-relayed
					 * messages are from GUESS, and they can travel at most
					 * through a leaf and an ultra node before reaching us.
					 */

					if (hops >= 3U)
						break;			/* Gnutella is very unlikely */

					/*
					 * Check the TTL, cleared from bits that indicate
					 * support for deflated UDP or a deflated payload.
					 * No servent should send a TTL greater than 7, which
					 * was the de-facto limit in the early Gnutella days.
					 */

					if ((ttl & ~(GTA_UDP_CAN_INFLATE | GTA_UDP_DEFLATED)) > 7U)
						break;			/* Gnutella is very unlikely */

					/*
					 * Make sure we're not mistaking a valid semi-reliable UDP
					 * message as a Gnutella message.
					 */

					if (udp_is_valid_semi_reliable(utp, s, data, len))
						break;		/* Validated it as semi-reliable UDP */

					g_warning("UDP ambiguous message from %s (%zu bytes total),"
						" Gnutella function is %s, hops=%u, TTL=%u",
						host_addr_port_to_string(s->addr, s->port),
						len, gmsg_name(function), hops, ttl);

					return GNUTELLA;

				case GTA_MSG_RUDP:
					/*
					 * RUDP traffic is special: the only meaningful fields
					 * of the Gnutella header are the opcode field (which we
					 * have read here since we fall into this case) and the
					 * Gnutella header size.
					 *
					 * The TTL and hops fields cannot be interpreted to
					 * disambiguate, so our only option is deeper inspection.
					 */

					if (udp_is_valid_semi_reliable(utp, s, data, len))
						break;		/* Validated it as semi-reliable UDP */

					g_warning("UDP ambiguous message from %s (%zu bytes total),"
						" interpreted as RUDP packet",
						host_addr_port_to_string(s->addr, s->port), len);

					return RUDP;
					
				case GTA_MSG_STANDARD:	/* Nobody is using this function code */
				default:
					break;				/* Not a function we expect over UDP */
				}

				/*
				 * Will be handled as semi-reliable UDP.
				 */

				gnet_stats_inc_general(GNR_UDP_AMBIGUOUS_AS_SEMI_RELIABLE);

				{
					udp_tag_t tag;

					memcpy(tag.value, data, sizeof tag.value);

					g_warning("UDP ambiguous message (%zu bytes total), "
						"not Gnutella (function is %d, hops=%u, TTL=%u) "
						"handling as semi-reliable UDP (tag=\"%s\")",
						len, function, hops, ttl, udp_tag_to_string(tag));
				}
				return utp;
			}
			/* FALL THROUGH */
		case GMSG_VALID_NO_PROCESS:
		case GMSG_INVALID:
			break;
		}
	}

	return utp;
}
Пример #28
0
/**
 * Look whether the datagram we received is a valid Gnutella packet.
 *
 * The routine also handles traffic statistics (reception and dropping).
 *
 * If ``n'' is not NULL, then ``s'' may be NULL.  If ``n'' is NULL, then
 * ``s'' must not be NULL.
 *
 * @param n				the pseudo UDP reception node (NULL if invalid IP:port)
 * @param s				the socket on which we got the UDP datagram
 * @param truncated		whether datagram was truncated during reception
 * @param header		header of message
 * @param payload		payload of message (maybe not contiguous with header)
 * @param len			total length of message (header + payload)
 *
 * @return TRUE if valid, FALSE otherwise.
 */
bool
udp_is_valid_gnet_split(gnutella_node_t *n, const gnutella_socket_t *s,
	bool truncated, const void *header, const void *payload, size_t len)
{
	const char *msg;
	uint16 size;			/**< Payload size, from the Gnutella message */

	g_assert(s != NULL || n != NULL);

	/*
	 * If we can't get a proper UDP node for this address/port combination,
	 * ignore the message.
	 */

	if (NULL == n) {
		msg = "Invalid address/port combination";
		goto not;
	}

	if (len < GTA_HEADER_SIZE) {
		msg = "Too short";
		goto not;
	}

	/*
	 * We have enough to account for packet reception.
	 * Note that packet could be garbage at this point.
	 */

	memcpy(n->header, header, sizeof n->header);
	n->size = len - GTA_HEADER_SIZE;		/* Payload size if Gnutella msg */

	gnet_stats_count_received_header(n);
	gnet_stats_count_received_payload(n, payload);

	/*
	 * If the message was truncated, then there is also going to be a
	 * size mismatch, but we want to flag truncated messages as being
	 * "too large" because this is mainly why we reject them.  They may
	 * be legitimate Gnutella packets, too bad.
	 */

	if (truncated) {
		msg = "Truncated (too large?)";
		goto too_large;
	}

	/*
	 * Message sizes are architecturally limited to 64K bytes.
	 *
	 * We don't ensure the leading bits are zero in the size field because
	 * this constraint we put allows us to use those bits for flags in
	 * future extensions.
	 *
	 * The downside is that we have only 3 bytes (2 bytes for the size and
	 * 1 byte for the function type) to identify a valid Gnutella packet.
	 */

	switch (gmsg_size_valid(header, &size)) {
	case GMSG_VALID:
	case GMSG_VALID_MARKED:
		break;
	case GMSG_VALID_NO_PROCESS:
		msg = "Header flags undefined for now";
		goto drop;
	case GMSG_INVALID:
		msg = "Invalid size (greater than 64 KiB without flags)";
		goto not;		/* Probably just garbage */
	}

	if ((size_t) size + GTA_HEADER_SIZE != len) {
		msg = "Size mismatch";
		goto not;
	}

	/*
	 * We only support a subset of Gnutella message from UDP.  In particular,
	 * messages like HSEP data, BYE or QRP are not expected!
	 */

	switch (gnutella_header_get_function(header)) {
	case GTA_MSG_INIT:
	case GTA_MSG_INIT_RESPONSE:
	case GTA_MSG_VENDOR:
	case GTA_MSG_STANDARD:
	case GTA_MSG_PUSH_REQUEST:
	case GTA_MSG_SEARCH_RESULTS:
	case GTA_MSG_RUDP:
	case GTA_MSG_DHT:
		return TRUE;
	case GTA_MSG_SEARCH:
		if (settings_is_ultra() && GNET_PROPERTY(enable_guess)) {
			return TRUE;	/* GUESS query accepted */
		}
		msg = "Query from UDP refused";
		goto drop;
	}
	msg = "Gnutella message not processed from UDP";

drop:
	gnet_stats_count_dropped(n, MSG_DROP_UNEXPECTED);
	gnet_stats_inc_general(GNR_UDP_UNPROCESSED_MESSAGE);
	goto log;

too_large:
	gnet_stats_count_dropped(n, MSG_DROP_TOO_LARGE);
	gnet_stats_inc_general(GNR_UDP_UNPROCESSED_MESSAGE);
	goto log;

not:
	gnet_stats_inc_general(GNR_UDP_ALIEN_MESSAGE);
	/* FALL THROUGH */

log:
	if (GNET_PROPERTY(udp_debug)) {
		g_warning("UDP got invalid %sGnutella packet (%zu byte%s) "
			"\"%s\" %sfrom %s: %s",
			socket_udp_is_old(s) ? "OLD " : "",
			len, 1 == len ? "" : "s",
			len >= GTA_HEADER_SIZE ?
				gmsg_infostr_full_split(header, payload, len - GTA_HEADER_SIZE)
				: "<incomplete Gnutella header>",
			truncated ? "(truncated) " : "",
			NULL == n ?
				host_addr_port_to_string(s->addr, s->port) :
				node_infostr(n),
			msg);
		if (len != 0) {
			iovec_t iov[2];
			iovec_set(&iov[0], header, GTA_HEADER_SIZE);
			iovec_set(&iov[1], payload, len - GTA_HEADER_SIZE);
			dump_hex_vec(stderr, "UDP datagram", iov, G_N_ELEMENTS(iov));
		}
	}

	return FALSE;		/* Dropped */
}
Пример #29
0
/**
 * Periodic host heartbeat timer.
 */
void
host_timer(void)
{
    guint count;
	int missing;
	host_addr_t addr;
	guint16 port;
	host_type_t htype;
	guint max_nodes;
	gboolean empty_cache = FALSE;

	if (in_shutdown || !GNET_PROPERTY(online_mode))
		return;

	max_nodes = settings_is_leaf() ?
		GNET_PROPERTY(max_ultrapeers) : GNET_PROPERTY(max_connections);
	count = node_count();			/* Established + connecting */
	missing = node_keep_missing();

	if (GNET_PROPERTY(host_debug) > 1)
		g_debug("host_timer - count %u, missing %u", count, missing);

	/*
	 * If we are not connected to the Internet, apparently, make sure to
	 * connect to at most one host, to avoid using all our hostcache.
	 * Also, we don't connect each time we are called.
	 */

	if (!GNET_PROPERTY(is_inet_connected)) {
		static time_t last_try;

		if (last_try && delta_time(tm_time(), last_try) < 20)
			return;
		last_try = tm_time();

		if (GNET_PROPERTY(host_debug))
			g_debug("host_timer - not connected, trying to connect");
	}

	/*
	 * Allow more outgoing connections than the maximum amount of
	 * established Gnet connection we can maintain, but not more
	 * than quick_connect_pool_size   This is the "greedy mode".
	 */

	if (count >= GNET_PROPERTY(quick_connect_pool_size)) {
		if (GNET_PROPERTY(host_debug) > 1)
			g_debug("host_timer - count %u >= pool size %u",
				count, GNET_PROPERTY(quick_connect_pool_size));
		return;
	}

	if (count < max_nodes)
		missing -= whitelist_connect();

	/*
	 * If we are under the number of connections wanted, we add hosts
	 * to the connection list
	 */

	htype = HOST_ULTRA;

	if (
        settings_is_ultra() &&
        GNET_PROPERTY(node_normal_count) < GNET_PROPERTY(normal_connections) &&
        GNET_PROPERTY(node_ultra_count) >=
			(GNET_PROPERTY(up_connections) - GNET_PROPERTY(normal_connections))
	) {
		htype = HOST_ANY;
    }

	if (hcache_size(htype) == 0)
		htype = HOST_ANY;

	if (hcache_size(htype) == 0)
		empty_cache = TRUE;

	if (GNET_PROPERTY(host_debug) && missing > 0)
		g_debug("host_timer - missing %d host%s%s",
			missing, missing == 1 ? "" : "s",
			empty_cache ? " [empty caches]" : "");

    if (!GNET_PROPERTY(stop_host_get)) {
        if (missing > 0) {
			static time_t last_try;
            unsigned fan, max_pool, to_add;

            max_pool = MAX(GNET_PROPERTY(quick_connect_pool_size), max_nodes);
            fan = (missing * GNET_PROPERTY(quick_connect_pool_size))/ max_pool;
			fan = MAX(1, fan);
            to_add = GNET_PROPERTY(is_inet_connected) ? fan : (guint) missing;

			/*
			 * Every so many calls, attempt to ping all our neighbours to
			 * get fresh pongs, in case our host cache is not containing
			 * sufficiently fresh hosts and we keep getting connection failures.
			 */

			if (
				0 == last_try ||
				delta_time(tm_time(), last_try) >= HOST_PINGING_PERIOD
			) {
				ping_all_neighbours();
				last_try = tm_time();
			}

            /*
             * Make sure that we never use more connections then the
             * quick pool or the maximum number of hosts allow.
             */
            if (to_add + count > max_pool)
                to_add = max_pool - count;

            if (GNET_PROPERTY(host_debug) > 2) {
                g_debug("host_timer - connecting - "
					"add: %d fan:%d miss:%d max_hosts:%d count:%d extra:%d",
					 to_add, fan, missing, max_nodes, count,
					 GNET_PROPERTY(quick_connect_pool_size));
            }

            missing = to_add;

			if (missing > 0 && (0 == connected_nodes() || host_low_on_pongs)) {
				gnet_host_t host[HOST_DHT_MAX];
				int hcount;
				int i;

				hcount = dht_fill_random(host,
					MIN(UNSIGNED(missing), G_N_ELEMENTS(host)));

				missing -= hcount;

				for (i = 0; i < hcount; i++) {
					addr = gnet_host_get_addr(&host[i]);
					port = gnet_host_get_port(&host[i]);
					if (!hcache_node_is_bad(addr)) {
						if (GNET_PROPERTY(host_debug) > 3) {
							g_debug("host_timer - UHC pinging and connecting "
								"to DHT node at %s",
								host_addr_port_to_string(addr, port));
						}
						/* Try to use the host as an UHC before connecting */
						udp_send_ping(NULL, addr, port, TRUE);
						if (!host_gnutella_connect(addr, port)) {
							missing++;	/* Did not use entry */
						}
					} else {
						missing++;	/* Did not use entry */
					}
				}
			}

			while (hcache_size(htype) && missing-- > 0) {
				if (hcache_get_caught(htype, &addr, &port)) {
					if (!(hostiles_check(addr) || hcache_node_is_bad(addr))) {
						if (!host_gnutella_connect(addr, port)) {
							missing++;	/* Did not use entry */
						}
					} else {
						missing++;	/* Did not use entry */
					}
				}
			}

			if (missing > 0 && (empty_cache || host_cache_allow_bypass())) {
				if (!uhc_is_waiting()) {
					if (GNET_PROPERTY(host_debug))
						g_debug("host_timer - querying UDP host cache");
					uhc_get_hosts();	/* Get new hosts from UHCs */
				}
			}
		}

	} else if (GNET_PROPERTY(use_netmasks)) {
		/* Try to find better hosts */
		if (hcache_find_nearby(htype, &addr, &port)) {
			if (node_remove_worst(TRUE))
				node_add(addr, port, 0);
			else
				hcache_add_caught(htype, addr, port, "nearby host");
		}
	}
}
Пример #30
0
void
hsep_send_msg(struct gnutella_node *n, time_t now)
{
	hsep_triple tmp[G_N_ELEMENTS(n->hsep->sent_table)], other;
	unsigned int i, j, msglen, msgsize, triples, opttriples;
	gnutella_msg_hsep_t *msg;
	hsep_ctx_t *hsep;

	g_assert(n);
	g_assert(n->hsep);

	hsep = n->hsep;
	ZERO(&other);

	/*
	 * If we are a leaf, we just need to send one triple,
	 * which contains our own data (this triple is expanded
	 * to the needed number of triples on the peer's side).
	 * As the 0'th global and 0'th connection triple are zero,
	 * it contains only our own triple, which is correct.
	 */

	triples = settings_is_leaf() ? 1 : G_N_ELEMENTS(tmp);

	/*
	 * Allocate and initialize message to send.
	 */

	msgsize = GTA_HEADER_SIZE + triples * (sizeof *msg - GTA_HEADER_SIZE);
	msg = walloc(msgsize);

	{
		gnutella_header_t *header;
		
		header = gnutella_msg_hsep_header(msg);
		message_set_muid(header, GTA_MSG_HSEP_DATA);
		gnutella_header_set_function(header, GTA_MSG_HSEP_DATA);
		gnutella_header_set_ttl(header, 1);
		gnutella_header_set_hops(header, 0);
	}

	/*
	 * Collect HSEP data to send and convert the data to
	 * little endian byte order.
	 */

	if (triples > 1) {
		/* determine what we know about non-HSEP nodes in 1 hop distance */
		hsep_get_non_hsep_triple(&other);
	}

	for (i = 0; i < triples; i++) {
		for (j = 0; j < G_N_ELEMENTS(other); j++) {
			uint64 val;

			val = hsep_own[j] + (0 == i ? 0 : other[j]) +
				hsep_global_table[i][j] - hsep->table[i][j];
			poke_le64(&tmp[i][j], val);
		}
	}

	STATIC_ASSERT(sizeof hsep->sent_table == sizeof tmp);
	/* check if the table differs from the previously sent table */
	if (
		0 == memcmp(tmp, hsep->sent_table, sizeof tmp)
	) {
		WFREE_NULL(msg, msgsize);
		goto charge_timer;
	}

	memcpy(cast_to_char_ptr(msg) + GTA_HEADER_SIZE,
		tmp, triples * sizeof tmp[0]);

	/* store the table for later comparison */
	memcpy(hsep->sent_table, tmp, triples * sizeof tmp[0]);

	/*
	 * Note that on big endian architectures the message data is now in
	 * the wrong byte order. Nevertheless, we can use hsep_triples_to_send()
	 * with that data.
	 */

	/* optimize number of triples to send */
	opttriples = hsep_triples_to_send(cast_to_pointer(tmp), triples);

	if (GNET_PROPERTY(hsep_debug) > 1) {
		printf("HSEP: Sending %d %s to node %s (msg #%u): ", opttriples,
		    opttriples == 1 ? "triple" : "triples",
			host_addr_port_to_string(n->addr, n->port),
			hsep->msgs_sent + 1);
	}

	for (i = 0; i < opttriples; i++) {
		if (GNET_PROPERTY(hsep_debug) > 1) {
			char buf[G_N_ELEMENTS(hsep_own)][32];

			for (j = 0; j < G_N_ELEMENTS(buf); j++) {
				uint64 v;

				v = hsep_own[j] + hsep_global_table[i][j] - hsep->table[i][j];
				uint64_to_string_buf(v, buf[j], sizeof buf[0]);
			}

			STATIC_ASSERT(3 == G_N_ELEMENTS(buf));
			printf("(%s, %s, %s) ", buf[0], buf[1], buf[2]);
		}
	}

	if (GNET_PROPERTY(hsep_debug) > 1)
		puts("\n");

	/* write message size */
	msglen = opttriples * 24;
	gnutella_header_set_size(gnutella_msg_hsep_header(msg), msglen);

	/* correct message length */
	msglen += GTA_HEADER_SIZE;

	/* send message to peer node */
	gmsg_sendto_one(n, msg, msglen);

	WFREE_NULL(msg, msgsize);

	/*
	 * Update counters.
	 */

	hsep->msgs_sent++;
	hsep->triples_sent += opttriples;

charge_timer:

	hsep->last_sent = now;
	hsep->random_skew = random_value(2 * HSEP_MSG_SKEW) - HSEP_MSG_SKEW;
}