/** * Add a new host to our pong reserve, although the information here * does not come from a pong but from a Query Hit packet, hence the port * may be unsuitable for Gnet connections. */ void host_add_semi_pong(const host_addr_t addr, guint16 port) { g_assert(host_low_on_pongs); /* Only used when low on pongs */ hcache_add_caught(HOST_ANY, addr, port, "semi-pong"); }
/** * Add a new host to our pong reserve. * * When `connect' is true, attempt to connect if we are low in Gnet links. */ void host_add(const host_addr_t addr, guint16 port, gboolean do_connect) { if (!do_connect || !hcache_add_caught(HOST_ANY, addr, port, "pong")) return; /* * If we are under the number of connections wanted, we add this host * to the connection list. */ if (node_keep_missing() > 0) node_add(addr, port, 0); else if ( GNET_PROPERTY(use_netmasks) && host_is_nearby(addr) && node_remove_worst(TRUE) ) { /* * If we are above the max connections, delete a non-nearby * connection before adding this better one */ node_add(addr, port, 0); } }
/** * Tree message iterator to handle "NH" nodes and extract their IP:port. */ static void g2_node_extract_nh(void *data, void *udata) { const g2_tree_t *t = data; (void) udata; if (0 == strcmp("NH", g2_tree_name(t))) { host_addr_t addr; uint16 port; if ( g2_node_parse_address(t, &addr, &port) && host_is_valid(addr, port) ) { hcache_add_caught(HOST_G2HUB, addr, port, "/KHL/NH"); } } }
/** * Called from parse_dispatch_lines() for each complete line of output. * * @return FALSE to stop processing of any remaining data. */ static bool ghc_host_line(struct parse_context *ctx, const char *buf, size_t len) { if (GNET_PROPERTY(bootstrap_debug) > 2) g_debug("BOOT GHC host line #%u (%zu bytes): %s", ctx->processed + 1, len, buf); if (len) { host_addr_t addr; uint16 port; if (string_to_host_addr_port(buf, NULL, &addr, &port)) { ctx->processed++; hcache_add_caught(HOST_ULTRA, addr, port, "GHC"); if (GNET_PROPERTY(bootstrap_debug) > 1) g_debug("BOOT collected %s from GHC %s", host_addr_to_string(addr), http_async_url(ctx->handle)); } } return TRUE; }
/** * 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(); } }
/** * 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"); } } }
/** * Called from gwc_parse_dispatch_lines() for each complete line of output. * * @return FALSE to stop processing of any remaining data. */ static bool gwc_host_line(struct gwc_parse_context *ctx, const char *buf, size_t len) { int c; if (GNET_PROPERTY(bootstrap_debug) > 3) g_message("BOOT GWC host line (%lu bytes): %s", (ulong) len, buf); if (is_strprefix(buf, "ERROR")) { g_warning("GWC cache \"%s\" returned %s", http_async_url(ctx->handle), buf); http_async_cancel(ctx->handle); return FALSE; } if (len <= 2) return TRUE; /* Skip this line silently */ /* * A line starting with "H|" is a host, with "U|" a GWC URL. * Letters are case-insensitive. */ if (buf[1] != '|') goto malformed; c = ascii_toupper(buf[0]); if ('H' == c) { host_addr_t addr; uint16 port; if (string_to_host_addr_port(&buf[2], NULL, &addr, &port)) { ctx->processed++; hcache_add_caught(HOST_G2HUB, addr, port, "GWC"); if (GNET_PROPERTY(bootstrap_debug) > 1) { g_message("BOOT (G2) collected %s from GWC %s", host_addr_port_to_string(addr, port), http_async_url(ctx->handle)); } } return TRUE; } else if ('U' == c) { char *end = strchr(&buf[2], '|'); char *url; if (NULL == end) goto malformed; ctx->processed++; url = h_strndup(&buf[2], ptr_diff(end, &buf[2])); gwc_add(url); hfree(url); return TRUE; } else if ('I' == c) { return TRUE; /* Ignore information line */ } /* * If we come here, we did not recognize the line properly. */ if (GNET_PROPERTY(bootstrap_debug) > 2) { g_warning("GWC ignoring unknown line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; malformed: if (GNET_PROPERTY(bootstrap_debug)) { g_warning("GWC ignoring malformed line \"%s\" from %s", buf, http_async_url(ctx->handle)); } return TRUE; }