~ubuntu-branches/ubuntu/raring/maradns/raring

« back to all changes in this revision

Viewing changes to deadwood-2.4.10/src/DwCompress.c

  • Committer: Bazaar Package Importer
  • Author(s): Kai Hendry
  • Date: 2010-01-24 12:17:40 UTC
  • mfrom: (1.1.13 upstream) (10.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100124121740-a4e1fjobwaouz443
Tags: 1.4.02-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2009 Sam Trenholme
 
2
 *
 
3
 * TERMS
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer.
 
11
 * 2. Redistributions in binary form must reproduce the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer in the
 
13
 *    documentation and/or other materials provided with the distribution.
 
14
 *
 
15
 * This software is provided 'as is' with no guarantees of correctness or
 
16
 * fitness for purpose.
 
17
 */
 
18
 
 
19
#include "DwStr.h"
 
20
#include "DwStr_functions.h"
 
21
#include "DwCompress.h"
 
22
#include "DwDnsStr.h"
 
23
 
 
24
/* Given a string with compressed DNS data, and an offset in the string
 
25
 * looking at a compression pointer, determine where to point to and in
 
26
 * what string (qlen is the length of the question).
 
27
 *
 
28
 * The return code is a little complicated.  -1 means error; -2 or lower
 
29
 * means an offset in the question string (-2 is beginning of question or
 
30
 * offset 12 in a DNS label, -3 is second byte in question, etc.)
 
31
 *
 
32
 * 0 or higher means an offset in the "answer" string or the string as
 
33
 * stored in the cache.
 
34
 */
 
35
 
 
36
int32_t dwc_decomp_offset(dw_str *in, int32_t offset, int32_t qlen) {
 
37
        int32_t t = 0;
 
38
 
 
39
        if(dw_assert_sanity(in) == -1) {
 
40
                return -1;
 
41
        }
 
42
 
 
43
        t = offset;
 
44
        if(t > in->len) {
 
45
                return -1;
 
46
        }
 
47
        if(*(in->str + t) < 64) { /* Not a compression pointer */
 
48
                return t;
 
49
        }
 
50
        offset = *(in->str + t) & 0x3f;
 
51
        offset <<= 8;
 
52
        offset += *(in->str + t + 1);
 
53
 
 
54
        offset -= 12; /* DNS header */
 
55
        if(offset < qlen && qlen > 0) { /* In question */
 
56
                return -2 - offset;
 
57
        }
 
58
        offset -= qlen;
 
59
        offset -= 2; /* CLASS part of question */
 
60
        if(offset < 0 || offset > in->len) { /* Invalid place */
 
61
                return -1;
 
62
        }
 
63
        return offset; /* In answer */
 
64
}
 
65
 
 
66
/* Finish up a name by putting the final '\0' at the end of the binary
 
67
 * dname, and adjusting the length for the relevant variables */
 
68
int32_t dwc_finish_name(dw_str *out, int32_t *delta, int32_t offset,
 
69
            int compress_followed, int32_t t) {
 
70
 
 
71
        if(dw_assert_sanity(out) == -1) {
 
72
                return -1;
 
73
        }
 
74
 
 
75
        dw_addchar(0,out);
 
76
        *delta += 1;
 
77
        if(compress_followed == 0) {
 
78
                t = offset + 1;
 
79
        }
 
80
        return t;
 
81
}
 
82
 
 
83
/* Given a string with the compressed DNS data, a string with the
 
84
 * uncompressed DNS data, a string with the DNS question, a pointer where
 
85
 * we will put how much longer the uncompressed string is
 
86
 * and an offset in the compressed string where the compressed dlabel begins,
 
87
 * we decompress the string
 
88
 *
 
89
 * We return -1 on error; on success, we return the offset in the source
 
90
 * string where the DNS name ends.
 
91
 */
 
92
int32_t dwc_decomp_dname(dw_str *in, dw_str *out, dw_str *q, int32_t *delta,
 
93
    int32_t where_dname) {
 
94
        int32_t counter = 0, offset = 0, t = 0;
 
95
        int compress_followed = 0, len = 0, use_q_string = 0;
 
96
        dw_str *tmp = 0;
 
97
 
 
98
        if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
 
99
                        dw_assert_sanity(q) == -1 || delta == 0) {
 
100
                return -1;
 
101
        }
 
102
 
 
103
        offset = where_dname;
 
104
        for(counter = 0 ; counter < 200; counter++) {
 
105
                if(use_q_string == 0) { /* Are we in question or answer? */
 
106
                        if(offset + 1 > in->len) { /* Sanity check */
 
107
                                return -1;
 
108
                        }
 
109
                        len = *(in->str + offset);
 
110
                } else {
 
111
                        if(offset + 1 > q->len) { /* Sanity check */
 
112
                                return -1;
 
113
                        }
 
114
                        len = *(q->str + offset);
 
115
                }
 
116
                if(len >= 192) { /* Compression pointer */
 
117
                        if(use_q_string == 1) { /* No comp pointers in Q */
 
118
                                return -1;
 
119
                        }
 
120
                        if(compress_followed == 0) { /* Mark orig length */
 
121
                                t = offset + 2;
 
122
                                *delta = -2;
 
123
                        }
 
124
                        offset = dwc_decomp_offset(in, offset, q->len);
 
125
                        if(offset == -1) {
 
126
                                return -1;
 
127
                        } else if(offset < 0) { /* In question, not answer */
 
128
                                use_q_string = 1;
 
129
                                offset = -2 - offset;
 
130
                        }
 
131
                        compress_followed = 1;
 
132
                } else if(len > 63 || len < 0) { /* Invalid length */
 
133
                        return -1;
 
134
                } else if(len == 0) { /* End of name */
 
135
                        t = dwc_finish_name(out,delta,offset,compress_followed,
 
136
                                t);
 
137
                        break;
 
138
                } else { /* Copy single label of dname over */
 
139
                        if(use_q_string == 0) { /* If we're in answer */
 
140
                                tmp = dw_substr(in,offset,len + 1, 1);
 
141
                        } else { /* otherwise, we're in question */
 
142
                                tmp = dw_substr(q,offset,len + 1, 1);
 
143
                        }
 
144
                        dw_append(tmp,out);
 
145
                        dw_destroy(tmp);
 
146
                        offset += len + 1;
 
147
                        *delta += len + 1;
 
148
                }
 
149
        }
 
150
        return t;
 
151
}
 
