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

« back to all changes in this revision

Viewing changes to src/core/extensions.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: extensions.c,v 1.25 2005/07/27 08:52:25 cbiere Exp $
 
3
 *
 
4
 * Copyright (c) 2002-2003, Raphael Manfredi
 
5
 *
 
6
 *----------------------------------------------------------------------
 
7
 * This file is part of gtk-gnutella.
 
8
 *
 
9
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 
10
 *  it under the terms of the GNU General Public License as published by
 
11
 *  the Free Software Foundation; either version 2 of the License, or
 
12
 *  (at your option) any later version.
 
13
 *
 
14
 *  gtk-gnutella is distributed in the hope that it will be useful,
 
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 *  GNU General Public License for more details.
 
18
 *
 
19
 *  You should have received a copy of the GNU General Public License
 
20
 *  along with gtk-gnutella; if not, write to the Free Software
 
21
 *  Foundation, Inc.:
 
22
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
23
 *----------------------------------------------------------------------
 
24
 */
 
25
 
 
26
/**
 
27
 * @ingroup core
 
28
 * @file
 
29
 *
 
30
 * Gnutella message extension handling.
 
31
 *
 
32
 * @author Raphael Manfredi
 
33
 * @date 2002-2003
 
34
 */
 
35
 
 
36
#include "common.h"
 
37
 
 
38
RCSID("$Id: extensions.c,v 1.25 2005/07/27 08:52:25 cbiere Exp $");
 
39
 
 
40
#include "extensions.h"
 
41
#include "ggep.h"
 
42
 
 
43
#include "lib/atoms.h"
 
44
#include "lib/misc.h"
 
45
#include "lib/walloc.h"
 
46
#include "lib/override.h"               /* Must be the last header included */
 
47
 
 
48
#include "if/gnet_property_priv.h"
 
49
 
 
50
#define HUGE_FS         '\x1c'          /**< Field separator (HUGE) */
 
51
 
 
52
#define GGEP_MAXLEN     65535           /**< Maximum decompressed length */
 
53
#define GGEP_GROW       512                     /**< Minimum chunk growth when resizing */
 
54
 
 
55
/**
 
56
 * An extension descriptor.
 
57
 *
 
58
 * The extension block is structured thustly:
 
59
 *
 
60
 *    - <.................len.......................>
 
61
 *    - <..headlen.><..........paylen...............>
 
62
 *    - +-----------+-------------------------------+
 
63
 *    - |   header  |      extension payload        |
 
64
 *    - +-----------+-------------------------------+
 
65
 *    - ^           ^
 
66
 *    - base        payload
 
67
 *
 
68
 * The "<headlen>" part is simply "<len>" - "<paylen>" so it is not stored.
 
69
 * Likewise, we store only the beginning of the payload, the base can be
 
70
 * computed if needed.
 
71
 *
 
72
 * All those pointers refer DIRECTLY to the message we received, so naturally
 
73
 * one MUST NOT alter the data we can read or we would corrupt the messages
 
74
 * before forwarding them.
 
75
 *
 
76
 * There is a slight complication introduced with GGEP extensions, since the
 
77
 * data there can be COBS encoded, and even deflated.  Therefore, reading
 
78
 * directly data from ext_phys_payload could yield compressed data, not
 
79
 * something really usable.
 
80
 *
 
81
 * Therefore, the extension structure is mostly private, and routines are
 
82
 * provided to access the data.  Decompression and decoding of COBS is lazily
 
83
 * performed when they wish to access the extension data.
 
84
 *
 
85
 * The ext_phys_xxx fields refer to the physical information about the
 
86
 * extension.  The ext_xxx() routines allow access to the virtual information
 
87
 * after decompression and COBS decoding.  Naturally, if the extension is
 
88
 * not compressed nor COBS-encoded, the ext_xxx() routine will return the
 
89
 * physical data.
 
90
 *
 
91
 * The structure here refers to the opaque data that is dynamically allocated
 
92
 * each time a new extension is found.
 
93
 */
 
94
typedef struct extdesc {
 
95
        gchar *ext_phys_payload;        /**< Start of payload buffer */
 
96
        gchar *ext_payload;                     /**< "virtual" payload */
 
97
        guint16 ext_phys_len;           /**< Extension length (header + payload) */
 
98
        guint16 ext_phys_paylen;        /**< Extension payload length */
 
99
        guint16 ext_paylen;                     /**< "virtual" payload length */
 
100
        guint16 ext_rpaylen;            /**< Length of buffer for "virtual" payload */
 
101
 
 
102
        union {
 
103
                struct {
 
104
                        gboolean extu_cobs;                     /**< Payload is COBS-encoded */
 
105
                        gboolean extu_deflate;          /**< Payload is deflated */
 
106
                        const gchar *extu_id;           /**< Extension ID */
 
107
                } extu_ggep;
 
108
        } ext_u;
 
109
 
 
110
} extdesc_t;
 
111
 
 
112
#define ext_phys_headlen(d)     ((d)->ext_phys_len - (d)->ext_phys_paylen)
 
113
#define ext_phys_base(d)        ((d)->ext_phys_payload - ext_phys_headlen(d))
 
114
 
 
115
/*
 
116
 * Union access shortcuts.
 
117
 */
 
118
 
 
119
#define ext_ggep_cobs           ext_u.extu_ggep.extu_cobs
 
120
#define ext_ggep_deflate        ext_u.extu_ggep.extu_deflate
 
121
#define ext_ggep_id                     ext_u.extu_ggep.extu_id
 
122
 
 
123
static const gchar * const extype[] = {
 
124
        "UNKNOWN",                                      /**< EXT_UNKNOWN */
 
125
        "XML",                                          /**< EXT_XML */
 
126
        "HUGE",                                         /**< EXT_HUGE */
 
127
        "GGEP",                                         /**< EXT_GGEP */
 
128
        "NONE",                                         /**< EXT_NONE */
 
129
};
 
130
 
 
131
/***
 
132
 *** Extension name screener.
 
133
 ***/
 
134
 
 
135
/**
 
136
 * Reserved word description.
 
137
 */
 
138
struct rwtable {
 
139
        const gchar *rw_name;   /**< Representation */
 
140
        ext_token_t rw_token;   /**< Token value */
 
141
};
 
142
 
 
143
/** URN name table (sorted) */
 
144
static const struct rwtable urntable[] =
 
145
{
 
146
        { "bitprint",           EXT_T_URN_BITPRINT },
 
147
        { "sha1",                       EXT_T_URN_SHA1 },
 
148
};
 
149
 
 
150
/** GGEP extension table (sorted) */
 
151
static const struct rwtable ggeptable[] =
 
