~ubuntu-branches/ubuntu/karmic/gtk-gnutella/karmic

« back to all changes in this revision

Viewing changes to src/vmsg.c

  • Committer: Bazaar Package Importer
  • Author(s): Anand Kumria
  • Date: 2005-08-04 11:32:05 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20050804113205-q746i4lgo3rtlegn
Tags: 0.95.4-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * $Id: vmsg.c,v 1.20 2004/01/14 20:52:33 rmanfredi Exp $
3
 
 *
4
 
 * Copyright (c) 2003, Raphael Manfredi
5
 
 *
6
 
 * Vendor-specific messages.
7
 
 *
8
 
 *----------------------------------------------------------------------
9
 
 * This file is part of gtk-gnutella.
10
 
 *
11
 
 *  gtk-gnutella is free software; you can redistribute it and/or modify
12
 
 *  it under the terms of the GNU General Public License as published by
13
 
 *  the Free Software Foundation; either version 2 of the License, or
14
 
 *  (at your option) any later version.
15
 
 *
16
 
 *  gtk-gnutella is distributed in the hope that it will be useful,
17
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 
 *  GNU General Public License for more details.
20
 
 *
21
 
 *  You should have received a copy of the GNU General Public License
22
 
 *  along with gtk-gnutella; if not, write to the Free Software
23
 
 *  Foundation, Inc.:
24
 
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 
 *----------------------------------------------------------------------
26
 
 */
27
 
 
28
 
#include "common.h"             /* For -DUSE_DMALLOC */
29
 
 
30
 
#include "vmsg.h"
31
 
#include "vendors.h"
32
 
#include "nodes.h"
33
 
#include "gmsg.h"
34
 
#include "routing.h"    /* For message_set_muid() */
35
 
#include "gnet_stats.h"
36
 
#include "settings.h"   /* For listen_ip() */
37
 
#include "override.h"           /* Must be the last header included */
38
 
 
39
 
RCSID("$Id: vmsg.c,v 1.20 2004/01/14 20:52:33 rmanfredi Exp $");
40
 
 
41
 
static gchar v_tmp[256];
42
 
 
43
 
/*
44
 
 * Vendor message handler.
45
 
 */
46
 
 
47
 
struct vmsg;
48
 
 
49
 
typedef void (*vmsg_handler_t)(struct gnutella_node *n,
50
 
        struct vmsg *vmsg, gchar *payload, gint size);
51
 
 
52
 
/*
53
 
 * Definition of vendor messages
54
 
 */
55
 
struct vmsg {
56
 
        guint32 vendor;
57
 
        guint16 id;
58
 
        guint16 version;
59
 
        vmsg_handler_t handler;
60
 
        gchar *name;
61
 
};
62
 
 
63
 
static void handle_messages_supported(struct gnutella_node *n,
64
 
        struct vmsg *vmsg, gchar *payload, gint size);
65
 
static void handle_hops_flow(struct gnutella_node *n,
66
 
        struct vmsg *vmsg, gchar *payload, gint size);
67
 
static void handle_connect_back(struct gnutella_node *n,
68
 
        struct vmsg *vmsg, gchar *payload, gint size);
69
 
static void handle_proxy_req(struct gnutella_node *n,
70
 
        struct vmsg *vmsg, gchar *payload, gint size);
71
 
static void handle_proxy_ack(struct gnutella_node *n,
72
 
        struct vmsg *vmsg, gchar *payload, gint size);
73
 
 
74
 
/*
75
 
 * Known vendor-specific messages.
76
 
 */
77
 
static struct vmsg vmsg_map[] = {
78
 
        /* This list MUST be sorted by vendor, id, version */
79
 
 
80
 
        { T_0000, 0x0000, 0x0000, handle_messages_supported, "Messages Supported" },
81
 
        { T_BEAR, 0x0004, 0x0001, handle_hops_flow, "Hops Flow" },
82
 
