Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/dnsd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 816 - (hide annotations) (download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 9893 byte(s)
-updated to busybox-1.13.4
1 niro 532 /* 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 niro 816 #include "libbb.h"
21     #include <syslog.h>
22 niro 532
23     //#define DEBUG 1
24     #define DEBUG 0
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 niro 816 MAX_PACK_LEN = 512,
40 niro 532
41     DEFAULT_TTL = 30, // increase this when not testing?
42    
43     REQ_A = 1,
44     REQ_PTR = 12
45     };
46    
47     struct dns_head { // the message from client and first part of response mag
48     uint16_t id;
49     uint16_t flags;
50     uint16_t nquer; // accepts 0
51     uint16_t nansw; // 1 in response
52     uint16_t nauth; // 0
53     uint16_t nadd; // 0
54     };
55     struct dns_prop {
56     uint16_t type;
57     uint16_t class;
58     };
59     struct dns_entry { // element of known name, ip address and reversed ip address
60     struct dns_entry *next;
61     char ip[IP_STRING_LEN]; // dotted decimal IP
62     char rip[IP_STRING_LEN]; // length decimal reversed IP
63     char name[MAX_HOST_LEN];
64     };
65    
66 niro 816 static struct dns_entry *dnsentry;
67 niro 532 static uint32_t ttl = DEFAULT_TTL;
68    
69 niro 816 static const char *fileconf = "/etc/dnsd.conf";
70    
71     // Must match getopt32 call
72     #define OPT_daemon (option_mask32 & 0x10)
73     #define OPT_verbose (option_mask32 & 0x20)
74    
75    
76 niro 532 /*
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])
95     i++;
96     for (--i; i >= 0; i--) {
97     if (rip[i] == '.') {
98     rip[i] = s;
99     s = 0;
100     } else s++;
101     }
102     }
103    
104     /*
105     * Read hostname/IP records from file
106     */
107     static void dnsentryinit(void)
108     {
109 niro 816 char *token[2];
110     parser_t *parser;
111 niro 532 struct dns_entry *m, *prev;
112 niro 816
113 niro 532 prev = dnsentry = NULL;
114 niro 816 parser = config_open(fileconf);
115     while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
116     unsigned a, b, c, d;
117     /*
118     * Assumes all host names are lower case only
119     * Hostnames with more than one label are not handled correctly.
120     * Presently the dot is copied into name without
121     * converting to a length/string substring for that label.
122     */
123     // if (!token[1] || sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
124     if (sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
125     continue;
126 niro 532
127 niro 816 m = xzalloc(sizeof(*m));
128     /*m->next = NULL;*/
129     sprintf(m->ip, ".%u.%u.%u.%u"+1, a, b, c, d);
130     sprintf(m->rip, ".%u.%u.%u.%u", d, c, b, a);
131     undot((uint8_t*)m->rip);
132     convname(m->name, (uint8_t*)token[0]);
133 niro 532
134 niro 816 if (OPT_verbose)
135     fprintf(stderr, "\tname:%s, ip:%s\n", &(m->name[1]), m->ip);
136 niro 532
137     if (prev == NULL)
138     dnsentry = m;
139     else
140     prev->next = m;
141     prev = m;
142     }
143 niro 816 config_close(parser);
144 niro 532 }
145    
146     /*
147     * Look query up in dns records and return answer if found
148     * qs is the query string, first byte the string length
149     */
150     static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs)
151     {
152     int i;
153 niro 816 struct dns_entry *d = dnsentry;
154 niro 532
155     do {
156     #if DEBUG
157     char *p,*q;
158     q = (char *)&(qs[1]);
159     p = &(d->name[1]);
160     fprintf(stderr, "\n%s: %d/%d p:%s q:%s %d",
161     __FUNCTION__, (int)strlen(p), (int)(d->name[0]),
162     p, q, (int)strlen(q));
163     #endif
164     if (type == REQ_A) { /* search by host name */
165     for (i = 1; i <= (int)(d->name[0]); i++)
166     if (tolower(qs[i]) != d->name[i])
167     break;
168 niro 816 if (i > (int)(d->name[0]) ||
169     (d->name[0] == 1 && d->name[1] == '*')) {
170 niro 532 strcpy((char *)as, d->ip);
171     #if DEBUG
172 niro 816 fprintf(stderr, " OK as:%s\n", as);
173 niro 532 #endif
174 niro 816 return 0;
175 niro 532 }
176 niro 816 } else if (type == REQ_PTR) { /* search by IP-address */
177     if ((d->name[0] != 1 || d->name[1] != '*') &&
178     !strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) {
179 niro 532 strcpy((char *)as, d->name);
180     return 0;
181     }
182     }
183     d = d->next;
184     } while (d);
185     return -1;
186     }
187    
188     /*
189     * Decode message and generate answer
190     */
191 niro 816 static int process_packet(uint8_t *buf)
192 niro 532 {
193 niro 816 uint8_t answstr[MAX_NAME_LEN + 1];
194 niro 532 struct dns_head *head;
195     struct dns_prop *qprop;
196 niro 816 uint8_t *from, *answb;
197     uint16_t outr_rlen;
198     uint16_t outr_flags;
199 niro 532 uint16_t flags;
200 niro 816 int lookup_result, type, packet_len;
201     int querystr_len;
202 niro 532
203     answstr[0] = '\0';
204    
205     head = (struct dns_head *)buf;
206 niro 816 if (head->nquer == 0) {
207     bb_error_msg("no queries");
208     return -1;
209     }
210 niro 532
211 niro 816 if (head->flags & 0x8000) {
212     bb_error_msg("ignoring response packet");
213     return -1;
214     }
215 niro 532
216     from = (void *)&head[1]; // start of query string
217 niro 816 //FIXME: strlen of untrusted data??!
218     querystr_len = strlen((char *)from) + 1 + sizeof(struct dns_prop);
219     answb = from + querystr_len; // where to append answer block
220 niro 532
221 niro 816 outr_rlen = 0;
222     outr_flags = 0;
223 niro 532
224     qprop = (struct dns_prop *)(answb - 4);
225     type = ntohs(qprop->type);
226    
227     // only let REQ_A and REQ_PTR pass
228     if (!(type == REQ_A || type == REQ_PTR)) {
229     goto empty_packet; /* we can't handle the query type */
230     }
231    
232     if (ntohs(qprop->class) != 1 /* class INET */ ) {
233 niro 816 outr_flags = 4; /* not supported */
234 niro 532 goto empty_packet;
235     }
236     /* we only support standard queries */
237    
238     if ((ntohs(head->flags) & 0x7800) != 0)
239     goto empty_packet;
240    
241     // We have a standard query
242     bb_info_msg("%s", (char *)from);
243 niro 816 lookup_result = table_lookup(type, answstr, from);
244 niro 532 if (lookup_result != 0) {
245 niro 816 outr_flags = 3 | 0x0400; // name do not exist and auth
246 niro 532 goto empty_packet;
247     }
248     if (type == REQ_A) { // return an address
249 niro 816 struct in_addr a; // NB! its "struct { unsigned __long__ s_addr; }"
250     uint32_t v32;
251     if (!inet_aton((char*)answstr, &a)) { //dotted dec to long conv
252     outr_flags = 1; /* Frmt err */
253 niro 532 goto empty_packet;
254     }
255 niro 816 v32 = a.s_addr; /* in case long != int */
256     memcpy(answstr, &v32, 4);
257     outr_rlen = 4; // uint32_t IP
258     } else
259     outr_rlen = strlen((char *)answstr) + 1; // a host name
260     outr_flags |= 0x0400; /* authority-bit */
261 niro 532 // we have an answer
262     head->nansw = htons(1);
263    
264     // copy query block to answer block
265 niro 816 memcpy(answb, from, querystr_len);
266     answb += querystr_len;
267 niro 532
268     // and append answer rr
269 niro 816 // FIXME: unaligned accesses??
270     *(uint32_t *) answb = htonl(ttl);
271     answb += 4;
272     *(uint16_t *) answb = htons(outr_rlen);
273     answb += 2;
274     memcpy(answb, answstr, outr_rlen);
275     answb += outr_rlen;
276 niro 532
277     empty_packet:
278    
279     flags = ntohs(head->flags);
280     // clear rcode and RA, set responsebit and our new flags
281 niro 816 flags |= (outr_flags & 0xff80) | 0x8000;
282 niro 532 head->flags = htons(flags);
283 niro 816 head->nauth = head->nadd = 0;
284 niro 532 head->nquer = htons(1);
285    
286 niro 816 packet_len = answb - buf;
287 niro 532 return packet_len;
288     }
289    
290     /*
291     * Exit on signal
292     */
293 niro 816 static void interrupt(int sig)
294 niro 532 {
295 niro 816 /* unlink("/var/run/dnsd.lock"); */
296 niro 532 bb_error_msg("interrupt, exiting\n");
297 niro 816 kill_myself_with_sig(sig);
298 niro 532 }
299    
300 niro 816 int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
301     int dnsd_main(int argc UNUSED_PARAM, char **argv)
302 niro 532 {
303 niro 816 const char *listen_interface = "0.0.0.0";
304 niro 532 char *sttl, *sport;
305 niro 816 len_and_sockaddr *lsa, *from, *to;
306     unsigned lsa_size;
307 niro 532 int udps;
308     uint16_t port = 53;
309 niro 816 /* Paranoid sizing: querystring x2 + ttl + outr_rlen + answstr */
310     /* I'd rather see process_packet() fixed instead... */
311     uint8_t buf[MAX_PACK_LEN * 2 + 4 + 2 + (MAX_NAME_LEN+1)];
312 niro 532
313 niro 816 getopt32(argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport);
314 niro 532 //if (option_mask32 & 0x1) // -i
315     //if (option_mask32 & 0x2) // -c
316     if (option_mask32 & 0x4) // -t
317     ttl = xatou_range(sttl, 1, 0xffffffff);
318     if (option_mask32 & 0x8) // -p
319 niro 816 port = xatou_range(sport, 1, 0xffff);
320 niro 532
321     if (OPT_verbose) {
322     bb_info_msg("listen_interface: %s", listen_interface);
323     bb_info_msg("ttl: %d, port: %d", ttl, port);
324     bb_info_msg("fileconf: %s", fileconf);
325     }
326    
327     if (OPT_daemon) {
328 niro 816 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
329     openlog(applet_name, LOG_PID, LOG_DAEMON);
330 niro 532 logmode = LOGMODE_SYSLOG;
331     }
332    
333     dnsentryinit();
334    
335     signal(SIGINT, interrupt);
336 niro 816 bb_signals(0
337     /* why? + (1 << SIGPIPE) */
338     + (1 << SIGHUP)
339 niro 532 #ifdef SIGTSTP
340 niro 816 + (1 << SIGTSTP)
341 niro 532 #endif
342     #ifdef SIGURG
343 niro 816 + (1 << SIGURG)
344 niro 532 #endif
345 niro 816 , SIG_IGN);
346 niro 532
347 niro 816 lsa = xdotted2sockaddr(listen_interface, port);
348     udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
349     xbind(udps, &lsa->u.sa, lsa->len);
350     socket_want_pktinfo(udps); /* needed for recv_from_to to work */
351     lsa_size = LSA_LEN_SIZE + lsa->len;
352     from = xzalloc(lsa_size);
353     to = xzalloc(lsa_size);
354    
355 niro 532 bb_info_msg("Accepting UDP packets on %s",
356 niro 816 xmalloc_sockaddr2dotted(&lsa->u.sa));
357 niro 532
358     while (1) {
359     int r;
360 niro 816 /* Try to get *DEST* address (to which of our addresses
361     * this query was directed), and reply from the same address.
362     * Or else we can exhibit usual UDP ugliness:
363     * [ip1.multihomed.ip2] <= query to ip1 <= peer
364     * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
365     memcpy(to, lsa, lsa_size);
366     r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
367     if (r < 12 || r > MAX_PACK_LEN) {
368     bb_error_msg("invalid packet size");
369     continue;
370 niro 532 }
371 niro 816 if (OPT_verbose)
372     bb_info_msg("Got UDP packet");
373     buf[r] = '\0'; /* paranoia */
374     r = process_packet(buf);
375     if (r <= 0)
376     continue;
377     send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
378 niro 532 }
379 niro 816 return 0;
380 niro 532 }