152
{
 
153
#define GGEP_ID(x) { STRINGIFY(x), CAT2(EXT_T_GGEP_,x) }
 
154
 
 
155
        { "<", EXT_T_GGEP_LIME_XML },   /**< '<' is less that 'A' */
 
156
        GGEP_ID(ALT),                                   /**< Alt-locs in qhits */
 
157
        GGEP_ID(BH),                                    /**< Browseable host indication */
 
158
        GGEP_ID(CT),                                    /**< Resource creation time */
 
159
        GGEP_ID(DU),                                    /**< Average servent uptime */
 
160
        GGEP_ID(GTKGV1),                                /**< GTKG complete version number (binary) */
 
161
        GGEP_ID(GUE),                                   /**< GUESS support */
 
162
        GGEP_ID(H),                                             /**< Hashes in binary form */
 
163
        GGEP_ID(HNAME),                                 /**< Hostname */
 
164
        GGEP_ID(IP),                                    /**< Ip:Port in ping and pongs (F2F) */
 
165
        GGEP_ID(IPP),                                   /**< IP:Port in pongs (UHC) */
 
166
        GGEP_ID(LF),                                    /**< Large file size in qhits */
 
167
        GGEP_ID(LOC),                                   /**< Locale preferences, for clustering  */
 
168
        GGEP_ID(PATH),                                  /**< Shared file path, in query hits */
 
169
        GGEP_ID(PHC),                                   /**< Packed host caches (UHC) in pongs */
 
170
        GGEP_ID(PUSH),                                  /**< Push proxy info, in qhits */
 
171
        GGEP_ID(SCP),                                   /**< Supports cached pongs, in pings (UHC) */
 
172
        GGEP_ID(T),                                             /**< Textual information in qhits */
 
173
        GGEP_ID(UDPHC),                                 /**< Is an UDP hostcache (UHC) , in pongs */
 
174
        GGEP_ID(UP),                                    /**< Ultrapeer information about free slots */
 
175
        GGEP_ID(VC),                                    /**< Vendor code, in pongs */
 
176
        GGEP_ID(u),                                             /**< HUGE URN in ASCII */
 
177
 
 
178
#undef GGEP_ID
 
179
};
 
180
 
 
181
/**
 
182
 * Perform a dichotomic search for keywords in the reserved-word table.
 
183
 * The `case_sensitive' parameter governs whether lookup is done with or
 
184
 * without paying attention to case.
 
185
 *
 
186
 * @return the keyword token value upon success, EXT_T_UNKNOWN if not found.
 
187
 * If keyword was found, its static shared string is returned in `retkw'.
 
188
 */
 
189
static ext_token_t
 
190
rw_screen(gboolean case_sensitive,
 
191
        const struct rwtable *table, size_t size,
 
192
        const gchar *word, const gchar **retkw)
 
193
{
 
194
        g_assert(retkw);
 
195
 
 
196
#define GET_KEY(i) (table[(i)].rw_name)
 
197
#define FOUND(i) \
 
198
        G_STMT_START { \
 
199
                *retkw = table[(i)].rw_name; \
 
200
                return table[(i)].rw_token; \
 
201
                /* NOTREACHED */ \
 
202
        } G_STMT_END
 
203
 
 
204
        if (case_sensitive)
 
205
                BINARY_SEARCH(const gchar *, word, size,
 
206
                                strcmp, GET_KEY, FOUND);
 
207
        else
 
208
                BINARY_SEARCH(const gchar *, word, size,
 
209
                                ascii_strcasecmp, GET_KEY, FOUND);
 
210
        
 
211
#undef FOUND
 
212
#undef GET_KEY
 
213
        
 
214
        *retkw = NULL;
 
215
        return EXT_T_UNKNOWN;
 
216
}
 
217
 
 
218
/**
 
219
 * Ensure the reserved-word table is lexically sorted.
 
220
 */
 
221
static void
 
222
rw_is_sorted(const gchar *name,
 
223
        const struct rwtable *table, size_t size)
 
224
{
 
225
        size_t i;
 
226
 
 
227
        /* Skip the first to have a previous element, tables with a single
 
228
         * element are sorted anyway. */
 
229
        for (i = 1; i < size; i++) {
 
230
                const struct rwtable *prev = &table[i - 1], *e = &table[i];
 
231
                if (strcmp(prev->rw_name, e->rw_name) >= 0)
 
232
                        g_error("reserved word table \"%s\" unsorted (near item \"%s\")",
 
233
                                name, e->rw_name);
 
234
        }
 
235
}
 
236
 
 
237
/**
 
238
 * @return the GGEP token value upon success, EXT_T_UNKNOWN_GGEP if not found.
 
239
 * If keyword was found, its static shared string is returned in `retkw'.
 
240
 */
 
241
static ext_token_t
 
242
rw_ggep_screen(gchar *word, const gchar **retkw)
 
243
{
 
244
        ext_token_t t;
 
245
 
 
246
        t = rw_screen(TRUE, ggeptable, G_N_ELEMENTS(ggeptable), word, retkw);
 
247
 
 
248
        return (t == EXT_T_UNKNOWN) ? EXT_T_UNKNOWN_GGEP : t;
 
249
}
 
250
 
 
251
/**
 
252
 * @return the URN token value upon success, EXT_T_UNKNOWN if not found.
 
253
 * If keyword was found, its static shared string is returned in `retkw'.
 
254
 */
 
255
static ext_token_t
 
256
rw_urn_screen(gchar *word, const gchar **retkw)
 
257
{
 
258
        return rw_screen(FALSE, urntable, G_N_ELEMENTS(urntable), word, retkw);
 
259
}
 
260
 
 
261
/***
 
262
 *** Extension name atoms.
 
263
 ***/
 
264
 
 
265
static GHashTable *ext_names = NULL;
 
266
 
 
267
/**
 
268
 * Transform the name into a printable form.
 
269
 *
 
270
 * @return an atom string of that printable form.
 
271
 */
 
272
static gchar *
 
273
ext_name_atom(const gchar *name)
 
274
{
 
275
        gchar *key;
 
276
        gchar *atom;
 
277
 
 
278
        /*
 
279
         * Look whether we already known about this name.
 
280
         */
 
281
 
 
282
        atom = g_hash_table_lookup(ext_names, name);
 
283
 
 
284
        if (atom != NULL)
 
285
                return atom;
 
286
 
 
287
        /*
 
288
         * The key is always the raw name we're given.
 
289
         *
 
290
         * The value is always a printable form of the name, where non-printable
 
291
         * chars are shown as hexadecimal escapes: \xhh.  However, if there is
 
292
         * no escaping, then the name is also the key (same object).
 
293
         */
 
294
 
 
295
        key = g_strdup(name);
 
296
        atom = hex_escape(key, TRUE); /* strict escaping */
 
297
 
 
298
        g_hash_table_insert(ext_names, key, atom);
 
299
 
 
300
        return atom;
 
301
}
 
302
 
 
303
/**
 
304
 * Callback for freeing entries in the `ext_names' hash table.
 
305
 */
 
306
static gboolean
 
307
ext_names_kv_free(gpointer key, gpointer value, gpointer unused_udata)
 
308
{
 
309
        (void) unused_udata;
 
310
 
 
311
        if (0 != strcmp((gchar *) key, (gchar *) value))
 
312
                G_FREE_NULL(value);
 
313
 
 
314
        G_FREE_NULL(key);
 
315
 
 
316
        return TRUE;
 
317
}
 
318
 
 
319
/***
 
320
 *** Extension parsing.
 
321
 ***
 
322
 *** All the ext_xxx_parse routines share the same signature and behaviour:
 
323
 ***
 
324
 *** They extract one extension, as guessed by the leading byte introducing
 
325
 *** those extensions and return the amount of entries they added to the
 
326
 *** supplied extension vector (this will be typically 1 but for GGEP which
 
327
 *** is structured and can therefore grab more than one extension in one call).
 
328
 ***
 
329
 *** Upon entry, `*retp' points to the start of the extension, and there are
 
330
 *** `len' bytes to parse.  There are `exvcnt' slots available in the extension
 
331
 *** vector, starting at `exv'.
 
332
 ***
 
333
 *** On exit, `p' is updated to the first byte following the last successfully
 
334
 *** parsed byte.  If the returned value is 0, then `p' is not updated.
 
335
 ***/
 
336
 
 
337
/**
 
338
 * Parses a GGEP block (can hold several extensions).
 
339
 */
 
340
static gint
 
341
ext_ggep_parse(gchar **retp, gint len, extvec_t *exv, gint exvcnt)
 
