Example #1
0
/**
 * Handle reception of a /LNI
 */
static void
g2_node_handle_lni(gnutella_node_t *n, const g2_tree_t *t)
{
	g2_tree_t *c;

	/*
	 * Handle the children of /LNI.
	 */

	G2_TREE_CHILD_FOREACH(t, c) {
		enum g2_lni_child ct = TOKENIZE(g2_tree_name(c), g2_lni_children);
		const char *payload;
		size_t paylen;

		switch (ct) {
		case G2_LNI_GU:			/* the node's GUID */
			payload = g2_tree_node_payload(c, &paylen);
			if (GUID_RAW_SIZE == paylen)
				node_set_guid(n, (guid_t *) payload, TRUE);
			break;

		case G2_LNI_NA:			/* the node's address, with listening port */
			{
				host_addr_t addr;
				uint16 port;

				if (g2_node_parse_address(c, &addr, &port)) {
					if (host_address_is_usable(addr))
						n->gnet_addr = addr;
					n->gnet_port = port;
				}
			}
			break;

		case G2_LNI_LS:			/* library statistics */
			payload = g2_tree_node_payload(c, &paylen);
			if (paylen >= 8) {
				uint32 files = peek_le32(payload);
				uint32 kbytes = peek_le32(&payload[4]);

				n->gnet_files_count = files;
				n->gnet_kbytes_count = kbytes;
				n->flags |= NODE_F_SHARED_INFO;
			}
			break;

		case G2_LNI_V:			/* vendor code */
			payload = g2_tree_node_payload(c, &paylen);
			if (paylen >= 4)
				n->vcode.u32 = peek_be32(payload);
			break;

		case G2_LNI_UP:			/* uptime */
			payload = g2_tree_node_payload(c, &paylen);
			if (paylen <= 4)
				n->up_date = tm_time() - vlint_decode(payload, paylen);
			break;
		}
	}
}
Example #2
0
g2_node_drop(const char *routine, gnutella_node_t *n, const g2_tree_t *t,
	const char *fmt, ...)
{
	if (GNET_PROPERTY(g2_debug) || GNET_PROPERTY(log_dropped_g2)) {
		va_list args;
		char buf[256];

		va_start(args, fmt);

		if (fmt != NULL)
			str_vbprintf(ARYLEN(buf), fmt, args);
		else
			buf[0] = '\0';

		g_debug("%s(): dropping /%s from %s%s%s",
			routine, g2_tree_name(t), node_infostr(n),
			NULL == fmt ? "" : ": ", buf);

		va_end(args);
	}

	gnet_stats_count_dropped(n, MSG_DROP_G2_UNEXPECTED);

	if (GNET_PROPERTY(log_dropped_g2)) {
		g2_tfmt_tree_dump(t, stderr, G2FMT_O_PAYLEN);
	}
}
Example #3
0
/**
 * Tree message iterator to handle "NH" nodes and extract their IP:port.
 */
static void
g2_node_extract_nh(void *data, void *udata)
{
	const g2_tree_t *t = data;

	(void) udata;

	if (0 == strcmp("NH", g2_tree_name(t))) {
		host_addr_t addr;
		uint16 port;

		if (
			g2_node_parse_address(t, &addr, &port) &&
			host_is_valid(addr, port)
		) {
			hcache_add_caught(HOST_G2HUB, addr, port, "/KHL/NH");
		}
	}
}
Example #4
0
/**
 * Fetch the MUID in the message, if any is architected.
 *
 * @param t		the message tree
 * @param buf	the buffer to fill with a copy of the MUID
 *
 * @return a pointer to `buf' if OK and we filled the MUID, NULL if there is
 * no valid MUID in the message or the message is not carrying any MUID.
 */