152
 
 
153
/* Given a rr type number (1 for A, etc.), return a character that
 
154
 * describes the RR in question.  The format for this is simple; we only
 
155
 * care about whether we are to compress the data in question (this is
 
156
 * the first nybble, '1' means yes, compress, and '0' means no, don't
 
157
 * compress), how many bytes at the beginning of the label (0-9, second
 
158
 * nybble), how many "dname"s we have to compress (usually 1 but
 
159
 * sometimes 2, third nybble), and how many 16-bit words we have after the
 
160
 * final dname (usually 0 but is "10" or "a" for SOA records; 20 bytes)
 
161
 *
 
162
 * Sometimes, the RR will have data after the dnames; we treat this data
 
163
 * as a black box and keep it as-is.  We return 0 in that case.
 
164
 *
 
165
 */
 
166
 
 
167
int16_t dwc_type_desc(int32_t type) {
 
168
        switch(type) {
 
169
                case 15: /* MX */
 
170
                        return 0x1210; /* Compress, 2 bytes then 1 dname */
 
171
                case 2: /* NS */
 
172
                case 12: /* PTR */
 
173
                case 5: /* CNAME */
 
174
                        return 0x1010; /* Compress, 1 dname at beginning */
 
175
                case 6: /* SOA */
 
176
                        return 0x102a; /* Compress, 2 dnames at beginning,
 
177
                                          20 bytes after dnames */
 
178
                case 3: /* MD */
 
179
                case 4: /* MF */
 
180
                case 7: /* MB */
 
181
                case 8: /* MG */
 
182
                case 9: /* MR */
 
183
                        return 0x0010; /* Don't compress, 1 dname at start */
 
184
                case 14: /* MINFO */
 
185
                case 17: /* RP */
 
186
                        return 0x0020; /* Don't compress, 2 dnames at start */
 
187
                case 18: /* AFSDB */
 
188
                case 21: /* RT */
 
189
                        return 0x0210; /* Don't compress, 2 bytes, 1 dname */
 
190
                case 33: /* SRV */
 
191
                        return 0x0610; /* Don't compress, 6 bytes, 1 dname */
 
192
                default:
 
193
                        return 0;
 
194
        }
 
195
}
 
196
 
 
197
/* Decompress the rddata of a single record.
 
198
 *
 
199
 * Input: Description of RR to decompress (bytes in lead, number of DNS
 
200
 * names to decompress; 16-bit words in tail as a packed 12-bit value)
 
201
 * compressed string offset, input string location, output string location,
 
202
 * question string location
 
203
 *
 
204
 * Output: New compressed string offset
 
205
 */
 
206
int32_t dwc_decomp_rddata(int16_t desc, int32_t offset, dw_str *in,
 
207
                dw_str *out, dw_str *q) {
 
208
        dw_str *tmp = 0;
 
209
        int lead = 0, ndnames = 0, tail = 0;
 
210
        int32_t rdo = 0, rdlength = 0, delta = 0;
 
211
 
 
212
        if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
 
213
                        dw_assert_sanity(q) == -1) {
 
214
                return -1;
 
215
        }
 
216
 
 
217
        lead = (desc & 0xf00) >> 8;
 
218
        ndnames = (desc & 0xf0) >> 4;
 
219
        tail = (desc & 0x0f) << 1;
 
220
        offset += 10;
 
221
        rdo = out->len - 2; /* Where RDLENGTH is in string */
 
222
        if(lead > 0) {
 
223
                tmp = dw_substr(in,offset,lead,1);
 
224
                if(dw_append(tmp,out) == -1) {
 
225
                        goto catch_dwc_decomp_rddata;
 
226
                }
 
227
                dw_destroy(tmp);
 
228
                tmp = 0;
 
229
                offset += lead;
 
230
        }
 
231
        while(ndnames > 0) {
 
232
                delta = 0;
 
233
                offset = dwc_decomp_dname(in,out,q,&delta,
 
234
                          offset);
 
235
                rdlength = dw_fetch_u16(out,rdo);
 
236
                rdlength += delta;
 
237
                dw_put_u16(out,rdlength,rdo);
 
238
                ndnames--;
 
239
        }
 
240
        if(tail > 0) {
 
241
                tmp = dw_substr(in,offset,tail,1);
 
242
                dw_append(tmp,out);
 
243
                dw_destroy(tmp);
 
244
                tmp = 0;
 
245
                offset += tail;
 
246
        }
 
247
 
 
248
        return offset;
 