342
{
 
343
        gchar *p = *retp;
 
344
        gchar *end = p + len;
 
345
        gchar *lastp = p;                               /* Last parsed point */
 
346
        gint count;
 
347
 
 
348
        for (count = 0; count < exvcnt && p < end; /* empty */) {
 
349
                guchar flags;
 
350
                gchar id[GGEP_F_IDLEN + 1];
 
351
                guint id_len, data_length, i;
 
352
                gboolean length_ended = FALSE;
 
353
                const gchar *name;
 
354
                extdesc_t *d;
 
355
 
 
356
                g_assert(exv->opaque == NULL);
 
357
 
 
358
                /*
 
359
                 * First byte is GGEP flags.
 
360
                 */
 
361
 
 
362
                flags = (guchar) *p++;
 
363
 
 
364
                if (flags & GGEP_F_MBZ)         /* A byte that Must Be Zero is set */
 
365
                        goto abort;
 
366
 
 
367
                id_len = flags & GGEP_F_IDLEN;
 
368
                g_assert(id_len < sizeof id);
 
369
 
 
370
                if (id_len == 0)
 
371
                        goto abort;
 
372
 
 
373
                if ((size_t) (end - p) < id_len) /* Not enough bytes to store the ID! */
 
374
                        goto abort;
 
375
 
 
376
                /*
 
377
                 * Read ID, and NUL-terminate it.
 
378
                 *
 
379
                 * As a safety precaution, only allow ASCII IDs, and nothing in
 
380
                 * the control space.  It's not really in the GGEP specs, but it's
 
381
                 * safer that way, and should protect us if we parse garbage starting
 
382
                 * with 0xC3....
 
383
                 *              --RAM, 2004-11-12
 
384
                 */
 
385
 
 
386
                for (i = 0; i < id_len; i++) {
 
387
                        gint c = *p++;
 
388
                        if (c == '\0' || !isascii(c) || is_ascii_cntrl(c))
 
389
                                goto abort;
 
390
                        id[i] = c; 
 
391
                }
 
392
                id[i] = '\0';
 
393
 
 
394
                /*
 
395
                 * Read the payload length (maximum of 3 bytes).
 
396
                 */
 
397
 
 
398
                data_length = 0;
 
399
                for (i = 0; i < 3 && p < end; i++) {
 
400
                        guchar b = *p++;
 
401
 
 
402
                        /*
 
403
                         * Either GGEP_L_CONT or GGEP_L_LAST must be set, thereby
 
404
                         * ensuring that the byte cannot be NUL.
 
405
                         */
 
406
 
 
407
                        if (((b & GGEP_L_XFLAGS) == GGEP_L_XFLAGS) || !(b & GGEP_L_XFLAGS))
 
408
                                goto abort;
 
409
 
 
410
                        data_length = (data_length << GGEP_L_VSHIFT) | (b & GGEP_L_VALUE);
 
411
 
 
412
                        if (b & GGEP_L_LAST) {
 
413
                                length_ended = TRUE;
 
414
                                break;
 
415
                        }
 
416
                }
 
417
 
 
418
                if (!length_ended)
 
419
                        goto abort;
 
420
 
 
421
                /*
 
422
                 * Ensure we have enough bytes left for the payload.  If not, it
 
423
                 * means the length is garbage.
 
424
                 */
 
425
 
 
426
                /* Check whether there are enough bytes for the payload */
 
427
                if ((size_t) (end - p) < data_length)
 
428
                        goto abort;
 
429
 
 
430
                /*
 
431
                 * Some sanity checks:
 
432
                 *
 
433
                 * A COBS-encoded buffer can be trivially validated.
 
434
                 * A deflated payload must be at least 6 bytes with a valid header.
 
435
                 */
 
436
 
 
437
                if (flags & (GGEP_F_COBS|GGEP_F_DEFLATE)) {
 
438
                        guint d_len = data_length;
 
439
 
 
440
                        if (flags & GGEP_F_COBS) {
 
441
                                if (d_len == 0 || !cobs_is_valid(p, d_len))
 
442
                                        goto abort;
 
443
                                d_len--;                                        /* One byte of overhead */
 
444
                        }
 
445
 
 
446
                        if (flags & GGEP_F_DEFLATE) {
 
447
                                guint offset = 0;
 
448
 
 
449
                                if (d_len < 6)
 
450
                                        goto abort;
 
451
 
 
452
                                /*
 
453
                                 * If COBS-ed, since neither the first byte nor the
 
454
                                 * second byte of the raw deflated payload can be NUL,
 
455
                                 * the leading COBS code will be at least 3.  Then
 
456
                                 * the next 2 bytes are the raw deflated header.
 
457
                                 *
 
458
                                 * If not COBS-ed, check whether payload holds a valid
 
459
                                 * deflated header.
 
460
                                 */
 
461
 
 
462
                                if (flags & GGEP_F_COBS) {
 
463
                                        if ((guchar) *p < 3)
 
464
                                                goto abort;
 
465
                                        offset = 1;                     /* Skip leading byte */
 
466
                                }
 
467
 
 
468
                                if (!zlib_is_valid_header(p + offset, d_len))
 
469
                                        goto abort;
 
470
                        }
 
471
                }
 
472
 
 
473
                /*
 
474
                 * OK, at this point we have validated the GGEP header.
 
475
                 */
 
476
 
 
477
                d = walloc(sizeof *d);
 
478
 
 
479
                d->ext_phys_payload = p;
 
480
                d->ext_phys_paylen = data_length;
 
481
                d->ext_phys_len = (p - lastp) + data_length;
 
482
                d->ext_ggep_cobs = flags & GGEP_F_COBS;
 
483
                d->ext_ggep_deflate = flags & GGEP_F_DEFLATE;
 
484
 
 
485
                if (0 == (flags & (GGEP_F_COBS|GGEP_F_DEFLATE))) {
 
486
                        d->ext_payload = d->ext_phys_payload;
 
487
                        d->ext_paylen = d->ext_phys_paylen;
 
488
                } else
 
489
                        d->ext_payload = NULL;          /* Will lazily compute, if accessed */
 
490
 
 
491
                exv->opaque = d;
 
492
 
 
493
                g_assert(ext_phys_headlen(d) >= 0);
 
494
 
 
495
                /*
 
496
                 * Look whether we know about this extension.
 
497
                 *
 
498
                 * If we do, the name is the ID as well.  Otherwise, for tracing
 
499
                 * and debugging purposes, save the name away, once.
 
500
                 */
 
501
 
 
502
                exv->ext_type = EXT_GGEP;
 
503
                exv->ext_token = rw_ggep_screen(id, &name);
 
504
                exv->ext_name = name;
 
505
 
 
506
                if (name != NULL)
 
507
                        d->ext_ggep_id = name;
 
508
                else
 
509
                        d->ext_ggep_id = ext_name_atom(id);
 
510
 
 
511
                /*
 
512
                 * One more entry, prepare next iteration.
 
513
                 */
 
514
 
 
515
                exv++;
 
516
                count++;
 
517
                lastp = p + data_length;
 
518
                p = lastp;
 
519
 
 
520
                /*
 
521
                 * Was this the last extension?
 
522
                 */
 
523
 
 
524
                if (flags & GGEP_F_LAST)
 
525
                        break;
 
526
        }
 
527
 
 
528
        *retp = lastp;  /* Points to first byte after what we parsed */
 
529
 
 
530
        return count;
 
531
 
 
532
abort:
 
533
        /*
 
534
         * Cleanup any extension we already parsed.
 
535
         */
 
536
 
 
537
        while (count--) {
 
538
                exv--;
 
539
                wfree(exv->opaque, sizeof(extdesc_t));
 
540
                exv->opaque = NULL;
 
541
        }
 
542
 
 
543
        return 0;               /* Cannot be a GGEP block: leave parsing pointer intact */
 
544
}
 