guid_t *
g2_msg_get_muid(const g2_tree_t *t, guid_t *buf)
{
	enum g2_msg m;
	const void *payload;
	size_t paylen;
	size_t offset;

	g_assert(t != NULL);
	g_assert(buf != NULL);

	m = g2_msg_name_type(g2_tree_name(t));

	switch (m) {
	case G2_MSG_Q2:
	case G2_MSG_QA:
		offset = 0;
		break;
	case G2_MSG_QH2:
		offset = 1;			/* First payload byte is the hop count */
		break;
	default:
		return NULL;		/* No MUID in message */
	}

	payload = g2_tree_node_payload(t, &paylen);

	if (NULL == payload || paylen < GUID_RAW_SIZE + offset)
		return NULL;

	/*
	 * Copy the MUID in the supplied buffer for alignment purposes, since
	 * the MUID is offset by 1 byte in /QH2 messages, and return that aligned
	 * pointer.
	 */

	memcpy(buf, const_ptr_add_offset(payload, offset), GUID_RAW_SIZE);

	return buf;
}
Example #5
0
/**
 * Tree message iterator to handle "CH" nodes and extract their IP:port.
 */
static void
g2_node_extract_ch(void *data, void *udata)
{
	const g2_tree_t *t = data;

	(void) udata;

	if (0 == strcmp("CH", g2_tree_name(t))) {
		const char *payload;
		size_t paylen;

		payload = g2_tree_node_payload(t, &paylen);

		if (10 == paylen) {		/* IPv4:port + 32-bit timestamp */
			host_addr_t addr = host_addr_peek_ipv4(payload);
			uint16 port = peek_le16(&payload[4]);

			if (host_is_valid(addr, port) && !hostiles_is_bad(addr))
				guess_add_hub(addr, port);
		}
	}
}
Example #6
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 #7
0
/**
 * Handle message coming from G2 node.
 */
void
g2_node_handle(gnutella_node_t *n)
{
	g2_tree_t *t;
	size_t plen;
	enum g2_msg type;

	node_check(n);
	g_assert(NODE_TALKS_G2(n));

	t = g2_frame_deserialize(n->data, n->size, &plen, FALSE);
	if (NULL == t) {
		if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) {
			g_warning("%s(): cannot deserialize /%s from %s",
				G_STRFUNC, g2_msg_raw_name(n->data, n->size), node_infostr(n));
		}
		if (GNET_PROPERTY(log_bad_g2))
			dump_hex(stderr, "G2 Packet", n->data, n->size);
		return;
	} else if (plen != n->size) {
		if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) {
			g_warning("%s(): consumed %zu bytes but /%s from %s had %u",
				G_STRFUNC, plen, g2_msg_raw_name(n->data, n->size),
				node_infostr(n), n->size);
		}
		if (GNET_PROPERTY(log_bad_g2))
			dump_hex(stderr, "G2 Packet", n->data, n->size);
		hostiles_dynamic_add(n->addr,
			"cannot parse incoming messages", HSTL_GIBBERISH);
		goto done;
	} else if (GNET_PROPERTY(g2_debug) > 19) {
		g_debug("%s(): received packet from %s", G_STRFUNC, node_infostr(n));
		g2_tfmt_tree_dump(t, stderr, G2FMT_O_PAYLEN);
	}

	type = g2_msg_name_type(g2_tree_name(t));

	switch (type) {
	case G2_MSG_PI:
		g2_node_handle_ping(n, t);
		break;
	case G2_MSG_PO:
		g2_node_handle_pong(n, t);
		break;
	case G2_MSG_LNI:
		g2_node_handle_lni(n, t);
		break;
	case G2_MSG_KHL:
		g2_node_handle_khl(t);
		break;
	case G2_MSG_PUSH:
		handle_push_request(n, t);
		break;
	case G2_MSG_Q2:
		g2_node_handle_q2(n, t);
		break;
	case G2_MSG_QA:
	case G2_MSG_QKA:
		g2_node_handle_rpc_answer(n, t, type);
		break;
	case G2_MSG_QH2:
		search_g2_results(n, t);
		break;
	default:
		g2_node_drop(G_STRFUNC, n, t, "default");
		break;
	}

done:
	g2_tree_free_null(&t);
}
Example #8
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;
}