249
 
 
250
catch_dwc_decomp_rddata:
 
251
        if(tmp != 0) {
 
252
                dw_destroy(tmp);
 
253
        }
 
254
        return -1; /* Error */
 
255
}
 
256
 
 
257
/* Decompress a single DNS record.
 
258
 *
 
259
 * Input: input string, output string, question string, "stack" string for
 
260
 *        storing offsets of where RRs and RR's post-name data are, offset
 
261
 *
 
262
 * Output: New offset
 
263
 */
 
264
int32_t dwc_decomp_rr(dw_str *in, dw_str *out, dw_str *q, dw_str *stack,
 
265
                int32_t offset) {
 
266
 
 
267
        int32_t val = 0, delta = 0;
 
268
        int16_t desc = 0;
 
269
        dw_str *tmp = 0;
 
270
 
 
271
        if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
 
272
                        dw_assert_sanity(q) == -1 ||
 
273
                        dw_assert_sanity(stack) == -1) {
 
274
                return -1;
 
275
        }
 
276
 
 
277
        dw_push_u16(out->len,stack); /* Beginning of RR */
 
278
 
 
279
        /* DNS name at beginning of RR */
 
280
        offset = dwc_decomp_dname(in,out,q,&delta,offset);
 
281
        if(offset < 0 || offset > in->len - 7) {
 
282
                goto catch_dwc_decomp_rr;
 
283
        }
 
284
 
 
285
        dw_push_u16(out->len,stack); /* Part of RR after RR name */
 
286
        val = dw_fetch_u16(in,offset); /* RR type */
 
287
        if(val == -1) {
 
288
                goto catch_dwc_decomp_rr;
 
289
        }
 
290
        desc = dwc_type_desc(val); /* Description for compression purposes */
 
291
 
 
292
        /* Add data between name and RDdata */
 
293
        tmp = dw_substr(in,offset,10,1);
 
294
        dw_append(tmp,out);
 
295
        dw_destroy(tmp);
 
296
        tmp = 0;
 
297
 
 
298
        val = dw_fetch_u16(in,offset + 8); /* RDLENGTH */
 
299
        if(desc == 0) { /* No compression pointers */
 
300
                tmp = dw_substr(in,offset + 10,val,1);
 
301
                dw_append(tmp,out);
 
302
                dw_destroy(tmp);
 
303
                offset += val + 10;
 
304
        } else { /* Decompress compression pointers */
 
305
                offset = dwc_decomp_rddata(desc, offset, in, out, q);
 
306
        }
 
307
 
 
308
        return offset;
 
309
 
 
310
catch_dwc_decomp_rr:
 
311
        if(tmp != 0) {
 
312
                dw_destroy(tmp);
 
313
        }
 
314
        return -1;
 
315
}
 
316
 
 
317
/* Given the question we are answering, and a packet as stored in the cache
 
318
 * (a DNS packet processed by dw_packet_to_cache() ) decompress the packet
 
319
 * and output the decompressed packet as a newly created DwStr() object
 
320
 */
 
321
 
 
322
dw_str *dwc_decompress(dw_str *q, dw_str *in) {
 
323
        dw_str *out = 0, *tmp = 0, *stack = 0;
 
324
        int32_t offset = 0;
 
325
        int rr = 0;
 
326
 
 
327
        if(dw_assert_sanity(in) == -1 || dw_assert_sanity(q) == -1) {
 
328
                return 0;
 
329
        }
 
330
 
 
331
        out = dw_create(2048);
 
332
        if(out == 0) {
 
333
                return 0;
 
334
        }
 
335
        stack = dw_create(2048); /* 1024 lengths or 512 dlabels */
 
336
        if(stack == 0) {
 
337
                goto catch_dwc_decompress;
 
338
        }
 
339
 
 
340
        for(rr = 0 ; rr < 1000 && offset < in->len - 7 ; rr++) {
 
341
                offset = dwc_decomp_rr(in, out, q, stack, offset);
 
342
                if(offset < 0 || offset > in->len - 7) {
 
343
                        goto catch_dwc_decompress;
 
344
                }
 
345
        }
 
346
 
 
347
        dw_append(stack,out);
 
348
        dw_destroy(stack);
 
349
        tmp = dw_substr(in,-7,-1,1);
 
350
        dw_append(tmp,out);
 
351
        dw_destroy(tmp);
 
352
        tmp = 0;
 
353
        return out;
 
354
 
 
355
catch_dwc_decompress:
 
356
        if(out != 0) {
 
357
                dw_destroy(out);
 
358
        }
 
359
        if(tmp != 0) {
 
360
                dw_destroy(tmp);
 
361
        }
 
362
        if(stack != 0) {
 
363
                dw_destroy(stack);
 
364
        }
 
365
        return 0;
 
366
}
 
367
 
 
368
/* Determine whether two single dlabels are identical; 1 if they are, 0
 
369
 * if they are not, and -1 on error
 
370
 * Input: String with first label, offset of begining of dlabel in
 
371
 *        first string, string and offset for second label
 
372
 */
 
