chiark / gitweb /
2505828f42331434fd968ac12134a7101969589f
[secnet.git] / dns-transp-common.c
1
2 static const char out_table[32]="0123456789abcdefghijklmnopqrstuv";
3
4 static inline int enoughinput(struct dnsbitenc *be) {
5     return be->npending >= 5;
6 }
7 static inline void startlabel(struct dnsbitenc *be) {
8     be->labremain=63;
9 }
10 static inline void outputchar(struct dnsbitenc *be) {
11     /* there must be enough input and enough space */
12     if (!be->labremain) {
13         *--(be->out) = 63;
14         startlabel(be);
15     }
16     *--(be->out) = out_table[be->pending & 0x1f];
17     be->pending >>= 5;
18     be->npending -= 5;
19 }
20
21 void dnsbitenc_start(struct dnsbitenc *be, uint8_t *buf, int buflen) {
22     assert(buflen>0);
23     be->pending=0;
24     be->npending=0;
25     be->bufstop=buf+1;
26     be->out=buf+buflen;
27     startlabel(be);
28 }
29
30 void dnsbitenc_addbits(struct dnsbitenc *be, uint32_t val, int nbits) {
31     assert(nbits<=28); /* otherwise pending might overflow */
32     assert(!(val & ~(((uint32_t)1 << nbits)-1)));
33     be->pending |= val << be->npending;
34     be->npending += nbits;
35     while (enoughinput(be)) {
36         assert(be->out > be->bufstop);
37         outputchar(be);
38     }
39 }
40
41 void dnsbitenc_addu32(struct dnsbitenc *be, uint32_t val) {
42     dnsbitenc_addbits(be, val & 0xffff, 16);
43     dnsbitenc_addbits(be, val >> 16,    16);
44 }
45
46 int dnsbitenc_restbytes(struct dnsbitenc *be, uint8_t *bytes, int qty) {
47     for (;;) {
48         if (buf==bufstop) {
49             return qty + ((be->npending + 7) / 8);
50         }
51         if (be->npending<5) {
52             if (qty) {
53                 be->pending |= (*bytes++) << be->npending;
54                 be->npending += 8;
55             } else if (be->npending <= 0) {
56                 return 0;
57             }
58         }       
59         outputchar(be);
60     }
61 }
62
63 uint8_t *dnsbitenc_getresult(struct dnsbitenc *be) {
64     *--(be->out) = 63 - be->labremain; /* finish the last label */
65     return be->out;
66 }
67
68
69
70
71 #define MAX_DATA_LABELS
72
73 struct labelinpacket {
74     const uint8_t *bytes;
75     int len;
76 };
77
78 struct dnsbitdec {
79     /* private for dnsbitdec_... functions; do not access direcctly */
80     const databuf[MAX_DOMAIN_BYTES];
81     const uint8_t *in;
82 };
83
84 static void setafter(const uint8_t *domain, const uint8_t **domain_end_r) {
85     if (domain_end_r && !*domain_end_r)
86         *domain_end_r=domain;
87 }
88
89 int dnsdomaindec_start(struct dnsbitdec *bd, const uint8_t *packet,
90                        const uint8_t *endpacket, const uint8_t *domain,
91                        int mydompathlen, const char *mydompath[],
92                        const uint8_t **domain_end_r) {
93     int maxlabels=mydompath+MAX_DATA_LABELS;
94     const strut labelinpacket labels[maxlabels];
95     int nlabels=0, totallen=0;
96
97     *domain_end_r=0;
98     for (;;) {
99         if (domain==endpacket)
100             return FORMERR;
101         unsigned b=*domain++;
102         if (!b) {
103             totallen++;
104             setafter(domain,domain_end_r);
105             break;
106         }
107         if ((b & 0xc0) == 0xc0) {
108             if (domain==endpacket)
109                 return FORMERR;
110             unsigned b2=*domain++;
111             setafter(domain,domain_end_r);
112             unsigned off= ((b & 0x3f)<<8) | b2;
113             if (off >= endpacket - packet)
114                 return False;
115             domain=packet+off;
116             b=*domain++;
117             if (!b) return FORMERR
118             /* now fall through to code expecting a literal */
119         }
120         if ((b & 0xc0) != 0x00)
121             return FORMERR;
122         if (nlabels>=maxlabels)
123             return NOTAUTH;
124         totallen+=b+1;
125         if (totallen >= MAX_DOMAIN_BYTES) /* we still need a nul label */
126             return FORMERR;
127         labels[nlabels].bytes=domain;
128         labels[nlabels].len=b;
129         nlabels++;
130         domain += b;
131     }
132     if (nlabels <= mydompathlen)
133         return NOTAUTH;
134     for (i=0; i<mydompathlen; i++) {
135         int l=strlen(mydompath[i]);
136         const struct labelinpacket *got=&labels[nlabels-mydompathlen];
137         if (got->len != l || memcmp(got->bytes,mydompath[i]))
138             return NOTAUTH;
139     }
140     /* OK, it's our domain and it has some data and a good
141      * number of labels.  Wow. */
142     uint8_t *copyto=&bd->databuf;
143     for (i=0; i<nlabels-mydompathlen; i++) {
144         const uint8_t *p=labels[i].bytes;
145         for (j=0; j<labels[i].len; j++) {
146             int val=in_table[*p++];
147             if (val & 0x80)
148                 return NXDOMAIN;
149             *copyto++=val;
150         }
151     }
152     assert(copyto <= bd->databuf+sizeof(bd->databuf));
153     bd->in=copyto;
154     return 0;
155 }
156
157 static uint8_t in_table[256];
158
159 static void dnsdomaindec_globalinit(void) {
160     memset(in_table,0xff,sizeof(in_table));
161     for (i=0; i<32; i++) {
162         in_table[out_table[i]]=i;
163         in_table[toupper((unsigned char)out_table[i])]=i;
164     }
165 }
166
167 static void inputchar(struct dnsbitdec *bd) {
168     /* must be enough input and enough space in pending */
169     int ch=*--(bd->in);
170     bd->pending <<= 8;
171     bd->pending |= ch; /* already decoded */
172     bd->npending += 5;
173 }
174 uint32_t dnsdomaindec_getbits(struct dnsbitdec *bd, int nbits)
175 {
176     /* must be enough */
177     assert(nbits<=28);
178     while (bd->pending < nbits)
179         inputchar();
180     int newnpending = bd->pending - nbits;
181     uint32_t rv = bd->pending >> newnpending;
182     bd->pending &= ((uint32_t)1 << newnpending)-1;
183     bd->npending = newnpending;
184 }
185 uint32_t dnsdomaindec_getu32(struct dnsbitdec *bd)
186 {
187     uint32_t lsw=dnsdomaindec_getbits(bd,16);
188     uint32_t msw=dnsdomaindec_getbits(bd,16);
189     return (msw << 16) | lsw;
190 }
191 int dnsbitdec_restbytes(struct dnsbitdec *bd, uint8_t outbuf[MAX_DOMAIN_BYTES])
192 {
193     uint8_t *out;
194     for (;;) {
195         if (bd->npending >= 8) {
196             *out++ = bd->pending;
197             bd->pending >>= 8;
198             bd->npending -= 8;
199         }
200         if (bd->in == bd->databuf) {
201             /* that's all the input */
202             return out - outbuf;
203         }
204         inputchar(bd);
205     }
206 }