/** * Free routine for the extended message blocks we send to the UDP layer. */ static void g2_qh2_pmsg_free(pmsg_t *mb, void *arg) { struct g2_qh2_pmsg_info *pmi = arg; gnutella_node_t *n; g2_qh2_pmsg_info_check(pmi); g_assert(pmsg_is_extended(mb)); if (pmsg_was_sent(mb)) goto done; /* * Message was unsent, probably because the UDP address in the /Q2 was * wrong for some reason. * * If we're still connected to the hub which passed us this /Q2, then * we can relay back the /QH2 to the hub and it will hopefully be able * to deliver it back to the querying node. */ n = node_by_id(pmi->hub_id); if (NULL == n) { if (GNET_PROPERTY(g2_debug) > 1) { g_debug("%s(): could not send %s, relaying hub is gone, dropping.", G_STRFUNC, g2_msg_infostr_mb(mb)); } gnet_stats_inc_general(GNR_UDP_G2_HITS_UNDELIVERED); goto done; } else { pmsg_t *nmb; if (GNET_PROPERTY(g2_debug) > 1) { g_debug("%s(): could not send %s, giving back to %s for relaying", G_STRFUNC, g2_msg_infostr_mb(mb), node_infostr(n)); } nmb = pmsg_clone_plain(mb); pmsg_clear_reliable(nmb); g2_node_send(n, nmb); gnet_stats_inc_general(GNR_UDP_G2_HITS_REROUTED_TO_HUB); } done: nid_unref(pmi->hub_id); pmi->magic = 0; WFREE(pmi); }
/** * Retrieve cached security token for a given KUID. * * @param id the KUID for which we'd like the security token * @param len_ptr where the length of the security token is written * @param tok_ptr where the address of the security token is written * @param time_ptr where the last update time of token is writen * * @return TRUE if we found a token, with len_ptr and tok_ptr filled with * the information about the length and the token pointer. Information is * returned from a static memory buffer so it must be perused immediately. */ bool tcache_get(const kuid_t *id, uint8 *len_ptr, const void **tok_ptr, time_t *time_ptr) { struct tokdata *td; g_assert(id != NULL); td = get_tokdata(id); if (NULL == td) return FALSE; if (delta_time(tm_time(), td->last_update) > token_life) { delete_tokdata(id); return FALSE; } if (len_ptr != NULL) *len_ptr = td->length; if (tok_ptr != NULL) *tok_ptr = td->token; if (time_ptr != NULL) *time_ptr = td->last_update; if (GNET_PROPERTY(dht_tcache_debug) > 4) { char buf[80]; bin_to_hex_buf(td->token, td->length, buf, sizeof buf); g_debug("DHT TCACHE security token for %s is %u-byte \"%s\" (%s)", kuid_to_hex_string(id), td->length, buf, compact_time(delta_time(tm_time(), td->last_update))); } gnet_stats_inc_general(GNR_DHT_CACHED_TOKENS_HITS); return TRUE; }
/** * Record activity on the node. */ void stable_record_activity(const knode_t *kn) { struct lifedata *ld; struct lifedata new_ld; knode_check(kn); g_assert(kn->flags & KNODE_F_ALIVE); ld = get_lifedata(kn->id); if (NULL == ld) { ld = &new_ld; new_ld.version = LIFEDATA_STRUCT_VERSION; new_ld.first_seen = kn->first_seen; new_ld.last_seen = kn->last_seen; gnet_stats_inc_general(GNR_DHT_STABLE_NODES_HELD); } else { if (kn->last_seen <= ld->last_seen) return; ld->last_seen = kn->last_seen; } dbmw_write(db_lifedata, kn->id->v, ld, sizeof *ld); }
/** * 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; }
/** * Add GUID to the banned list or refresh the fact that we are still seeing * it as being worth banning. */ void guid_add_banned(const struct guid *guid) { struct guiddata *gd; struct guiddata new_gd; gd = get_guiddata(guid); if (NULL == gd) { gd = &new_gd; gd->create_time = gd->last_time = tm_time(); gnet_stats_inc_general(GNR_BANNED_GUID_HELD); if (GNET_PROPERTY(guid_debug)) { g_debug("GUID banning %s", guid_hex_str(guid)); } } else { gd->last_time = tm_time(); } dbmw_write(db_guid, guid, gd, sizeof *gd); }
/** * Computes republish delay for a value. * * @param info the information from the publishing layer * @param expiration expected value expiration time * * @return republishing delay, in seconds. * * @attention * As a side effect, increases the count of satisfactory publishes if the * information from the publishing layer lead us to believe it is. Therefore * this routine is not idempotent and should be called only once per callback. */ int publisher_delay(const pdht_info_t *info, time_delta_t expiration) { int delay; if (0 == info->all_roots) { delay = PUBLISH_SAFETY; } else if ( info->all_roots >= KDA_K || info->presence >= PUBLISH_MIN_PROBABILITY ) { delay = expiration - PUBLISH_SAFETY; gnet_stats_inc_general(GNR_DHT_PUBLISHING_SATISFACTORY); } else { g_assert(uint_is_positive(info->all_roots)); delay = inverse_decimation[info->all_roots - 1] * expiration; delay -= PUBLISH_SAFETY; delay = MAX(delay, PUBLISH_SAFETY); } return delay; }
/** * Map iterator to record security tokens in the database. */ static void record_token(void *key, void *value, void *unused_u) { kuid_t *id = key; lookup_token_t *ltok = value; struct tokdata td; (void) unused_u; td.last_update = ltok->retrieved; td.length = ltok->token->length; td.token = td.length ? wcopy(ltok->token->v, td.length) : NULL; if (GNET_PROPERTY(dht_tcache_debug) > 4) { char buf[80]; bin_to_hex_buf(td.token, td.length, buf, sizeof buf); g_debug("DHT TCACHE adding security token for %s: %u-byte \"%s\"", kuid_to_hex_string(id), td.length, buf); } /* * Data is put in the DBMW cache and the dynamically allocated token * will be freed via free_tokdata() when the cached entry is released. */ if (!dbmw_exists(db_tokdata, id->v)) gnet_stats_inc_general(GNR_DHT_CACHED_TOKENS_HELD); dbmw_write(db_tokdata, id->v, &td, sizeof td); if (GNET_PROPERTY(dht_tcache_debug_flags) & DBG_DSF_USR1) { g_debug("DHT TCACHE %s: stats=%s, count=%zu, id=%s", G_STRFUNC, uint64_to_string( gnet_stats_get_general(GNR_DHT_CACHED_TOKENS_HELD)), dbmw_count(db_tokdata), kuid_to_hex_string(id)); } }
/** * 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 semi-reliable UDP header corresponds to valid traffic. * * This routine is only used for ambiguous traffic that looks like both * Gnutella and semi-reliable UDP: we want to make sure we're not mistaking * a legitimate semi-reliable fragment / ACK for a Gnutella message. * * @param utp already classified semi-reliable protocol * @param s socket which received the message * @param data received data * @param len length of data * * @return TRUE if message corresponds to valid semi-reliable UDP traffic. */ static bool udp_is_valid_semi_reliable(enum udp_traffic utp, const gnutella_socket_t *s, const void *data, size_t len) { struct ut_header uth; void *message = NULL; size_t msglen; bool valid = TRUE; /* * Since we're talking about an ambiguous message, it is highly unlikely * we'll ever be called with an acknowledgement: they should have been * ruled out earlier as improbable since ACKs are short message, much * shorter than a Gnuella header typically. * * So we'll only handle fragments for now, assuming ACKs are legitimate. */ gnet_stats_inc_general(GNR_UDP_AMBIGUOUS_DEEPER_INSPECTION); uth.count = udp_reliable_header_get_count(data); if (0 == uth.count) return TRUE; /* Acknoweldgments */ uth.part = udp_reliable_header_get_part(data) - 1; /* Zero-based */ uth.flags = udp_reliable_header_get_flags(data); uth.seqno = udp_reliable_header_get_seqno(data); /* * We're going to ask the RX layer about the message: is it a known * sequence ID for this host? * * This works only for messages with more than one fragment, of course, * but chances are that, for these, we would have possibly already * received another fragment, not mistaken as a Gnutella message... * * This is OK for acknowledged fragments: we're not going to acknowledge * the unprocessed fragment, but we'll receive other fragments of the * message, and later on we'll get a retransmission of the unprocessed * fragment, which this time will be validated since we have already * partially received the message. */ if (uth.count > 1) { rxdrv_t *rx; gnet_host_t from; gnet_host_set(&from, s->addr, s->port); rx = udp_get_rx_semi_reliable(utp, s->addr, len); return NULL == rx ? FALSE : ut_valid_message(rx, &uth, &from); } /* * We're facing a single-fragment message. * * We can trivially probe it and validate it to see whether it can still * be interpreted as a valid Gnutella message on its own... If the answer * is yes, then we can assert we're facing a valid semi-reliable UDP * message. * * For deflated payloads, we already validated that the start of the * payload is a well-formed zlib header, but we'll attempt deflation anyway * so we will know for sure whether it's a valid message! * * Of course we're doing here work that will have to be redone later when * processing the message, but this is for proper classification and not * happening very often: only on a very very small fraction of messages for * which there is a high level of ambiguity. */ g_assert(0 == uth.part); /* First (and only) fragment */ if (uth.flags & UDP_RF_DEFLATED) { int outlen = settings_max_msg_size(); int ret; message = xmalloc(outlen); ret = zlib_inflate_into( const_ptr_add_offset(data, UDP_RELIABLE_HEADER_SIZE), len - UDP_RELIABLE_HEADER_SIZE, message, &outlen); if (ret != Z_OK) { valid = FALSE; /* Does not inflate properly */ goto done; } msglen = outlen; } else { message = ptr_add_offset( deconstify_pointer(data), UDP_RELIABLE_HEADER_SIZE); msglen = len - UDP_RELIABLE_HEADER_SIZE; } switch (utp) { case SEMI_RELIABLE_GTA: /* * Assume message is valid if the Gnutella size header is consistent * with the length of the whole message. */ { uint16 size; switch (gmsg_size_valid(message, &size)) { case GMSG_VALID: case GMSG_VALID_MARKED: break; case GMSG_VALID_NO_PROCESS: /* Header flags undefined for now */ case GMSG_INVALID: valid = FALSE; goto done; } valid = (size_t) size + GTA_HEADER_SIZE == msglen; } break; case SEMI_RELIABLE_GND: valid = TRUE; /* For now */ break; case GNUTELLA: case DHT: case RUDP: case UNKNOWN: g_assert_not_reached(); } done: if (uth.flags & UDP_RF_DEFLATED) xfree(message); return valid; }
/** * 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 */ }
/** * Handle reception of a /Q2 */ static void g2_node_handle_q2(gnutella_node_t *n, const g2_tree_t *t) { const guid_t *muid; size_t paylen; const g2_tree_t *c; char *dn = NULL; char *md = NULL; uint32 iflags = 0; search_request_info_t sri; bool has_interest = FALSE; node_inc_rx_query(n); /* * As a G2 leaf, we cannot handle queries coming from UDP because we * are not supposed to get any! */ if (NODE_IS_UDP(n)) { g2_node_drop(G_STRFUNC, n, t, "coming from UDP"); return; } /* * The MUID of the query is the payload of the root node. */ muid = g2_tree_node_payload(t, &paylen); if (paylen != GUID_RAW_SIZE) { g2_node_drop(G_STRFUNC, n, t, "missing MUID"); return; } /* * Make sure we have never seen this query already. * * To be able to leverage on Gnutella's routing table to detect duplicates * over a certain lifespan, we are going to fake a minimal Gnutella header * with a message type of GTA_MSG_G2_SEARCH, which is never actually used * on the network. * * The TTL and hops are set to 1 and 0 initially, so that the message seems * to come from a neighbouring host and cannot be forwarded. * * When that is done, we will be able to call route_message() and have * all the necessary bookkeeping done for us. */ { struct route_dest dest; gnutella_header_set_muid(&n->header, muid); gnutella_header_set_function(&n->header, GTA_MSG_G2_SEARCH); gnutella_header_set_ttl(&n->header, 1); gnutella_header_set_hops(&n->header, 0); if (!route_message(&n, &dest)) return; /* Already accounted as duplicated, and logged */ } /* * Setup request information so that we can call search_request() * to process our G2 query. */ ZERO(&sri); sri.magic = SEARCH_REQUEST_INFO_MAGIC; /* * Handle the children of /Q2. */ G2_TREE_CHILD_FOREACH(t, c) { enum g2_q2_child ct = TOKENIZE(g2_tree_name(c), g2_q2_children); const char *payload; switch (ct) { case G2_Q2_DN: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == dn) { uint off = 0; /* Not NUL-terminated, need to h_strndup() it */ dn = h_strndup(payload, paylen); if (!query_utf8_decode(dn, &off)) { gnet_stats_count_dropped(n, MSG_DROP_MALFORMED_UTF_8); goto done; /* Drop the query */ } sri.extended_query = dn + off; sri.search_len = paylen - off; /* In bytes */ } break; case G2_Q2_I: if (!has_interest) iflags = g2_node_extract_interest(c); has_interest = TRUE; break; case G2_Q2_MD: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == md) { /* Not NUL-terminated, need to h_strndup() it */ md = h_strndup(payload, paylen); } break; case G2_Q2_NAT: sri.flags |= QUERY_F_FIREWALLED; break; case G2_Q2_SZR: /* Size limits */ if (g2_node_extract_size_request(c, &sri.minsize, &sri.maxsize)) sri.size_restrictions = TRUE; break; case G2_Q2_UDP: if (!sri.oob) g2_node_extract_udp(c, &sri, n); break; case G2_Q2_URN: g2_node_extract_urn(c, &sri); break; } } /* * When there is no /Q2/I, return a default set of information. */ if (!has_interest) iflags = G2_Q2_F_DFLT; /* * If there are meta-data, try to intuit which media types there are * looking for. * * The payload is XML looking like "<audio/>" or "<video/>" but there * can be attributes and we don't want to do a full XML parsing there. * Hence we'll base our analysis on simple lexical parsing, which is * why we call a routine to "intuit", not to "extract". * * Also, this is poorer than Gnutella's GGEP "M" because apparently there * can be only one single type, since the XML payload must obey some * kind of schema and there is an audio schema, a video schema, etc... * XML was just a wrong design choice there. */ if (md != NULL) sri.media_types = g2_node_intuit_media_type(md); /* * Validate the return address if OOB hit delivery is configured. */ if (sri.oob && !search_oob_is_allowed(n, &sri)) goto done; /* * Update statistics, as done in search_request_preprocess() for Gnutella. */ if (sri.exv_sha1cnt) { gnet_stats_inc_general(GNR_QUERY_G2_SHA1); if (NULL == dn) { int i; for (i = 0; i < sri.exv_sha1cnt; i++) { search_request_listener_emit(QUERY_SHA1, sha1_base32(&sri.exv_sha1[i].sha1), n->addr, n->port); } } } if (dn != NULL && !is_ascii_string(dn)) gnet_stats_inc_general(GNR_QUERY_G2_UTF8); if (dn != NULL) search_request_listener_emit(QUERY_STRING, dn, n->addr, n->port); if (!search_is_valid(n, 0, &sri)) goto done; /* * Perform the query. */ sri.g2_query = TRUE; sri.partials = booleanize(iflags & G2_Q2_F_PFS); sri.g2_wants_url = booleanize(iflags & G2_Q2_F_URL); sri.g2_wants_alt = booleanize(iflags & G2_Q2_F_A); sri.g2_wants_dn = booleanize(iflags & G2_Q2_F_DN); search_request(n, &sri, NULL); done: HFREE_NULL(dn); HFREE_NULL(md); }
/** * Generate a SHA1 digest of the current UDP statistics. * * This is meant for dynamic entropy collection. */ void gnet_stats_udp_digest(sha1_t *digest) { gnet_stats_inc_general(GNR_STATS_UDP_DIGEST); gnet_stats_digest(digest, &gnet_udp_stats); }
/** * Fill supplied value vector with the DHT values we have under the key that * match the specifications: among those bearing the specified secondary keys * (or all of them if no secondary keys are supplied), return only those with * the proper DHT value type. * * @param id the primary key of the value * @param type type of DHT value they want * @param secondary optional secondary keys * @param secondary_count amount of secondary keys supplied * @param valvec value vector where results are stored * @param valcnt size of value vector * @param loadptr where to write the average request load for key * @param cached if non-NULL, filled with whether key was cached * * @return amount of values filled into valvec. The values are dynamically * created and must be freed by caller through dht_value_free(). */ int keys_get(const kuid_t *id, dht_value_type_t type, kuid_t **secondary, int secondary_count, dht_value_t **valvec, int valcnt, float *loadptr, bool *cached) { struct keyinfo *ki; struct keydata *kd; int i; int vcnt = valcnt; dht_value_t **vvec = valvec; g_assert(secondary_count == 0 || secondary != NULL); g_assert(valvec); g_assert(valcnt > 0); g_assert(loadptr); ki = hikset_lookup(keys, id); g_assert(ki); /* If called, we know the key exists */ if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s (load = %g, current reqs = %u) type %s" " with %d secondary key%s", kuid_to_hex_string(id), ki->get_req_load, ki->get_requests, dht_value_type_to_string(type), secondary_count, plural(secondary_count)); *loadptr = ki->get_req_load; ki->get_requests++; kd = get_keydata(id); if (kd == NULL) /* DB failure */ return 0; /* * If secondary keys were requested, lookup them up and make sure * they have the right DHT type (or skip them). */ for (i = 0; i < secondary_count && vcnt > 0; i++) { uint64 dbkey = lookup_secondary(kd, secondary[i]); dht_value_t *v; if (0 == dbkey) continue; v = values_get(dbkey, type); if (v == NULL) continue; g_assert(kuid_eq(dht_value_key(v), id)); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s via secondary key %s has matching %s", kuid_to_hex_string(id), kuid_to_hex_string2(secondary[i]), dht_value_to_string(v)); *vvec++ = v; vcnt--; } /* * Don't count secondary-key fetches in the local hit stats: in order to * be able to get these fetches, we must have initially provided the * list of these keys, and thus we have already traversed the code below * for that fetch, which accounted the hit already. */ if (secondary_count) { int n = vvec - valvec; /* Amount of entries filled */ gnet_stats_count_general(GNR_DHT_CLAIMED_SECONDARY_KEYS, n); if (ki->flags & DHT_KEY_F_CACHED) gnet_stats_count_general(GNR_DHT_CLAIMED_CACHED_SECONDARY_KEYS, n); goto done; } /* * No secondary keys specified. Look them all up. */ for (i = 0; i < kd->values && vcnt > 0; i++) { uint64 dbkey = kd->dbkeys[i]; dht_value_t *v; g_assert(0 != dbkey); v = values_get(dbkey, type); if (v == NULL) continue; g_assert(kuid_eq(dht_value_key(v), id)); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s has matching %s", kuid_to_hex_string(id), dht_value_to_string(v)); *vvec++ = v; vcnt--; } /* * Stats update: we count all the hits, plus successful hits on keys * that do not fall within our k-ball, i.e. keys for which we act as * a "cache". Note that our k-ball frontier can evolve through time, * so we rely on the DHT_KEY_F_CACHED flag, positionned at creation time. */ if (vvec != valvec) { gnet_stats_inc_general(GNR_DHT_FETCH_LOCAL_HITS); if (ki->flags & DHT_KEY_F_CACHED) gnet_stats_inc_general(GNR_DHT_FETCH_LOCAL_CACHED_HITS); } done: if (cached) *cached = (ki->flags & DHT_KEY_F_CACHED) ? TRUE : FALSE; return vvec - valvec; /* Amount of entries filled */ }
/** * Add value to a key, recording the new association between the KUID of the * creator (secondary key) and the 64-bit DB key under which the value is * stored. * * @param id the primary key (may not exist yet) * @param cid the secondary key (creator's ID) * @param dbkey the 64-bit DB key * @param expire expiration time for the value */ void keys_add_value(const kuid_t *id, const kuid_t *cid, uint64 dbkey, time_t expire) { struct keyinfo *ki; struct keydata *kd; struct keydata new_kd; ki = hikset_lookup(keys, id); /* * If we're storing the first value under a key, we do not have any * keyinfo structure yet. */ if (NULL == ki) { size_t common; bool in_kball; common = kuid_common_prefix(get_our_kuid(), id); in_kball = bits_within_kball(common); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT STORE new %s %s (%zu common bit%s) with creator %s", in_kball ? "key" : "cached key", kuid_to_hex_string(id), common, plural(common), kuid_to_hex_string2(cid)); ki = allocate_keyinfo(id, common); ki->next_expire = expire; ki->flags = in_kball ? 0 : DHT_KEY_F_CACHED; hikset_insert_key(keys, &ki->kuid); kd = &new_kd; kd->values = 0; /* will be incremented below */ kd->creators[0] = *cid; /* struct copy */ kd->dbkeys[0] = dbkey; kd->expire[0] = expire; gnet_stats_inc_general(GNR_DHT_KEYS_HELD); if (!in_kball) gnet_stats_inc_general(GNR_DHT_CACHED_KEYS_HELD); } else { int low = 0; int high = ki->values - 1; kd = get_keydata(id); if (NULL == kd) return; g_assert(kd->values == ki->values); g_assert(kd->values < MAX_VALUES); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT STORE existing key %s (%u common bit%s) " "has new creator %s", kuid_to_hex_string(id), ki->common_bits, plural(ki->common_bits), kuid_to_hex_string2(cid)); /* * Keys are collected asynchronously, so it is possible that * the key structure still exists, yet holds no values. If this * happens, then we win because we spared the useless deletion of * the key structure to recreate it a little bit later. */ if (0 == kd->values) goto empty; /* * Insert KUID of creator in array, which must be kept sorted. * We perform a binary insertion. */ while (low <= high) { int mid = low + (high - low) / 2; int c; g_assert(mid >= 0 && mid < ki->values); c = kuid_cmp(&kd->creators[mid], cid); if (0 == c) g_error("new creator KUID %s must not already be present", kuid_to_hex_string(cid)); else if (c < 0) low = mid + 1; else high = mid - 1; } /* Make room for inserting new item at `low' */ ARRAY_FIXED_MAKEROOM(kd->creators, low, kd->values); ARRAY_FIXED_MAKEROOM(kd->dbkeys, low, kd->values); ARRAY_FIXED_MAKEROOM(kd->expire, low, kd->values); /* FALL THROUGH */ empty: /* Insert new item at `low' */ kd->creators[low] = *cid; /* struct copy */ kd->dbkeys[low] = dbkey; kd->expire[low] = expire; ki->next_expire = MIN(ki->next_expire, expire); } kd->values++; ki->values++; dbmw_write(db_keydata, id, kd, sizeof *kd); if (GNET_PROPERTY(dht_storage_debug) > 2) g_debug("DHT STORE %s key %s now holds %d/%d value%s", &new_kd == kd ? "new" : "existing", kuid_to_hex_string(id), ki->values, MAX_VALUES, plural(ki->values)); }
/* * Offload keys to remote node, as appropriate. * * Firstly we only consider remote nodes whose KUID falls within our k-ball. * * Secondly, we are only considering remote nodes that end-up being in our * routing table (i.e. ones which are close enough to us to get room in the * table, which also means they're not firewalled nor going to shutdown soon). * This is normally ensured by our caller. * * Thirdly, we are only going to consider keys closer to the node than we are * and for which we are the closest among our k-closest nodes, to avoid too * many redundant STORE operations. */ void keys_offload(const knode_t *kn) { struct offload_context ctx; unsigned n; knode_t *kclosest[KDA_K]; /* Our known k-closest nodes */ bool debug; knode_check(kn); if (kn->flags & (KNODE_F_FIREWALLED | KNODE_F_SHUTDOWNING)) return; if ( !dht_bootstrapped() || /* Not bootstrapped */ !keys_within_kball(kn->id) || /* Node KUID outside our k-ball */ 0 == hikset_count(keys) /* No keys held */ ) return; debug = GNET_PROPERTY(dht_storage_debug) > 1 || GNET_PROPERTY(dht_publish_debug) > 1; if (debug) g_debug("DHT preparing key offloading to %s", knode_to_string(kn)); gnet_stats_inc_general(GNR_DHT_KEY_OFFLOADING_CHECKS); ctx.our_kuid = get_our_kuid(); ctx.remote_kuid = kn->id; ctx.found = NULL; ctx.count = 0; /* * We need to have KDA_K closest known alive neighbours in order to * be able to select proper keys to offload. * * Note that we make sure to NOT include the new node in our k-closest set * since it would always be closer than ourselves to keys we wish to * offload to it... */ n = dht_fill_closest(ctx.our_kuid, kclosest, G_N_ELEMENTS(kclosest), ctx.remote_kuid, TRUE); if (n < G_N_ELEMENTS(kclosest)) { if (debug) g_warning("DHT got only %u closest alive nodes, cannot offload", n); return; } /* * Prepare a PATRICIA containing the ID of our k-closest alive nodes * plus ourselves. */ ctx.kclosest = patricia_create(KUID_RAW_BITSIZE); for (n = 0; n < G_N_ELEMENTS(kclosest); n++) { patricia_insert(ctx.kclosest, kclosest[n]->id, kclosest[n]->id); } patricia_insert(ctx.kclosest, ctx.our_kuid, ctx.our_kuid); /* * Select offloading candidate keys. */ hikset_foreach(keys, keys_offload_prepare, &ctx); patricia_destroy(ctx.kclosest); if (debug) { g_debug("DHT found %u/%zu offloading candidate%s", ctx.count, hikset_count(keys), plural(ctx.count)); } if (ctx.count) publish_offload(kn, ctx.found); pslist_free_null(&ctx.found); }
/** * Publishing callback invoked when asynchronous publication is completed, * or ended with an error. * * @return TRUE if we accept the publishing, FALSE otherwise to get the * publishing layer to continue attempts to failed STORE roots and report * on progress using the same callback. */ static bool publisher_done(void *arg, pdht_error_t code, const pdht_info_t *info) { struct publisher_entry *pe = arg; struct pubdata *pd; int delay = PUBLISH_BUSY; bool expired = FALSE; bool accepted = TRUE; publisher_check(pe); pd = get_pubdata(pe->sha1); /* * Update stats on republishing before value expiration. */ if (PDHT_E_OK == code) { if (pe->last_publish && info->roots > 0) { if (pd != NULL) { if (pd->expiration && delta_time(tm_time(), pd->expiration) > 0) expired = TRUE; } else { time_delta_t elapsed = delta_time(tm_time(), pe->last_publish); if (elapsed > DHT_VALUE_ALOC_EXPIRE) expired = TRUE; } if (expired) gnet_stats_inc_general(GNR_DHT_REPUBLISHED_LATE); } } /* * Compute retry delay. */ switch (code) { case PDHT_E_OK: /* * If we were not able to publish to KDA_K nodes, decrease the * delay before republishing. We use a non-linear decimation of * the republish time, as a function of the number of nodes to which * we could publish. */ delay = publisher_delay(info, DHT_VALUE_ALOC_EXPIRE); accepted = publisher_is_acceptable(info); break; case PDHT_E_POPULAR: /* * Compute the suitable delay: the first time, we use PUBLISH_POPULAR, * and then we double each time until we reach PUBLISH_POPULAR_MAX. * * If we already tried to publish the entry, pe->last_delayed will * be non-zero. */ if (0 != pe->last_delayed) { time_delta_t elapsed = delta_time(tm_time(), pe->last_delayed); if (elapsed < PUBLISH_POPULAR) { delay = PUBLISH_POPULAR; } else if (elapsed >= PUBLISH_POPULAR_MAX / 2) { delay = PUBLISH_POPULAR_MAX; } else { delay = elapsed * 2; } } else { delay = PUBLISH_POPULAR; } break; case PDHT_E_NOT_SHARED: case PDHT_E_LOOKUP_EXPIRED: case PDHT_E_LOOKUP: case PDHT_E_UDP_CLOGGED: case PDHT_E_PUBLISH_EXPIRED: case PDHT_E_PUBLISH_ERROR: case PDHT_E_SHA1: case PDHT_E_PENDING: case PDHT_E_CANCELLED: case PDHT_E_GGEP: case PDHT_E_NONE: delay = PUBLISH_BUSY; break; case PDHT_E_MAX: g_assert_not_reached(); } /* * For a backgrounded entry publishing, we need to adjust the computed * delay with the time that was elapsed */ g_assert(!pe->backgrounded == !(pe->publish_ev != NULL)); if (pe->backgrounded) { time_delta_t elapsed = delta_time(tm_time(), pe->last_delayed); g_assert(pe->last_delayed > 0); cq_cancel(&pe->publish_ev); if (delay > elapsed) { delay -= elapsed; } else { delay = 1; } } /* * Logging. */ if (GNET_PROPERTY(publisher_debug) > 1) { shared_file_t *sf = shared_file_by_sha1(pe->sha1); char retry[80]; char after[80]; const char *late = ""; after[0] = '\0'; if (pe->last_publish) { time_delta_t elapsed = delta_time(tm_time(), pe->last_publish); str_bprintf(after, sizeof after, " after %s", compact_time(elapsed)); if (pd != NULL) { if (expired) late = "late, "; } else { late = "no data, "; } } str_bprintf(retry, sizeof retry, "%s", compact_time(delay)); g_debug("PUBLISHER SHA-1 %s %s%s\"%s\" %spublished to %u node%s%s: %s" " (%stook %s, total %u node%s, proba %.3f%%, retry in %s," " %s bg, path %u) [%s]", sha1_to_string(pe->sha1), pe->backgrounded ? "[bg] " : "", (sf && sf != SHARE_REBUILDING && shared_file_is_partial(sf)) ? "partial " : "", (sf && sf != SHARE_REBUILDING) ? shared_file_name_nfc(sf) : "", pe->last_publish ? "re" : "", info->roots, plural(info->roots), after, pdht_strerror(code), late, compact_time(delta_time(tm_time(), pe->last_enqueued)), info->all_roots, plural(info->all_roots), info->presence * 100.0, retry, info->can_bg ? "can" : "no", info->path_len, accepted ? "OK" : "INCOMPLETE"); shared_file_unref(&sf); } /* * Update last publishing time and remember expiration time. */ if (PDHT_E_OK == code && info->roots > 0) { pe->last_publish = tm_time(); if (pd != NULL) { pd->expiration = time_advance(pe->last_publish, DHT_VALUE_ALOC_EXPIRE); dbmw_write(db_pubdata, pe->sha1, pd, sizeof *pd); } } /* * If entry was deemed popular, we're going to delay its republishing * by a larger amount of time and any data we published already about * it will surely expire. Since this is our decision, we do not want * to be told that republishing, if it occurs again, was done later than * required. Hence call publisher_hold() to mark that we don't care. */ if (PDHT_E_POPULAR == code) publisher_hold(pe, delay, "popular entry"); else publisher_retry(pe, delay, accepted ? "accepted publish" : "published"); pe->backgrounded = !accepted; return accepted; }
/** * Generate a SHA1 digest of the current general statistics. * * This is meant for dynamic entropy collection. */ void gnet_stats_general_digest(sha1_t *digest) { gnet_stats_inc_general(GNR_STATS_DIGEST); SHA1_COMPUTE(gnet_stats.general, digest); }
/** * 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; }