373
int dwc_label_same(dw_str *a, int32_t a_o, dw_str *b, int32_t b_o) {
 
374
        int c = 0, noloop = 0;
 
375
 
 
376
        if(dw_assert_sanity(a) == -1 || dw_assert_sanity(b) == -1 ||
 
377
           a_o > a->len || b_o > b->len) {
 
378
                return -1;
 
379
        }
 
380
 
 
381
        if(*(a->str + a_o) != *(b->str + b_o)) {
 
382
                return 0;
 
383
        }
 
384
 
 
385
        c = *(a->str + a_o);
 
386
 
 
387
        if(c < 0 || c > 64 || (a_o + c > a->len) || (b_o + c > b->len)) {
 
388
                return -1;
 
389
        }
 
390
 
 
391
        for(noloop = 0; noloop < 128 && c > 0; noloop++) {
 
392
                if(*(a->str + a_o + c) != *(b->str + b_o + c)) {
 
393
                        return 0;
 
394
                }
 
395
                c--;
 
396
        }
 
397
 
 
398
        return 1;
 
399
}
 
400
 
 
401
/* Determine whether two dlabels are the same, where one dlabel can
 
402
 * have compression pointers (the other doesn't)
 
403
 *
 
404
 * Input: String with uncompressed dname (u), offset to dname in that string,
 
405
 *        String with compressed dname (c), offset to dname in compressed
 
406
 *        string,
 
407
 *        pointer to string with question (q), since compression pointers
 
408
 *        sometimes point here
 
409
 *
 
410
 * Output: -1 if error, 0 if different, 1 if same
 
411
 */
 
412
 
 
413
int dwc_dname_same(dw_str *u, int32_t u_o, dw_str *c, int32_t c_o, dw_str *q) {
 
414
        int count = 0, in_q = 0, len = 0;
 
415
        int32_t place = 0;
 
416
 
 
417
        place = c_o;
 
418
 
 
419
        if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1) {
 
420
                return -1;
 
421
        }
 
422
 
 
423
        for(count = 0; count < 300; count++) {
 
424
                /* Follow any compression pointers as needed */
 
425
                if(in_q == 0) {
 
426
                        if(q != 0) {
 
427
                                place = dwc_decomp_offset(c, place, q->len);
 
428
                        } else {
 
429
                                place = dwc_decomp_offset(c, place, 0);
 
430
                        }
 
431
                        if(place == -1 || (q == 0 && place < 0)) {
 
432
                                return -1;
 
433
                        }
 
434
                        if(place < -1 && q != 0) {
 
435
                                in_q = 1;
 
436
                                place = -2 - place;
 
437
                        }
 
438
                }
 
439
                if(in_q == 0) {
 
440
                        if(dwc_label_same(u,u_o,c,place) != 1) {
 
441
                                return 0; /* Not same */
 
442
                        }
 
443
                } else {
 
444
                        if(dwc_label_same(u,u_o,q,place) != 1) {
 
445
                                return 0; /* Not same */
 
446
                        }
 
447
                }
 
448
                len = *(u->str + u_o);
 
449
                if(len == 0) {
 
450
                        return 1; /* Same */
 
451
                }
 
452
                if(len < 0 || len > 64 || (u_o + len >= u->len)) {
 
453
                        return -1;
 
454
                }
 
455
                if(in_q == 0 && (place + len >= c->len)) {
 
456
                        return -1;
 
457
                } else if(in_q == 1 && q != 0 && (place + len >= q->len)) {
 
458
                        return -1;
 
459
                }
 
460
                u_o += len + 1;
 
461
                place += len + 1;
 
462
        }
 
463
        return -1;
 
464
}
 
465
 
 
466
/* Determine whether a given uncompressed domain name (b) is the ending of
 
467
 * another, compressed domain name (h, which may also point back to the
 
468
 * question of the domain name).  If it is, we output a postive number
 
469
 * if the offset of where it is can be found in the compressed answer (0
 
470
 * if it's at the start of the answer); and a negative number -3 or higher
 
471
 * if it is in the question (-3 is start of question, -4 first byte in
 
472
 * question, etc.). -1 is a fatal error; -2 is "not found"
 
473
 *
 
474
 * Input: String with uncompressed name, offset in string of uncompressed
 
475
 *        name, String with compressed name, offset in string with compressed
 
476
 *        name, String with question.  If string with compressed name is
 
477
 *        zero, we look in the question for a match; if question is zero, we
 
478
 *        only look in the string with the compressed name for a match.
 
479
 *
 
480
 * Output: As described above; offset if found, -2 if not found
 
481
 *
 
482
 * Named "in_bailiwick" because we can use this for bailiwick tests, such
 
483
 * as dwc_in_bailiwick(bailiwick,0,hostname,0,0) and see if we get a
 
484
 * positive number or 0 (yes, we're in bailiwick), or a negative number
 
485
 * (no, we're not)
 
486
 *
 
487
 * In a loop, we follow the compression pointer of the hostname until we're
 
488
 * at a non-compression pointer.  We then see if the label is the same as
 
489
 * the first bailiwick label.
 
490
 *
 
491
 * If it is, we repeat the process until we're at the end of the
 
492
 * bailiwick label.
 
493
 *
 
494
 * If it isn't, then we look at the next label in the hostname
 
495
 * string and perform the same comparison until the hostname string
 
496
 * is zero-length.
 
497
 */
 
