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 }
|