diff src/message/query_reply.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/message/query_reply.c	Sat Feb 20 21:18:28 2010 -0800
     1.3 @@ -0,0 +1,324 @@
     1.4 +/*
     1.5 + * $Id: query_reply.c,v 1.3 2004/03/24 06:35:10 hipnod Exp $
     1.6 + *
     1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
     1.8 + *
     1.9 + * This program is free software; you can redistribute it and/or modify it
    1.10 + * under the terms of the GNU General Public License as published by the
    1.11 + * Free Software Foundation; either version 2, or (at your option) any
    1.12 + * later version.
    1.13 + *
    1.14 + * This program is distributed in the hope that it will be useful, but
    1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of
    1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.17 + * General Public License for more details.
    1.18 + */
    1.19 +
    1.20 +#include "gt_gnutella.h"
    1.21 +#include "message/msg_handler.h"
    1.22 +
    1.23 +#include "sha1.h"
    1.24 +#include "xml.h"
    1.25 +
    1.26 +#include "gt_search.h"
    1.27 +
    1.28 +#include "gt_urn.h"
    1.29 +#include "gt_ban.h"
    1.30 +
    1.31 +#include "gt_share_file.h"
    1.32 +
    1.33 +#include <libgift/mime.h>
    1.34 +
    1.35 +/*****************************************************************************/
    1.36 +
    1.37 +/*
    1.38 + * Whether to attach the number of hops each result travelled as a metadata
    1.39 + * field "Hops". Interesting to see but probably not too useful for the
    1.40 + * average user, and so disabled by default.
    1.41 + */
    1.42 +#define HOPS_AS_META         gt_config_get_int("search/hops_as_meta=0")
    1.43 +
    1.44 +/*****************************************************************************/
    1.45 +
    1.46 +extern void gt_parse_extended_data (char *ext_block, gt_urn_t **r_urn,
    1.47 +                                    Dataset **r_meta); /* query.c */
    1.48 +
    1.49 +/*****************************************************************************/
    1.50 +
    1.51 +static void add_meta (ds_data_t *key, ds_data_t *value, FileShare *file)
    1.52 +{
    1.53 +	char *keystr   = key->data;
    1.54 +	char *valuestr = value->data;
    1.55 +
    1.56 +	share_set_meta (file, keystr, valuestr);
    1.57 +}
    1.58 +
    1.59 +/* parse XML data right before the client guid */
    1.60 +static void parse_xml_block (GtPacket *packet, size_t xml_bin_len,
    1.61 +                             Share **results, size_t hits)
    1.62 +{
    1.63 +	int      old_offset;
    1.64 +	char    *xml;
    1.65 +	uint8_t  savechr;
    1.66 +
    1.67 +	/* if the length is one there is only the null terminator */
    1.68 +	if (xml_bin_len < 1)
    1.69 +		return;
    1.70 +
    1.71 +	/*
    1.72 +	 * Look for the XML before the client guid. Subtract the length of the
    1.73 +	 * client guid and the size of the XML.
    1.74 +	 */
    1.75 +	old_offset = gt_packet_seek (packet, packet->len - 16 - xml_bin_len);
    1.76 +
    1.77 +	/* hopefully, if xml_bin_len is bogus this will fail */
    1.78 +	if (old_offset < 0)
    1.79 +		return;
    1.80 +
    1.81 +	xml = gt_packet_get_ustr (packet, xml_bin_len);
    1.82 +
    1.83 +	if (!xml)
    1.84 +		return;
    1.85 +
    1.86 +	/*
    1.87 +	 * Null terminate the packet even if the remote end didn't.  We know this
    1.88 +	 * is safe because the client GUID resides after the XML, and is 16 bytes
    1.89 +	 * long.
    1.90 +	 *
    1.91 +	 * Note that the null char should be at xml[xml_bin_len - 1], so we end up
    1.92 +	 * actually null-terminating twice if the XML is already terminated.
    1.93 +	 * gt_xml_parse_indexed must take this into account (and it does by
    1.94 +	 * largely ignoring xml_bin_len when the XML is plaintext).
    1.95 +	 */
    1.96 +	savechr = xml[xml_bin_len];
    1.97 +	xml[xml_bin_len] = 0;
    1.98 +
    1.99 +	if (XML_DEBUG)
   1.100 +		GT->dbg (GT, "xmldata=%s", xml);
   1.101 +
   1.102 +	/*
   1.103 +	 * The XML before the client guid that we are parsing has a special
   1.104 +	 * property "index" indicating which share the XML property corresponds
   1.105 +	 * to, and so needs to be handled differently from XML appearing in
   1.106 +	 * each individual hit.
   1.107 +	 */
   1.108 +	gt_xml_parse_indexed (xml, xml_bin_len, results, hits);
   1.109 +
   1.110 +	/* put the saved character back */
   1.111 +	xml[xml_bin_len] = savechr;
   1.112 +}
   1.113 +
   1.114 +/*
   1.115 + * Attach the travelled hops as a metadata field.
   1.116 + */
   1.117 +static void attach_hops (Share *share, int hops)
   1.118 +{
   1.119 +	char buf[12];
   1.120 +
   1.121 +	if (!HOPS_AS_META)
   1.122 +		return;
   1.123 +
   1.124 +	snprintf (buf, sizeof (buf) - 1, "%u", hops);
   1.125 +	share_set_meta (share, "Hops", buf);
   1.126 +}
   1.127 +
   1.128 +void gt_query_hits_parse (GtPacket *packet, GtSearch *search,
   1.129 +                          TCPC *c, gt_guid_t *client_guid)
   1.130 +{
   1.131 +	uint8_t      count;
   1.132 +	in_port_t    port;
   1.133 +	in_addr_t    host;
   1.134 +	uint32_t     speed;
   1.135 +	Share       *results[255];
   1.136 +	uint16_t     xml_len         = 0;
   1.137 +	int          i, availability = 1;
   1.138 +	BOOL         firewalled      = FALSE;
   1.139 +	int          total;
   1.140 +
   1.141 +	count = gt_packet_get_uint8  (packet);
   1.142 +	port  = gt_packet_get_port   (packet);
   1.143 +	host  = gt_packet_get_ip     (packet);
   1.144 +	speed = gt_packet_get_uint32 (packet);
   1.145 +
   1.146 +	/* check if this host is banned */
   1.147 +	if (gt_ban_ipv4_is_banned (host))
   1.148 +	{
   1.149 +		GT->dbg (GT, "discarding search results from %s [address banned]",
   1.150 +		         net_ip_str (host));
   1.151 +		return;
   1.152 +	}
   1.153 +
   1.154 +	for (i = 0; i < count; i++)
   1.155 +	{
   1.156 +		uint32_t       index;
   1.157 +		uint32_t       size;
   1.158 +		char          *fname, *data;
   1.159 +		gt_urn_t      *urn  = NULL;
   1.160 +		Dataset       *meta = NULL;
   1.161 +		Share         *file;
   1.162 +
   1.163 +		index = gt_packet_get_uint32 (packet);
   1.164 +		size  = gt_packet_get_uint32 (packet);
   1.165 +		fname = gt_packet_get_str    (packet);
   1.166 +		data  = gt_packet_get_str    (packet);
   1.167 +
   1.168 +		/* If there was an error parsing the packet (not enough results),
   1.169 +		 * stop parsing */
   1.170 +		if (gt_packet_error (packet))
   1.171 +			break;
   1.172 +
   1.173 +		if (!fname || string_isempty (fname))
   1.174 +		{
   1.175 +			results[i] = NULL;
   1.176 +			continue;
   1.177 +		}
   1.178 +
   1.179 +		gt_parse_extended_data (data, &urn, &meta);
   1.180 +
   1.181 +		/*
   1.182 +		 * WARNING: calling gt_urn_data here assumes sha1.
   1.183 +		 *
   1.184 +		 * TODO: this is a potential bug if gt_share_new() makes assumptions
   1.185 +		 * about the hash data's size and gt_urn_t changes to be multiple
   1.186 +		 * sizes later.
   1.187 +		 */
   1.188 +		if (!(file = gt_share_new (fname, index, size, gt_urn_data (urn))))
   1.189 +		{
   1.190 +			GIFT_ERROR (("error making fileshare, why?"));
   1.191 +
   1.192 +			dataset_clear (meta);
   1.193 +			free (urn);
   1.194 +
   1.195 +			/* make sure we find out about it if we're missing results ;) */
   1.196 +			assert (0);
   1.197 +
   1.198 +			results[i] = NULL;
   1.199 +			continue;
   1.200 +		}
   1.201 +
   1.202 +		/* HACK: set the mimetype from the file extension */
   1.203 +		share_set_mime (file, mime_type (fname));
   1.204 +
   1.205 +		dataset_foreach (meta, DS_FOREACH(add_meta), file);
   1.206 +
   1.207 +		/* Attach the hops the search travelled as a metadata field */
   1.208 +		attach_hops (file, gt_packet_hops (packet));
   1.209 +
   1.210 +		dataset_clear (meta);
   1.211 +		free (urn);
   1.212 +
   1.213 +		results[i] = file;
   1.214 +	}
   1.215 +
   1.216 +	total = i;
   1.217 +
   1.218 +	/* look for the query hit descriptor */
   1.219 +	if (!gt_packet_error (packet) &&
   1.220 +	    packet->len - packet->offset >= 16 + 7 /* min qhd len */)
   1.221 +	{
   1.222 +		unsigned char *vendor;
   1.223 +		uint8_t        eqhd_len;
   1.224 +		uint8_t        eqhd[2];
   1.225 +
   1.226 +		vendor   = gt_packet_get_ustr  (packet, 4);
   1.227 +		eqhd_len = gt_packet_get_uint8 (packet);
   1.228 +		eqhd[0]  = gt_packet_get_uint8 (packet);
   1.229 +		eqhd[1]  = gt_packet_get_uint8 (packet);
   1.230 +
   1.231 +		/* set availability to 0 or 1 depending on the busy flag */
   1.232 +		availability = ((eqhd[0] & EQHD1_HAS_BUSY) &&
   1.233 +		                !(eqhd[1] & EQHD2_BUSY_FLAG)) ? 1 : 0;
   1.234 +
   1.235 +		/* set firewalled status based on the PUSH flag */
   1.236 +		firewalled   = BOOL_EXPR ((eqhd[0] & EQHD1_PUSH_FLAG) &&
   1.237 +		                          (eqhd[1] & EQHD2_HAS_PUSH));
   1.238 +
   1.239 +		/*
   1.240 +		 * Check for an XML metadata block, that is usually present
   1.241 +		 * when the size of the "public area" is 4
   1.242 +		 */
   1.243 +		if (eqhd_len >= 4)
   1.244 +			xml_len = gt_packet_get_uint16 (packet);
   1.245 +
   1.246 +		if (xml_len > 0)
   1.247 +		{
   1.248 +			if (XML_DEBUG)
   1.249 +			{
   1.250 +				char str[5] = { 0 };
   1.251 +
   1.252 +				if (vendor)
   1.253 +					memcpy (str, vendor, 4);
   1.254 +
   1.255 +				GT->dbg (GT, "(%s) xml_len=%d", str, xml_len);
   1.256 +			}
   1.257 +
   1.258 +			parse_xml_block (packet, xml_len, results, total);
   1.259 +		}
   1.260 +
   1.261 +#if 0
   1.262 +		if (MSG_DEBUG)
   1.263 +		{
   1.264 +			GT->DBGFN (GT, "vendor = %s, qhd_len = %u, qhd_0 = %x, qhd_1 = %x,"
   1.265 +			           " availability = %i, firewalled = %i",
   1.266 +			           make_str (vendor, 4), qhd_len, qhd[0], qhd[1],
   1.267 +			           availability, firewalled);
   1.268 +		}
   1.269 +#endif
   1.270 +	}
   1.271 +
   1.272 +	/* send the results to the interface protocol */
   1.273 +	for (i = 0; i < total; i++)
   1.274 +	{
   1.275 +		if (results[i])
   1.276 +		{
   1.277 +			gt_search_reply (search, c, host, port, client_guid, availability,
   1.278 +			                 firewalled, results[i]);
   1.279 +
   1.280 +			gt_share_unref (results[i]);
   1.281 +		}
   1.282 +	}
   1.283 +}
   1.284 +
   1.285 +/* should split this up into two routines */
   1.286 +GT_MSG_HANDLER(gt_msg_query_reply)
   1.287 +{
   1.288 +	GtSearch   *search;
   1.289 +	int         save_offset;
   1.290 +	gt_guid_t  *client_guid;
   1.291 +
   1.292 +	/* Each client has a unique identifier at the end of the
   1.293 +	 * packet.  Grab that first. */
   1.294 +	if (packet->len < 16)
   1.295 +	{
   1.296 +		if (MSG_DEBUG)
   1.297 +			GT->DBGSOCK (GT, c, "illegal query response packet, < 16 bytes");
   1.298 +
   1.299 +		return;
   1.300 +	}
   1.301 +
   1.302 +	/* hack the offset in the packet */
   1.303 +	save_offset = packet->offset;
   1.304 +	packet->offset = packet->len - 16;
   1.305 +
   1.306 +	client_guid = gt_packet_get_ustr (packet, 16);
   1.307 +
   1.308 +	/* put the offset back */
   1.309 +	packet->offset = save_offset;
   1.310 +
   1.311 +	if (!(search = gt_search_find (gt_packet_guid (packet)))
   1.312 +		/*&& query_cache_lookup (packet->guid)*/)
   1.313 +	{
   1.314 +		/* TODO: support forwarding of query responses by
   1.315 +		 * looking up their destinations in the guid cache */
   1.316 +
   1.317 +		/*gt_route_forward_packet (packet, c);*/
   1.318 +
   1.319 +		/* add the client GUID to the push cache: in case of a
   1.320 +		 * push request we know where to send it */
   1.321 +		/*push_cache_add (client_guid, c);*/
   1.322 +
   1.323 +		return;
   1.324 +	}
   1.325 +
   1.326 +	gt_query_hits_parse (packet, search, c, client_guid);
   1.327 +}