        { T_BEAR, 0x0007, 0x0001, handle_connect_back, "Connect Back" },
83
 
        { T_LIME, 0x0015, 0x0002, handle_proxy_req, "Push Proxy Request" },
84
 
        { T_LIME, 0x0016, 0x0002, handle_proxy_ack, "Push Proxy Acknowledgment" },
85
 
 
86
 
        /* Above line intentionally left blank (for "!}sort" on vi) */
87
 
};
88
 
 
89
 
#define END(v)          (v - 1 + sizeof(v) / sizeof(v[0]))
90
 
 
91
 
/*
92
 
 * Items in the "Message Supported" vector.
93
 
 */
94
 
struct vms_item {
95
 
        guint32 vendor;
96
 
        guint16 selector_id;
97
 
        guint16 version;
98
 
};
99
 
 
100
 
#define VMS_ITEM_SIZE   8               /* Each entry is 8 bytes (4+2+2) */
101
 
 
102
 
/*
103
 
 * find_message
104
 
 *
105
 
 * Find message, given vendor code, and id, version.
106
 
 *
107
 
 * We don't necessarily match the version exactly: we only guarantee to
108
 
 * return a handler whose version number is greater or equal than the message
109
 
 * received.
110
 
 *
111
 
 * Returns handler callback if found, NULL otherwise.
112
 
 */
113
 
static struct vmsg *find_message(
114
 
        guint32 vendor, guint16 id, guint16 version)
115
 
{
116
 
        struct vmsg *low = vmsg_map;
117
 
        struct vmsg *high = END(vmsg_map);
118
 
 
119
 
        while (low <= high) {
120
 
                struct vmsg *mid = low + (high - low) / 2;
121
 
                gint c;
122
 
 
123
 
                c = vendor_code_cmp(mid->vendor, vendor);
124
 
 
125
 
                if (c == 0) {
126
 
                        if (mid->id != id)
127
 
                                c = mid->id < id ? -1 : +1;
128
 
                }
129
 
 
130
 
                if (c == 0) {
131
 
                        if (mid->version < version)             /* Return match if >= */
132
 
                                c = -1;
133
 
                }
134
 
 
135
 
                if (c == 0)
136
 
                        return mid;
137
 
                else if (c < 0)
138
 
                        low = mid + 1;
139
 
                else
140
 
                        high = mid - 1;
141
 
        }
142
 
 
143
 
        return NULL;            /* Not found */
144
 
}
145
 
 
146
 
/*
147
 
 * vmsg_handle
148
 
 *
149
 
 * Main entry point to handle reception of vendor-specific message.
150
 
 */
151
 
void vmsg_handle(struct gnutella_node *n)
152
 
{
153
 
        struct gnutella_vendor *v = (struct gnutella_vendor *) n->data;
154
 
        guint32 vendor;
155
 
        guint16 id;
156
 
        guint16 version;
157
 
        struct vmsg *vm;
158
 
 
159
 
        READ_GUINT32_BE(v->vendor, vendor);
160
 
        READ_GUINT16_LE(v->selector_id, id);
161
 
        READ_GUINT16_LE(v->version, version);
162
 
 
163
 
        vm = find_message(vendor, id, version);
164
 
 
165
 
        if (dbg > 4)
166
 
                printf("VMSG %s \"%s\": vendor=%s, id=%u, version=%u\n",
167
 
                        gmsg_infostr(&n->header), vm == NULL ? "UNKNOWN" : vm->name,
168
 
                        vendor_code_str(vendor), id, version);
169
 
 
170
 
        /*
171
 
         * If we can't handle the message, we count it as "unknown type", which
172
 
         * is not completely exact because the type (vendor-specific) is known,
173
 
         * it was only the subtype of that message which was unknown.  Still, I
174
 
         * don't think it is ambiguous enough to warrant another drop type.
175
 
         *              --RAM, 04/01/2003.
176
 
         */
177
 
 
178
 
        if (vm == NULL) {
179
 
                gnet_stats_count_dropped(n, MSG_DROP_UNKNOWN_TYPE);
180
 
                if (dbg)
181
 
                        g_warning("unknown vendor message: %s vendor=%s id=%u version=%u",
182
 
                                gmsg_infostr(&n->header), vendor_code_str(vendor), id, version);
183
 
                return;
184
 
        }
185
 
 
186
 
        (*vm->handler)(n, vm, n->data + sizeof(*v), n->size - sizeof(*v));
187
 
}
188
 
 
189
 
