Exemple #1
0
/**
 * DBMW foreach iterator to remove old entries.
 * @return TRUE if entry must be deleted.
 */
static bool
guid_prune_old_entries(void *key, void *value, size_t u_len, void *u_data)
{
	const guid_t *guid = key;
	const struct guiddata *gd = value;
	time_delta_t d;
	double p = 0.0;
	bool expired;

	(void) u_len;
	(void) u_data;

	/*
	 * We reuse the statistical probability model of DHT nodes to project
	 * whether it makes sense to keep an entry.
	 */

	d = delta_time(tm_time(), gd->last_time);
	if (gd->create_time == gd->last_time) {
		expired = d > GUID_STABLE_LIFETIME;
	} else {
		p = stable_still_alive_probability(gd->create_time, gd->last_time);
		expired = p < GUID_STABLE_PROBA;
	}

	if (GNET_PROPERTY(guid_debug) > 5) {
		g_debug("GUID cached %s life=%s last_seen=%s, p=%.2f%%%s",
			guid_hex_str(guid),
			compact_time(delta_time(gd->last_time, gd->create_time)),
			compact_time2(d), p * 100.0,
			expired ? " [EXPIRED]" : "");
	}

	return expired;
}
Exemple #2
0
/**
 * Fill supplied buffer with the formatted string describing the message.
 *
 * @param data		start of the G2 message
 * @param len		length of the message
 * @param buf		buffer where formatted string is written
 * @param buflen	length of the destination buffer
 *
 * @return the amount of bytes written.
 */
size_t
g2_msg_infostr_to_buf(const void *data, size_t len, char *buf, size_t buflen)
{
	enum g2_msg m;
	const guid_t *muid = NULL;

	g_assert(size_is_non_negative(len));
	g_assert(size_is_non_negative(buflen));

	/*
	 * Check whether we need to decompile the packet to access the GUID, which
	 * is the payload of the root element in the tree.  Given the way things
	 * are serialized, that would be the last 16 bytes of the message, so
	 * we don't have to deserialize everything just to access it.
	 */

	m = g2_msg_type(data, len);

	switch (m) {
	case G2_MSG_Q2:
	case G2_MSG_QA:
	case G2_MSG_QH2:
		if (len > GUID_RAW_SIZE)
			muid = const_ptr_add_offset(data, len - GUID_RAW_SIZE);
		/* FALL THROUGH */
	default:
		break;
	}

	return str_bprintf(buf, buflen,
		"/%s (%zu byte%s)%s%s",
		g2_msg_type_name(m), len, plural(len),
		NULL == muid ? "" : " #",
		NULL == muid ? "" : guid_hex_str(muid));
}
Exemple #3
0
/**
 * Send a Gnutella ping to the specified host via UDP, using the
 * specified MUID.
 */
void
udp_connect_back(const host_addr_t addr, uint16 port, const struct guid *muid)
{
	if (udp_send_ping(muid, addr, port, FALSE)) {
		if (GNET_PROPERTY(udp_debug) > 19)
			g_debug("UDP queued connect-back PING #%s to %s\n",
				guid_hex_str(muid), host_addr_port_to_string(addr, port));
	}
}
Exemple #4
0
/**
 * Flush current /QH2.
 *
 * Depending how the QH2 builder is configured, this either sends the message
 * to the target node or invokes a processing callback.
 */
static void
g2_build_qh2_flush(struct g2_qh2_builder *ctx)
{
	pmsg_t *mb;

	g_assert(ctx != NULL);
	g_assert(ctx->t != NULL);
	g_assert((ctx->n != NULL) ^ (ctx->cb != NULL));

	/*
	 * Restore the order of children in the root packet to be the order we
	 * used when we added the nodes, since we prepend new children.
	 */

	g2_tree_reverse_children(ctx->t);

	/*
	 * If sending over UDP, ask for reliable delivery of the query hit.
	 * To be able to monitor the fate of the message, we asssociate a free
	 * routine to it.
	 */

	if (ctx->to_udp) {
		struct g2_qh2_pmsg_info *pmi;

		WALLOC0(pmi);
		pmi->magic = G2_QH2_PMI_MAGIC;
		pmi->hub_id = nid_ref(NODE_ID(ctx->hub));
		mb = g2_build_pmsg_extended(ctx->t, g2_qh2_pmsg_free, pmi);
		pmsg_mark_reliable(mb);
	} else {
		mb = g2_build_pmsg(ctx->t);
	}

	if (GNET_PROPERTY(g2_debug) > 3) {
		g_debug("%s(): flushing the following hit for "
			"Q2 #%s to %s%s (%d bytes):",
			G_STRFUNC, guid_hex_str(ctx->muid),
			NULL == ctx->n ?
				stacktrace_function_name(ctx->cb) : node_infostr(ctx->n),
			NULL == ctx->n ? "()" : "", pmsg_size(mb));
		g2_tfmt_tree_dump(ctx->t, stderr, G2FMT_O_PAYLOAD | G2FMT_O_PAYLEN);
	}

	if (ctx->n != NULL)
		g2_node_send(ctx->n, mb);
	else
		(*ctx->cb)(mb, ctx->arg);

	ctx->messages++;
	ctx->current_size = 0;
	g2_tree_free_null(&ctx->t);
}
Exemple #5
0
/**
 * Send an UDP ping to the host cache.
 */
