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