/*
190
 
 * vmsg_fill_header
191
 
 *
192
 
 * Fill common message header part for all vendor-specific messages.
193
 
 * The GUID is blanked (all zero bytes), TTL is set to 1 and hops to 0.
194
 
 * Those common values can be superseded by the caller if needed.
195
 
 *
196
 
 * `size' is only the size of the payload we filled so far.
197
 
 * `maxsize' is the size of the already allocated vendor messsage.
198
 
 *
199
 
 * Returns the total size of the whole Gnutella message.
200
 
 */
201
 
static guint32 vmsg_fill_header(struct gnutella_header *header,
202
 
        guint32 size, guint32 maxsize)
203
 
{
204
 
        guint32 msize;
205
 
 
206
 
        memset(header->muid, 0, 16);                            /* Default GUID: all blank */
207
 
        header->function = GTA_MSG_VENDOR;
208
 
        header->ttl = 1;
209
 
        header->hops = 0;
210
 
 
211
 
        msize = size + sizeof(struct gnutella_vendor);
212
 
 
213
 
        WRITE_GUINT32_LE(msize, header->size);
214
 
 
215
 
        msize += sizeof(struct gnutella_header);
216
 
 
217
 
        if (msize > maxsize)
218
 
                g_error("allocated vendor message is only %u bytes, would need %u",
219
 
                        maxsize, msize);
220
 
 
221
 
        return msize;
222
 
}
223
 
 
224
 
/*
225
 
 * vmsg_fill_type
226
 
 *
227
 
 * Fill leading part of the payload data, containing the common part for
228
 
 * all vendor-specific messages.
229
 
 *
230
 
 * Returns start of payload after that common part.
231
 
 */
232
 
static guchar *vmsg_fill_type(
233
 
        struct gnutella_vendor *base, guint32 vendor, guint16 id, guint16 version)
234
 
{
235
 
        WRITE_GUINT32_BE(vendor, base->vendor);
236
 
        WRITE_GUINT16_LE(id, base->selector_id);
237
 
        WRITE_GUINT16_LE(version, base->version);
238
 
 
239
 
        return (guchar *) (base + 1);
240
 
}
241
 
 
242
 
/*
243
 
 * handle_messages_supported
244
 
 *
245
 
 * Handle the "Messages Supported" message.
246
 
 */
247
 
static void handle_messages_supported(struct gnutella_node *n,
248
 
        struct vmsg *vmsg, gchar *payload, gint size)
249
 
{
250
 
        guint16 count;
251
 
 
252
 
        READ_GUINT16_LE(payload, count);
253
 
 
254
 
        if (dbg > 2)
255
 
                printf("VMSG node %s <%s> supports %u vendor message%s\n",
256
 
                        node_ip(n), node_vendor(n), count,
257
 
                        count == 1 ? "" : "s");
258
 
 
259
 
        if (size != sizeof(count) + count * VMS_ITEM_SIZE) {
260
 
                g_warning("bad payload length in \"Messages Supported\" from %s <%s>: "
261
 
                        "expected %d bytes in vector for %d item%s, got %d",
262
 
                        node_ip(n), node_vendor(n),
263
 
                        count * VMS_ITEM_SIZE, count, count == 1 ? "" : "s",
264
 
                        size - (gint) sizeof(count));
265
 
                return;
266
 
        }
267
 
 
268
 
        /* XXX -- we don't need this support yet -- RAM, 30/01/2003 */
269
 
        if (dbg)
270
 
                g_warning("handle_messages_supported() not implemented yet!");
271
 
}
272
 
 
273
 