545
 
 
546
/**
 
547
 * Parses a URN block (one URN only).
 
548
 */
 
549
static gint
 
550
ext_huge_parse(gchar **retp, gint len, extvec_t *exv, gint exvcnt)
 
551
{
 
552
        gchar *p = *retp;
 
553
        gchar *end = p + len;
 
554
        gchar *lastp = p;                               /* Last parsed point */
 
555
        gchar *name_start;
 
556
        ext_token_t token;
 
557
        gchar *payload_start = NULL;
 
558
        gint data_length = 0;
 
559
        const gchar *name = NULL;
 
560
        extdesc_t *d;
 
561
 
 
562
        g_assert(exvcnt > 0);
 
563
        g_assert(exv->opaque == NULL);
 
564
 
 
565
        /*
 
566
         * Make sure we can at least read "urn:", i.e. that we have 4 chars.
 
567
         */
 
568
 
 
569
        if (len < 4)
 
570
                return 0;
 
571
 
 
572
        /*
 
573
         * Recognize "urn:".
 
574
         */
 
575
 
 
576
        p = is_strcaseprefix(p, "urn:");
 
577
        if (!p)
 
578
                return 0;
 
579
 
 
580
        /*
 
581
         * Maybe it's simply a "urn:" empty specification?
 
582
         */
 
583
 
 
584
        if (p == end || *p == '\0' || *p == HUGE_FS) {
 
585
                token = EXT_T_URN_EMPTY;
 
586
                payload_start = p;
 
587
                g_assert(data_length == 0);
 
588
                goto found;
 
589
        }
 
590
 
 
591
        /*
 
592
         * Look for the end of the name, identified by ':'.
 
593
         */
 
594
 
 
595
        name_start = p;
 
596
 
 
597
        while (p < end) {
 
598
                if (*p == ':')
 
599
                        break;
 
600
                p++;
 
601
        }
 
602
 
 
603
        if (p == end || p == name_start)        /* Not found, or empty name */
 
604
                return 0;
 
605
 
 
606
        g_assert(*p == ':');
 
607
 
 
608
        /*
 
609
         * Lookup the token.
 
610
         */
 
611
 
 
612
        *p = '\0';
 
613
        token = rw_urn_screen(name_start, &name);
 
614
        *p++ = ':';
 
615
 
 
616
        /*
 
617
         * Now extract the payload (must be made of alphanum chars),
 
618
         * until we reach a delimiter (NUL byte, GGEP header, GEM separator).
 
619
         * NB: of those, only GGEP_MAGIC could be "alnum" under some locales.
 
620
         */
 
621
 
 
622
        payload_start = p;
 
623
 
 
624
        while (p < end) {
 
625
                guchar c = *p++;
 
626
                if (!is_ascii_alnum(c) || c == (guchar) GGEP_MAGIC) {
 
627
                        p--;
 
628
                        break;
 
629
                }
 
630
                data_length++;
 
631
        }
 
632
 
 
633
        g_assert(data_length == p - payload_start);
 
634
 
 
635
found:
 
636
        g_assert(payload_start);
 
637
 
 
638
        d = walloc(sizeof(*d));
 
639
 
 
640
        d->ext_phys_payload = payload_start;
 
641
        d->ext_phys_paylen = data_length;
 
642
        d->ext_phys_len = (payload_start - lastp) + data_length;
 
643
        d->ext_payload = d->ext_phys_payload;
 
644
        d->ext_paylen = d->ext_phys_paylen;
 
645
 
 
646
        exv->opaque = d;
 
647
        exv->ext_type = EXT_HUGE;
 
648
        exv->ext_name = name;
 
649
        exv->ext_token = token;
 
650
 
 
651
        g_assert(ext_phys_headlen(d) >= 0);
 
652
        g_assert(p - lastp == d->ext_phys_len);
 
653
 
 
654
        *retp = p;      /* Points to first byte after what we parsed */
 
655
 
 
656
        return 1;
 
657
}
 
658
 
 
659
/**
 
660
 * Parses a XML block (grabs the whole xml up to the first NUL or separator).
 
661
 */
 
662
static gint
 
663
ext_xml_parse(gchar **retp, gint len, extvec_t *exv, gint exvcnt)
 
664
{
 
665
        gchar *p = *retp;
 
666
        gchar *end = p + len;
 
667
        gchar *lastp = p;                               /* Last parsed point */
 
668
        extdesc_t *d;
 
669
 
 
670
        g_assert(exvcnt > 0);
 
671
        g_assert(exv->opaque == NULL);
 
672
 
 
673
        while (p < end) {
 
674
                guchar c = *p++;
 
675
                if (c == '\0' || c == (guchar) HUGE_FS) {
 
676
                        p--;
 
677
                        break;
 
678
                }
 
679
        }
 
680
 
 
681
        /*
 
682
         * We don't analyze the XML, encapsulate as one big opaque chunk.
 
683
         */
 
684
 
 
685
        d = walloc(sizeof(*d));
 
686
 
 
687
        d->ext_phys_payload = lastp;
 
688
        d->ext_phys_len = d->ext_phys_paylen = p - lastp;
 
689
        d->ext_payload = d->ext_phys_payload;
 
690
        d->ext_paylen = d->ext_phys_paylen;
 
691
 
 
692
        exv->opaque = d;
 
693
        exv->ext_type = EXT_XML;
 
694
        exv->ext_name = NULL;
 
695
        exv->ext_token = EXT_T_XML;
 
696
 
 
697
        g_assert(p - lastp == d->ext_phys_len);
 
698
 
 
699
        *retp = p;                      /* Points to first byte after what we parsed */
 
700
 
 
701
        return 1;
 
702
}
 
703
 
 
704
/**
 
705
 * Parses an unknown block, attempting to resynchronize on a known separator.
 
706
 * Everything up to the resync point is wrapped as an "unknown" extension.
 
707
 *
 
708
 * If `skip' is TRUE, we don't resync on the first resync point.
 
709
 */
 
710
static gint
 
711
ext_unknown_parse(gchar **retp, gint len, extvec_t *exv,
 
712
        gint exvcnt, gboolean skip)
 
713
{
 
714
        gchar *p = *retp;
 
715
        gchar *end = p + len;
 
716
        gchar *lastp = p;                               /* Last parsed point */
 
717
        extdesc_t *d;
 
718
 
 
719
        g_assert(exvcnt > 0);
 
720
        g_assert(exv->opaque == NULL);
 
721
 
 
722
        /*
 
723
         * Try to resync on a NUL byte, the HUGE_FS separator, "urn:" or what
 
724
         * could appear to be the start of a GGEP block or XML.
 
725
         */
 
726
 
 
727
        while (p < end) {
 
728
                guchar c = *p++;
 
729
                if (
 
730
                        (c == '\0' || c == (guchar) HUGE_FS || c == (guchar) GGEP_MAGIC) ||
 
731
                        (
 
732
                                (c == 'u' || c == 'U') &&
 
733
                                (end - p) >= 3 &&
 
734
                                is_strcaseprefix(p, "rn:")
 
735
                        ) ||
 
736
                        (c == '<' && (p < end) && is_ascii_alpha((guchar) *p))
 
737
                ) {
 
738
                        if (skip) {
 
739
                                skip = FALSE;
 
740
                                continue;
 
741
                        }
 
742
                        p--;
 
743
                        break;
 
744
                }
 
745
        }
 
746
 
 
747
        /*
 
748
         * Encapsulate as one big opaque chunk.
 
749
         */
 
750
 
 
751
        d = walloc(sizeof(*d));
 
752
 
 
753
        d->ext_phys_payload = lastp;
 
754
        d->ext_phys_len = d->ext_phys_paylen = p - lastp;
 
755
        d->ext_payload = d->ext_phys_payload;
 
756
        d->ext_paylen = d->ext_phys_paylen;
 
757
 
 
758
        exv->opaque = d;
 
759
        exv->ext_type = EXT_UNKNOWN;
 
760
        exv->ext_name = NULL;
 
761
        exv->ext_token = EXT_T_UNKNOWN;
 
762
 
 
763
        g_assert(p - lastp == d->ext_phys_len);
 
764
 
 
765
        *retp = p;                      /* Points to first byte after what we parsed */
 
766
 
 
767
        return 1;
 
768
}
 
