/** * Select proper RX layer for semi-reliable UDP traffic. * * Source address is checked for hostile hosts in order to enforce a * total blackout. * * @param utp UDP traffic type * @param from source address * @param len length of message, for logging only * * @return the RX layer if found, NULL if none or the address is hostile. */ static rxdrv_t * udp_get_rx_semi_reliable(enum udp_traffic utp, host_addr_t from, size_t len) { unsigned i = 0; if (hostiles_is_bad(from)) { if (GNET_PROPERTY(udp_debug)) { hostiles_flags_t flags = hostiles_check(from); g_warning("UDP got %s (%zu bytes) from hostile %s (%s) -- dropped", udp_traffic_to_string(utp), len, host_addr_to_string(from), hostiles_flags_to_string(flags)); } gnet_stats_inc_general(GNR_UDP_SR_RX_FROM_HOSTILE_IP); return NULL; /* Ignore message */ } switch (host_addr_net(from)) { case NET_TYPE_IPV4: i = 0; break; case NET_TYPE_IPV6: i = 1; break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_assert_not_reached(); } return SEMI_RELIABLE_GTA == utp ? rx_sr_gta[i] : SEMI_RELIABLE_GND == utp ? rx_sr_gnd[i] : NULL; }
/** * @return whether host's address is a valid DHT value creator. */ gboolean knode_addr_is_usable(const knode_t *kn) { knode_check(kn); if (!host_address_is_usable(kn->addr)) return FALSE; if (hostiles_check(kn->addr)) return FALSE; return TRUE; }
/** * @return whether host can be kept as a valid contact */ gboolean knode_is_usable(const knode_t *kn) { knode_check(kn); if (!host_is_valid(kn->addr, kn->port)) return FALSE; if (hostiles_check(kn->addr)) return FALSE; return TRUE; }
/** * 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"); } } }
/** * 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, dht = FALSE, rudp = FALSE, g2 = FALSE; hostiles_flags_t hflags; /* * 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: break; case SEMI_RELIABLE_GND: if (!node_g2_active()) return; /* Blackout, ignore datagram if G2 was disabled */ g2 = TRUE; 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. */ g_assert(!g2); /* All G2 UDP traffic comes via the semi-reliable layer */ /* * 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, plural(len), host_addr_to_string(s->addr)); } gnet_stats_inc_general(GNR_UDP_BOGUS_SOURCE_IP); } /* * Traffic from hosts sending gibberish data or information we previously * determined as being invalid / suspicious are simply discarded to avoid * further processing (which would probably lead to them being further * discarded as invalid, unparseable, etc...). * * We let statically-banned hosts through though so that we may parse * their message and log dropping in upper layers, for statistics per * message type. We only drop known gibberish at this level. */ hflags = hostiles_check(s->addr); if (hflags & HSTL_GIBBERISH) { if (GNET_PROPERTY(udp_debug)) { g_warning("UDP %sdatagram (%zu byte%s) received from " "shunned IP %s (%s) -- dropped", truncated ? "truncated " : "", len, plural(len), host_addr_to_string(s->addr), hostiles_flags_to_string(hflags)); } gnet_stats_inc_general(GNR_UDP_SHUNNED_SOURCE_IP); return; } /* * 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); }
/** * 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: hostiles_dynamic_add(n->addr, "improper Gnutella header", HSTL_GIBBERISH); msg = "Header flags undefined for now"; goto drop; case GMSG_INVALID: hostiles_dynamic_add(n->addr, "invalid Gnutella header size", HSTL_GIBBERISH); 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)) { hostiles_flags_t flags; /* * Do not pollute logs with errors from messages coming from known * hostile addresses: no dumping of datagram, and flag the host as * hostile anyway so that we know. */ flags = hostiles_check(s->addr); g_warning("UDP got invalid %sGnutella packet (%zu byte%s) " "\"%s\" %sfrom %s%s: %s", socket_udp_is_old(s) ? "OLD " : "", len, plural(len), len >= GTA_HEADER_SIZE ? gmsg_infostr_full_split(header, payload, len - GTA_HEADER_SIZE) : "<incomplete Gnutella header>", truncated ? "(truncated) " : "", (flags & HSTL_STATIC) ? "static hostile " : "", NULL == n ? host_addr_port_to_string(s->addr, s->port) : node_infostr(n), msg); if (len != 0 && !(flags & HSTL_STATIC)) { if (len <= GTA_HEADER_SIZE) { dump_hex(stderr, "UDP datagram", header, len); } else { 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 */ }