static void
uhc_send_ping(void)
{
	g_assert(uhc_connecting);

	guid_random_muid(&uhc_ctx.muid);

	if (udp_send_ping(&uhc_ctx.muid, uhc_ctx.addr, uhc_ctx.port, TRUE)) {

		if (GNET_PROPERTY(bootstrap_debug) || GNET_PROPERTY(log_uhc_pings_tx)) {
			g_debug("BOOT sent UDP SCP ping #%s to %s:%u",
				guid_hex_str(&uhc_ctx.muid), uhc_ctx.host, uhc_ctx.port);
		}

		/*
		 * Give GUI feedback.
		 */
		{
			char msg[256];

			str_bprintf(msg, sizeof msg,
				_("Sent ping to UDP host cache %s:%u"),
				uhc_ctx.host, uhc_ctx.port);
			gcu_statusbar_message(msg);
		}

		/*
		 * Arm a timer to see whether we should not try to ping another
		 * host cache if we don't get a timely reply.
		 */

		g_assert(uhc_ctx.timeout_ev == NULL);

		uhc_ctx.timeout_ev = cq_main_insert(UHC_TIMEOUT,
			uhc_ping_timeout, NULL);
	} else {
		g_warning("BOOT failed to send UDP SCP to %s",
			host_addr_port_to_string(uhc_ctx.addr, uhc_ctx.port));
		uhc_try_next();
	}
}
Exemple #6
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);
}
Exemple #7
0
/**
 * Called when a pong with an "IPP" extension was received.
 */
void
uhc_ipp_extract(gnutella_node_t *n, const char *payload, int paylen,
	enum net_type type)
{
	int i, cnt;
	int len = NET_TYPE_IPV6 == type ? 18 : 6;
	const void *p;

	g_assert(0 == paylen % len);

	cnt = paylen / len;

	if (GNET_PROPERTY(bootstrap_debug))
		g_debug("extracting %d host%s in UDP IPP pong #%s from %s",
			cnt, plural(cnt),
			guid_hex_str(gnutella_header_get_muid(&n->header)), node_addr(n));

	for (i = 0, p = payload; i < cnt; i++, p = const_ptr_add_offset(p, len)) {
		host_addr_t ha;
		uint16 port;

		host_ip_port_peek(p, type, &ha, &port);
		hcache_add_caught(HOST_ULTRA, ha, port, "UDP-HC");

		if (GNET_PROPERTY(bootstrap_debug) > 2)
			g_debug("BOOT collected %s from UDP IPP pong from %s",
				host_addr_port_to_string(ha, port), node_addr(n));
	}

	if (!uhc_connecting)
		return;

	/*
	 * Check whether this was a reply from our request.
	 *
	 * The reply could come well after we decided it timed out and picked
	 * another UDP host cache, which ended-up replying, so we must really
	 * check whether we're still in a probing cycle.
	 */

	if (!guid_eq(&uhc_ctx.muid, gnutella_header_get_muid(&n->header)))
		return;

	if (GNET_PROPERTY(bootstrap_debug)) {
		g_debug("BOOT UDP cache \"%s\" replied: got %d host%s from %s",
			uhc_ctx.host, cnt, plural(cnt), node_addr(n));
	}

	/*
	 * Terminate the probing cycle if we got hosts.
	 */

	if (cnt > 0) {
		char msg[256];

		cq_cancel(&uhc_ctx.timeout_ev);
		uhc_connecting = FALSE;

		str_bprintf(msg, sizeof(msg),
			NG_("Got %d host from UDP host cache %s",
				"Got %d hosts from UDP host cache %s",
				cnt),
			cnt, uhc_ctx.host);

		gcu_statusbar_message(msg);
	} else {
		uhc_try_next();
	}
}
/**
 * Determine unique filename for `file' in `path', with optional trailing
 * extension `ext'.  If no `ext' is wanted, one must supply an empty string.
 *
 * @param path A directory path.
 * @param file The basename for the resulting pathname.
 * @param ext An optional filename extension to be appended to the basename.
 * @param name_is_uniq An optional callback to decide whether a created
 *        pathname is uniq. If omitted, the default is file_does_not_exist().
 *
 * @returns the chosen unique complete filename as a pointer which must be
 * freed via hfree().
 */