769
 
 
770
/**
 
771
 * Parses a "no extension" block, made of NUL bytes or HUGE field separators
 
772
 * exclusively.  Obviously, this is unneeded stuff that simply accounts
 
773
 * for overhead!
 
774
 *
 
775
 * If more that one separator in a row is found, they are all wrapped as a
 
776
 * "none" extension.
 
777
 */
 
778
static gint
 
779
ext_none_parse(gchar **retp, gint len, extvec_t *exv, gint exvcnt)
 
780
{
 
781
        gchar *p = *retp;
 
782
        gchar *end = p + len;
 
783
        gchar *lastp = p;                               /* Last parsed point */
 
784
        extdesc_t *d;
 
785
 
 
786
        g_assert(exvcnt > 0);
 
787
        g_assert(exv->opaque == NULL);
 
788
 
 
789
        while (p < end) {
 
790
                guchar c = *p++;
 
791
                if (c == '\0' || c == (guchar) HUGE_FS)
 
792
                        continue;
 
793
                p--;                                            /* Point back to the non-NULL char */
 
794
                break;
 
795
        }
 
796
 
 
797
        /*
 
798
         * If we're still at the beginning, it means there was no separator
 
799
         * at all, so we did not find any "null" extension.
 
800
         */
 
801
 
 
802
        if (p == lastp)
 
803
                return 0;
 
804
 
 
805
        /*
 
806
         * Encapsulate as one big opaque chunk.
 
807
         */
 
808
 
 
809
        d = walloc(sizeof(*d));
 
810
 
 
811
        d->ext_phys_payload = lastp;
 
812
        d->ext_phys_len = d->ext_phys_paylen = p - lastp;
 
813
        d->ext_payload = d->ext_phys_payload;
 
814
        d->ext_paylen = d->ext_phys_paylen;
 
815
 
 
816
        exv->opaque = d;
 
817
        exv->ext_type = EXT_NONE;
 
818
        exv->ext_name = NULL;
 
819
        exv->ext_token = EXT_T_OVERHEAD;
 
820
 
 
821
        g_assert(p - lastp == d->ext_phys_len);
 
822
 
 
823
        *retp = p;                      /* Points to first byte after what we parsed */
 
824
 
 
825
        return 1;
 
826
}
 
827
 
 
828
/**
 
829
 * Merge two consecutive extensions `exv' and `next' into one big happy
 
830
 * extension, in `exv'.   The resulting extension type is that of `exv'.
 
831
 */
 
832
static void
 
833
ext_merge_adjacent(extvec_t *exv, extvec_t *next)
 
834
{
 
835
        gchar *end;
 
836
        gchar *nend;
 
837
        gchar *nbase;
 
838
        guint16 added;
 
839
        extdesc_t *d = exv->opaque;
 
840
        extdesc_t *nd = next->opaque;
 
841
 
 
842
        g_assert(exv->opaque != NULL);
 
843
        g_assert(next->opaque != NULL);
 
844
 
 
845
        end = d->ext_phys_payload + d->ext_phys_paylen;
 
846
        nbase = ext_phys_base(nd);
 
847
        nend = nd->ext_phys_payload + nd->ext_phys_paylen;
 
848
 
 
849
        g_assert(nbase + nd->ext_phys_len == nend);
 
850
        g_assert(nend > end);
 
851
 
 
852
        /*
 
853
         * Extensions are adjacent, but can be separated by a single NUL or other
 
854
         * one byte separator.
 
855
         */
 
856
 
 
857
        g_assert(nbase == end || nbase == (end + 1));
 
858
 
 
859
        added = nend - end;                     /* Includes any separator between the two */
 
860
 
 
861
        /*
 
862
         * By incrementing the total length and the payload length of `exv',
 
863
         * we catenate `next' at the tail of `exv'.
 
864
         */
 
865
 
 
866
        d->ext_phys_len += added;
 
867
        d->ext_phys_paylen += added;
 
868
 
 
869
        if (d->ext_payload != NULL) {
 
870
                g_assert(d->ext_payload == d->ext_phys_payload);
 
871
 
 
872
                d->ext_paylen += added;
 
873
        }
 
874
 
 
875
        /*
 
876
         * Get rid of the `next' opaque descriptor.
 
877
         * We should not have computed any "virtual" payload at this point.
 
878
         */
 
879
 
 
880
        g_assert(
 
881
                nd->ext_payload == NULL || nd->ext_payload == nd->ext_phys_payload);
 
882
 
 
883
        wfree(nd, sizeof(*nd));
 
884
        next->opaque = NULL;
 
885
}
 
886
 
 
887
/**
 
888
 * Parse extension block of `len' bytes starting at `buf' and fill the
 
889
 * supplied extension vector `exv', whose size is `exvcnt' entries.
 
890
 *
 
891
 * @return the number of filled entries.
 
892
 */
 
893
gint
 
894
ext_parse(gchar *buf, gint len, extvec_t *exv, gint exvcnt)
 
895
{
 
896
        gchar *p = buf;
 
897
        gchar *end = buf + len;
 
898
        gint cnt = 0;
 
899
 
 
900
        g_assert(buf);
 
901
        g_assert(len > 0);
 
902
        g_assert(exv);
 
903
        g_assert(exvcnt > 0);
 
904
        g_assert(exv->opaque == NULL);
 
905
 
 
906
        while (p < end && exvcnt > 0) {
 
907
                gint found = 0;
 
908
                gchar *old_p = p;
 
909
 
 
910
                g_assert(len > 0);
 
911
 
 
912
                /*
 
913
                 * From now on, all new Gnutella extensions will be done via GGEP.
 
914
                 * However, we have to be backward compatible with legacy extensions
 
915
                 * that predate GGEP (HUGE and XML) and were not properly encapsulated.
 
916
                 */
 
917
 
 
918
                switch (*p) {
 
919
                case GGEP_MAGIC:
 
920
                        p++;
 
921
                        if (p == end)
 
922
                                goto out;
 
923
                        found = ext_ggep_parse(&p, len-1, exv, exvcnt);
 
924
                        break;
 
925
                case 'u':
 
926
                case 'U':
 
927
                        found = ext_huge_parse(&p, len, exv, exvcnt);
 
928
                        break;
 
929
                case '<':
 
930
                        found = ext_xml_parse(&p, len, exv, exvcnt);
 
931
                        break;
 
932
                case HUGE_FS:
 
933
                case '\0':
 
934
                        p++;
 
935
                        if (p == end)
 
936
                                goto out;
 
937
                        found = ext_none_parse(&p, len-1, exv, exvcnt);
 
938
                        if (!found) {
 
939
                                len--;
 
940
                                continue;                       /* Single separator, no bloat then */
 
941
                        }
 
942
                        break;
 
943
                default:
 
944
                        found = ext_unknown_parse(&p, len, exv, exvcnt, FALSE);
 
945
                        break;
 
946
                }
 
947
 
 
948
                /*
 
949
                 * If parsing did not advance one bit, grab as much as we can as
 
950
                 * an "unknown" extension.
 
951
                 */
 
952
 
 
953
                g_assert(found == 0 || p != old_p);
 
954
 
 
955
                if (found == 0) {
 
956
                        g_assert(*old_p == GGEP_MAGIC || p == old_p);
 
957
 
 
958
                        /*
 
959
                         * If we were initially on a GGEP magic byte, and since we did
 
960
                         * not find any valid GGEP extension, go back one byte.  We're
 
961
                         * about to skip the first synchronization point...
 
962
                         */
 
963
 
 
964
                        if (*old_p == GGEP_MAGIC) {
 
965
                                p--;
 
966
                                g_assert(p == old_p);
 
967
                        }
 
968
 
 
969
                        found = ext_unknown_parse(&p, len, exv, exvcnt, TRUE);
 
970
                }
 
971
 
 
972
                g_assert(found > 0);
 
973
                g_assert(found <= exvcnt);
 
974
                g_assert(p != old_p);
 
975
 
 
976
                len -= p - old_p;
 
977
 
 
978
                /*
 
979
                 * If we found an "unknown" or "none" extension, and the previous
 
980
                 * extension was "unknown", merge them.  The result will be "unknown".
 
981
                 */
 
982
 
 
983
                if (
 
984
                        found == 1 && cnt > 0 &&
 
985
                        (exv->ext_type == EXT_UNKNOWN || exv->ext_type == EXT_NONE)
 
986
                ) {
 
987
                        extvec_t *prev = exv - 1;
 
988
                        if (prev->ext_type == EXT_UNKNOWN) {
 
989
                                ext_merge_adjacent(prev, exv);
 
990
                                continue;                                       /* Don't move `exv' */
 
991
                        }
 
992
                }
 
993
 
 
994
                exv += found;
 
995
                exvcnt -= found;
 
996
                cnt += found;
 
997
        }
 
998
 
 
999
out:
 
1000
        return cnt;
 
1001
}
 
