Example #1
0
void
gnet_stats_g2_count_flowc(const gnutella_node_t *n,
	const void *base, size_t len)
{
	uint t;
	uint8 f, ttl, hops;

	g_assert(thread_is_main());

	f = g2_msg_type(base, len);

	if (GNET_PROPERTY(node_debug) > 3)
		g_debug("FLOWC G2 %s", g2_msg_type_name(f));

	if (f != G2_MSG_MAX) {
		f += MSG_G2_BASE;
	} else {
		f = G_N_ELEMENTS(stats_lut) - 1;	/* Last, holds MSG_UNKNOWN */
	}

	ttl = NODE_USES_UDP(n) ? 1 : 2;		/* Purely made up, but cannot be 0 */
	hops = 0;		/* Locally generated, this is TX flowc */

	t = stats_lut[f];

	gnet_stats_flowc_internal(t, f, ttl, hops, len);
}
Example #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));
}
Example #3
0
/**
 * RPC timeout callback.
 */
static void
g2_rpc_timeout(cqueue_t *cq, void *obj)
{
	struct g2_rpc *gr = obj;

	g2_rpc_check(gr);

	if (GNET_PROPERTY(g2_rpc_debug) > 1) {
		g_debug("%s(): /%s RPC to %s timed out, calling %s()",
			G_STRFUNC, g2_msg_type_name(gr->key.type),
			host_addr_to_string(gr->key.addr),
			stacktrace_function_name(gr->cb));
	}

	cq_zero(cq, &gr->timeout_ev);
	(*gr->cb)(NULL, NULL, gr->arg);
	g2_rpc_free(gr, FALSE);
}
Example #4
0
/**
 * Notification that a message was received that could be the answer to
 * a pending RPC.
 *
 * @param n		the node from which we got the message
 * @param t		the received message tree
 *
 * @return TRUE if the message was indeed an RPC reply, FALSE otherwise.
 */
bool
g2_rpc_answer(const gnutella_node_t *n, const g2_tree_t *t)
{
	struct g2_rpc *gr;
	struct g2_rpc_key key;
	enum g2_msg type;

	type = g2_msg_name_type(g2_tree_name(t));

	key.type = g2_rpc_send_type(type);
	key.addr = n->addr;

	gr = hevset_lookup(g2_rpc_pending, &key);

	if (NULL == gr) {
		/*
		 * No known RPC, but wait... we can receive a /QKA when we issue a /Q2
		 * and the query key we knew for the remote host has expired, hence
		 * we must look whether there is not a /Q2 pending as well in that
		 * case.  Once again, the lack of MUID in these messages is a handicap.
		 */

		if (G2_MSG_QKA == type) {
			key.type = G2_MSG_Q2;
			gr = hevset_lookup(g2_rpc_pending, &key);
			if (gr != NULL)
				goto found;		/* Sent a /Q2, got a /QKA back */
		}

		if (GNET_PROPERTY(g2_rpc_debug) > 1) {
			g_debug("%s(): unexpected /%s RPC reply from %s",
				G_STRFUNC, g2_msg_type_name(key.type), node_infostr(n));
		}

		return FALSE;
	}

found:

	/*
	 * Got a reply for an RPC we sent, based solely on message type and
	 * source address of the message.  This is weak, but G2 works like that.
	 *
	 * Weakness comes from the fact that we cannot have multiple RPCs with
	 * a same IP address but towards different ports, nor have concurrent
	 * RPCs with the same host for several different by similar requests
	 * (although this can be viewed as anti-hammering, servers should protect
	 * against that in different ways, a crippled protocol not being an answer).
	 *
	 * The only transaction where we could use the MUID is /Q2 -> /QA but we
	 * leave that check to the GUESS layer and make sure here that we have only
	 * one single RPC transaction at a time with a given IP address.
	 */

	if (GNET_PROPERTY(g2_rpc_debug) > 2) {
		g_debug("%s(): /%s RPC to %s got a /%s reply, calling %s()",
			G_STRFUNC, g2_msg_type_name(gr->key.type),
			host_addr_to_string(gr->key.addr), g2_tree_name(t),
			stacktrace_function_name(gr->cb));
	}

	(*gr->cb)(n, t, gr->arg);
	g2_rpc_free(gr, FALSE);

	return TRUE;
}
Example #5
0
/**
 * Start a G2 RPC with the specified host.
 *
 * @param host		the host to which message is sent
 * @param mb		the message to send
 * @param cb		if non-NULL, callback to invoke on reply or timeout
 * @param arg		additional callback argument
 * @param timeout	amount of seconds before timeout
 *
 * @return TRUE if we initiated the RPC, FALSE if another of the same
 * kind was already in progress with the host.
 */
