Add --nostatslog option
[novacoin-seeder.git] / dns.c
1 #include <stdbool.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <stdint.h>
8 #include <sys/types.h>
9 #include <arpa/inet.h>
10 #include <time.h>
11 #include <ctype.h>
12 #include <unistd.h>
13
14 #include "dns.h"
15
16 #define BUFLEN 512
17
18 #if defined IP_RECVDSTADDR
19 # define DSTADDR_SOCKOPT IP_RECVDSTADDR
20 # define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_addr)))
21 # define dstaddr(x) (CMSG_DATA(x))
22 #elif defined IP_PKTINFO
23 struct in_pktinfo {
24   unsigned int   ipi_ifindex;  /* Interface index */
25   struct in_addr ipi_spec_dst; /* Local address */
26   struct in_addr ipi_addr;     /* Header Destination address */
27 };
28
29 # define DSTADDR_SOCKOPT IP_PKTINFO
30 # define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_pktinfo)))
31 # define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
32 #else
33 # error "can't determine socket option"
34 #endif
35
36 union control_data {
37   struct cmsghdr cmsg;
38   unsigned char data[DSTADDR_DATASIZE];
39 };
40
41 typedef enum {
42   CLASS_IN = 1,
43   QCLASS_ANY = 255
44 } dns_class;
45
46 typedef enum {
47   TYPE_A = 1,
48   TYPE_NS = 2,
49   TYPE_CNAME = 5,
50   TYPE_SOA = 6,
51   TYPE_MX = 15,
52   TYPE_AAAA = 28,
53   TYPE_SRV = 33,
54   QTYPE_ANY = 255
55 } dns_type;
56
57
58 //  0: ok
59 // -1: premature end of input, forward reference, component > 63 char, invalid character
60 // -2: insufficient space in output
61 int static parse_name(const unsigned char **inpos, const unsigned char *inend, const unsigned char *inbuf, char *buf, size_t bufsize) {
62   size_t bufused = 0;
63   int init = 1;
64   do {
65     if (*inpos == inend)
66       return -1;
67     // read length of next component
68     int octet = *((*inpos)++);
69     if (octet == 0) {
70       buf[bufused] = 0;
71       return 0;
72     }
73     // add dot in output
74     if (!init) {
75       if (bufused == bufsize-1)
76         return -2;
77       buf[bufused++] = '.';
78     } else
79       init = 0;
80     // handle references
81     if ((octet & 0xC0) == 0xC0) {
82       if (*inpos == inend)
83         return -1;
84       int ref = ((octet - 0xC0) << 8) + *((*inpos)++);
85       if (ref < 0 || ref >= (*inpos)-inbuf) return -1;
86       const unsigned char *newbuf = inbuf + ref;
87       return parse_name(&newbuf, *inpos, inbuf, buf+bufused, bufsize-bufused);
88     }
89     if (octet > 63) return -1;
90     // copy label
91     while (octet) {
92       if (*inpos == inend)
93         return -1;
94       if (bufused == bufsize-1)
95         return -2;
96       int c = *((*inpos)++);
97       if (c == '.')
98         return -1;
99       octet--;
100       buf[bufused++] = c;
101     }
102   } while(1);
103 }
104
105 //  0: k
106 // -1: component > 63 characters
107 // -2: insufficent space in output
108 // -3: two subsequent dots
109 int static write_name(unsigned char** outpos, const unsigned char *outend, const char *name, int offset) {
110   while (*name != 0) {
111     char *dot = strchr(name, '.');
112     const char *fin = dot;
113     if (!dot) fin = name + strlen(name);
114     if (fin - name > 63) return -1;
115     if (fin == name) return -3;
116     if (outend - *outpos < fin - name + 2) return -2;
117     *((*outpos)++) = fin - name;
118     memcpy(*outpos, name, fin - name);
119     *outpos += fin - name;
120     if (!dot) break;
121     name = dot + 1;
122   }
123   if (offset < 0) {
124     // no reference
125     if (outend == *outpos) return -2;
126     *((*outpos)++) = 0;
127   } else {
128     if (outend - *outpos < 2) return -2;
129     *((*outpos)++) = (offset >> 8) | 0xC0;
130     *((*outpos)++) = offset & 0xFF;
131   }
132   return 0;
133 }
134
135 int static write_record(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_type typ, dns_class cls, int ttl) {
136   unsigned char *oldpos = *outpos;
137   int error = 0;
138   // name
139   int ret = write_name(outpos, outend, name, offset);
140   if (ret) { error = ret; goto error; }
141   if (outend - *outpos < 8) { error = -4; goto error; }
142   // type
143   *((*outpos)++) = typ >> 8; *((*outpos)++) = typ & 0xFF;
144   // class
145   *((*outpos)++) = cls >> 8; *((*outpos)++) = cls & 0xFF;
146   // ttl
147   *((*outpos)++) = (ttl >> 24) & 0xFF; *((*outpos)++) = (ttl >> 16) & 0xFF; *((*outpos)++) = (ttl >> 8) & 0xFF; *((*outpos)++) = ttl & 0xFF;
148   return 0;
149 error:
150   *outpos = oldpos;
151   return error;
152 }
153
154
155 int static write_record_a(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const addr_t *ip) {
156   if (ip->v != 4)
157      return -6;
158   unsigned char *oldpos = *outpos;
159   int error = 0;
160   int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl);
161   if (ret) return ret;
162   if (outend - *outpos < 6) { error = -5; goto error; }
163   // rdlength
164   *((*outpos)++) = 0; *((*outpos)++) = 4;
165   // rdata
166   for (int i=0; i<4; i++)
167     *((*outpos)++) = ip->data.v4[i];
168   return 0;
169 error:
170   *outpos = oldpos;
171   return error;
172 }
173
174 int static write_record_aaaa(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const addr_t *ip) {
175   if (ip->v != 6)
176      return -6;
177   unsigned char *oldpos = *outpos;
178   int error = 0;
179   int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl);
180   if (ret) return ret;
181   if (outend - *outpos < 6) { error = -5; goto error; }
182   // rdlength
183   *((*outpos)++) = 0; *((*outpos)++) = 16;
184   // rdata
185   for (int i=0; i<16; i++)
186     *((*outpos)++) = ip->data.v6[i];
187   return 0;
188 error:
189   *outpos = oldpos;
190   return error;
191 }
192
193 int static write_record_ns(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char *ns) {
194   unsigned char *oldpos = *outpos;
195   int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl);
196   if (ret) return ret;
197   int error = 0;
198   if (outend - *outpos < 2) { error = -5; goto error; }
199   (*outpos) += 2;
200   unsigned char *curpos = *outpos;
201   ret = write_name(outpos, outend, ns, -1);
202   if (ret) { error = ret; goto error; }
203   curpos[-2] = (*outpos - curpos) >> 8;
204   curpos[-1] = (*outpos - curpos) & 0xFF;
205   return 0;
206 error:
207   *outpos = oldpos;
208   return error;
209 }
210
211 int static write_record_soa(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char* mname, const char *rname,
212                      uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
213   unsigned char *oldpos = *outpos;
214   int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl);
215   if (ret) return ret;
216   int error = 0;
217   if (outend - *outpos < 2) { error = -5; goto error; }
218   (*outpos) += 2;
219   unsigned char *curpos = *outpos;
220   ret = write_name(outpos, outend, mname, -1);
221   if (ret) { error = ret; goto error; }
222   ret = write_name(outpos, outend, rname, -1);
223   if (ret) { error = ret; goto error; }
224   if (outend - *outpos < 20) { error = -5; goto error; }
225   *((*outpos)++) = (serial  >> 24) & 0xFF; *((*outpos)++) = (serial  >> 16) & 0xFF; *((*outpos)++) = (serial  >> 8) & 0xFF; *((*outpos)++) = serial  & 0xFF;
226   *((*outpos)++) = (refresh >> 24) & 0xFF; *((*outpos)++) = (refresh >> 16) & 0xFF; *((*outpos)++) = (refresh >> 8) & 0xFF; *((*outpos)++) = refresh & 0xFF;
227   *((*outpos)++) = (retry   >> 24) & 0xFF; *((*outpos)++) = (retry   >> 16) & 0xFF; *((*outpos)++) = (retry   >> 8) & 0xFF; *((*outpos)++) = retry   & 0xFF;
228   *((*outpos)++) = (expire  >> 24) & 0xFF; *((*outpos)++) = (expire  >> 16) & 0xFF; *((*outpos)++) = (expire  >> 8) & 0xFF; *((*outpos)++) = expire  & 0xFF;
229   *((*outpos)++) = (minimum >> 24) & 0xFF; *((*outpos)++) = (minimum >> 16) & 0xFF; *((*outpos)++) = (minimum >> 8) & 0xFF; *((*outpos)++) = minimum & 0xFF;
230   curpos[-2] = (*outpos - curpos) >> 8;
231   curpos[-1] = (*outpos - curpos) & 0xFF;
232   return 0;
233 error:
234   *outpos = oldpos;
235   return error;
236 }
237
238 ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insize, unsigned char* outbuf) {
239   int error = 0;
240   if (insize < 12) // DNS header
241     return -1;
242   // copy id
243   outbuf[0] = inbuf[0];
244   outbuf[1] = inbuf[1];
245   // copy flags;
246   outbuf[2] = inbuf[2];
247   outbuf[3] = inbuf[3];
248   // clear error
249   outbuf[3] &= ~15;
250   // check qr
251   if (inbuf[2] & 128) { /* printf("Got response?\n"); */ error = 1; goto error; }
252   // check opcode
253   if (((inbuf[2] & 120) >> 3) != 0) { /* printf("Opcode nonzero?\n"); */ error = 4; goto error; }
254   // check Z
255   if (((inbuf[3] & 112) >> 4) != 0) { /* printf("Z nonzero?\n"); */ error = 1; goto error; }
256   // unset TC
257   outbuf[2] &= ~2;
258   // unset RA
259   outbuf[3] &= ~128;
260   // check questions
261   int nquestion = (inbuf[4] << 8) + inbuf[5];
262   if (nquestion == 0) { /* printf("No questions?\n"); */ error = 0; goto error; }
263   if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ error = 4; goto error; }
264   const unsigned char *inpos = inbuf + 12;
265   const unsigned char *inend = inbuf + insize;
266   char name[256];
267   int offset = inpos - inbuf;
268   int ret = parse_name(&inpos, inend, inbuf, name, 256);
269   if (ret == -1) { error = 1; goto error; }
270   if (ret == -2) { error = 5; goto error; }
271   int namel = strlen(name), hostl = strlen(opt->host);
272   if (strcmp(name, opt->host) && (namel<hostl+2 || name[namel-hostl-1]!='.' || strcmp(name+namel-hostl,opt->host))) { error = 5; goto error; }
273   if (inend - inpos < 4) { error = 1; goto error; }
274   // copy question to output
275   memcpy(outbuf+12, inbuf+12, inpos+4 - (inbuf+12));
276   // set counts
277   outbuf[4] = 0;  outbuf[5] = 1;
278   outbuf[6] = 0;  outbuf[7] = 0;
279   outbuf[8] = 0;  outbuf[9] = 0;
280   outbuf[10] = 0; outbuf[11] = 0;
281   // set qr
282   outbuf[2] |= 128;
283   
284   int typ = (inpos[0] << 8) + inpos[1];
285   int cls = (inpos[2] << 8) + inpos[3];
286   inpos += 4;
287   
288   unsigned char *outpos = outbuf+(inpos-inbuf);
289   unsigned char *outend = outbuf + BUFLEN;
290   
291   // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls);
292   
293   // calculate size of authority section
294   
295   int auth_size = 0;
296   
297   if (!((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY))) {
298     // authority section will be necessary
299     unsigned char *oldpos = outpos;
300     write_record_ns(&oldpos, outend, "", offset, CLASS_IN, 0, opt->ns);
301     auth_size = oldpos - outpos;
302 //    printf("Authority section will claim %i bytes\n", auth_size);
303   }
304   
305   // Answer section
306
307   int have_ns = 0;
308
309   // NS records
310   if ((typ == TYPE_NS || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
311     int ret2 = write_record_ns(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns);
312 //    printf("wrote NS record: %i\n", ret2);
313     if (!ret2) { outbuf[7]++; have_ns++; }
314   }
315
316   // SOA records
317   if ((typ == TYPE_SOA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY) && opt->mbox) {
318     int ret2 = write_record_soa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->nsttl, opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, 604800);
319 //    printf("wrote SOA record: %i\n", ret2);
320     if (!ret2) { outbuf[7]++; }
321   }
322   
323   // A/AAAA records
324   if ((typ == TYPE_A || typ == TYPE_AAAA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
325     addr_t addr[32];
326     int naddr = opt->cb((void*)opt, addr, 32, typ == TYPE_A || typ == QTYPE_ANY, typ == TYPE_AAAA || typ == QTYPE_ANY);
327     int n = 0;
328     while (n < naddr) {
329       int ret = 1;
330       if (addr[n].v == 4)
331          ret = write_record_a(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
332       else if (addr[n].v == 6)
333          ret = write_record_aaaa(&outpos, outend - auth_size, "", offset, CLASS_IN, opt->datattl, &addr[n]);
334 //      printf("wrote A record: %i\n", ret);
335       if (!ret) {
336         n++;
337         outbuf[7]++;
338       } else
339         break;
340     }
341   }
342   
343   // Authority section
344   if (!have_ns) {
345     int ret2 = write_record_ns(&outpos, outend, "", offset, CLASS_IN, opt->nsttl, opt->ns);
346 //    printf("wrote NS record: %i\n", ret2);
347     if (!ret2) {
348       outbuf[9]++;
349     }
350   }
351   
352   // set AA
353   outbuf[2] |= 4;
354   
355   return outpos - outbuf;
356 error:
357   // set error
358   outbuf[3] |= error & 0xF;
359   // set counts
360   outbuf[4] = 0;  outbuf[5] = 0;
361   outbuf[6] = 0;  outbuf[7] = 0;
362   outbuf[8] = 0;  outbuf[9] = 0;
363   outbuf[10] = 0; outbuf[11] = 0;
364   return 12;
365 }
366
367 static int listenSocket = -1;
368
369 int dnsserver(dns_opt_t *opt) {
370   struct sockaddr_in si_other;
371   int senderSocket = -1;
372   senderSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
373   if (senderSocket == -1) 
374     return -3;
375
376   int replySocket;
377   if (listenSocket == -1) {
378     struct sockaddr_in si_me;
379     if ((listenSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
380       listenSocket = -1;
381       return -1;
382     }
383     replySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
384     if (replySocket == -1)
385     {
386       close(listenSocket);
387       return -1;
388     }
389     int sockopt = 1;
390     setsockopt(listenSocket, IPPROTO_IP, DSTADDR_SOCKOPT, &sockopt, sizeof sockopt);
391     memset((char *) &si_me, 0, sizeof(si_me));
392     si_me.sin_family = AF_INET;
393     si_me.sin_port = htons(opt->port);
394     si_me.sin_addr.s_addr = INADDR_ANY;
395     if (bind(listenSocket, (struct sockaddr*)&si_me, sizeof(si_me))==-1)
396       return -2;
397   }
398   
399   unsigned char inbuf[BUFLEN], outbuf[BUFLEN];
400   struct iovec iov[1] = {
401     {
402       .iov_base = inbuf,
403       .iov_len = sizeof(inbuf),
404     },
405   };
406   union control_data cmsg;
407   struct msghdr msg = {
408     .msg_name = &si_other,
409     .msg_namelen = sizeof(si_other),
410     .msg_iov = iov,
411     .msg_iovlen = 1,
412     .msg_control = &cmsg,
413     .msg_controllen = sizeof(cmsg),
414   };
415   for (; 1; ++(opt->nRequests))
416   {
417     ssize_t insize = recvmsg(listenSocket, &msg, 0);
418     unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr;
419 //    printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], addr[3], ntohs(si_other.sin_port), (int)insize);
420     if (insize <= 0)
421       continue;
422
423     ssize_t ret = dnshandle(opt, inbuf, insize, outbuf);
424     if (ret <= 0)
425       continue;
426
427     bool handled = false;
428     for (struct cmsghdr*hdr = CMSG_FIRSTHDR(&msg); hdr; hdr = CMSG_NXTHDR(&msg, hdr))
429     {
430       if (hdr->cmsg_level == IPPROTO_IP && hdr->cmsg_type == DSTADDR_SOCKOPT)
431       {
432         msg.msg_iov[0].iov_base = outbuf;
433         sendmsg(listenSocket, &msg, 0);
434         msg.msg_iov[0].iov_base = inbuf;
435         handled = true;
436       }
437     }
438     if (!handled)
439       sendto(listenSocket, outbuf, ret, 0, (struct sockaddr*)&si_other, sizeof(si_other));
440   }
441   return 0;
442 }