1002
 
 
1003
/**
 
1004
 * Inflate `len' bytes starting at `buf', up to GGEP_MAXLEN bytes.
 
1005
 * The payload `name' is given only in case there is an error to report.
 
1006
 *
 
1007
 * @returns the allocated inflated buffer, and its inflated length in `retlen'.
 
1008
 * @returns NULL on error.
 
1009
 */
 
1010
static gchar *
 
1011
ext_ggep_inflate(gchar *buf, gint len, guint16 *retlen, const gchar *name)
 
1012
{
 
1013
        gchar *result;                                  /* Inflated buffer */
 
1014
        gint rsize;                                             /* Result's buffer size */
 
1015
        z_streamp inz;
 
1016
        gint ret;
 
1017
        gint inflated;                                  /* Amount of inflated data so far */
 
1018
        gboolean failed = FALSE;
 
1019
 
 
1020
        g_assert(buf);
 
1021
        g_assert(len > 0);
 
1022
        g_assert(retlen);
 
1023
 
 
1024
        /*
 
1025
         * Allocate decompressor.
 
1026
         */
 
1027
 
 
1028
        inz = walloc(sizeof(*inz));
 
1029
 
 
1030
        inz->zalloc = NULL;
 
1031
        inz->zfree = NULL;
 
1032
        inz->opaque = NULL;
 
1033
 
 
1034
        ret = inflateInit(inz);
 
1035
 
 
1036
        if (ret != Z_OK) {
 
1037
                wfree(inz, sizeof(*inz));
 
1038
                g_warning("unable to setup decompressor for GGEP payload \"%s\": %s",
 
1039
                        name, zlib_strerror(ret));
 
1040
                return NULL;
 
1041
        }
 
1042
 
 
1043
        rsize = len * 2;                                /* Assume a 50% compression ratio */
 
1044
        rsize = MIN(rsize, GGEP_MAXLEN);
 
1045
        result = g_malloc(rsize);
 
1046
 
 
1047
        /*
 
1048
         * Prepare call to inflate().
 
1049
         */
 
1050
 
 
1051
        inz->next_in = (gpointer) buf;
 
1052
        inz->avail_in = len;
 
1053
 
 
1054
        inflated = 0;
 
1055
 
 
1056
        for (;;) {
 
1057
                /*
 
1058
                 * Resize output buffer if needed.
 
1059
                 * Never grow the result buffer to more than MAX_PAYLOAD_LEN bytes.
 
1060
                 */
 
1061
 
 
1062
                if (rsize == inflated) {
 
1063
                        rsize += MAX(len, GGEP_GROW);
 
1064
                        rsize = MIN(rsize, GGEP_MAXLEN);
 
1065
 
 
1066
                        if (rsize == inflated) {                /* Reached maximum size! */
 
1067
                                g_warning("GGEP payload \"%s\" would be larger than %d bytes",
 
1068
                                        name, GGEP_MAXLEN);
 
1069
                                failed = TRUE;
 
1070
                                break;
 
1071
                        }
 
1072
 
 
1073
                        g_assert(rsize > inflated);
 
1074
 
 
1075
                        result = g_realloc(result, rsize);
 
1076
                }
 
1077
 
 
1078
                inz->next_out = (guchar *) result + inflated;
 
1079
                inz->avail_out = rsize - inflated;
 
1080
 
 
1081
                /*
 
1082
                 * Decompress data.
 
1083
                 */
 
1084
 
 
1085
                ret = inflate(inz, Z_SYNC_FLUSH);
 
1086
                inflated += rsize - inflated - inz->avail_out;
 
1087
 
 
1088
                g_assert(inflated <= rsize);
 
1089
 
 
1090
                if (ret == Z_STREAM_END)                                /* All done! */
 
1091
                        break;
 
1092
 
 
1093
                if (ret != Z_OK) {
 
1094
                        g_warning("decompression of GGEP payload \"%s\" failed: %s",
 
1095
                                name, zlib_strerror(ret));
 
1096
                        failed = TRUE;
 
1097
                        break;
 
1098
                }
 
1099
        }
 
1100
 
 
1101
        /*
 
1102
         * Dispose of decompressor.
 
1103
         */
 
1104
 
 
1105
        ret = inflateEnd(inz);
 
1106
        if (ret != Z_OK)
 
1107
                g_warning("while freeing decompressor for GGEP payload \"%s\": %s",
 
1108
                        name, zlib_strerror(ret));
 
1109
 
 
1110
        wfree(inz, sizeof(*inz));
 
1111
 
 
1112
        /*
 
1113
         * @return NULL on error, fill `retlen' if OK.
 
1114
         */
 
1115
 
 
1116
        if (failed) {
 
1117
                G_FREE_NULL(result);
 
1118
                return NULL;
 
1119
        }
 
1120
 
 
1121
        *retlen = inflated;
 
1122
 
 
1123
        g_assert(*retlen == inflated);  /* Make sure it was not truncated */
 
1124
 
 
1125
        return result;                                  /* OK, successfully inflated */
 
1126
}
 
1127
 
 
1128
/**
 
1129
 * Decode the GGEP payload pointed at by `e', allocating a new buffer capable
 
1130
 * of holding the decoded data.
 
1131
 *
 
1132
 * This is performed only when the GGEP payload is either COBS-encoded or
 
1133
 * deflated.
 
1134
 */
 
1135
static void
 
1136
ext_ggep_decode(const extvec_t *e)
 
