/** * Read data from the message buffer we just received. * * @return TRUE whilst we think there is more data to read in the buffer. */ static gboolean browse_data_read(struct browse_ctx *bc, pmsg_t *mb) { /* * Read header if it has not been fully fetched yet. */ if (!bc->has_header) { char *w = cast_to_gpointer(&bc->header); g_assert(sizeof bc->header >= bc->pos); bc->pos += pmsg_read(mb, &w[bc->pos], sizeof bc->header - bc->pos); if (bc->pos < sizeof bc->header) return FALSE; bc->has_header = TRUE; /* We have read the full header */ bc->size = gnutella_header_get_size(&bc->header); /* * Protect against too large data. */ if (bc->size > BH_DL_MAX_SIZE) { download_stop(bc->owner, GTA_DL_ERROR, "Gnutella payload too big"); return FALSE; } /* * Resize payload buffer if needed */ if (bc->size > bc->data_size) { bc->data_size = MAX(BH_DL_DEFAULT_SIZE, bc->size); bc->data = hrealloc(bc->data, bc->data_size); } bc->pos = 0; /* FALL THROUGH */ } /* * Read message data, if any. */ if (bc->size) { g_assert(bc->size >= bc->pos); bc->pos += pmsg_read(mb, &bc->data[bc->pos], bc->size - bc->pos); } if (bc->pos >= bc->size) { bc->has_header = FALSE; /* For next message */ bc->pos = 0; return TRUE; /* Must process message and continue */ } else { return FALSE; } }
/** * 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; }