char *
filename_unique(const char *path, const char *name, const char *ext,
		bool (*name_is_uniq)(const char *pathname))
{
	char filename_buf[FILENAME_MAXBYTES];
	char name_buf[FILENAME_MAXBYTES];
	char mid_buf[32];
	char ext_buf[32];
	const char *mid;
	char *pathname;
	size_t name_len, mid_len, ext_len;
	int i;

	g_assert(path);
	g_assert(name);
	g_assert(ext);
	g_assert(is_absolute_path(path));

	STATIC_ASSERT(sizeof filename_buf >
		sizeof mid_buf + sizeof ext_buf + GUID_HEX_SIZE);

	/**
	 * NOTE: The generated filename must not exceed FILENAME_MAXBYTES
	 *		 because such a file cannot be created. In reality, it depends
	 *		 on the filesystem as well and the limit might be even smaller.
	 *		 In any case, we don't want to cut-off arbitrary bytes but
	 *		 at least preserve the filename extension and the (potential)
	 *		 UTF-8 encoding.
	 */

	/* Because "ext" can be an additional extension like .BAD rather than
	 * one that indicates the filetype, try to preserve the next "extension"
	 * as well, if there's any. */
	mid = strrchr(name, '.');
	if (NULL == mid || mid == name || strlen(mid) >= sizeof mid_buf) {
		mid = strchr(name, '\0');
	}

	ext_len = strlen(ext);
	mid_len = strlen(mid);
	name_len = strlen(name) - mid_len;

	ext_len = MIN(ext_len, sizeof ext_buf - 1);
	mid_len = MIN(mid_len, sizeof mid_buf - 1);
	name_len = MIN(name_len, sizeof name_buf - 1);

	if (name_len + mid_len + ext_len >= sizeof filename_buf) {
		g_assert(name_len >= ext_len);
		name_len -= ext_len;
	}

	/* Truncate strings so that an UTF-8 encoding is preserved */
	ext_len = utf8_truncate(ext, ext_buf, ext_len + 1);
	mid_len = utf8_truncate(mid, mid_buf, mid_len + 1);
	name_len = utf8_truncate(name, name_buf, name_len + 1);

	str_bprintf(filename_buf, sizeof filename_buf, "%s%s%s",
		name_buf, mid_buf, ext_buf);

	pathname = unique_pathname(path, filename_buf, name_is_uniq);
	if (pathname)
		goto finish;

	if (!is_directory(path))
		return NULL;

	/*
	 * Looks like we need to make the filename more unique.  Append .00, then
	 * .01, etc... until .99.
	 */

	while (name_len + mid_len + ext_len + 3 >= sizeof filename_buf) {
		g_assert(name_len > 0);
		name_len--;
	}
	name_len = utf8_truncate(name, name_buf, name_len + 1);

	for (i = 0; i < 100; i++) {
		str_bprintf(filename_buf, sizeof filename_buf, "%s.%02u%s%s",
			name_buf, i, mid_buf, ext_buf);

		pathname = unique_pathname(path, filename_buf, name_is_uniq);
		if (pathname)
			goto finish;
	}

	/*
	 * OK, no luck.  Try with a few random numbers then.
	 */

	while (name_len + mid_len + ext_len + 9 >= sizeof filename_buf) {
		g_assert(name_len > 0);
		name_len--;
	}
	name_len = utf8_truncate(name, name_buf, name_len + 1);

	for (i = 0; i < 100; i++) {
		str_bprintf(filename_buf, sizeof filename_buf, "%s.%x%s%s",
			name_buf, (unsigned) random_u32(), mid_buf, ext_buf);

		pathname = unique_pathname(path, filename_buf, name_is_uniq);
		if (pathname)
			goto finish;
	}

	/*
	 * Bad luck.  Allocate a random GUID then.
	 */

	while (
		name_len + mid_len + ext_len + GUID_HEX_SIZE + 1 >= sizeof filename_buf
	) {
		g_assert(name_len > 0);
		name_len--;
	}
	name_len = utf8_truncate(name, name_buf, name_len + 1);

	{
		struct guid guid;

		guid_random_fill(&guid);
		str_bprintf(filename_buf, sizeof filename_buf, "%s.%s%s%s",
			name_buf, guid_hex_str(&guid), mid_buf, ext_buf);
	}

	pathname = unique_pathname(path, filename_buf, name_is_uniq);
	if (pathname)
		goto finish;

	/*
	 * This may also be the result of permission problems or inode
	 * exhaustion.
	 */
	g_warning("%s(): no luck with random number generator", G_STRFUNC);

finish:
	return pathname;
}
Exemple #9
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;
}