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