/*
274
 
 * vmsg_send_messages_supported
275
 
 *
276
 
 * Send a "Messages Supported" message to specified node, telling it which
277
 
 * subset of the vendor messages we can understand.  We don't send information
278
 
 * about the "Messages Supported" message itself, since this one is guarateeed
279
 
 * to be always understood
280
 
 */
281
 
void vmsg_send_messages_supported(struct gnutella_node *n)
282
 
{
283
 
        struct gnutella_msg_vendor *m = (struct gnutella_msg_vendor *) v_tmp;
284
 
        guint16 count = G_N_ELEMENTS(vmsg_map) - 1;
285
 
        guint32 paysize = sizeof(count) + count * VMS_ITEM_SIZE;
286
 
        guint32 msgsize;
287
 
        guchar *payload;
288
 
        gint i;
289
 
 
290
 
        msgsize = vmsg_fill_header(&m->header, paysize, sizeof(v_tmp));
291
 
        payload = vmsg_fill_type(&m->data, T_0000, 0, 0);
292
 
 
293
 
        /*
294
 
         * First 2 bytes is the number of entries in the vector.
295
 
         */
296
 
 
297
 
        WRITE_GUINT16_LE(count, payload);
298
 
        payload += 2;
299
 
 
300
 
        /*
301
 
         * Fill one entry per message type supported, excepted ourselves.
302
 
         */
303
 
 
304
 
        for (i = 0; i < G_N_ELEMENTS(vmsg_map); i++) {
305
 
                struct vmsg *msg = &vmsg_map[i];
306
 
 
307
 
                if (msg->vendor == T_0000)              /* Don't send info about ourselves */
308
 
                        continue;
309
 
 
310
 
                WRITE_GUINT32_BE(msg->vendor, payload);
311
 
                payload += 4;
312
 
                WRITE_GUINT16_LE(msg->id, payload);
313
 
                payload += 2;
314
 
                WRITE_GUINT16_LE(msg->version, payload);
315
 
                payload += 2;
316
 
        }
317
 
 
318
 
        gmsg_sendto_one(n, (gchar *) m, msgsize);
319
 
}
320
 
 
321
 
/*
322
 
 * handle_hops_flow
323
 
 *
324
 
 * Handle the "Hops Flow" message.
325
 
 */
326
 
static void handle_hops_flow(struct gnutella_node *n,
327
 
        struct vmsg *vmsg, gchar *payload, gint size)
328
 
{
329
 
        guint8 hops;
330
 
 
331
 
        g_assert(vmsg->version <= 1);
332
 
 
333
 
        if (size != 1) {
334
 
                g_warning("got improper %s (payload has %d bytes) from %s <%s>",
335
 
                        vmsg->name, size, node_ip(n), node_vendor(n));
336
 
                return;
337
 
        }
338
 
 
339
 
        hops = *payload;
340
 
        node_set_hops_flow(n, hops);
341
 
}
342
 
 
343
 
/*
344
 
 * vmsg_send_hops_flow
345
 
 *
346
 
 * Send an "Hops Flow" message to specified node.
347
 
 */
348
 
void vmsg_send_hops_flow(struct gnutella_node *n, guint8 hops)
349
 
{
350
 
        struct gnutella_msg_vendor *m = (struct gnutella_msg_vendor *) v_tmp;
351
 
        guint32 paysize = sizeof(hops);
352
 
        guint32 msgsize;
353
 
        guchar *payload;
354
 
 
355
 
        msgsize = vmsg_fill_header(&m->header, paysize, sizeof(v_tmp));
356
 
        payload = vmsg_fill_type(&m->data, T_BEAR, 4, 1);
357
 
 
358
 
        *payload = hops;
359
 
 
360
 
        /*
361
 
         * Send the message as a control message, so that it gets sent ASAP.
362
 
         */
363
 
 
364
 
        gmsg_ctrl_sendto_one(n, (gchar *) m, msgsize);
365
 
}
366
 
 
367
 
