getopt_ulflags -> getopt32.
[platform/upstream/busybox.git] / networking / dnsd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini DNS server implementation for busybox
4  *
5  * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
6  * Copyright (C) 2005 Odd Arild Olsen (oao at fibula dot no)
7  * Copyright (C) 2003 Paul Sheer
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  *
11  * Odd Arild Olsen started out with the sheerdns [1] of Paul Sheer and rewrote
12  * it into a shape which I believe is both easier to understand and maintain.
13  * I also reused the input buffer for output and removed services he did not
14  * need.  [1] http://threading.2038bug.com/sheerdns/
15  *
16  * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
17  * the first porting of oao' scdns to busybox also.
18  */
19
20 #include "busybox.h"
21
22 static char *fileconf = "/etc/dnsd.conf";
23 #define LOCK_FILE       "/var/run/dnsd.lock"
24 #define LOG_FILE        "/var/log/dnsd.log"
25
26 enum {
27         MAX_HOST_LEN = 16,      // longest host name allowed is 15
28         IP_STRING_LEN = 18,     // .xxx.xxx.xxx.xxx\0
29
30 //must be strlen('.in-addr.arpa') larger than IP_STRING_LEN
31         MAX_NAME_LEN = (IP_STRING_LEN + 13),
32
33 /* Cannot get bigger packets than 512 per RFC1035
34    In practice this can be set considerably smaller:
35    Length of response packet is  header (12B) + 2*type(4B) + 2*class(4B) +
36    ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) +
37    2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte
38 */
39         MAX_PACK_LEN = 512 + 1,
40
41         DEFAULT_TTL = 30,       // increase this when not testing?
42
43         REQ_A = 1,
44         REQ_PTR = 12
45 };
46
47 struct dns_repl {               // resource record, add 0 or 1 to accepted dns_msg in resp
48         uint16_t rlen;
49         uint8_t *r;             // resource
50         uint16_t flags;
51 };
52
53 struct dns_head {               // the message from client and first part of response mag
54         uint16_t id;
55         uint16_t flags;
56         uint16_t nquer;         // accepts 0
57         uint16_t nansw;         // 1 in response
58         uint16_t nauth;         // 0
59         uint16_t nadd;          // 0
60 };
61 struct dns_prop {
62         uint16_t type;
63         uint16_t class;
64 };
65 struct dns_entry {              // element of known name, ip address and reversed ip address
66         struct dns_entry *next;
67         char ip[IP_STRING_LEN];         // dotted decimal IP
68         char rip[IP_STRING_LEN];        // length decimal reversed IP
69         char name[MAX_HOST_LEN];
70 };
71
72 static struct dns_entry *dnsentry = NULL;
73 static int daemonmode = 0;
74 static uint32_t ttl = DEFAULT_TTL;
75
76 /*
77  * Convert host name from C-string to dns length/string.
78  */
79 static void convname(char *a, uint8_t *q)
80 {
81         int i = (q[0] == '.') ? 0 : 1;
82         for(; i < MAX_HOST_LEN-1 && *q; i++, q++)
83                 a[i] = tolower(*q);
84         a[0] = i - 1;
85         a[i] = 0;
86 }
87
88 /*
89  * Insert length of substrings instead of dots
90  */
91 static void undot(uint8_t * rip)
92 {
93         int i = 0, s = 0;
94         while(rip[i]) i++;
95         for(--i; i >= 0; i--) {
96                 if(rip[i] == '.') {
97                         rip[i] = s;
98                         s = 0;
99                 } else s++;
100         }
101 }
102
103 /*
104  * Append message to log file
105  */
106 static void log_message(char *filename, char *message)
107 {
108         FILE *logfile;
109         if (!daemonmode)
110                 return;
111         logfile = fopen(filename, "a");
112         if (!logfile)
113                 return;
114         fprintf(logfile, "%s\n", message);
115         fclose(logfile);
116 }
117
118 /*
119  * Read one line of hostname/IP from file
120  * Returns 0 for each valid entry read, -1 at EOF
121  * Assumes all host names are lower case only
122  * Hostnames with more than one label is not handled correctly.
123  * Presently the dot is copied into name without
124  * converting to a length/string substring for that label.
125  */
126
127 static int getfileentry(FILE * fp, struct dns_entry *s, int verb)
128 {
129         unsigned int a,b,c,d;
130         char *r, *name;
131
132 restart:
133         if(!(r = bb_get_line_from_file(fp)))
134                 return -1;
135         while(*r == ' ' || *r == '\t') {
136                 r++;
137                 if(!*r || *r == '#' || *r == '\n')
138                         goto restart; /* skipping empty/blank and commented lines  */
139         }
140         name = r;
141         while(*r != ' ' && *r != '\t')
142                 r++;
143         *r++ = 0;
144         if(sscanf(r,"%u.%u.%u.%u",&a,&b,&c,&d) != 4)
145                         goto restart; /* skipping wrong lines */
146
147         sprintf(s->ip,"%u.%u.%u.%u",a,b,c,d);
148         sprintf(s->rip,".%u.%u.%u.%u",d,c,b,a);
149         undot((uint8_t*)s->rip);
150         convname(s->name,(uint8_t*)name);
151
152         if(verb)
153                 fprintf(stderr,"\tname:%s, ip:%s\n",&(s->name[1]),s->ip);
154
155         return 0; /* warningkiller */
156 }
157
158 /*
159  * Read hostname/IP records from file
160  */
161 static void dnsentryinit(int verb)
162 {
163         FILE *fp;
164         struct dns_entry *m, *prev;
165         prev = dnsentry = NULL;
166
167         fp = xfopen(fileconf, "r");
168
169         while (1) {
170                 m = xmalloc(sizeof(struct dns_entry));
171
172                 m->next = NULL;
173                 if (getfileentry(fp, m, verb))
174                         break;
175
176                 if (prev == NULL)
177                         dnsentry = m;
178                 else
179                         prev->next = m;
180                 prev = m;
181         }
182         fclose(fp);
183 }
184
185
186 /*
187  * Set up UDP socket
188  */
189 static int listen_socket(char *iface_addr, int listen_port)
190 {
191         struct sockaddr_in a;
192         char msg[100];
193         int s;
194         int yes = 1;
195         s = xsocket(PF_INET, SOCK_DGRAM, 0);
196 #ifdef SO_REUSEADDR
197         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0)
198                 bb_perror_msg_and_die("setsockopt() failed");
199 #endif
200         memset(&a, 0, sizeof(a));
201         a.sin_port = htons(listen_port);
202         a.sin_family = AF_INET;
203         if (!inet_aton(iface_addr, &a.sin_addr))
204                 bb_perror_msg_and_die("bad iface address");
205         xbind(s, (struct sockaddr *)&a, sizeof(a));
206         xlisten(s, 50);
207         sprintf(msg, "accepting UDP packets on addr:port %s:%d\n",
208                 iface_addr, (int)listen_port);
209         log_message(LOG_FILE, msg);
210         return s;
211 }
212
213 /*
214  * Look query up in dns records and return answer if found
215  * qs is the query string, first byte the string length
216  */
217 static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)
218 {
219         int i;
220         struct dns_entry *d=dnsentry;
221
222         do {
223 #ifdef DEBUG
224                 char *p,*q;
225                 q = (char *)&(qs[1]);
226                 p = &(d->name[1]);
227                 fprintf(stderr, "\ntest: %d <%s> <%s> %d", strlen(p), p, q, strlen(q));
228 #endif
229                 if (type == REQ_A) { /* search by host name */
230                         for(i = 1; i <= (int)(d->name[0]); i++)
231                                 if(tolower(qs[i]) != d->name[i])
232                                         continue;
233 #ifdef DEBUG
234                         fprintf(stderr, " OK");
235 #endif
236                         strcpy((char *)as, d->ip);
237 #ifdef DEBUG
238                         fprintf(stderr, " %s ", as);
239 #endif
240                                         return 0;
241                                 }
242                 else if (type == REQ_PTR) { /* search by IP-address */
243                         if (!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {
244                                 strcpy((char *)as, d->name);
245                                 return 0;
246                         }
247                 }
248         } while ((d = d->next) != NULL);
249         return -1;
250 }
251
252
253 /*
254  * Decode message and generate answer
255  */
256 #define eret(s) do { fprintf (stderr, "%s\n", s); return -1; } while (0)
257 static int process_packet(uint8_t * buf)
258 {
259         struct dns_head *head;
260         struct dns_prop *qprop;
261         struct dns_repl outr;
262         void *next, *from, *answb;
263
264         uint8_t answstr[MAX_NAME_LEN + 1];
265         int lookup_result, type, len, packet_len;
266         uint16_t flags;
267
268         answstr[0] = '\0';
269
270         head = (struct dns_head *)buf;
271         if (head->nquer == 0)
272                 eret("no queries");
273
274         if ((head->flags & 0x8000))
275                 eret("ignoring response packet");
276
277         from = (void *)&head[1];        //  start of query string
278         next = answb = from + strlen((char *)&head[1]) + 1 + sizeof(struct dns_prop);   // where to append answer block
279
280         outr.rlen = 0;                  // may change later
281         outr.r = NULL;
282         outr.flags = 0;
283
284         qprop = (struct dns_prop *)(answb - 4);
285         type = ntohs(qprop->type);
286
287         // only let REQ_A and REQ_PTR pass
288         if (!(type == REQ_A || type == REQ_PTR)) {
289                 goto empty_packet;      /* we can't handle the query type */
290         }
291
292         if (ntohs(qprop->class) != 1 /* class INET */ ) {
293                 outr.flags = 4; /* not supported */
294                 goto empty_packet;
295         }
296         /* we only support standard queries */
297
298         if ((ntohs(head->flags) & 0x7800) != 0)
299                 goto empty_packet;
300
301         // We have a standard query
302
303         log_message(LOG_FILE, (char *)head);
304         lookup_result = table_lookup(type, answstr, (uint8_t*)(&head[1]));
305         if (lookup_result != 0) {
306                 outr.flags = 3 | 0x0400;        //name do not exist and auth
307                 goto empty_packet;
308         }
309         if (type == REQ_A) {    // return an address
310                 struct in_addr a;
311                 if (!inet_aton((char*)answstr, &a)) {//dotted dec to long conv
312                         outr.flags = 1; /* Frmt err */
313                         goto empty_packet;
314                 }
315                 memcpy(answstr, &a.s_addr, 4);  // save before a disappears
316                 outr.rlen = 4;                  // uint32_t IP
317         }
318         else
319                 outr.rlen = strlen((char *)answstr) + 1;        // a host name
320         outr.r = answstr;                       // 32 bit ip or a host name
321         outr.flags |= 0x0400;                   /* authority-bit */
322         // we have an answer
323         head->nansw = htons(1);
324
325         // copy query block to answer block
326         len = answb - from;
327         memcpy(answb, from, len);
328         next += len;
329
330         // and append answer rr
331         *(uint32_t *) next = htonl(ttl);
332         next += 4;
333         *(uint16_t *) next = htons(outr.rlen);
334         next += 2;
335         memcpy(next, (void *)answstr, outr.rlen);
336         next += outr.rlen;
337
338       empty_packet:
339
340         flags = ntohs(head->flags);
341         // clear rcode and RA, set responsebit and our new flags
342         flags |= (outr.flags & 0xff80) | 0x8000;
343         head->flags = htons(flags);
344         head->nauth = head->nadd = htons(0);
345         head->nquer = htons(1);
346
347         packet_len = next - (void *)buf;
348         return packet_len;
349 }
350
351 /*
352  * Exit on signal
353  */
354 static void interrupt(int x)
355 {
356         unlink(LOCK_FILE);
357         write(2, "interrupt exiting\n", 18);
358         exit(2);
359 }
360
361 #define is_daemon()  (flags&16)
362 #define is_verbose() (flags&32)
363 //#define DEBUG 1
364
365 int dnsd_main(int argc, char **argv)
366 {
367         int udps;
368         uint16_t port = 53;
369         uint8_t buf[MAX_PACK_LEN];
370         unsigned long flags = 0;
371         char *listen_interface = "0.0.0.0";
372         char *sttl=NULL, *sport=NULL;
373
374         if(argc > 1)
375                 flags = getopt32(argc, argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);
376         if(sttl)
377                 if(!(ttl = atol(sttl)))
378                         bb_show_usage();
379         if(sport)
380                 if(!(port = atol(sport)))
381                         bb_show_usage();
382
383         if(is_verbose()) {
384                 fprintf(stderr,"listen_interface: %s\n", listen_interface);
385                 fprintf(stderr,"ttl: %d, port: %d\n", ttl, port);
386                 fprintf(stderr,"fileconf: %s\n", fileconf);
387         }
388
389         if(is_daemon())
390 #ifdef BB_NOMMU
391                 /* reexec for vfork() do continue parent */
392                 vfork_daemon_rexec(1, 0, argc, argv, "-d");
393 #else
394                 xdaemon(1, 0);
395 #endif
396
397         dnsentryinit(is_verbose());
398
399         signal(SIGINT, interrupt);
400         signal(SIGPIPE, SIG_IGN);
401         signal(SIGHUP, SIG_IGN);
402 #ifdef SIGTSTP
403         signal(SIGTSTP, SIG_IGN);
404 #endif
405 #ifdef SIGURG
406         signal(SIGURG, SIG_IGN);
407 #endif
408
409         udps = listen_socket(listen_interface, port);
410         if (udps < 0)
411                 exit(1);
412
413         while (1) {
414                 fd_set fdset;
415                 int r;
416
417                 FD_ZERO(&fdset);
418                 FD_SET(udps, &fdset);
419                 // Block until a message arrives
420                 if((r = select(udps + 1, &fdset, NULL, NULL, NULL)) < 0)
421                         bb_perror_msg_and_die("select error");
422                 else
423                 if(r == 0)
424                         bb_perror_msg_and_die("select spurious return");
425
426                 /* Can this test ever be false? */
427                 if (FD_ISSET(udps, &fdset)) {
428                         struct sockaddr_in from;
429                         int fromlen = sizeof(from);
430                         r = recvfrom(udps, buf, sizeof(buf), 0,
431                                      (struct sockaddr *)&from,
432                                      (void *)&fromlen);
433                         if(is_verbose())
434                                 fprintf(stderr, "\n--- Got UDP  ");
435                         log_message(LOG_FILE, "\n--- Got UDP  ");
436
437                         if (r < 12 || r > 512) {
438                                 bb_error_msg("invalid packet size");
439                                 continue;
440                         }
441                         if (r > 0) {
442                                 r = process_packet(buf);
443                                 if (r > 0)
444                                         sendto(udps, buf,
445                                                r, 0, (struct sockaddr *)&from,
446                                                fromlen);
447                         }
448                 }               // end if
449         }                       // end while
450         return 0;
451 }
452
453