/** * 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; }
/** * 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); }