/*
368
 
 * handle_connect_back
369
 
 *
370
 
 * Handle the "Connect Back" message.
371
 
 */
372
 
static void handle_connect_back(struct gnutella_node *n,
373
 
        struct vmsg *vmsg, gchar *payload, gint size)
374
 
{
375
 
        guint16 port;
376
 
 
377
 
        g_assert(vmsg->version <= 1);
378
 
 
379
 
        if (size != 2) {
380
 
                g_warning("got improper %s (payload has %d byte%ss) "
381
 
                        "from %s <%s>", vmsg->name, size, size == 1 ? "" : "s",
382
 
                        node_ip(n), node_vendor(n));
383
 
                return;
384
 
        }
385
 
 
386
 
        READ_GUINT16_LE(payload, port);
387
 
 
388
 
        if (port == 0) {
389
 
                g_warning("got improper port #%d in %s from %s <%s>",
390
 
                        port, vmsg->name, node_ip(n), node_vendor(n));
391
 
                return;
392
 
        }
393
 
 
394
 
        node_connect_back(n, port);
395
 
}
396
 
 
397
 
/*
398
 
 * vmsg_send_connect_back
399
 
 *
400
 
 * Send an "Connect Back" message to specified node, telling it to connect
401
 
 * back to us on the specified port.
402
 
 */
403
 
void vmsg_send_connect_back(struct gnutella_node *n, guint16 port)
404
 
{
405
 
        struct gnutella_msg_vendor *m = (struct gnutella_msg_vendor *) v_tmp;
406
 
        guint32 paysize = sizeof(port);
407
 
        guint32 msgsize;
408
 
        guchar *payload;
409
 
 
410
 
        msgsize = vmsg_fill_header(&m->header, paysize, sizeof(v_tmp));
411
 
        payload = vmsg_fill_type(&m->data, T_BEAR, 7, 1);
412
 
 
413
 
        WRITE_GUINT16_LE(port, payload);
414
 
 
415
 
        gmsg_sendto_one(n, (gchar *) m, msgsize);
416
 
}
417
 
 
418
 
/*
419
 
 * handle_proxy_req
420
 
 *
421
 
 * Handle reception of the "Push Proxy Request" message.
422
 
 */
423
 
static void handle_proxy_req(struct gnutella_node *n,
424
 
        struct vmsg *vmsg, gchar *payload, gint size)
425
 
{
426
 
        if (size != 0) {
427
 
                g_warning("got improper %s (payload has %d byte%ss) "
428
 
                        "from %s <%s>", vmsg->name, size, size == 1 ? "" : "s",
429
 
                        node_ip(n), node_vendor(n));
430
 
                return;
431
 
        }
432
 
 
433
 
        /*
434
 
         * Normally, a firewalled host should be a leaf node, not an UP.
435
 
         * Warn if node is not a leaf, but accept to be the push proxy
436
 
         * nonetheless.
437
 
         */
438
 
 
439
 
        if (!NODE_IS_LEAF(n))
440
 
                g_warning("got %s from non-leaf node %s <%s>",
441
 
                        vmsg->name, node_ip(n), node_vendor(n));
442
 
 
443
 
        /*
444
 
         * Add proxying info for this node.  On successful completion,
445
 
         * we'll send an acknowledgement.
446
 
         */
447
 
 
448
 
        if (node_proxying_add(n, n->header.muid))       /* MUID is the node's GUID */
449
 
                vmsg_send_proxy_ack(n, n->header.muid);
450
 
}
451
 
 
452
 
/*
453
 
 * vmsg_send_proxy_req
454
 
 *
455
 
 * Send a "Push Proxy Request" message to specified node, using supplied
456
 
 * `muid' as the message ID (which is our GUID).
457
 
 */
