예제 #1
0
파일: node.c 프로젝트: graaff/gtk-gnutella
/**
 * 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);
}
예제 #2
0
void CServer::handle_request(const char *request)
{
	const char *p;
	int cmd_len;
	int ok;

	logger->log(3, "Got request: %s", request);
	p = strchr(request,' ');
	if (p == 0) {
		p = request + strlen(request);
	}
	cmd_len = p - request;
	while (*p == ' ') {
		p++;
	}
	ok = 0;
	if (cmd_len == 0) {
		logger->log(2, "Empty request!");
		return;
	}
	char *command = new char[cmd_len+1];
	memcpy(command, request, cmd_len);
	command[cmd_len] = 0;
	try {
		if (strcmp(command, "SEARCH") == 0) {
			ok = 1;
			search_request(p);
		}
		if (strcmp(command, "COUNT") == 0) {
			ok = 1;
			count_request();
		}
		if (strcmp(command, "DELETEHOST") == 0) {
			ok = 1;
			delete_request(p);
		}
		if (strcmp(command, "LOAD") == 0) {
			ok = 1;
			load_request(p);
		}
		if (strcmp(command, "STATUS") == 0) {
			ok = 1;
			status_request();
		}
		if (strcmp(command, "HOSTINFO") == 0) {
			ok = 1;
			host_info_request(p);
		}
		if (strcmp(command, "HOSTSTAT") == 0) {
			ok = 1;
			host_stat_request(p);
		}
		if (strcmp(command, "CACHEINFO") == 0) {
			ok = 1;
			cache_info_request();
		}
		if (strcmp(command, "UPDATEHOSTS") == 0) {
			ok = 1;
			update_hosts_request(p);
		}
		if (strcmp(command, "SERVERSTAT") == 0) {
			ok = 1;
			server_stat_request();
		}
		if (strcmp(command, "DEBUG") == 0) {
			ok = 1;
			debug_request(p);
		}
		if (strcmp(command, "DUMP") == 0) {
			ok = 1;
			dump_state_request();
		}
		if (strcmp(command, "FULLSCAN") == 0) {
			ok = 1;
			enable_full_request(p);
		}
		if (strcmp(command, "WORDORDER") == 0) {
			ok = 1;
			word_order_request();
		}
		if (strcmp(command, "CLEARPAGECACHE") == 0) {
			ok = 1;
			clear_pagecache_request();
		}
		if (strcmp(command, "CLEARQUERYCACHE") == 0) {
			ok = 1;
			clear_querycache_request();
		}
		if (strcmp(command, "DUMPFILES") == 0) {
			ok = 1;
			dump_files_request(p);
		}
		if (strcmp(command, "DUMPINDEX") == 0) {
			ok = 1;
			dump_index_request(p);
		}
		if (!ok) {
			throw std::runtime_error(std::string("Unrecognized request: ") + request);
		}
	}
	catch (std::runtime_error &e) {
		logger->log(1, "Error: %s", e.what());
		send_string("ERROR");
	}
	delete[] command;	
	m_request_count++;
}