1
/* Copyright (c) 2009 Sam Trenholme
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
15
* This software is provided 'as is' with no guarantees of correctness or
16
* fitness for purpose.
20
#include "DwStr_functions.h"
21
#include "DwCompress.h"
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).
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.)
32
* 0 or higher means an offset in the "answer" string or the string as
33
* stored in the cache.
36
int32_t dwc_decomp_offset(dw_str *in, int32_t offset, int32_t qlen) {
39
if(dw_assert_sanity(in) == -1) {
47
if(*(in->str + t) < 64) { /* Not a compression pointer */
50
offset = *(in->str + t) & 0x3f;
52
offset += *(in->str + t + 1);
54
offset -= 12; /* DNS header */
55
if(offset < qlen && qlen > 0) { /* In question */
59
offset -= 2; /* CLASS part of question */
60
if(offset < 0 || offset > in->len) { /* Invalid place */
63
return offset; /* In answer */
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) {
71
if(dw_assert_sanity(out) == -1) {
77
if(compress_followed == 0) {
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
89
* We return -1 on error; on success, we return the offset in the source
90
* string where the DNS name ends.
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;
98
if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
99
dw_assert_sanity(q) == -1 || delta == 0) {
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 */
109
len = *(in->str + offset);
111
if(offset + 1 > q->len) { /* Sanity check */
114
len = *(q->str + offset);
116
if(len >= 192) { /* Compression pointer */
117
if(use_q_string == 1) { /* No comp pointers in Q */
120
if(compress_followed == 0) { /* Mark orig length */
124
offset = dwc_decomp_offset(in, offset, q->len);
127
} else if(offset < 0) { /* In question, not answer */
129
offset = -2 - offset;
131
compress_followed = 1;
132
} else if(len > 63 || len < 0) { /* Invalid length */
134
} else if(len == 0) { /* End of name */
135
t = dwc_finish_name(out,delta,offset,compress_followed,
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);
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)
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.
167
int16_t dwc_type_desc(int32_t type) {
170
return 0x1210; /* Compress, 2 bytes then 1 dname */
174
return 0x1010; /* Compress, 1 dname at beginning */
176
return 0x102a; /* Compress, 2 dnames at beginning,
177
20 bytes after dnames */
183
return 0x0010; /* Don't compress, 1 dname at start */
186
return 0x0020; /* Don't compress, 2 dnames at start */
189
return 0x0210; /* Don't compress, 2 bytes, 1 dname */
191
return 0x0610; /* Don't compress, 6 bytes, 1 dname */
197
/* Decompress the rddata of a single record.
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
204
* Output: New compressed string offset
206
int32_t dwc_decomp_rddata(int16_t desc, int32_t offset, dw_str *in,
207
dw_str *out, dw_str *q) {
209
int lead = 0, ndnames = 0, tail = 0;
210
int32_t rdo = 0, rdlength = 0, delta = 0;
212
if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
213
dw_assert_sanity(q) == -1) {
217
lead = (desc & 0xf00) >> 8;
218
ndnames = (desc & 0xf0) >> 4;
219
tail = (desc & 0x0f) << 1;
221
rdo = out->len - 2; /* Where RDLENGTH is in string */
223
tmp = dw_substr(in,offset,lead,1);
224
if(dw_append(tmp,out) == -1) {
225
goto catch_dwc_decomp_rddata;
233
offset = dwc_decomp_dname(in,out,q,&delta,
235
rdlength = dw_fetch_u16(out,rdo);
237
dw_put_u16(out,rdlength,rdo);
241
tmp = dw_substr(in,offset,tail,1);
250
catch_dwc_decomp_rddata:
254
return -1; /* Error */
257
/* Decompress a single DNS record.
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
264
int32_t dwc_decomp_rr(dw_str *in, dw_str *out, dw_str *q, dw_str *stack,
267
int32_t val = 0, delta = 0;
271
if(dw_assert_sanity(in) == -1 || dw_assert_sanity(out) == -1 ||
272
dw_assert_sanity(q) == -1 ||
273
dw_assert_sanity(stack) == -1) {
277
dw_push_u16(out->len,stack); /* Beginning of RR */
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;
285
dw_push_u16(out->len,stack); /* Part of RR after RR name */
286
val = dw_fetch_u16(in,offset); /* RR type */
288
goto catch_dwc_decomp_rr;
290
desc = dwc_type_desc(val); /* Description for compression purposes */
292
/* Add data between name and RDdata */
293
tmp = dw_substr(in,offset,10,1);
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);
304
} else { /* Decompress compression pointers */
305
offset = dwc_decomp_rddata(desc, offset, in, out, q);
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
322
dw_str *dwc_decompress(dw_str *q, dw_str *in) {
323
dw_str *out = 0, *tmp = 0, *stack = 0;
327
if(dw_assert_sanity(in) == -1 || dw_assert_sanity(q) == -1) {
331
out = dw_create(2048);
335
stack = dw_create(2048); /* 1024 lengths or 512 dlabels */
337
goto catch_dwc_decompress;
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;
347
dw_append(stack,out);
349
tmp = dw_substr(in,-7,-1,1);
355
catch_dwc_decompress:
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
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;
376
if(dw_assert_sanity(a) == -1 || dw_assert_sanity(b) == -1 ||
377
a_o > a->len || b_o > b->len) {
381
if(*(a->str + a_o) != *(b->str + b_o)) {
387
if(c < 0 || c > 64 || (a_o + c > a->len) || (b_o + c > b->len)) {
391
for(noloop = 0; noloop < 128 && c > 0; noloop++) {
392
if(*(a->str + a_o + c) != *(b->str + b_o + c)) {
401
/* Determine whether two dlabels are the same, where one dlabel can
402
* have compression pointers (the other doesn't)
404
* Input: String with uncompressed dname (u), offset to dname in that string,
405
* String with compressed dname (c), offset to dname in compressed
407
* pointer to string with question (q), since compression pointers
408
* sometimes point here
410
* Output: -1 if error, 0 if different, 1 if same
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;
419
if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1) {
423
for(count = 0; count < 300; count++) {
424
/* Follow any compression pointers as needed */
427
place = dwc_decomp_offset(c, place, q->len);
429
place = dwc_decomp_offset(c, place, 0);
431
if(place == -1 || (q == 0 && place < 0)) {
434
if(place < -1 && q != 0) {
440
if(dwc_label_same(u,u_o,c,place) != 1) {
441
return 0; /* Not same */
444
if(dwc_label_same(u,u_o,q,place) != 1) {
445
return 0; /* Not same */
448
len = *(u->str + u_o);
452
if(len < 0 || len > 64 || (u_o + len >= u->len)) {
455
if(in_q == 0 && (place + len >= c->len)) {
457
} else if(in_q == 1 && q != 0 && (place + len >= q->len)) {
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"
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.
480
* Output: As described above; offset if found, -2 if not found
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
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.
491
* If it is, we repeat the process until we're at the end of the
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
499
int32_t dwc_in_bailiwick(dw_str *b, int32_t b_o, dw_str *h,
500
int32_t h_o, dw_str *q) {
502
int count = 0, in_q_string = 0, len = 0;
505
if(dw_assert_sanity(b) == -1 || dw_assert_sanity(h) == -1) {
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);
516
place = dwc_decomp_offset(h, place, 0);
518
if(place == -1 || (q == 0 && place < 0)) { /* Sanity */
521
if(place < 0 && q != 0) {
526
if(in_q_string == 0) { /* Look in answer */
527
if(dwc_dname_same(b, b_o, h, place, q) == 1) {
530
} else { /* Look in question */
531
if(dwc_dname_same(b, b_o, q, place, q) == 1) {
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 */
542
len = *(q->str + place);
543
if(len + place + 1 > q->len) {
544
return -1; /* Out of bounds */
547
return -1; /* Invalid */
550
return -2; /* No match */
552
place += len + 1; /* Next part of name */
554
return -1; /* Error */
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)
561
* Input: String with dname, offset of dname in string, string with
562
* stack, value to add to any number put on the stack
564
* Output: 0 on success, -1 on error
567
int dwc_push_offsets(dw_str *dns, int32_t place, dw_str *stack, int32_t
571
if(dw_assert_sanity(dns) == -1 || dw_assert_sanity(stack) == -1) {
575
for(count = 0; count < 300; count++) { /* Infinite loop protection */
576
if(place > dns->len) {
577
return -1; /* Error */
579
len=*(dns->str + place);
580
if(len >= 192 /* Comp marker */ || len == 0 /* End */) {
584
return -1; /* Invalid length field */
586
if(place < 0 || place >= 0x3fff ||
587
place + offset < 0 || place + offset > 0xffff) {
588
return -1; /* Invalid offset */
590
if(dw_push_u16(place + offset, stack) == -1) {
595
return 0; /* Success */
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.
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)
607
* Output: -2: Not found
609
* 0-16383: Have the compression pointer point this many bytes in to
610
* the final compressed string
613
int32_t dwc_seek_comp_pointer(dw_str *u, int32_t u_o, dw_str *c, dw_str *q,
615
int32_t s_o = 0; /* Stack offset (which thingy we're looking at in
618
int in_q = 0; /* Whether we're in the question string or not */
619
int count = 0; /* Infinite loop protection */
621
if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1) {
625
for(count = 0; count < 1000 && s_o < s->len; count++) {
626
offset = dw_fetch_u16(s, s_o);
631
if(offset >= 0x8000) {
636
if(dwc_dname_same(u, u_o, q, offset, 0) == 1) {
640
if(dwc_dname_same(u, u_o, c, offset, q) == 1) {
641
return 12 + q->len + 2 + offset;
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.
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)
660
* Output: New uncompressed offset ; -1 on fatal error
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) {
666
int count = 0, len = 0;
669
if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1 ||
670
dw_assert_sanity(s) == -1) {
674
if(d >= 20 || num_dlabels <= 0) {
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 */
683
if(dw_push_u16(point,c) == -1) {
686
point = dw_get_dn_end(u,u_o); /* End of uncomp label */
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) {
697
} else if(len == 0) { /* End of dname */
699
return dwc_compress_dlabels(u, u_o + 1, c,
700
num_dlabels - 1, s, q, d + 1);
702
/* Add this label to stack of uncompressed dlabels */
703
if(dw_push_u16(c->len,s) == -1) {
706
/* Copy label to compressed string */
707
tmp = dw_substr(u, u_o, len + 1, 1);
720
return -1; /* We should never get here */
723
/* Compress the rddata part of a record with dlabels
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)
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) {
736
int lead = 0, ndnames = 0, tail = 0;
738
if(dw_assert_sanity(u) == -1 || dw_assert_sanity(c) == -1 ||
739
dw_assert_sanity(s) == -1) {
743
lead = (desc & 0xf00) >> 8;
744
ndnames = (desc & 0xf0) >> 4;
745
tail = (desc & 0x0f) << 1;
747
/* The stuff in the rddata before the dlabels (such as the preference
748
* for a MX record) */
750
tmp = dw_substr(u,offset,lead,1);
751
if(dw_append(tmp,c) == -1) {
759
offset = dwc_compress_dlabels(u,offset,c,ndnames,s,q,0);
764
/* Anything in the RDDATA after the dlabel (the only RR type I know
765
* of that does this is the SOA record */
767
tmp = dw_substr(u,offset,tail,1);
768
if(dw_append(tmp,c) == -1) {
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
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)
788
* Output: -1 on error, 1 on success
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;
794
int32_t offset = 0, rdplace = 0, type = 0, len = 0, rdold = 0;
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) {
807
if(name > u->len || data > u->len || name > data) { /* Sanity check */
811
offset = dwc_compress_dlabels(u, offset, c, 1, s, q, 0); /* Name */
812
if(offset == -1 || offset != data) {
816
/* Type, class, TTL, rdlength (which we will change) */
817
tmp = dw_substr(u,offset,10,1);
824
type = dw_fetch_u16(u,offset);
825
rdplace = offset + 8;
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) {
838
offset = dwc_comp_rddata(desc, u, offset, c, s, q);
839
dw_put_u16(c,c->len - rdold,rdold - 2);
848
/* Compress all of the RR records in a string */
850
int dwc_compress_all_rrs(dns_string *unpack, dw_str *in, dw_str *out,
851
dw_str *q, dw_str *stack) {
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) {
861
for(seek = 0; seek < unpack->ancount * 2; seek += 2) {
862
if(dwc_compress_rr(unpack->an + seek, in,out,q, stack) == -1) {
868
for(seek = 0; seek < unpack->nscount * 2; seek += 2) {
869
if(dwc_compress_rr(unpack->ns + seek, in,out,q, stack) == -1) {
875
for(seek = 0; seek < unpack->arcount * 2; seek += 2) {
876
if(dwc_compress_rr(unpack->ar + seek, in,out,q, stack) == -1) {
885
/* Compress a DNS string, and return a newly created compressed string. */
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;
892
if(dw_assert_sanity(q) == -1 || dw_assert_sanity(in) == -1) {
896
stack = dw_create(1024);
897
out = dw_create(515);
899
goto catch_dwc_compress;
901
if(dwc_push_offsets(q,0,stack,0x8000) == -1) {
902
goto catch_dwc_compress;
904
unpack = dwc_make_dns_str(in);
906
goto catch_dwc_compress;
909
if(dwc_compress_all_rrs(unpack, in, out, q, stack) == -1) {
910
goto catch_dwc_compress;
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);
918
dwc_zap_dns_str(unpack);
929
dwc_zap_dns_str(unpack);
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 */
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) {
947
for(c = 0; c < u->len; c++) {
948
printf("%d %x %c\n",c,*(u->str + c),*(u->str + c));
953
dw_str *q = 0, *a = 0, *u = 0, *c = 0;
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);
970
#endif /* HAVE_MAIN */