498
 
 
499
int32_t dwc_in_bailiwick(dw_str *b, int32_t b_o, dw_str *h,
 
500
        int32_t h_o, dw_str *q) {
 
501
 
 
502
        int count = 0, in_q_string = 0, len = 0;
 
503
        int32_t place = 0;
 
504
 
 
505
        if(dw_assert_sanity(b) == -1 || dw_assert_sanity(h) == -1) {
 
506
                return -1;
 
507
        }
 
508
 
 
509
        place = h_o;
 
510
 
 
511
        for(count = 0; count < 300; count++) { /* Infinite loop protect */
 
512
                if(in_q_string == 0) { /* If not in question */
 
513
                        if(q != 0) { /* No segfault if question is unset */
 
514
                                place = dwc_decomp_offset(h, place, q->len);
 
515
                        } else {
 
516
                                place = dwc_decomp_offset(h, place, 0);
 
517
                        }
 
518
                        if(place == -1 || (q == 0 && place < 0)) { /* Sanity */
 
519
                                return -1;
 
520
                        }
 
521
                        if(place < 0 && q != 0) {
 
522
                                place = -2 - place;
 
523
                                in_q_string = 1;
 
524
                        }
 
525
                }
 
526
                if(in_q_string == 0) { /* Look in answer */
 
527
                        if(dwc_dname_same(b, b_o, h, place, q) == 1) {
 
528
                                return place;
 
529
                        }
 
530
                } else { /* Look in question */
 
531
                        if(dwc_dname_same(b, b_o, q, place, q) == 1) {
 
532
                                return -3 - place;
 
533
                        }
 
534
                }
 
535
                /* Move to next dlabel */
 
536
                if(in_q_string == 0) {
 
537
                        len = *(h->str + place);
 
538
                        if(len + place + 1 > h->len) {
 
539
                                return -1; /* Out of bounds */
 
540
                        }
 
541
                } else if(q != 0) {
 
542
                        len = *(q->str + place);
 
543
                        if(len + place + 1 > q->len) {
 
544
                                return -1; /* Out of bounds */
 
545
                        }
 
546
                } else {
 
547
                        return -1; /* Invalid */
 
548
                }
 
549
                if(len == 0) {
 
550
                        return -2; /* No match */
 
551
                }
 
552
                place += len + 1; /* Next part of name */
 
553
        }
 
554
        return -1; /* Error */
 
555
}
 
556
 
 
557
/* Look at a DNS name and put all of its non-compressed labels on
 
558
 * a stack (a dw_str object, since the dw_str library has support for
 
559
 * putting and reading 16-bit values there)
 
560
 *
 
561
 * Input: String with dname, offset of dname in string, string with
 
562
 *        stack, value to add to any number put on the stack
 
563
 *
 
564
 * Output: 0 on success, -1 on error
 
565
 */
 
566
 
 
567
int dwc_push_offsets(dw_str *dns, int32_t place, dw_str *stack, int32_t
 
568
                offset) {
 
569
        int len, count;
 
570
 
 
571
        if(dw_assert_sanity(dns) == -1 || dw_assert_sanity(stack) == -1) {
 
572
                return -1;
 
573
        }
 
574
 
 
575
        for(count = 0; count < 300; count++) { /* Infinite loop protection */
 
576
                if(place > dns->len) {
 
577
                        return -1; /* Error */
 
578
                }
 
579
                len=*(dns->str + place);
 
580
                if(len >= 192 /* Comp marker */ || len == 0 /* End */) {
 
581
                        return 0; /* Done */
 
582
                }
 
583
                if(len >= 64) {
 
584
                        return -1; /* Invalid length field */
 
585
                }
 
586
                if(place < 0 || place >= 0x3fff ||
 
587
                   place + offset < 0 || place + offset > 0xffff) {
 
588
                        return -1; /* Invalid offset */
 
589
                }
 
590
                if(dw_push_u16(place + offset, stack) == -1) {
 
591
                        return -1;
 
592
                }
 
593
                place += len + 1;
 
594
        }
 
595
        return 0; /* Success */
 
596
}
 
597
 
 
598
/* Given an uncompressed string, an offset we're looking at in said
 
599
 * uncompressed string, a partially compressed string, a question string,
 
600
 * and a "stack" of offsets, determine if we should return a compression
 
601
 * pointer.  If so, where will the compression pointer point.
 
602
 *
 
603
 * Input: u (uncompressed string), u_o (offset in uncompressed string),
 
604
 * c (partially compressed string, just the answer part of the DNS packet),
 
605
 * q (question string), s (stack of offsets)
 
606
 *
 
607
 * Output: -2: Not found
 
608
 *         -1: Error
 
609
 *         0-16383: Have the compression pointer point this many bytes in to
 
610
 *                  the final compressed string
 
611
 */
 
612
 
 
613
int32_t dwc_seek_comp_pointer(dw_str *u, int32_t u_o, dw_str *c, dw_str *q,
 
614
                dw_str *s) {
 
615
        int32_t s_o = 0; /* Stack offset (which thingy we're looking at in
 
616
                          * the stack) */
 
617
        int32_t offset = 0;
 
618
        int in_q = 0; /* Whether we're in the question string or not */
 
619
        int count = 0; /* Infinite loop protection */
 
620
 
 
621
        if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1) {
 
622
                return -1;
 
623
        }
 
624
 
 
625
        for(count = 0; count < 1000 && s_o < s->len; count++) {
 
626
                offset = dw_fetch_u16(s, s_o);
 
627
                in_q = 0;
 
628
                if(offset < 0) {
 
629
                        return -1;
 
630
                }
 
631
                if(offset >= 0x8000) {
 
632
                        in_q = 1;
 
633
                }
 
634
                offset &= 0x3fff;
 
635
                if(in_q == 1) {
 
636
                        if(dwc_dname_same(u, u_o, q, offset, 0) == 1) {
 
637
                                return 12 + offset;
 
638
                        }
 
639
                } else {
 
640
                        if(dwc_dname_same(u, u_o, c, offset, q) == 1) {
 
641
                                return 12 + q->len + 2 + offset;
 
642
                        }
 
643
                }
 
644
                s_o += 2;
 
645
        }
 