1137
{
 
1138
        gchar *pbase;                                   /* Current payload base */
 
1139
        size_t plen;                                    /* Curernt payload length */
 
1140
        gchar *uncobs = NULL;                   /* COBS-decoded buffer */
 
1141
        size_t uncobs_len = 0;                  /* Length of walloc()'ed buffer */
 
1142
        size_t result;                                  /* Decoded length */
 
1143
        extdesc_t *d;
 
1144
 
 
1145
        g_assert(e);
 
1146
        g_assert(e->ext_type == EXT_GGEP);
 
1147
        g_assert(e->opaque != NULL);
 
1148
 
 
1149
        d = e->opaque;
 
1150
 
 
1151
        g_assert(d->ext_ggep_cobs || d->ext_ggep_deflate);
 
1152
        g_assert(d->ext_payload == NULL);
 
1153
 
 
1154
        pbase = d->ext_phys_payload;
 
1155
        plen = d->ext_phys_paylen;
 
1156
 
 
1157
        if (plen == 0)
 
1158
                goto out;
 
1159
 
 
1160
        /*
 
1161
         * COBS decoding must be performed before inflation, if any.
 
1162
         */
 
1163
 
 
1164
        if (d->ext_ggep_cobs) {
 
1165
                uncobs = walloc(plen);          /* At worse slightly oversized */
 
1166
                uncobs_len = plen;
 
1167
 
 
1168
                if (!d->ext_ggep_deflate) {
 
1169
                        if (!cobs_decode_into(pbase, plen, uncobs, plen, &result)) {
 
1170
                                if (ggep_debug)
 
1171
                                        g_warning("unable to decode COBS buffer");
 
1172
                                goto out;
 
1173
                        }
 
1174
 
 
1175
                        g_assert(result <= plen);
 
1176
 
 
1177
                        d->ext_payload = uncobs;
 
1178
                        d->ext_paylen = result;
 
1179
                        d->ext_rpaylen = plen;          /* Signals it was walloc()'ed */
 
1180
 
 
1181
                        return;
 
1182
                } else {
 
1183
                        if (!cobs_decode_into(pbase, plen, uncobs, plen, &result)) {
 
1184
                                if (ggep_debug)
 
1185
                                        g_warning("unable to decode COBS buffer");
 
1186
                                goto out;
 
1187
                        }
 
1188
 
 
1189
                        g_assert(result <= plen);
 
1190
 
 
1191
                        /*
 
1192
                         * Replace current payload base/length with the COBS buffer.
 
1193
                         */
 
1194
 
 
1195
                        pbase = uncobs;
 
1196
                        plen = result;
 
1197
                }
 
1198
 
 
1199
                if (plen == 0)          /* 0 bytes cannot be a valid deflated payload */
 
1200
                        goto out;
 
1201
 
 
1202
                /* FALL THROUGH */
 
1203
        }
 
1204
 
 
1205
        /*
 
1206
         * Payload is deflated, inflate it.
 
1207
         */
 
1208
 
 
1209
        g_assert(d->ext_ggep_deflate);
 
1210
 
 
1211
        d->ext_rpaylen = 0;                     /* Signals it was malloc()'ed */
 
1212
        d->ext_payload =
 
1213
                ext_ggep_inflate(pbase, plen, &d->ext_paylen, d->ext_ggep_id);
 
1214
 
 
1215
        /* FALL THROUGH */
 
1216
out:
 
1217
        if (uncobs != NULL)
 
1218
                wfree(uncobs, uncobs_len);
 
1219
 
 
1220
        /*
 
1221
         * If something went wrong, setup a zero-length payload so that we
 
1222
         * don't go through this whole decoding again.
 
1223
         */
 
1224
 
 
1225
        if (d->ext_payload == NULL) {
 
1226
                if (dbg || ggep_debug)
 
1227
                        g_warning("unable to get GGEP \"%s\" %d-byte payload (%s)",
 
1228
                                d->ext_ggep_id, d->ext_phys_paylen,
 
1229
                                (d->ext_ggep_deflate && d->ext_ggep_cobs) ? "COBS + deflated" :
 
1230
                                d->ext_ggep_cobs ? "COBS" : "deflated");
 
1231
 
 
1232
                d->ext_paylen = 0;
 
1233
                d->ext_payload = d->ext_phys_payload;
 
1234
        }
 
1235
}
 
1236
 
 
1237
/**
 
1238
 * @returns a pointer to the extension's payload.
 
1239
 */
 
1240
const gchar *
 
1241
ext_payload(const extvec_t *e)
 
1242
{
 
1243
        extdesc_t *d = e->opaque;
 
1244
 
 
1245
        g_assert(e->opaque != NULL);
 
1246
 
 
1247
        if (d->ext_payload != NULL)
 
1248
                return d->ext_payload;
 
1249
 
 
1250
        /*
 
1251
         * GGEP payload is COBS-ed and/or deflated.
 
1252
         */
 
1253
 
 
1254
        ext_ggep_decode(e);
 
1255
 
 
1256
        return d->ext_payload;
 
1257
}
 
1258
 
 
1259
/**
 
1260
 * @returns a pointer to the extension's payload length.
 
1261
 */
 
1262
guint16
 
1263
ext_paylen(const extvec_t *e)
 
1264
{
 
1265
        extdesc_t *d = e->opaque;
 
1266
 
 
1267
        g_assert(e->opaque != NULL);
 
1268
 
 
1269
        if (d->ext_payload != NULL)
 
1270
                return d->ext_paylen;
 
1271
 
 
1272
        /*
 
1273
         * GGEP payload is COBS-ed and/or deflated.
 
1274
         */
 
1275
 
 
1276
        ext_ggep_decode(e);
 
1277
 
 
1278
        return d->ext_paylen;
 
1279
}
 
1280
 
 
1281
/**
 
1282
 * @returns a pointer to the extension's header.
 
1283
 *
 
1284
 * @warning the actual "virtual" payload may not be contiguous to the end
 
1285
 * of the header: don't read past the ext_headlen() first bytes of the
 
1286
 * header.
 
1287
 */
 
1288
const gchar *
 
1289
ext_base(const extvec_t *e)
 
1290
{
 
1291
        extdesc_t *d = e->opaque;
 
1292
 
 
1293
        g_assert(e->opaque != NULL);
 
1294
 
 
1295
        return ext_phys_base(d);
 
1296
}
 
1297
 
 
1298
/**
 
1299
 * @returns the length of the extensions's header.
 
1300
 */
 
1301
guint16
 
1302
ext_headlen(const extvec_t *e)
 
1303
{
 
1304
        extdesc_t *d = e->opaque;
 
1305
 
 
1306
        g_assert(e->opaque != NULL);
 
1307
 
 
1308
        return ext_phys_headlen(d);
 
1309
}
 
1310
 
 
1311
/**
 
1312
 * @returns the total length of the extension (payload + extension header).
 
1313
 */
 
1314
guint16
 
1315
ext_len(const extvec_t *e)
 
1316
{
 
1317
        extdesc_t *d = e->opaque;
 
1318
        gint headlen;
 
1319
 
 
1320
        g_assert(e->opaque != NULL);
 
1321
 
 
1322
        headlen = ext_phys_headlen(d);
 
1323
 
 
1324
        if (d->ext_payload != NULL)
 
1325
                return headlen + d->ext_paylen;
 
1326
 
 
1327
        return headlen + ext_paylen(e);         /* Will decompress / COBS decode */
 
1328
}
 
1329
 
 
1330
/**
 
1331
 * @returns extension's GGEP ID, or "" if not a GGEP one.
 
1332
 */
 
1333
const gchar *
 
1334
ext_ggep_id_str(const extvec_t *e)
 
1335
{
 
1336
        extdesc_t *d = e->opaque;
 
1337
 
 
1338
        g_assert(e->opaque != NULL);
 
1339
 
 
1340
        if (e->ext_type != EXT_GGEP)
 
1341
                return "";
 
1342
 
 
1343
        return d->ext_ggep_id;
 
1344
}
 
