chiark / gitweb /
f0c56fb6fe75d6a7d427564e48551a39097f6b3f
[secnet.git] / dns-transp-common.c
1 /**/
2
3 #include "dns-transp-common.h"
4
5 #define MAX_DATA_LABELS 4
6
7 static const char out_table[32]="0123456789abcdefghijklmnopqrstuv";
8
9 static void dump_enc(struct dnsdomainenc *be, const char *what) {
10     printf("ENC p=%08"PRIx32"/%-2d o=[%02d]|%02x %s\n",
11            be->pending, be->npending,
12            (int)(be->out - be->bufstop), *be->out,
13            what);
14 }
15
16 static inline int enoughinput(struct dnsdomainenc *be) {
17     return be->npending >= 5;
18 }
19 static inline void startlabel(struct dnsdomainenc *be) {
20     be->labremain=63;
21 }
22 static inline void outputchar(struct dnsdomainenc *be) {
23     /* there must be enough input and enough space */
24     if (!be->labremain) {
25         *--(be->out) = 63;
26         startlabel(be);
27         dump_enc(be,"outputchar labelfull");
28     }
29     *--(be->out) = out_table[be->pending & 0x1f];
30     be->labremain--;
31     be->pending >>= 5;
32     be->npending -= 5;
33     dump_enc(be,"outputchar");
34 }
35
36 int dnsdomainenc_start(struct dnsdomainenc *be, uint8_t *buf, int buflen,
37                        int mydompathlen, const char *const mydompath[]) {
38     int i;
39     assert(buflen>0);
40     be->pending=0;
41     be->npending=0;
42     be->bufstop=buf+1;
43     be->out=buf+buflen;
44     *--be->out=0;
45     for (i=mydompathlen-1; i>=0; i--) {
46         const char *lab=mydompath[i];
47         int ll=strlen(lab);
48         if (ll>63) return FAKERCODE_MYDOMAINLABELTOOLONG;
49         if (be->out <= be->bufstop+ll+1) return FAKERCODE_MYDOMAINTOOLONG;
50         be->out -= ll;
51         memcpy(be->out, lab, ll);
52         *--be->out = ll;
53         dump_enc(be,"start mydompath");
54     }
55     startlabel(be);
56     dump_enc(be,"start");
57     return 0;
58 }
59
60 void dnsdomainenc_addbits(struct dnsdomainenc *be, uint32_t val, int nbits) {
61     assert(nbits<=28); /* otherwise pending might overflow */
62     assert(!(val & ~(((uint32_t)1 << nbits)-1)));
63     be->pending |= val << be->npending;
64     be->npending += nbits;
65     dump_enc(be,"addbits 1");
66     while (enoughinput(be)) {
67         assert(be->out > be->bufstop);
68         outputchar(be);
69     }
70     dump_enc(be,"addbits 2");
71 }
72
73 void dnsdomainenc_addu32(struct dnsdomainenc *be, uint32_t val) {
74     dnsdomainenc_addbits(be, val & 0xffff, 16);
75     dnsdomainenc_addbits(be, val >> 16,    16);
76 }
77
78 int dnsdomainenc_restbytes(struct dnsdomainenc *be,
79                            const uint8_t *bytes, int avail) {
80     for (;;) {
81         if (be->out == be->bufstop) {
82             dump_enc(be,"restbytes bufstop");
83             return avail + ((be->npending + 7) / 8);
84         }
85         if (be->npending<5) {
86             if (avail) {
87                 be->pending |= (*bytes++) << be->npending;
88                 be->npending += 8;
89                 avail--;
90                 dump_enc(be,"restbytes moreavail");
91             } else if (be->npending <= 0) {
92                 dump_enc(be,"restbytes nopending");
93                 return 0;
94             }
95         }
96         outputchar(be);
97         dump_enc(be,"restbytes outputchar");
98     }
99 }
100
101 uint8_t *dnsdomainenc_getresult(struct dnsdomainenc *be) {
102     if (be->labremain != 63)
103         *--(be->out) = 63 - be->labremain; /* finish the last label */
104     dump_enc(be,"getresult");
105     return be->out;
106 }
107
108
109
110 static uint8_t in_table[256];
111
112 void dnsdomaindec_globalinit(void) {
113     int i;
114     memset(in_table,0xff,sizeof(in_table));
115     for (i=0; i<32; i++) {
116         in_table[(uint8_t)out_table[i]]=i;
117         in_table[toupper((unsigned char)out_table[i])]=i;
118     }
119 }
120
121
122 static void dump_dec(struct dnsdomaindec *bd, const char *what) {
123     int remain=bd->in - bd->databuf;
124     printf("DEC4 i=[%02d]|%02x p=%08"PRIx32"/%-2d %s\n",
125            remain, remain ? *(bd->in-1) : 0,
126            bd->pending, bd->npending,
127            what);
128 }
129
130 struct labelinpacket {
131     const uint8_t *bytes;
132     int len;
133 };
134
135 static void setafter(const uint8_t *domain, const uint8_t **domain_end_r) {
136     if (domain_end_r && !*domain_end_r)
137         *domain_end_r=domain;
138 }
139
140 int dnsdomaindec_start(struct dnsdomaindec *bd, const uint8_t *packet,
141                        const uint8_t *endpacket, const uint8_t *domain,
142                        int mydompathlen, const char *const mydompath[],
143                        const uint8_t **domain_end_r) {
144     int maxlabels=mydompathlen+MAX_DATA_LABELS;
145     struct labelinpacket labels[maxlabels];
146     int nlabels=0, totallen=0, i, j;
147
148     bd->pending=0;
149     bd->npending=0;
150
151     *domain_end_r=0;
152     for (;;) {
153         if (domain==endpacket)
154             return RCODE_FORMERR;
155         printf("DEC1 dom=|[%02d]|%02x[%02d]\n", domain-packet,
156                *domain, endpacket-domain);
157         unsigned b=*domain++;
158         if (!b) {
159             totallen++;
160             setafter(domain,domain_end_r);
161             break;
162         }
163         if ((b & 0xc0) == 0xc0) {
164             if (domain==endpacket)
165                 return RCODE_FORMERR;
166             unsigned b2=*domain++;
167             setafter(domain,domain_end_r);
168             int off= ((b & 0x3f)<<8) | b2;
169             if (off >= endpacket - packet)
170                 return RCODE_FORMERR;
171             domain=packet+off;
172             b=*domain++;
173             if (!b) return RCODE_FORMERR;
174             /* now fall through to code expecting a literal */
175         }
176         if ((b & 0xc0) != 0x00)
177             return RCODE_FORMERR;
178         if (nlabels>=maxlabels)
179             return FAKERCODE_NOTAUTH;
180         totallen+=b+1;
181         if (totallen >= MAX_DOMAIN_BYTES) /* we still need a nul label */
182             return RCODE_FORMERR;
183         labels[nlabels].bytes=domain;
184         labels[nlabels].len=b;
185         printf("DEC1 labels[%d]=%.*s\n", nlabels,
186                labels[nlabels].len, labels[nlabels].bytes);
187         nlabels++;
188         domain += b;
189     }
190     if (nlabels <= mydompathlen)
191         return FAKERCODE_NOTAUTH;
192     for (i=0; i<mydompathlen; i++) {
193         int l=strlen(mydompath[i]);
194         const struct labelinpacket *got=&labels[nlabels-mydompathlen+i];
195         printf("DEC2 [%d] %s ?= %.*s\n", i, mydompath[i],
196                got->len, got->bytes);
197         if (got->len != l || memcmp(got->bytes, mydompath[i], l))
198             return FAKERCODE_NOTAUTH;
199     }
200     /* OK, it's our domain and it has some data and a good
201      * number of labels.  Wow. */
202     uint8_t *copyto=bd->databuf;
203     printf("DEC3");
204     for (i=nlabels-mydompathlen-1; i>=0; i--) {
205         const uint8_t *p=labels[i].bytes;
206         for (j=0; j<labels[i].len; j++) {
207             int val=in_table[*p++];
208             if (val & 0x80)
209                 return RCODE_NXDOMAIN;
210             printf(" %02x",val);
211             *copyto++=val;
212         }
213         printf(" .");
214     }
215     printf("\n");
216     assert(copyto <= bd->databuf+sizeof(bd->databuf));
217     bd->in=copyto;
218     return 0;
219 }
220
221 static void inputchar(struct dnsdomaindec *bd) {
222     /* must be enough input and enough space in pending */
223     dump_dec(bd,"inputchar 1");
224     int ch=*--(bd->in);
225     bd->pending |= (ch << bd->npending); /* already decoded */
226     bd->npending += 5;
227     dump_dec(bd,"inputchar 2");
228 }
229 uint32_t dnsdomaindec_getbits(struct dnsdomaindec *bd, int nbits)
230 {
231     /* must be enough */
232     assert(nbits<=28);
233     while (bd->npending < nbits)
234         inputchar(bd);
235     int newnpending = bd->pending - nbits;
236     uint32_t rv = bd->pending >> newnpending;
237     bd->pending &= ((uint32_t)1 << newnpending)-1;
238     bd->npending = newnpending;
239     return rv;
240 }
241 uint32_t dnsdomaindec_getu32(struct dnsdomaindec *bd)
242 {
243     uint32_t lsw=dnsdomaindec_getbits(bd,16);
244     uint32_t msw=dnsdomaindec_getbits(bd,16);
245     return (msw << 16) | lsw;
246 }
247 int dnsdomaindec_restbytes(struct dnsdomaindec *bd, uint8_t outbuf[MAX_DOMAIN_BYTES])
248 {
249     uint8_t *out = outbuf;
250     for (;;) {
251         if (bd->npending >= 8) {
252             *out++ = bd->pending;
253             bd->pending >>= 8;
254             bd->npending -= 8;
255         }
256         if (bd->in == bd->databuf) {
257             /* that's all the input */
258             return out - outbuf;
259         }
260         inputchar(bd);
261     }
262 }