458
 
void vmsg_send_proxy_req(struct gnutella_node *n, gchar *muid)
459
 
{
460
 
        struct gnutella_msg_vendor *m = (struct gnutella_msg_vendor *) v_tmp;
461
 
        guint32 msgsize;
462
 
 
463
 
        g_assert(!NODE_IS_LEAF(n));
464
 
 
465
 
        msgsize = vmsg_fill_header(&m->header, 0, sizeof(v_tmp));
466
 
        memcpy(m->header.muid, muid, 16);
467
 
        (void) vmsg_fill_type(&m->data, T_LIME, 21, 2);
468
 
 
469
 
        gmsg_sendto_one(n, (gchar *) m, msgsize);
470
 
 
471
 
        if (dbg > 2)
472
 
                g_warning("sent proxy REQ to %s <%s>", node_ip(n), node_vendor(n));
473
 
}
474
 
 
475
 
/*
476
 
 * handle_proxy_ack
477
 
 *
478
 
 * Handle reception of the "Push Proxy Acknowledgment" message.
479
 
 */
480
 
static void handle_proxy_ack(struct gnutella_node *n,
481
 
        struct vmsg *vmsg, gchar *payload, gint size)
482
 
{
483
 
        guint32 ip;
484
 
        guint16 port;
485
 
 
486
 
        g_assert(vmsg->version >= 2);
487
 
 
488
 
        if (size != 6) {
489
 
                g_warning("got improper %s (payload has %d byte%ss) "
490
 
                        "from %s <%s>", vmsg->name, size, size == 1 ? "" : "s",
491
 
                        node_ip(n), node_vendor(n));
492
 
                return;
493
 
        }
494
 
 
495
 
        READ_GUINT32_BE(payload, ip);
496
 
        payload += 4;
497
 
        READ_GUINT16_LE(payload, port);
498
 
 
499
 
        if (dbg > 2)
500
 
                g_warning("got proxy ACK from %s <%s>: proxy at %s",
501
 
                        node_ip(n), node_vendor(n), ip_port_to_gchar(ip, port));
502
 
 
503
 
 
504
 
        if (!host_is_valid(ip, port)) {
505
 
                g_warning("got improper address %s in %s from %s <%s>",
506
 
                        ip_port_to_gchar(ip, port), vmsg->name,
507
 
                        node_ip(n), node_vendor(n));
508
 
                return;
509
 
        }
510
 
 
511
 
        node_proxy_add(n, ip, port);
512
 
}
513
 
 
514
 
/*
515
 
 * vmsg_send_proxy_ack
516
 
 *
517
 
 * Send a "Push Proxy Acknowledgment" message to specified node, using
518
 
 * supplied `muid' as the message ID (which is the target node's GUID).
519
 
 */
520
 
void vmsg_send_proxy_ack(struct gnutella_node *n, gchar *muid)
521
 
{
522
 
        struct gnutella_msg_vendor *m = (struct gnutella_msg_vendor *) v_tmp;
523
 
        guint32 paysize = sizeof(guint32) + sizeof(guint16);
524
 
        guint32 msgsize;
525
 
        guchar *payload;
526
 
 
527
 
        msgsize = vmsg_fill_header(&m->header, paysize, sizeof(v_tmp));
528
 
        memcpy(m->header.muid, muid, 16);
529
 
        payload = vmsg_fill_type(&m->data, T_LIME, 22, 2);
530
 
 
531
 
        WRITE_GUINT32_BE(listen_ip(), payload);
532
 
        payload += 4;
533
 
        WRITE_GUINT16_LE(listen_port, payload);
534
 
 
535
 
        /*
536
 
         * Reply with a control message, so that the issuer knows that we can
537
 
         * proxyfy pushes to it ASAP.
538
 
         */
539
 
 
540
 
        gmsg_ctrl_sendto_one(n, (gchar *) m, msgsize);
541
 
}
542