1345
 
 
1346
/**
 
1347
 * @return TRUE if extension is printable.
 
1348
 */
 
1349
gboolean
 
1350
ext_is_printable(const extvec_t *e)
 
1351
{
 
1352
        const gchar *p = ext_payload(e);
 
1353
        gint len = ext_paylen(e);
 
1354
 
 
1355
        g_assert(len >= 0);
 
1356
        while (len--) {
 
1357
                guchar c = *p++;
 
1358
                if (!isprint(c))
 
1359
                        return FALSE;
 
1360
        }
 
1361
 
 
1362
        return TRUE;
 
1363
}
 
1364
 
 
1365
/**
 
1366
 * @return TRUE if extension is ASCII.
 
1367
 */
 
1368
gboolean
 
1369
ext_is_ascii(const extvec_t *e)
 
1370
{
 
1371
        const gchar *p = ext_payload(e);
 
1372
        gint len = ext_paylen(e);
 
1373
 
 
1374
        g_assert(len >= 0);
 
1375
        while (len--) {
 
1376
                guchar c = *p++;
 
1377
                if (!isascii(c))
 
1378
                        return FALSE;
 
1379
        }
 
1380
 
 
1381
        return TRUE;
 
1382
}
 
1383
 
 
1384
/**
 
1385
 * @return TRUE if extension is ASCII and contains at least a character.
 
1386
 */
 
1387
gboolean
 
1388
ext_has_ascii_word(const extvec_t *e)
 
1389
{
 
1390
        const gchar *p = ext_payload(e);
 
1391
        gint len = ext_paylen(e);
 
1392
        gboolean has_alnum = FALSE;
 
1393
 
 
1394
        g_assert(len >= 0);
 
1395
        while (len--) {
 
1396
                guchar c = *p++;
 
1397
                if (!isascii(c))
 
1398
                        return FALSE;
 
1399
                if (!has_alnum && is_ascii_alnum(c))
 
1400
                        has_alnum = TRUE;
 
1401
        }
 
1402
 
 
1403
        return has_alnum;
 
1404
}
 
1405
 
 
1406
/**
 
1407
 * Dump an extension to specified stdio stream.
 
1408
 */
 
1409
static void
 
1410
ext_dump_one(FILE *f, const extvec_t *e, const gchar *prefix,
 
1411
        const gchar *postfix, gboolean payload)
 
1412
{
 
1413
        guint16 paylen;
 
1414
 
 
1415
        g_assert(e->ext_type < EXT_TYPE_COUNT);
 
1416
        g_assert(e->opaque != NULL);
 
1417
 
 
1418
        if (prefix)
 
1419
                fputs(prefix, f);
 
1420
 
 
1421
        fputs(extype[e->ext_type], f);
 
1422
        fprintf(f, " (token=%d) ", e->ext_token);
 
1423
 
 
1424
        if (e->ext_name)
 
1425
                fprintf(f, "\"%s\" ", e->ext_name);
 
1426
 
 
1427
        paylen = ext_paylen(e);
 
1428
 
 
1429
        fprintf(f, "%d byte%s", paylen, paylen == 1 ? "" : "s");
 
1430
 
 
1431
        if (e->ext_type == EXT_GGEP) {
 
1432
                extdesc_t *d = e->opaque;
 
1433
                fprintf(f, " (ID=\"%s\", COBS: %s, deflate: %s)",
 
1434
                        d->ext_ggep_id,
 
1435
                        d->ext_ggep_cobs ? "yes" : "no",
 
1436
                        d->ext_ggep_deflate ? "yes" : "no");
 
1437
        }
 
1438
 
 
1439
        if (postfix)
 
1440
                fputs(postfix, f);
 
1441
 
 
1442
        if (payload && paylen > 0) {
 
1443
                if (ext_is_printable(e)) {
 
1444
                        if (prefix)
 
1445
                                fputs(prefix, f);
 
1446
 
 
1447
                        fputs("Payload: ", f);
 
1448
                        fwrite(ext_payload(e), paylen, 1, f);
 
1449
 
 
1450
                        if (postfix)
 
1451
                                fputs(postfix, f);
 
1452
                } else
 
1453
                        dump_hex(f, "Payload", ext_payload(e), paylen);
 
1454
        }
 
1455
 
 
1456
        fflush(f);
 
1457
}
 
1458
 
 
1459
/**
 
1460
 * Dump all extensions in vector to specified stdio stream.
 
1461
 *
 
1462
 * The `prefix' and `postfix' strings, if non-NULL, are emitted before and
 
1463
 * after the extension summary.
 
1464
 *
 
1465
 * If `payload' is true, the payload is dumped in hexadecimal if it contains
 
1466
 * non-printable characters, as text otherwise.
 
1467
 */
 
1468
void
 
1469
ext_dump(FILE *fd, const extvec_t *exv, gint exvcnt,
 
1470
        const gchar *prefix, const gchar *postfix, gboolean payload)
 
1471
{
 
1472
        while (exvcnt--)
 
1473
                ext_dump_one(fd, exv++, prefix, postfix, payload);
 
1474
}
 
1475
 
 
1476
/**
 
1477
 * Prepare the vector for parsing, by ensuring the `opaque' pointers are
 
1478
 * all set to NULL.
 
1479
 */
 
1480
void
 
1481
ext_prepare(extvec_t *exv, gint exvcnt)
 
1482
{
 
1483
        while (exvcnt--)
 
1484
                (exv++)->opaque = NULL;
 
1485
}
 
1486
 
 
1487
/**
 
1488
 * Reset an extension vector by disposing of the opaque structures
 
1489
 * and of any allocated "virtual" payload.
 
1490
 */
 
1491
void
 
1492
ext_reset(extvec_t *exv, gint exvcnt)
 
1493
{
 
1494
        while (exvcnt--) {
 
1495
                extvec_t *e = exv++;
 
1496
                extdesc_t *d;
 
1497
 
 
1498
                if (e->opaque == NULL)          /* No more allocated extensions */
 
1499
                        break;
 
1500
 
 
1501
                d = e->opaque;
 
1502
 
 
1503
                if (d->ext_payload != NULL && d->ext_payload != d->ext_phys_payload) {
 
1504
                        if (d->ext_rpaylen == 0)
 
1505
                                g_free(d->ext_payload);
 
1506
                        else
 
1507
                                wfree(d->ext_payload, d->ext_rpaylen);
 
1508
                }
 
1509
 
 
1510
                wfree(d, sizeof(*d));
 
1511
                e->opaque = NULL;
 
1512
        }
 
1513
}
 
1514
 
 
1515
/***
 
1516
 *** Init & Shutdown
 
1517
 ***/
 
1518
 
 
1519
/**
 
1520
 * Initialize the extension subsystem.
 
1521
 */
 
1522
void
 
1523
ext_init(void)
 
1524
{
 
1525
        ext_names = g_hash_table_new(g_str_hash, g_str_equal);
 
1526
 
 
1527
        rw_is_sorted("ggeptable", ggeptable, G_N_ELEMENTS(ggeptable));
 
1528
        rw_is_sorted("urntable", urntable, G_N_ELEMENTS(urntable));
 
1529
}
 
1530
 
 
1531
/**
 
1532
 * Free resources used by the extension subsystem.
 
1533
 */
 
1534
void
 
1535
ext_close(void)
 
1536
{
 
1537
        g_hash_table_foreach_remove(ext_names, ext_names_kv_free, NULL);
 
1538
        g_hash_table_destroy(ext_names);
 
1539
}
 
1540
 
 
1541
/* vi: set ts=4 sw=4 cindent: */