bool
g2_rpc_launch(const gnet_host_t *host, pmsg_t *mb,
	g2_rpc_cb_t cb, void *arg, unsigned timeout)
{
	struct g2_rpc *gr;
	struct g2_rpc_key key;
	gnutella_node_t *n;

	key.type = g2_msg_type_mb(mb);
	key.addr = gnet_host_get_addr(host);

	/*
	 * Because there is no MUID in /PI and /QKR messages, we cannot use that
	 * as a key to detect the RPC reply.  Therefore, we use the message type
	 * and the IP address of the host.  When a /PO or /QKA comes back, we'll
	 * be able to see whether we had a pending RPC from that host for that
	 * type of transaction.
	 *
	 * The downside is that we can only have one pending RPC at a time of
	 * a given kind towards a given IP address.  We don't use the port in
	 * the key because we cannot assume the reply will come from the same port
	 * we sent the message to, if the remote host is behind NAT or does not
	 * use its listening UDP socket to reply.
	 */

	if (hevset_contains(g2_rpc_pending, &key)) {
		if (GNET_PROPERTY(g2_rpc_debug)) {
			g_debug("%s(): cannot issue /%s RPC to %s: concurrent request",
				G_STRFUNC, g2_msg_type_name(key.type),
				gnet_host_to_string(host));
		}

		return FALSE;
	}

	/*
	 * Make sure the node is valid.
	 */

	n = node_udp_g2_get_addr_port(key.addr, gnet_host_get_port(host));

	if (NULL == n) {
		if (GNET_PROPERTY(g2_rpc_debug)) {
			g_debug("%s(): cannot issue /%s RPC to %s: cannot get G2 node",
				G_STRFUNC, g2_msg_type_name(key.type),
				gnet_host_to_string(host));
		}

		return FALSE;		/* Invalid node, or G2 disabled */
	}

	/*
	 * Good, we can issue the RPC.
	 */

	WALLOC(gr);
	gr->magic = G2_RPC_MAGIC;
	gr->key = key;				/* struct copy */
	gr->cb = cb;
	gr->arg = arg;
	gr->timeout_ev = cq_main_insert(timeout * 1000, g2_rpc_timeout, gr);

	hevset_insert(g2_rpc_pending, gr);

	if (GNET_PROPERTY(g2_rpc_debug) > 1) {
		g_debug("%s(): issuing /%s RPC to %s, timeout %u sec%s",
			G_STRFUNC, g2_msg_type_name(key.type),
			gnet_host_to_string(host), timeout, plural(timeout));
	}

	/*
	 * Do not send RPCs reliably: this can cause problems if we don't receive
	 * the ACK backm yet the message was received and processed remotely: the
	 * remote host will send a reply back and the message will still appear to
	 * be "unsent" locally.
	 *
	 * Furthermore, this alleviates the need for the remote side to actually
	 * acknowledge the request: targeted hosts can be busy so it's best to
	 * make the RPC "unreliable" to limit processing and bandwidth requirements.
	 */

	g2_node_send(n, mb);

	return TRUE;
}