646
        return -2;
 
647
 
 
648
}
 
649
 
 
650
/* Given the beginning of a DNS domain name (dname), and the number of dnames
 
651
 * we will look at, a pointer to the stack to add uncompressed labels, the
 
652
 * question string, and the partially compressed string (which we will append
 
653
 * to), compress part of the string.
 
654
 *
 
655
 * Input: Uncompressed string (u), uncompressed offset (u_o), partially
 
656
 *        compressed string (c; adding to end), number dlabels (num_dlabels),
 
657
 *        stack of uncompressed labels (s; which we will append to if the
 
658
 *        label can't be compressed), question string (q), recursion depth (d)
 
659
 *
 
660
 * Output: New uncompressed offset ; -1 on fatal error
 
661
 */
 
662
 
 
663
int32_t dwc_compress_dlabels(dw_str *u, int32_t u_o, dw_str *c,
 
664
                int num_dlabels, dw_str *s, dw_str *q, uint8_t d) {
 
665
        int32_t point = 0;
 
666
        int count = 0, len = 0;
 
667
        dw_str *tmp = 0;
 
668
 
 
669
        if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1 ||
 
670
                        dw_assert_sanity(s) == -1) {
 
671
                return -1;
 
672
        }
 
673
 
 
674
        if(d >= 20 || num_dlabels <= 0) {
 
675
                return u_o;
 
676
        }
 
677
 
 
678
        for(count = 0; count < 300; count++) {
 
679
                point = dwc_seek_comp_pointer(u,u_o,c,q,s);
 
680
                if(point > 0) { /* Compression pointer to be used */
 
681
                        point &= 0x3fff;
 
682
                        point |= 0xc000;
 
683
                        if(dw_push_u16(point,c) == -1) {
 
684
                                return -1;
 
685
                        }
 
686
                        point = dw_get_dn_end(u,u_o); /* End of uncomp label */
 
687
                        if(point == -1) {
 
688
                                return -1;
 
689
                        }
 
690
                        return dwc_compress_dlabels(u, point, c,
 
691
                                        num_dlabels - 1, s, q, d + 1);
 
692
                } else if(point == -2) { /* Not found */
 
693
                        /* Go to the next label */
 
694
                        len = *(u->str + u_o);
 
695
                        if(len > 63 || len < 0) {
 
696
                                return -1;
 
697
                        } else if(len == 0) { /* End of dname */
 
698
                                dw_addchar(0,c);
 
699
                                return dwc_compress_dlabels(u, u_o + 1, c,
 
700
                                        num_dlabels - 1, s, q, d + 1);
 
701
                        }
 
702
                        /* Add this label to stack of uncompressed dlabels */
 
703
                        if(dw_push_u16(c->len,s) == -1) {
 
704
                                return -1;
 
705
                        }
 
706
                        /* Copy label to compressed string */
 
707
                        tmp = dw_substr(u, u_o, len + 1, 1);
 
708
                        if(tmp == 0) {
 
709
                                return -1;
 
710
                        }
 
711
                        dw_append(tmp,c);
 
712
                        dw_destroy(tmp);
 
713
                        u_o += len + 1;
 
714
 
 
715
                } else { /* Error */
 
716
                        return -1;
 
717
                }
 
718
        }
 
719
 
 
720
        return -1; /* We should never get here */
 
721
}
 
722
 
 
723
/* Compress the rddata part of a record with dlabels
 
724
 *
 
725
 * Input: desc (description of rddata), u (unompressed string),
 
726
 * offset (offset in uncompressed string), c (partially compressed string),
 
727
 * s (stack of places compression pointer can point to), q (question string)
 
728
 *
 
729
 * Output: New offset
 
730
 */
 
731
 
 
732
int32_t dwc_comp_rddata(int16_t desc, dw_str *u, int32_t offset, dw_str *c,
 
733
                dw_str *s, dw_str *q) {
 
734
 
 
735
        dw_str *tmp = 0;
 
736
        int lead = 0, ndnames = 0, tail = 0;
 
737
 
 
738
        if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1 ||
 
739
                        dw_assert_sanity(s) == -1) {
 
740
                return -1;
 
741
        }
 
742
 
 
743
        lead = (desc & 0xf00) >> 8;
 
744
        ndnames = (desc & 0xf0) >> 4;
 
745
        tail = (desc & 0x0f) << 1;
 
746
 
 
747
        /* The stuff in the rddata before the dlabels (such as the preference
 
748
         * for a MX record) */
 
749
        if(lead > 0) {
 
750
                tmp = dw_substr(u,offset,lead,1);
 
751
                if(dw_append(tmp,c) == -1) {
 
752
                        dw_destroy(tmp);
 
753
                        return -1;
 
754
                }
 
755
                dw_destroy(tmp);
 
756
                offset += lead;
 
757
        }
 
758
 
 
759
        offset = dwc_compress_dlabels(u,offset,c,ndnames,s,q,0);
 
760
        if(offset == -1) {
 
761
                return -1;
 
762
        }
 
763
 
 
764
        /* Anything in the RDDATA after the dlabel (the only RR type I know
 
765
         * of that does this is the SOA record */
 
