void gnet_stats_count_flowc(const void *head, bool head_only) { uint t; uint16 size = gmsg_size(head) + GTA_HEADER_SIZE; uint8 function = gnutella_header_get_function(head); uint8 ttl = gnutella_header_get_ttl(head); uint8 hops = gnutella_header_get_hops(head); g_assert(thread_is_main()); if (GNET_PROPERTY(node_debug) > 3) g_debug("FLOWC function=%d ttl=%d hops=%d", function, ttl, hops); /* * Adjust for Kademlia messages. */ if (GTA_MSG_DHT == function && size >= KDA_HEADER_SIZE && !head_only) { uint8 opcode = kademlia_header_get_function(head); if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) { t = stats_lut[opcode + MSG_DHT_BASE]; } else { t = stats_lut[function]; /* Invalid opcode? */ } hops = 0; ttl = 0; } else { t = stats_lut[function]; } gnet_stats_flowc_internal(t, function, ttl, hops, size); }
/** * Process the whole message we read. * * @return FALSE if an error was reported (processing aborted). */ static gboolean browse_data_process(struct browse_ctx *bc) { gnutella_node_t *n; /* * We accept only query hits. */ if (gnutella_header_get_function(&bc->header) != GTA_MSG_SEARCH_RESULTS) { download_stop(bc->owner, GTA_DL_ERROR, "Non query-hit received"); return FALSE; } n = node_browse_prepare( &bc->host, bc->vendor, &bc->header, bc->data, bc->size); dump_rx_packet(n); gnet_stats_count_received_header(n); gnet_stats_count_received_payload(n, bc->data); search_browse_results(n, bc->sh); node_browse_cleanup(n); return TRUE; }
void gnet_stats_count_dropped(gnutella_node_t *n, msg_drop_reason_t reason) { uint32 size; uint type; gnet_stats_t *stats; g_assert(UNSIGNED(reason) < MSG_DROP_REASON_COUNT); g_assert(thread_is_main()); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; if (NODE_TALKS_G2(n)) { int f = g2_msg_type(n->data, n->size); if (f != G2_MSG_MAX) { f += MSG_G2_BASE; } else { f = G_N_ELEMENTS(stats_lut) - 1; /* Last, holds MSG_UNKNOWN */ } type = stats_lut[f]; size = n->size; } else { type = stats_lut[gnutella_header_get_function(&n->header)]; size = n->size + sizeof(n->header); } entropy_harvest_small( VARLEN(n->addr), VARLEN(n->port), VARLEN(reason), VARLEN(type), VARLEN(size), NULL); DROP_STATS(stats, type, size); node_inc_rxdrop(n); switch (reason) { case MSG_DROP_HOSTILE_IP: n->n_hostile++; break; case MSG_DROP_SPAM: n->n_spam++; break; case MSG_DROP_EVIL: n->n_evil++; break; default: ; } if (NODE_TALKS_G2(n)) { if (GNET_PROPERTY(log_dropped_g2)) { g2_msg_log_dropped_data(n->data, n->size, "from %s: %s", node_infostr(n), gnet_stats_drop_reason_to_string(reason)); } } else { if (GNET_PROPERTY(log_dropped_gnutella)) { gmsg_log_split_dropped(&n->header, n->data, n->size, "from %s: %s", node_infostr(n), gnet_stats_drop_reason_to_string(reason)); } } }
/** * Send given Gnutella ping message to the host, monitoring replies and * timeouts through specified callback. * * @param m the Ping message to send * @param size size of the Ping message, in bytes * @param addr address to which ping should be sent * @param port port number * @param cb callback to invoke on reply or timeout * @param arg additional callback argument * @param multiple whether multiple replies (Pongs) are expected * * @return TRUE if we sent the ping, FALSE it we throttled it. */ bool udp_send_ping_callback( gnutella_msg_init_t *m, uint32 size, const host_addr_t addr, uint16 port, udp_ping_cb_t cb, void *arg, bool multiple) { g_assert(cb != NULL); g_assert(GTA_MSG_INIT == gnutella_header_get_function(m)); return udp_send_ping_with_callback(m, size, addr, port, cb, arg, multiple); }
/** * Called when Gnutella header has been read. */ void gnet_stats_count_received_header(gnutella_node_t *n) { uint t = stats_lut[gnutella_header_get_function(&n->header)]; uint8 ttl, hops; g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); ttl = gnutella_header_get_ttl(&n->header); hops = gnutella_header_get_hops(&n->header); gnet_stats_count_received_header_internal(n, GTA_HEADER_SIZE, t, ttl, hops); }
/** * Dump relayed or locally-emitted packet. * If ``from'' is NULL, packet was emitted locally. */ static void dump_packet_from_to(struct dump *dump, const struct gnutella_node *from, const struct gnutella_node *to, const pmsg_t *mb) { struct dump_header dh_to; struct dump_header dh_from; g_assert(to != NULL); g_assert(mb != NULL); g_assert(pmsg_read_base(mb) == pmsg_start(mb)); if (!dump_initialize(dump)) return; /* * This is only for Gnutella packets, leave DHT messages out. */ if (GTA_MSG_DHT == gnutella_header_get_function(pmsg_start(mb))) return; if (!ipset_contains_addr(&dump_tx_to_addrs, to->addr, TRUE)) return; if (NULL == from) { struct gnutella_node local; local.peermode = NODE_IS_UDP(to) ? NODE_P_UDP : NODE_P_NORMAL; local.addr = listen_addr(); local.port = GNET_PROPERTY(listen_port); if (!ipset_contains_addr(&dump_tx_from_addrs, local.addr, TRUE)) return; dump_header_set(&dh_from, &local); } else { if (!ipset_contains_addr(&dump_tx_from_addrs, from->addr, TRUE)) return; dump_header_set(&dh_from, from); } dump_header_set(&dh_to, to); dh_to.data[0] |= DH_F_TO; if (pmsg_prio(mb) != PMSG_P_DATA) dh_to.data[0] |= DH_F_CTRL; dump_append(dump, dh_to.data, sizeof dh_to.data); dump_append(dump, dh_from.data, sizeof dh_from.data); dump_append(dump, pmsg_read_base(mb), pmsg_size(mb)); dump_flush(dump); }
void gnet_stats_count_expired(const gnutella_node_t *n) { uint32 size = n->size + sizeof(n->header); uint t = stats_lut[gnutella_header_get_function(&n->header)]; gnet_stats_t *stats; g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; gnet_stats.pkg.expired[MSG_TOTAL]++; gnet_stats.pkg.expired[t]++; gnet_stats.byte.expired[MSG_TOTAL] += size; gnet_stats.byte.expired[t] += size; stats->pkg.expired[MSG_TOTAL]++; stats->pkg.expired[t]++; stats->byte.expired[MSG_TOTAL] += size; stats->byte.expired[t] += size; }
/** * Called to transform Gnutella header counting into Kademlia header counting. * * @param n the node receiving the message * @param kt */ static void gnet_stats_count_kademlia_header(const gnutella_node_t *n, uint kt) { uint t = stats_lut[gnutella_header_get_function(&n->header)]; uint i; gnet_stats_t *stats; g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; gnet_stats.pkg.received[t]--; gnet_stats.pkg.received[kt]++; gnet_stats.byte.received[t] -= GTA_HEADER_SIZE; gnet_stats.byte.received[kt] += GTA_HEADER_SIZE; stats->pkg.received[t]--; stats->pkg.received[kt]++; stats->byte.received[t] -= GTA_HEADER_SIZE; stats->byte.received[kt] += GTA_HEADER_SIZE; i = MIN(gnutella_header_get_ttl(&n->header), STATS_RECV_COLUMNS - 1); stats->pkg.received_ttl[i][MSG_TOTAL]--; stats->pkg.received_ttl[i][t]--; i = MIN(gnutella_header_get_hops(&n->header), STATS_RECV_COLUMNS - 1); stats->pkg.received_hops[i][MSG_TOTAL]--; stats->pkg.received_hops[i][t]--; /* DHT messages have no hops nor ttl, use 0 */ stats->pkg.received_ttl[0][MSG_TOTAL]++; stats->pkg.received_ttl[0][kt]++; stats->pkg.received_hops[0][MSG_TOTAL]++; stats->pkg.received_hops[0][kt]++; }
void gnet_stats_count_dropped_nosize( const gnutella_node_t *n, msg_drop_reason_t reason) { uint type; gnet_stats_t *stats; g_assert(UNSIGNED(reason) < MSG_DROP_REASON_COUNT); g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); type = stats_lut[gnutella_header_get_function(&n->header)]; stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; entropy_harvest_small(VARLEN(n->addr), VARLEN(n->port), NULL); /* Data part of message not read */ DROP_STATS(stats, type, sizeof(n->header)); if (GNET_PROPERTY(log_dropped_gnutella)) gmsg_log_split_dropped(&n->header, n->data, 0, "from %s: %s", node_infostr(n), gnet_stats_drop_reason_to_string(reason)); }
/** * Called when Gnutella payload has been read, or when a G2 messsage is read. * * The actual payload size (effectively read) is expected to be found * in n->size for Gnutella messages and G2 messages. * * @param n the node from which message was received * @param payload start of Gnutella payload, or head of G2 frame */ void gnet_stats_count_received_payload(const gnutella_node_t *n, const void *payload) { uint8 f; uint t; uint i; gnet_stats_t *stats; uint32 size; uint8 hops, ttl; g_assert(thread_is_main()); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; size = n->size; /* * Size is NOT read in the Gnutella header but in n->size, which * reflects how much data we have in the payload, as opposed to the * size in the header which may be wrong, or have highest bits set * because they indicate flags. * * In particular, broken DHT messages often come with an invalid size in * the header. * --RAM, 2010-10-30 */ if (NODE_TALKS_G2(n)) { f = g2_msg_type(payload, size); if (f != G2_MSG_MAX) { f += MSG_G2_BASE; } else { f = G_N_ELEMENTS(stats_lut) - 1; /* Last, holds MSG_UNKNOWN */ } ttl = 1; hops = NODE_USES_UDP(n) ? 0 : 1; t = stats_lut[f]; /* * No header for G2, so count header reception now with a size of zero. * This is required to update the other packet reception statistics. */ gnet_stats_count_received_header_internal( deconstify_pointer(n), 0, t, ttl, hops); } else { f = gnutella_header_get_function(&n->header); hops = gnutella_header_get_hops(&n->header); ttl = gnutella_header_get_ttl(&n->header); t = stats_lut[f]; } gnet_stats_randomness(n, f, size); /* * If we're dealing with a Kademlia message, we need to do two things: * * We counted the Gnutella header for the GTA_MSG_DHT message, but we * now need to undo that and count it as a Kademlia message. * * To access the proper entry in the array, we need to offset the * Kademlia OpCode from the header with MSG_DHT_BASE to get the entry * in the statistics that are associated with that particular message. * --RAM, 2010-11-01 */ if (GTA_MSG_DHT == f && size + GTA_HEADER_SIZE >= KDA_HEADER_SIZE) { uint8 opcode = peek_u8(payload); /* Kademlia Opcode */ if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) { t = stats_lut[opcode + MSG_DHT_BASE]; gnet_stats_count_kademlia_header(n, t); } } g_assert(t < MSG_TOTAL); gnet_stats.byte.received[MSG_TOTAL] += size; gnet_stats.byte.received[t] += size; stats->byte.received[MSG_TOTAL] += size; stats->byte.received[t] += size; i = MIN(ttl, STATS_RECV_COLUMNS - 1); stats->byte.received_ttl[i][MSG_TOTAL] += size; stats->byte.received_ttl[i][t] += size; i = MIN(hops, STATS_RECV_COLUMNS - 1); stats->byte.received_hops[i][MSG_TOTAL] += size; stats->byte.received_hops[i][t] += size; }
/** * 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; bool dht = FALSE; bool rudp = FALSE; /* * 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: case SEMI_RELIABLE_GND: 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. */ /* * 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, 1 == len ? "" : "s", host_addr_to_string(s->addr)); } gnet_stats_inc_general(GNR_UDP_BOGUS_SOURCE_IP); } /* * 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); }
/** * 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; }
/** * 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 */ }
/** * Route query hits from one node to the other. */ void dh_route(gnutella_node_t *src, gnutella_node_t *dest, int count) { pmsg_t *mb; struct dh_pmsg_info *pmi; const struct guid *muid; dqhit_t *dh; mqueue_t *mq; g_assert( gnutella_header_get_function(&src->header) == GTA_MSG_SEARCH_RESULTS); g_assert(count >= 0); if (!NODE_IS_WRITABLE(dest)) goto drop_shutdown; muid = gnutella_header_get_muid(&src->header); dh = dh_locate(muid); g_assert(dh != NULL); /* Must have called dh_got_results() first! */ if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH #%s got %d hit%s: " "msg=%u, hits_recv=%u, hits_sent=%u, hits_queued=%u", guid_hex_str(muid), count, plural(count), dh->msg_recv, dh->hits_recv, dh->hits_sent, dh->hits_queued); } mq = dest->outq; /* * Can we forward the message? */ switch (dh_can_forward(dh, mq, FALSE)) { case DH_DROP_FC: goto drop_flow_control; case DH_DROP_THROTTLE: goto drop_throttle; case DH_DROP_TRANSIENT: goto drop_transient; case DH_FORWARD: default: break; } /* * Allow message through. */ WALLOC(pmi); pmi->hits = count; dh->hits_queued += count; dh->msg_queued++; g_assert(dh->hits_queued >= UNSIGNED(count)); /* * Magic: we create an extended version of a pmsg_t that contains a * free routine, which will be invoked when the message queue frees * the message. * * This enables us to track how much results we already queued/sent. */ if (NODE_IS_UDP(dest)) { gnet_host_t to; pmsg_t *mbe; gnet_host_set(&to, dest->addr, dest->port); /* * With GUESS we may route back a query hit to an UDP node. */ if (GNET_PROPERTY(guess_server_debug) > 19) { g_debug("GUESS sending %d hit%s (%s) for #%s to %s", count, plural(count), NODE_CAN_SR_UDP(dest) ? "reliably" : NODE_CAN_INFLATE(dest) ? "possibly deflated" : "uncompressed", guid_hex_str(muid), node_infostr(dest)); } /* * Attempt to compress query hit if the destination supports it. * * If we're going to send the hit using semi-reliable UDP, there's * no need to compress beforehand, since the transport layer will * attempt its own compression anyway. */ if (!NODE_CAN_SR_UDP(dest) && NODE_CAN_INFLATE(dest)) { mb = gmsg_split_to_deflated_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); if (gnutella_header_get_ttl(pmsg_start(mb)) & GTA_UDP_DEFLATED) gnet_stats_inc_general(GNR_UDP_TX_COMPRESSED); } else { mb = gmsg_split_to_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); } mbe = pmsg_clone_extend(mb, dh_pmsg_free, pmi); pmsg_free(mb); if (NODE_CAN_SR_UDP(dest)) pmsg_mark_reliable(mbe); mq_udp_putq(mq, mbe, &to); } else { mb = gmsg_split_to_pmsg_extend(&src->header, src->data, src->size + GTA_HEADER_SIZE, dh_pmsg_free, pmi); mq_tcp_putq(mq, mb, src); if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH enqueued %d hit%s for #%s to %s", count, plural(count), guid_hex_str(muid), node_infostr(dest)); } } return; drop_shutdown: gnet_stats_count_dropped(src, MSG_DROP_SHUTDOWN); return; drop_flow_control: gnet_stats_count_dropped(src, MSG_DROP_FLOW_CONTROL); gnet_stats_count_flowc(&src->header, TRUE); return; drop_throttle: gnet_stats_count_dropped(src, MSG_DROP_THROTTLE); return; drop_transient: gnet_stats_count_dropped(src, MSG_DROP_TRANSIENT); return; }