/** * 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"); } } }
/** * 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 */ }
/** * Displays assorted status information */ enum shell_reply shell_exec_status(struct gnutella_shell *sh, int argc, const char *argv[]) { const char *cur; const option_t options[] = { { "i", &cur }, }; int parsed; char buf[2048]; time_t now; shell_check(sh); g_assert(argv); g_assert(argc > 0); parsed = shell_options_parse(sh, argv, options, G_N_ELEMENTS(options)); if (parsed < 0) return REPLY_ERROR; argv += parsed; /* args[0] is first command argument */ argc -= parsed; /* counts only command arguments now */ now = tm_time(); /* Leading flags */ { char flags[47]; const char *fw; const char *fd; const char *pmp; const char *dht; /* * The flags are displayed as followed: * * UMP port mapping configured via UPnP * NMP port mapping configured via NAT-PMP * pmp port mapping available (UPnP or NAT-PMP), un-configured * CLK clock, GTKG expired * !FD or FD red or yellow bombs for fd shortage * STL upload stalls * gUL/yUL/rUL green, yellow or red upload early stalling levels * CPU cpu overloaded * MOV file moving * SHA SHA-1 rebuilding or verifying * TTH TTH rebuilding or verifying * LIB library rescan * :FW or FW indicates whether hole punching is possible * udp or UDP indicates UDP firewalling (lowercased for hole punching) * TCP indicates TCP-firewalled * - the happy face: no firewall * sDH/lDH/bDH seeded, own KUID looking or bootstrapping DHT * A or P active or passive DHT mode * UP or LF ultrapeer or leaf mode */ pmp = (GNET_PROPERTY(upnp_possible) || GNET_PROPERTY(natpmp_possible)) ? "pmp " : empty; if ( (GNET_PROPERTY(enable_upnp) || GNET_PROPERTY(enable_natpmp)) && GNET_PROPERTY(port_mapping_successful) ) { pmp = GNET_PROPERTY(enable_natpmp) ? "NMP " : "UMP "; } if (dht_enabled()) { dht = empty; switch ((enum dht_bootsteps) GNET_PROPERTY(dht_boot_status)) { case DHT_BOOT_NONE: case DHT_BOOT_SHUTDOWN: break; case DHT_BOOT_SEEDED: dht = "sDH "; break; case DHT_BOOT_OWN: dht = "lDH "; break; case DHT_BOOT_COMPLETING: dht = "bDH "; break; case DHT_BOOT_COMPLETED: dht = dht_is_active() ? "A " : "P "; break; case DHT_BOOT_MAX_VALUE: g_assert_not_reached(); } } else { dht = empty; } if (GNET_PROPERTY(is_firewalled) && GNET_PROPERTY(is_udp_firewalled)) { fw = GNET_PROPERTY(recv_solicited_udp) ? ":FW " : "FW "; } else if (GNET_PROPERTY(is_firewalled)) { fw = "TCP "; } else if (GNET_PROPERTY(is_udp_firewalled)) { fw = GNET_PROPERTY(recv_solicited_udp) ? "udp " : "UDP "; } else { fw = "- "; } if (GNET_PROPERTY(file_descriptor_runout)) { fd = "!FD "; } else if (GNET_PROPERTY(file_descriptor_shortage)) { fd = "FD "; } else { fd = empty; } gm_snprintf(flags, sizeof flags, "<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>", pmp, GNET_PROPERTY(download_queue_frozen) ? "DFZ " : empty, GNET_PROPERTY(ancient_version) ? "CLK " : empty, fd, GNET_PROPERTY(uploads_stalling) ? "STL " : empty, GNET_PROPERTY(uploads_bw_ignore_stolen) ? "gUL " : empty, GNET_PROPERTY(uploads_bw_uniform) ? "yUL " : empty, GNET_PROPERTY(uploads_bw_no_stealing) ? "rUL " : empty, GNET_PROPERTY(overloaded_cpu) ? "CPU " : empty, GNET_PROPERTY(file_moving) ? "MOV " : empty, (GNET_PROPERTY(sha1_rebuilding) || GNET_PROPERTY(sha1_verifying)) ? "SHA " : empty, (GNET_PROPERTY(tth_rebuilding) || GNET_PROPERTY(tth_verifying)) ? "TTH " : empty, GNET_PROPERTY(library_rebuilding) ? "LIB " : empty, fw, dht, settings_is_ultra() ? "UP" : "LF"); gm_snprintf(buf, sizeof buf, "+%s+\n" "| %-18s%51s |\n" "|%s|\n", dashes, "Status", flags, equals); shell_write(sh, buf); } /* General status */ { const char *blackout; short_string_t leaf_switch; short_string_t ultra_check; leaf_switch = timestamp_get_string( GNET_PROPERTY(node_last_ultra_leaf_switch)); ultra_check = timestamp_get_string( GNET_PROPERTY(node_last_ultra_check)); if (GNET_PROPERTY(is_firewalled) && GNET_PROPERTY(is_udp_firewalled)) { blackout = GNET_PROPERTY(recv_solicited_udp) ? "TCP,udp" : "TCP,UDP"; } else if (GNET_PROPERTY(is_firewalled)) { blackout = "TCP"; } else if (GNET_PROPERTY(is_udp_firewalled)) { blackout = GNET_PROPERTY(recv_solicited_udp) ? "udp" : "UDP"; } else { blackout = "None"; } gm_snprintf(buf, sizeof buf, "| Mode: %-9s Last Switch: %-19s%2s|\n" "| Uptime: %-9s Last Check: %-19s%2s|\n" "| Port: %-9u Blackout: %-7s%14s|\n" "|%s|\n", GNET_PROPERTY(online_mode) ? node_peermode_to_string(GNET_PROPERTY(current_peermode)) : "offline", GNET_PROPERTY(node_last_ultra_leaf_switch) ? leaf_switch.str : "never", space, short_time(delta_time(now, GNET_PROPERTY(start_stamp))), GNET_PROPERTY(node_last_ultra_check) ? ultra_check.str : "never", space, socket_listen_port(), blackout, space, equals); shell_write(sh, buf); } /* IPv4 info */ switch (GNET_PROPERTY(network_protocol)) { case NET_USE_BOTH: case NET_USE_IPV4: gm_snprintf(buf, sizeof buf, "| IPv4: %-44s Since: %-12s|\n", host_addr_to_string(listen_addr()), short_time(delta_time(now, GNET_PROPERTY(current_ip_stamp)))); shell_write(sh, buf); } /* IPv6 info */ switch (GNET_PROPERTY(network_protocol)) { case NET_USE_BOTH: gm_snprintf(buf, sizeof buf, "|%s|\n", dashes); shell_write(sh, buf); /* FALL THROUGH */ case NET_USE_IPV6: gm_snprintf(buf, sizeof buf, "| IPv6: %-44s Since: %-12s|\n", host_addr_to_string(listen_addr6()), short_time(delta_time(now, GNET_PROPERTY(current_ip6_stamp)))); shell_write(sh, buf); } /* Node counts */ gm_snprintf(buf, sizeof buf, "|%s|\n" "| Peers: %-7u Ultra %4u/%-7u Leaf %4u/%-6u Legacy %4u/%-4u |\n" "| Downloads %4u/%-4u Uploads %4u/%-7u Browse %4u/%-4u |\n" "|%s|\n", equals, GNET_PROPERTY(node_ultra_count) + GNET_PROPERTY(node_leaf_count) + GNET_PROPERTY(node_normal_count), GNET_PROPERTY(node_ultra_count), settings_is_ultra() ? GNET_PROPERTY(max_connections) : GNET_PROPERTY(max_ultrapeers), GNET_PROPERTY(node_leaf_count), GNET_PROPERTY(max_leaves), GNET_PROPERTY(node_normal_count), GNET_PROPERTY(normal_connections), GNET_PROPERTY(dl_active_count), GNET_PROPERTY(dl_running_count), GNET_PROPERTY(ul_running), GNET_PROPERTY(ul_registered), GNET_PROPERTY(html_browse_served) + GNET_PROPERTY(qhits_browse_served), GNET_PROPERTY(html_browse_count) + GNET_PROPERTY(qhits_browse_count), equals); shell_write(sh, buf); /* Bandwidths */ { const bool metric = GNET_PROPERTY(display_metric_units); short_string_t gnet_in, http_in, leaf_in, gnet_out, http_out, leaf_out; short_string_t dht_in, dht_out; gnet_bw_stats_t bw_stats, bw2_stats; const char *bwtype = cur ? "(cur)" : "(avg)"; gnet_get_bw_stats(BW_GNET_IN, &bw_stats); gnet_get_bw_stats(BW_GNET_UDP_IN, &bw2_stats); gnet_in = short_rate_get_string( cur ? bw_stats.current + bw2_stats.current : bw_stats.average + bw2_stats.average, metric); gnet_get_bw_stats(BW_GNET_OUT, &bw_stats); gnet_get_bw_stats(BW_GNET_UDP_OUT, &bw2_stats); gnet_out = short_rate_get_string( cur ? bw_stats.current + bw2_stats.current : bw_stats.average + bw2_stats.average, metric); gnet_get_bw_stats(BW_HTTP_IN, &bw_stats); http_in = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gnet_get_bw_stats(BW_HTTP_OUT, &bw_stats); http_out = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gnet_get_bw_stats(BW_LEAF_IN, &bw_stats); leaf_in = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gnet_get_bw_stats(BW_LEAF_OUT, &bw_stats); leaf_out = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gnet_get_bw_stats(BW_DHT_IN, &bw_stats); dht_in = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gnet_get_bw_stats(BW_DHT_OUT, &bw_stats); dht_out = short_rate_get_string( cur ? bw_stats.current : bw_stats.average, metric); gm_snprintf(buf, sizeof buf, "| %-70s|\n" "|%71s|\n" "| %5s In: %13s %13s %13s %13s |\n" "| %5s Out: %13s %13s %13s %13s |\n", "Bandwidth:" " Gnutella Leaf HTTP DHT", dashes, bwtype, gnet_in.str, leaf_in.str, http_in.str, dht_in.str, bwtype, gnet_out.str, leaf_out.str, http_out.str, dht_out.str); shell_write(sh, buf); } { char line[128]; bool metric = GNET_PROPERTY(display_metric_units); gm_snprintf(buf, sizeof buf, "|%s|\n", equals); shell_write(sh, buf); concat_strings(line, sizeof line, "Shares ", uint64_to_string(shared_files_scanned()), " file", shared_files_scanned() == 1 ? "" : "s", " ", short_kb_size(shared_kbytes_scanned(), metric), " total", (void *) 0); gm_snprintf(buf, sizeof buf, "| %-35s Up: %-11s Down: %-11s |\n", line, short_byte_size(GNET_PROPERTY(ul_byte_count), metric), short_byte_size2(GNET_PROPERTY(dl_byte_count), metric)); shell_write(sh, buf); gm_snprintf(buf, sizeof buf, "+%s+\n", dashes); shell_write(sh, buf); } return REPLY_READY; }