766
        if(tail > 0) {
 
767
                tmp = dw_substr(u,offset,tail,1);
 
768
                if(dw_append(tmp,c) == -1) {
 
769
                        dw_destroy(tmp);
 
770
                        return -1;
 
771
                }
 
772
                dw_destroy(tmp);
 
773
                offset += tail;
 
774
        }
 
775
 
 
776
        return offset;
 
777
}
 
778
 
 
779
/* Given a pointer to a list of 16-bit offsets (we will use two of them),
 
780
 * a question string, a partially compressed string, and the uncompressed
 
781
 * string, add this particular DNS record to the partially compressed
 
782
 * string.
 
783
 *
 
784
 * Input: Pointer to 2 offsets used for this RR (o), uncompressed string (u),
 
785
 *        partially compressed string (c), question string (q), stack
 
786
 *        of pointers to dlabels (s)
 
787
 *
 
788
 * Output: -1 on error, 1 on success
 
789
 */
 
790
 
 
791
int dwc_compress_rr(uint16_t *o, dw_str *u, dw_str *c, dw_str *q, dw_str *s) {
 
792
        uint16_t name = 0, data = 0;
 
793
        int16_t desc = 0;
 
794
        int32_t offset = 0, rdplace = 0, type = 0, len = 0, rdold = 0;
 
795
        dw_str *tmp = 0;
 
796
 
 
797
        if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1 ||
 
798
                        dw_assert_sanity(q) == -1 ||
 
799
                        dw_assert_sanity(s) == -1 || o == 0) {
 
800
                return -1;
 
801
        }
 
802
 
 
803
        name = o[0];
 
804
        data = o[1];
 
805
        offset = name;
 
806
 
 
807
        if(name > u->len || data > u->len || name > data) { /* Sanity check */
 
808
                return -1;
 
809
        }
 
810
 
 
811
        offset = dwc_compress_dlabels(u, offset, c, 1, s, q, 0); /* Name */
 
812
        if(offset == -1 || offset != data) {
 
813
                return -1;
 
814
        }
 
815
 
 
816
        /* Type, class, TTL, rdlength (which we will change) */
 
817
        tmp = dw_substr(u,offset,10,1);
 
818
        if(tmp == 0) {
 
819
                return -1;
 
820
        }
 
821
        dw_append(tmp,c);
 
822
        dw_destroy(tmp);
 
823
 
 
824
        type = dw_fetch_u16(u,offset);
 
825
        rdplace = offset + 8;
 
826
        offset += 10;
 
827
        desc = dwc_type_desc(type);
 
828
        if((desc & 0x1000) == 0) { /* Unknown/no dnames to compress */
 
829
                len = dw_fetch_u16(u,rdplace);
 
830
                tmp = dw_substr(u,offset,len,1);
 
831
                if(dw_append(tmp,c) == -1) {
 
832
                        dw_destroy(tmp);
 
833
                        return -1;
 
834
                }
 
835
                dw_destroy(tmp);
 
836
        } else {
 
837
                rdold = c->len;
 
838
                offset = dwc_comp_rddata(desc, u, offset, c, s, q);
 
839
                dw_put_u16(c,c->len - rdold,rdold - 2);
 
840
                if(offset == -1) {
 
841
                        return -1;
 
842
                }
 
843
        }
 
844
 
 
845
        return 1;
 
846
}
 
847
 
 
848
/* Compress all of the RR records in a string */
 
849
 
 
850
int dwc_compress_all_rrs(dns_string *unpack, dw_str *in, dw_str *out,
 
851
                dw_str *q, dw_str *stack) {
 
852
        int32_t seek = 0;
 
853
 
 
854
        if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
 
855
                        dw_assert_sanity(q) == -1 ||
 
856
                        dw_assert_sanity(stack) == -1 || unpack == 0) {
 
857
                return -1;
 
858
        }
 
859
 
 
860
        /* AN section */
 
861
        for(seek = 0; seek < unpack->ancount * 2; seek += 2) {
 
862
                if(dwc_compress_rr(unpack->an + seek, in,out,q, stack) == -1) {
 
863
                        return -1;
 
864
                }
 
865
        }
 
866
 
 
867
        /* NS section */
 
868
        for(seek = 0; seek < unpack->nscount * 2; seek += 2) {
 
869
                if(dwc_compress_rr(unpack->ns + seek, in,out,q, stack) == -1) {
 
870
                        return -1;
 
871
                }
 
872
        }
 
873
 
 
874
        /* AR section */
 
875
        for(seek = 0; seek < unpack->arcount * 2; seek += 2) {
 
876
                if(dwc_compress_rr(unpack->ar + seek, in,out,q, stack) == -1) {
 
877
                        return -1;
 
878
                }
 
879
        }
 
880
 
 
881
        return 1;
 
882
}
 
883
 
 
884
 
 
885
/* Compress a DNS string, and return a newly created compressed string. */
 
