Example #1
0
/**
 * 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);
}
Example #2
0
/**
 * 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;
}
Example #3
0
/**
 * 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);
}
Example #4
0
/**
 * 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;
}
Example #5
0
/**
 * 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);
}
Example #6
0
/**
 * 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;
}
Example #7
0
/**
 * 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));
	}
}
Example #8
0
/**
 * 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);
}
Example #9
0
/**
 * 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;
}
Example #10
0
/**
 * 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;
}
Example #11
0
/**
 * 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 */
}
Example #12
0
/**
 * 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);
}
Example #13
0
/**
 * 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);
}
Example #14
0
/**
 * 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 */
}
Example #15
0
/**
 * 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));
}
Example #16
0
/*
 * 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);
}
Example #17
0
/**
 * 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;
}
Example #18
0
/**
 * 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);
}
Example #19
0
/**
 * 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;
}