annotate 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
rev   line source
paulo@0 1 /*
paulo@0 2 * $Id: query_reply.c,v 1.3 2004/03/24 06:35:10 hipnod Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
paulo@0 5 *
paulo@0 6 * This program is free software; you can redistribute it and/or modify it
paulo@0 7 * under the terms of the GNU General Public License as published by the
paulo@0 8 * Free Software Foundation; either version 2, or (at your option) any
paulo@0 9 * later version.
paulo@0 10 *
paulo@0 11 * This program is distributed in the hope that it will be useful, but
paulo@0 12 * WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
paulo@0 14 * General Public License for more details.
paulo@0 15 */
paulo@0 16
paulo@0 17 #include "gt_gnutella.h"
paulo@0 18 #include "message/msg_handler.h"
paulo@0 19
paulo@0 20 #include "sha1.h"
paulo@0 21 #include "xml.h"
paulo@0 22
paulo@0 23 #include "gt_search.h"
paulo@0 24
paulo@0 25 #include "gt_urn.h"
paulo@0 26 #include "gt_ban.h"
paulo@0 27
paulo@0 28 #include "gt_share_file.h"
paulo@0 29
paulo@0 30 #include <libgift/mime.h>
paulo@0 31
paulo@0 32 /*****************************************************************************/
paulo@0 33
paulo@0 34 /*
paulo@0 35 * Whether to attach the number of hops each result travelled as a metadata
paulo@0 36 * field "Hops". Interesting to see but probably not too useful for the
paulo@0 37 * average user, and so disabled by default.
paulo@0 38 */
paulo@0 39 #define HOPS_AS_META gt_config_get_int("search/hops_as_meta=0")
paulo@0 40
paulo@0 41 /*****************************************************************************/
paulo@0 42
paulo@0 43 extern void gt_parse_extended_data (char *ext_block, gt_urn_t **r_urn,
paulo@0 44 Dataset **r_meta); /* query.c */
paulo@0 45
paulo@0 46 /*****************************************************************************/
paulo@0 47
paulo@0 48 static void add_meta (ds_data_t *key, ds_data_t *value, FileShare *file)
paulo@0 49 {
paulo@0 50 char *keystr = key->data;
paulo@0 51 char *valuestr = value->data;
paulo@0 52
paulo@0 53 share_set_meta (file, keystr, valuestr);
paulo@0 54 }
paulo@0 55
paulo@0 56 /* parse XML data right before the client guid */
paulo@0 57 static void parse_xml_block (GtPacket *packet, size_t xml_bin_len,
paulo@0 58 Share **results, size_t hits)
paulo@0 59 {
paulo@0 60 int old_offset;
paulo@0 61 char *xml;
paulo@0 62 uint8_t savechr;
paulo@0 63
paulo@0 64 /* if the length is one there is only the null terminator */
paulo@0 65 if (xml_bin_len < 1)
paulo@0 66 return;
paulo@0 67
paulo@0 68 /*
paulo@0 69 * Look for the XML before the client guid. Subtract the length of the
paulo@0 70 * client guid and the size of the XML.
paulo@0 71 */
paulo@0 72 old_offset = gt_packet_seek (packet, packet->len - 16 - xml_bin_len);
paulo@0 73
paulo@0 74 /* hopefully, if xml_bin_len is bogus this will fail */
paulo@0 75 if (old_offset < 0)
paulo@0 76 return;
paulo@0 77
paulo@0 78 xml = gt_packet_get_ustr (packet, xml_bin_len);
paulo@0 79
paulo@0 80 if (!xml)
paulo@0 81 return;
paulo@0 82
paulo@0 83 /*
paulo@0 84 * Null terminate the packet even if the remote end didn't. We know this
paulo@0 85 * is safe because the client GUID resides after the XML, and is 16 bytes
paulo@0 86 * long.
paulo@0 87 *
paulo@0 88 * Note that the null char should be at xml[xml_bin_len - 1], so we end up
paulo@0 89 * actually null-terminating twice if the XML is already terminated.
paulo@0 90 * gt_xml_parse_indexed must take this into account (and it does by
paulo@0 91 * largely ignoring xml_bin_len when the XML is plaintext).
paulo@0 92 */
paulo@0 93 savechr = xml[xml_bin_len];
paulo@0 94 xml[xml_bin_len] = 0;
paulo@0 95
paulo@0 96 if (XML_DEBUG)
paulo@0 97 GT->dbg (GT, "xmldata=%s", xml);
paulo@0 98
paulo@0 99 /*
paulo@0 100 * The XML before the client guid that we are parsing has a special
paulo@0 101 * property "index" indicating which share the XML property corresponds
paulo@0 102 * to, and so needs to be handled differently from XML appearing in
paulo@0 103 * each individual hit.
paulo@0 104 */
paulo@0 105 gt_xml_parse_indexed (xml, xml_bin_len, results, hits);
paulo@0 106
paulo@0 107 /* put the saved character back */
paulo@0 108 xml[xml_bin_len] = savechr;
paulo@0 109 }
paulo@0 110
paulo@0 111 /*
paulo@0 112 * Attach the travelled hops as a metadata field.
paulo@0 113 */
paulo@0 114 static void attach_hops (Share *share, int hops)
paulo@0 115 {
paulo@0 116 char buf[12];
paulo@0 117
paulo@0 118 if (!HOPS_AS_META)
paulo@0 119 return;
paulo@0 120
paulo@0 121 snprintf (buf, sizeof (buf) - 1, "%u", hops);
paulo@0 122 share_set_meta (share, "Hops", buf);
paulo@0 123 }
paulo@0 124
paulo@0 125 void gt_query_hits_parse (GtPacket *packet, GtSearch *search,
paulo@0 126 TCPC *c, gt_guid_t *client_guid)
paulo@0 127 {
paulo@0 128 uint8_t count;
paulo@0 129 in_port_t port;
paulo@0 130 in_addr_t host;
paulo@0 131 uint32_t speed;
paulo@0 132 Share *results[255];
paulo@0 133 uint16_t xml_len = 0;
paulo@0 134 int i, availability = 1;
paulo@0 135 BOOL firewalled = FALSE;
paulo@0 136 int total;
paulo@0 137
paulo@0 138 count = gt_packet_get_uint8 (packet);
paulo@0 139 port = gt_packet_get_port (packet);
paulo@0 140 host = gt_packet_get_ip (packet);
paulo@0 141 speed = gt_packet_get_uint32 (packet);
paulo@0 142
paulo@0 143 /* check if this host is banned */
paulo@0 144 if (gt_ban_ipv4_is_banned (host))
paulo@0 145 {
paulo@0 146 GT->dbg (GT, "discarding search results from %s [address banned]",
paulo@0 147 net_ip_str (host));
paulo@0 148 return;
paulo@0 149 }
paulo@0 150
paulo@0 151 for (i = 0; i < count; i++)
paulo@0 152 {
paulo@0 153 uint32_t index;
paulo@0 154 uint32_t size;
paulo@0 155 char *fname, *data;
paulo@0 156 gt_urn_t *urn = NULL;
paulo@0 157 Dataset *meta = NULL;
paulo@0 158 Share *file;
paulo@0 159
paulo@0 160 index = gt_packet_get_uint32 (packet);
paulo@0 161 size = gt_packet_get_uint32 (packet);
paulo@0 162 fname = gt_packet_get_str (packet);
paulo@0 163 data = gt_packet_get_str (packet);
paulo@0 164
paulo@0 165 /* If there was an error parsing the packet (not enough results),
paulo@0 166 * stop parsing */
paulo@0 167 if (gt_packet_error (packet))
paulo@0 168 break;
paulo@0 169
paulo@0 170 if (!fname || string_isempty (fname))
paulo@0 171 {
paulo@0 172 results[i] = NULL;
paulo@0 173 continue;
paulo@0 174 }
paulo@0 175
paulo@0 176 gt_parse_extended_data (data, &urn, &meta);
paulo@0 177
paulo@0 178 /*
paulo@0 179 * WARNING: calling gt_urn_data here assumes sha1.
paulo@0 180 *
paulo@0 181 * TODO: this is a potential bug if gt_share_new() makes assumptions
paulo@0 182 * about the hash data's size and gt_urn_t changes to be multiple
paulo@0 183 * sizes later.
paulo@0 184 */
paulo@0 185 if (!(file = gt_share_new (fname, index, size, gt_urn_data (urn))))
paulo@0 186 {
paulo@0 187 GIFT_ERROR (("error making fileshare, why?"));
paulo@0 188
paulo@0 189 dataset_clear (meta);
paulo@0 190 free (urn);
paulo@0 191
paulo@0 192 /* make sure we find out about it if we're missing results ;) */
paulo@0 193 assert (0);
paulo@0 194
paulo@0 195 results[i] = NULL;
paulo@0 196 continue;
paulo@0 197 }
paulo@0 198
paulo@0 199 /* HACK: set the mimetype from the file extension */
paulo@0 200 share_set_mime (file, mime_type (fname));
paulo@0 201
paulo@0 202 dataset_foreach (meta, DS_FOREACH(add_meta), file);
paulo@0 203
paulo@0 204 /* Attach the hops the search travelled as a metadata field */
paulo@0 205 attach_hops (file, gt_packet_hops (packet));
paulo@0 206
paulo@0 207 dataset_clear (meta);
paulo@0 208 free (urn);
paulo@0 209
paulo@0 210 results[i] = file;
paulo@0 211 }
paulo@0 212
paulo@0 213 total = i;
paulo@0 214
paulo@0 215 /* look for the query hit descriptor */
paulo@0 216 if (!gt_packet_error (packet) &&
paulo@0 217 packet->len - packet->offset >= 16 + 7 /* min qhd len */)
paulo@0 218 {
paulo@0 219 unsigned char *vendor;
paulo@0 220 uint8_t eqhd_len;
paulo@0 221 uint8_t eqhd[2];
paulo@0 222
paulo@0 223 vendor = gt_packet_get_ustr (packet, 4);
paulo@0 224 eqhd_len = gt_packet_get_uint8 (packet);
paulo@0 225 eqhd[0] = gt_packet_get_uint8 (packet);
paulo@0 226 eqhd[1] = gt_packet_get_uint8 (packet);
paulo@0 227
paulo@0 228 /* set availability to 0 or 1 depending on the busy flag */
paulo@0 229 availability = ((eqhd[0] & EQHD1_HAS_BUSY) &&
paulo@0 230 !(eqhd[1] & EQHD2_BUSY_FLAG)) ? 1 : 0;
paulo@0 231
paulo@0 232 /* set firewalled status based on the PUSH flag */
paulo@0 233 firewalled = BOOL_EXPR ((eqhd[0] & EQHD1_PUSH_FLAG) &&
paulo@0 234 (eqhd[1] & EQHD2_HAS_PUSH));
paulo@0 235
paulo@0 236 /*
paulo@0 237 * Check for an XML metadata block, that is usually present
paulo@0 238 * when the size of the "public area" is 4
paulo@0 239 */
paulo@0 240 if (eqhd_len >= 4)
paulo@0 241 xml_len = gt_packet_get_uint16 (packet);
paulo@0 242
paulo@0 243 if (xml_len > 0)
paulo@0 244 {
paulo@0 245 if (XML_DEBUG)
paulo@0 246 {
paulo@0 247 char str[5] = { 0 };
paulo@0 248
paulo@0 249 if (vendor)
paulo@0 250 memcpy (str, vendor, 4);
paulo@0 251
paulo@0 252 GT->dbg (GT, "(%s) xml_len=%d", str, xml_len);
paulo@0 253 }
paulo@0 254
paulo@0 255 parse_xml_block (packet, xml_len, results, total);
paulo@0 256 }
paulo@0 257
paulo@0 258 #if 0
paulo@0 259 if (MSG_DEBUG)
paulo@0 260 {
paulo@0 261 GT->DBGFN (GT, "vendor = %s, qhd_len = %u, qhd_0 = %x, qhd_1 = %x,"
paulo@0 262 " availability = %i, firewalled = %i",
paulo@0 263 make_str (vendor, 4), qhd_len, qhd[0], qhd[1],
paulo@0 264 availability, firewalled);
paulo@0 265 }
paulo@0 266 #endif
paulo@0 267 }
paulo@0 268
paulo@0 269 /* send the results to the interface protocol */
paulo@0 270 for (i = 0; i < total; i++)
paulo@0 271 {
paulo@0 272 if (results[i])
paulo@0 273 {
paulo@0 274 gt_search_reply (search, c, host, port, client_guid, availability,
paulo@0 275 firewalled, results[i]);
paulo@0 276
paulo@0 277 gt_share_unref (results[i]);
paulo@0 278 }
paulo@0 279 }
paulo@0 280 }
paulo@0 281
paulo@0 282 /* should split this up into two routines */
paulo@0 283 GT_MSG_HANDLER(gt_msg_query_reply)
paulo@0 284 {
paulo@0 285 GtSearch *search;
paulo@0 286 int save_offset;
paulo@0 287 gt_guid_t *client_guid;
paulo@0 288
paulo@0 289 /* Each client has a unique identifier at the end of the
paulo@0 290 * packet. Grab that first. */
paulo@0 291 if (packet->len < 16)
paulo@0 292 {
paulo@0 293 if (MSG_DEBUG)
paulo@0 294 GT->DBGSOCK (GT, c, "illegal query response packet, < 16 bytes");
paulo@0 295
paulo@0 296 return;
paulo@0 297 }
paulo@0 298
paulo@0 299 /* hack the offset in the packet */
paulo@0 300 save_offset = packet->offset;
paulo@0 301 packet->offset = packet->len - 16;
paulo@0 302
paulo@0 303 client_guid = gt_packet_get_ustr (packet, 16);
paulo@0 304
paulo@0 305 /* put the offset back */
paulo@0 306 packet->offset = save_offset;
paulo@0 307
paulo@0 308 if (!(search = gt_search_find (gt_packet_guid (packet)))
paulo@0 309 /*&& query_cache_lookup (packet->guid)*/)
paulo@0 310 {
paulo@0 311 /* TODO: support forwarding of query responses by
paulo@0 312 * looking up their destinations in the guid cache */
paulo@0 313
paulo@0 314 /*gt_route_forward_packet (packet, c);*/
paulo@0 315
paulo@0 316 /* add the client GUID to the push cache: in case of a
paulo@0 317 * push request we know where to send it */
paulo@0 318 /*push_cache_add (client_guid, c);*/
paulo@0 319
paulo@0 320 return;
paulo@0 321 }
paulo@0 322
paulo@0 323 gt_query_hits_parse (packet, search, c, client_guid);
paulo@0 324 }