886
 
 
887
dw_str *dwc_compress(dw_str *q, dw_str *in) {
 
888
        dw_str *stack = 0; /* Stack of uncompressed dlabels */
 
889
        dw_str *out = 0; /* Compressed string */
 
890
        dns_string *unpack = 0;
 
891
 
 
892
        if(dw_assert_sanity(q) == -1 || dw_assert_sanity(in) == -1) {
 
893
                return 0;
 
894
        }
 
895
 
 
896
        stack = dw_create(1024);
 
897
        out = dw_create(515);
 
898
        if(stack == 0) {
 
899
                goto catch_dwc_compress;
 
900
        }
 
901
        if(dwc_push_offsets(q,0,stack,0x8000) == -1) {
 
902
                goto catch_dwc_compress;
 
903
        }
 
904
        unpack = dwc_make_dns_str(in);
 
905
        if(unpack == 0) {
 
906
                goto catch_dwc_compress;
 
907
        }
 
908
 
 
909
        if(dwc_compress_all_rrs(unpack, in, out, q, stack) == -1) {
 
910
                goto catch_dwc_compress;
 
911
        }
 
912
 
 
913
        dw_push_u16(unpack->ancount,out);
 
914
        dw_push_u16(unpack->nscount,out);
 
915
        dw_push_u16(unpack->arcount,out);
 
916
        dw_addchar(unpack->type,out);
 
917
        dw_destroy(stack);
 
918
        dwc_zap_dns_str(unpack);
 
919
        return out;
 
920
 
 
921
catch_dwc_compress:
 
922
        if(stack != 0) {
 
923
                dw_destroy(stack);
 
924
        }
 
925
        if(out != 0) {
 
926
                dw_destroy(out);
 
927
        }
 
928
        if(unpack != 0) {
 
929
                dwc_zap_dns_str(unpack);
 
930
        }
 
931
        return 0;
 
932
}
 
933
 
 
934
#ifdef HAVE_MAIN
 
935
/* Question and answer for "example.com" to test (de)compression */
 
936
#define EXAMPLE_COM_Q "\x07\x65\x78\x61\x6D\x70\x6C\x65\x03\x63\x6F\x6D" \
 
937
                      "\x00\x00\x01" /* 15 bytes */
 
938
#define EXAMPLE_COM_A "\xC0\x0C\x00\x01\x00\x01\x00\x00\x46\x50\x00\x04\x0A" \
 
939
        "\x02\x04\x08\xC0\x0C\x00\x02\x00\x01\x00\x00\x46\x50\x00\x05\x02" \
 
940
        "\x6E\x73\xC0\x0C\xC0\x39\x00\x01\x00\x01\x00\x00\x46\x50\x00\x04" \
 
941
        "\x0A\x01\x02\x03\x00\x01\x00\x01\x00\x01\x00" /* 50 bytes */
 
942
 
 
943
#define PA "\300\014\000\005\000\001\000\011-]\000\027\014safebrowsing\005cache\001l\300\037\300;\000\001\000\001\000\000\001\000\000\004J}\245\047\300;\000\001\000\001\000\000\001\000\000\004J}\245\020\300;\000\001\000\001\000\000\001\000\000\004J}\245\021\300;\000\001\000\001\000\000\001\000\000\004J}\245\022\300;\000\001\000\001\000\000\001\000\000\004J}\245\023\300;\000\001\000\001\000\000\001\000\000\004J}\245\024\300;\000\001\000\001\000\000\001\000\000\004J}\245\025\300;\000\001\000\001\000\000\001\000\000\004J}\245\026\300;\000\001\000\001\000\000\001\000\000\004J}\245\027\300;\000\001\000\001\000\000\001\000\000\004J}\245\030\300;\000\001\000\001\000\000\001\000\000\004J}\245\031\300;\000\001\000\001\000\000\001\000\000\004J}\245\032\300;\000\001\000\001\000\000\001\000\000\004J}\245\033\300;\000\001\000\001\000\000\001\000\000\004J}\245\034\300;\000\001\000\001\000\000\001\000\000\004J}\245\035\300;\000\001\000\001\000\000\001\000\000\004J}\245\036\300;\000\001\000\001\000\000\001\000\000\004J}\245\037\300;\000\001\000\001\000\000\001\000\000\004J}\245 \300;\000\001\000\001\000\000\001\000\000\004J}\245!\300;\000\001\000\001\000\000\001\000\000\004J}\245\042\300;\000\001\000\001\000\000\001\000\000\004J}\245#\300;\000\001\000\001\000\000\001\000\000\004J}\245$\300;\000\001\000\001\000\000\001\000\000\004J}\245\045\300;\000\001\000\001\000\000\001\000\000\004J}\245&\000\031\000\000\000\000\000" /* 426 bytes */
 
944
#define PQ "\022safebrowsing-cache\006google\003com\000\000\001" /* 33 bytes */
 
945
void show_str(dw_str *u) {
 
946
        int c = 0;
 
947
        for(c = 0; c < u->len; c++) {
 
948
                printf("%d %x %c\n",c,*(u->str + c),*(u->str + c));
 
949
        }
 
950
}
 
951
 
 
952
int main() {
 
953
        dw_str *q = 0, *a = 0, *u = 0, *c = 0;
 
954
        q = dw_create(256);
 
955
        a = dw_create(512);
 
956
        dw_cstr_append((uint8_t *)PQ,33,q);
 
957
        dw_cstr_append((uint8_t *)PA,426,a);
 
958
        u = dwc_decompress(q,a);
 
959
        c = dwc_compress(q,u);
 
960
        dw_stdout(q);
 
961
        dw_stdout(a);
 
962
        dw_stdout(u);
 
963
        dw_stdout(c);
 
964
        dw_destroy(q);
 
965
        dw_destroy(a);
 
966
        dw_destroy(u);
 
967
        dw_destroy(c);
 
968
        return 0;
 
969
}
 
970
#endif /* HAVE_MAIN */