blob: 4f980dccd1d05e968aa3da94e0bcd2231159cd34 [file] [log] [blame]
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +00001/* vi: set sw=4 ts=4: */
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +00002/*
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
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +00008 *
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +00009 * 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
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000012 * 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
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000014 * need. [1] http://threading.2038bug.com/sheerdns/
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000015 *
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000016 * 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
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000020#include "libbb.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000021#include <syslog.h>
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000022
Denis Vlasenkoc12f5302006-10-06 09:49:47 +000023//#define DEBUG 1
Denis Vlasenko2c916522007-01-12 14:57:37 +000024#define DEBUG 0
Denis Vlasenkoc12f5302006-10-06 09:49:47 +000025
Rob Landleybc68cd12006-03-10 19:22:06 +000026enum {
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000027 /* Can tweak this */
28 DEFAULT_TTL = 120,
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000029
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000030/* Cannot get bigger packets than 512 per RFC1035.
31 * In practice this can be set considerably smaller:
32 * Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) +
33 * ttl(4B) + rlen(2B) + r (MAX_NAME_LEN = 21B) +
34 * 2*querystring (2 MAX_NAME_LEN = 42B), all together 90 Bytes
35 */
Denis Vlasenko081eb712008-03-17 09:02:21 +000036 MAX_PACK_LEN = 512,
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000037 IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"),
38 MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"),
Rob Landleybc68cd12006-03-10 19:22:06 +000039 REQ_A = 1,
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000040 REQ_PTR = 12,
Rob Landleybc68cd12006-03-10 19:22:06 +000041};
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000042
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000043/* the message from client and first part of response msg */
44struct dns_head {
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000045 uint16_t id;
46 uint16_t flags;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000047 uint16_t nquer;
48 uint16_t nansw;
49 uint16_t nauth;
50 uint16_t nadd;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000051};
52struct dns_prop {
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000053 uint16_t type;
54 uint16_t class;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000055};
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000056/* element of known name, ip address and reversed ip address */
57struct dns_entry {
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000058 struct dns_entry *next;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000059 uint32_t ip;
60 char rip[IP_STRING_LEN]; /* length decimal reversed IP */
61 char name[1];
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000062};
63
Denis Vlasenkoa19e6492009-03-11 14:40:00 +000064#define OPT_verbose (option_mask32)
Denis Vlasenkod3bac032007-03-24 12:13:04 +000065
66
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000067/*
Bernhard Reutner-Fischerc418d482006-05-31 10:19:51 +000068 * Insert length of substrings instead of dots
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000069 */
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000070static void undot(char *rip)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000071{
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000072 int i = 0;
73 int s = 0;
74
Denis Vlasenkoc12f5302006-10-06 09:49:47 +000075 while (rip[i])
76 i++;
77 for (--i; i >= 0; i--) {
78 if (rip[i] == '.') {
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000079 rip[i] = s;
80 s = 0;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000081 } else {
82 s++;
83 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000084 }
85}
86
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +000087/*
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000088 * Read hostname/IP records from file
89 */
Denis Vlasenkoa19e6492009-03-11 14:40:00 +000090static struct dns_entry *parse_conf_file(const char *fileconf)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000091{
Denis Vlasenko084266e2008-07-26 23:08:31 +000092 char *token[2];
Denis Vlasenkoa34f1ed2008-07-20 17:48:59 +000093 parser_t *parser;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000094 struct dns_entry *m, *conf_data;
95 struct dns_entry **nextp;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +000096
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +000097 conf_data = NULL;
98 nextp = &conf_data;
99
Denis Vlasenkoa34f1ed2008-07-20 17:48:59 +0000100 parser = config_open(fileconf);
Denis Vlasenko084266e2008-07-26 23:08:31 +0000101 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000102 struct in_addr ip;
103 uint32_t v32;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000104
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000105 if (inet_aton(token[1], &ip) == 0) {
106 bb_error_msg("error at line %u, skipping", parser->lineno);
107 continue;
108 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000109
Denis Vlasenko084266e2008-07-26 23:08:31 +0000110 if (OPT_verbose)
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000111 bb_error_msg("name:%s, ip:%s", token[0], token[1]);
Denis Vlasenkoa34f1ed2008-07-20 17:48:59 +0000112
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000113 /* sizeof(*m) includes 1 byte for m->name[0] */
114 m = xzalloc(sizeof(*m) + strlen(token[0]) + 1);
115 /*m->next = NULL;*/
116 *nextp = m;
117 nextp = &m->next;
118
119 m->name[0] = '.';
120 strcpy(m->name + 1, token[0]);
121 undot(m->name);
122 m->ip = ip.s_addr; /* in network order */
123 v32 = ntohl(m->ip);
124 /* inverted order */
125 sprintf(m->rip, ".%u.%u.%u.%u",
126 (uint8_t)(v32),
127 (uint8_t)(v32 >> 8),
128 (uint8_t)(v32 >> 16),
129 (v32 >> 24)
130 );
131 undot(m->rip);
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000132 }
Denis Vlasenko084266e2008-07-26 23:08:31 +0000133 config_close(parser);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000134 return conf_data;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000135}
136
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000137/*
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000138 * Look query up in dns records and return answer if found.
139 * qs is the query string.
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000140 */
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000141static int table_lookup(uint8_t *as, struct dns_entry *d, uint16_t type, uint8_t *qs)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000142{
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000143 while (d) {
144 unsigned len = d->name[0];
145 /* d->name[len] is the last (non NUL) char */
Denis Vlasenko91f20ab2007-01-20 01:47:44 +0000146#if DEBUG
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000147 char *p, *q;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000148 q = (char *)&(qs[1]);
149 p = &(d->name[1]);
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000150 fprintf(stderr, "%d/%d p:%s q:%s %d\n",
151 (int)strlen(p), len,
152 p, q, (int)strlen(q)
153 );
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000154#endif
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000155 if (type == htons(REQ_A)) {
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000156 /* search by host name */
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000157 if (len != 1 || d->name[1] != '*') {
158 if (strcasecmp(d->name, (char*)qs) != 0)
159 goto next;
160 }
161 move_to_unaligned32((uint32_t *)as, d->ip);
Denis Vlasenko91f20ab2007-01-20 01:47:44 +0000162#if DEBUG
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000163 fprintf(stderr, "OK as:%x\n", (int)d->ip);
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000164#endif
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000165 return 0;
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000166 }
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000167 /* search by IP-address */
168 if ((len != 1 || d->name[1] != '*')
169 /* assume (do not check) that qs ends in ".in-addr.arpa" */
170 && strncmp(d->rip, (char*)qs, strlen(d->rip)) == 0
171 ) {
172 strcpy((char *)as, d->name);
173#if DEBUG
174 fprintf(stderr, "OK as:%s\n", as);
175#endif
176 return 0;
177 }
178 next:
Denis Vlasenkoc12f5302006-10-06 09:49:47 +0000179 d = d->next;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000180 }
181
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000182 return -1;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000183}
184
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000185/*
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000186 * Decode message and generate answer
187 */
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000188/* RFC 1035
189...
190Whenever an octet represents a numeric quantity, the left most bit
191in the diagram is the high order or most significant bit.
192That is, the bit labeled 0 is the most significant bit.
193...
194
1954.1.1. Header section format
196 1 1 1 1 1 1
197 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
198 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
199 | ID |
200 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
201 |QR| OPCODE |AA|TC|RD|RA| Z | RCODE |
202 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
203 | QDCOUNT |
204 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
205 | ANCOUNT |
206 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
207 | NSCOUNT |
208 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
209 | ARCOUNT |
210 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
211ID 16 bit random identifier assigned by query.
212 Used to match query/response.
213QR message is a query (0), or a response (1).
214OPCODE 0 standard query (QUERY)
215 1 inverse query (IQUERY)
216 2 server status request (STATUS)
217AA Authoritative Answer - this bit is valid in responses,
218 and specifies that the responding name server is an
219 authority for the domain name in question section.
220 Note that the contents of the answer section may have
221 multiple owner names because of aliases. The AA bit
222 corresponds to the name which matches the query name, or
223 the first owner name in the answer section.
224TC TrunCation - specifies that this message was truncated.
225RD Recursion Desired - this bit may be set in a query and
226 is copied into the response. If RD is set, it directs
227 the name server to pursue the query recursively.
228 Recursive query support is optional.
229RA Recursion Available - this be is set or cleared in a
230 response, and denotes whether recursive query support is
231 available in the name server.
232Z Reserved for future use. Must be zero.
233RCODE Response code.
234 0 No error condition
235 1 Format error
236 2 Server failure - The name server was
237 unable to process this query due to a
238 problem with the name server.
239 3 Name Error - Meaningful only for
240 responses from an authoritative name
241 server, this code signifies that the
242 domain name referenced in the query does
243 not exist.
244 4 Not Implemented.
245 5 Refused.
246QDCOUNT number of entries in the question section.
247ANCOUNT number of resource records in the answer section.
248NSCOUNT number of name server resource records in the authority records section.
249ARCOUNT number of resource records in the additional records section.
250
2514.1.2. Question section format
252
253The section contains QDCOUNT (usually 1) entries, each of the following format:
254 1 1 1 1 1 1
255 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
256 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
257 / QNAME /
258 / /
259 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
260 | QTYPE |
261 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
262 | QCLASS |
263 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
264QNAME a domain name represented as a sequence of labels, where
265 each label consists of a length octet followed by that
266 number of octets. The domain name terminates with the
267 zero length octet for the null label of the root. Note
268 that this field may be an odd number of octets; no
269 padding is used.
270QTYPE a two octet type of the query.
271 1 a host address [REQ_A const]
272 2 an authoritative name server
273 3 a mail destination (Obsolete - use MX)
274 4 a mail forwarder (Obsolete - use MX)
275 5 the canonical name for an alias
276 6 marks the start of a zone of authority
277 7 a mailbox domain name (EXPERIMENTAL)
278 8 a mail group member (EXPERIMENTAL)
279 9 a mail rename domain name (EXPERIMENTAL)
280 10 a null RR (EXPERIMENTAL)
281 11 a well known service description
282 12 a domain name pointer [REQ_PTR const]
283 13 host information
284 14 mailbox or mail list information
285 15 mail exchange
286 16 text strings
287 0x1c IPv6?
288 252 a request for a transfer of an entire zone
289 253 a request for mailbox-related records (MB, MG or MR)
290 254 a request for mail agent RRs (Obsolete - see MX)
291 255 a request for all records
292QCLASS a two octet code that specifies the class of the query.
293 1 the Internet
294 (others are historic only)
295 255 any class
296
2974.1.3. Resource record format
298
299The answer, authority, and additional sections all share the same
300format: a variable number of resource records, where the number of
301records is specified in the corresponding count field in the header.
302Each resource record has the following format:
303 1 1 1 1 1 1
304 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
305 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
306 / /
307 / NAME /
308 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
309 | TYPE |
310 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
311 | CLASS |
312 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
313 | TTL |
314 | |
315 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
316 | RDLENGTH |
317 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
318 / RDATA /
319 / /
320 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
321NAME a domain name to which this resource record pertains.
322TYPE two octets containing one of the RR type codes. This
323 field specifies the meaning of the data in the RDATA
324 field.
325CLASS two octets which specify the class of the data in the
326 RDATA field.
327TTL a 32 bit unsigned integer that specifies the time
328 interval (in seconds) that the resource record may be
329 cached before it should be discarded. Zero values are
330 interpreted to mean that the RR can only be used for the
331 transaction in progress, and should not be cached.
332RDLENGTH an unsigned 16 bit integer that specifies the length in
333 octets of the RDATA field.
334RDATA a variable length string of octets that describes the
335 resource. The format of this information varies
336 according to the TYPE and CLASS of the resource record.
337 For example, if the TYPE is A and the CLASS is IN,
338 the RDATA field is a 4 octet ARPA Internet address.
339
3404.1.4. Message compression
341
342In order to reduce the size of messages, the domain system utilizes a
343compression scheme which eliminates the repetition of domain names in a
344message. In this scheme, an entire domain name or a list of labels at
345the end of a domain name is replaced with a pointer to a prior occurance
346of the same name.
347
348The pointer takes the form of a two octet sequence:
349 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
350 | 1 1| OFFSET |
351 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
352The first two bits are ones. This allows a pointer to be distinguished
353from a label, since the label must begin with two zero bits because
354labels are restricted to 63 octets or less. The OFFSET field specifies
355an offset from the start of the message (i.e., the first octet
356of the ID field in the domain header).
357A zero offset specifies the first byte of the ID field, etc.
358
359The compression scheme allows a domain name in a message to be
360represented as either:
361 - a sequence of labels ending in a zero octet
362 - a pointer
363 - a sequence of labels ending with a pointer
364 */
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000365static int process_packet(struct dns_entry *conf_data, uint32_t conf_ttl, uint8_t *buf)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000366{
Denis Vlasenko081eb712008-03-17 09:02:21 +0000367 uint8_t answstr[MAX_NAME_LEN + 1];
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000368 struct dns_head *head;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000369 struct dns_prop *unaligned_qprop;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000370 uint8_t *from, *answb;
371 uint16_t outr_rlen;
372 uint16_t outr_flags;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000373 uint16_t type;
374 uint16_t class;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000375 int querystr_len;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000376
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000377 answstr[0] = '\0';
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000378
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000379 head = (struct dns_head *)buf;
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000380 if (head->nquer == 0) {
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000381 bb_error_msg("packet has 0 queries, ignored");
Denis Vlasenkob5b45a92007-03-24 13:09:07 +0000382 return -1;
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000383 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000384
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000385 if (head->flags & htons(0x8000)) { /* QR bit */
386 bb_error_msg("response packet, ignored");
Denis Vlasenkob5b45a92007-03-24 13:09:07 +0000387 return -1;
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000388 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000389
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000390 /* start of query string */
391 from = (void *)(head + 1);
392 /* caller guarantees strlen is <= MAX_PACK_LEN */
393 querystr_len = strlen((char *)from) + 1;
394 /* may be unaligned! */
395 unaligned_qprop = (void *)(from + querystr_len);
396 /* where to append answer block */
397 answb = (void *)(unaligned_qprop + 1);
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000398
Denis Vlasenko081eb712008-03-17 09:02:21 +0000399 outr_rlen = 0;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000400 /* QR = 1 "response", RCODE = 4 "Not Implemented" */
401 outr_flags = htons(0x8000 | 4);
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000402
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000403 move_from_unaligned16(type, &unaligned_qprop->type);
404 if (type != htons(REQ_A) && type != htons(REQ_PTR)) {
405 /* we can't handle the query type */
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000406 goto empty_packet;
407 }
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000408 move_from_unaligned16(class, &unaligned_qprop->class);
409 if (class != htons(1)) { /* not class INET? */
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000410 goto empty_packet;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000411 }
412 /* OPCODE != 0 "standard query" ? */
413 if ((head->flags & htons(0x7800)) != 0) {
414 goto empty_packet;
415 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000416
Denis Vlasenko2c916522007-01-12 14:57:37 +0000417 bb_info_msg("%s", (char *)from);
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000418 if (table_lookup(answstr, conf_data, type, from) != 0) {
419 /* QR = 1 "response"
420 * AA = 1 "Authoritative Answer"
421 * RCODE = 3 "Name Error" */
422 outr_flags = htons(0x8000 | 0x0400 | 3);
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000423 goto empty_packet;
424 }
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000425 /* return an address */
426 outr_rlen = 4;
427 if (type == htons(REQ_PTR)) {
428 /* return a host name */
429 outr_rlen = strlen((char *)answstr) + 1;
430 }
431 /* QR = 1 "response",
432 * AA = 1 "Authoritative Answer",
433 * RCODE = 0 "success" */
434 outr_flags = htons(0x8000 | 0x0400 | 0);
435 /* we have one answer */
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000436 head->nansw = htons(1);
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000437 /* copy query block to answer block */
438 querystr_len += sizeof(unaligned_qprop);
Denis Vlasenko081eb712008-03-17 09:02:21 +0000439 memcpy(answb, from, querystr_len);
440 answb += querystr_len;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000441 /* append answer Resource Record */
442 move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl));
Denis Vlasenko081eb712008-03-17 09:02:21 +0000443 answb += 4;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000444 move_to_unaligned32((uint16_t *)answb, htons(outr_rlen));
Denis Vlasenko081eb712008-03-17 09:02:21 +0000445 answb += 2;
446 memcpy(answb, answstr, outr_rlen);
447 answb += outr_rlen;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000448
Denis Vlasenkoc12f5302006-10-06 09:49:47 +0000449 empty_packet:
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000450 head->flags |= outr_flags;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000451 head->nauth = head->nadd = 0;
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000452 head->nquer = htons(1); // why???
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000453
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000454 return answb - buf;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000455}
456
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000457int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000458int dnsd_main(int argc UNUSED_PARAM, char **argv)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000459{
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000460 const char *listen_interface = "0.0.0.0";
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000461 const char *fileconf = "/etc/dnsd.conf";
462 struct dns_entry *conf_data;
463 uint32_t conf_ttl = DEFAULT_TTL;
Denis Vlasenko2c916522007-01-12 14:57:37 +0000464 char *sttl, *sport;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000465 len_and_sockaddr *lsa, *from, *to;
466 unsigned lsa_size;
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000467 int udps, opts;
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000468 uint16_t port = 53;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000469 /* Paranoid sizing: querystring x2 + ttl + outr_rlen + answstr */
470 /* I'd rather see process_packet() fixed instead... */
471 uint8_t buf[MAX_PACK_LEN * 2 + 4 + 2 + (MAX_NAME_LEN+1)];
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000472
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000473 opts = getopt32(argv, "vi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport);
474 //if (opts & 0x1) // -v
475 //if (opts & 0x2) // -i
476 //if (opts & 0x4) // -c
477 if (opts & 0x8) // -t
478 conf_ttl = xatou_range(sttl, 1, 0xffffffff);
479 if (opts & 0x10) // -p
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000480 port = xatou_range(sport, 1, 0xffff);
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000481 if (opts & 0x20) { // -d
Denis Vlasenko5a142022007-03-26 13:20:54 +0000482 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
Denis Vlasenko5b27fbe2007-03-24 14:06:51 +0000483 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko2c916522007-01-12 14:57:37 +0000484 logmode = LOGMODE_SYSLOG;
485 }
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000486 /* Clear all except "verbose" bit */
487 option_mask32 &= 1;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000488
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000489 conf_data = parse_conf_file(fileconf);
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000490
Denis Vlasenko25591c32008-02-16 22:58:56 +0000491 bb_signals(0
492 /* why? + (1 << SIGPIPE) */
493 + (1 << SIGHUP)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000494#ifdef SIGTSTP
Denis Vlasenko25591c32008-02-16 22:58:56 +0000495 + (1 << SIGTSTP)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000496#endif
497#ifdef SIGURG
Denis Vlasenko25591c32008-02-16 22:58:56 +0000498 + (1 << SIGURG)
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000499#endif
Denis Vlasenko25591c32008-02-16 22:58:56 +0000500 , SIG_IGN);
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000501
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000502 lsa = xdotted2sockaddr(listen_interface, port);
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000503 udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
504 xbind(udps, &lsa->u.sa, lsa->len);
Denis Vlasenko081eb712008-03-17 09:02:21 +0000505 socket_want_pktinfo(udps); /* needed for recv_from_to to work */
506 lsa_size = LSA_LEN_SIZE + lsa->len;
507 from = xzalloc(lsa_size);
508 to = xzalloc(lsa_size);
509
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000510 {
511 char *p = xmalloc_sockaddr2dotted(&lsa->u.sa);
512 bb_info_msg("Accepting UDP packets on %s", p);
513 free(p);
514 }
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000515
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000516 while (1) {
"Vladimir N. Oleynik"2e5ee8e2006-01-25 14:40:24 +0000517 int r;
Denis Vlasenko081eb712008-03-17 09:02:21 +0000518 /* Try to get *DEST* address (to which of our addresses
519 * this query was directed), and reply from the same address.
520 * Or else we can exhibit usual UDP ugliness:
521 * [ip1.multihomed.ip2] <= query to ip1 <= peer
522 * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
523 memcpy(to, lsa, lsa_size);
524 r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
525 if (r < 12 || r > MAX_PACK_LEN) {
Denis Vlasenkoddbf3bf2009-04-12 04:09:09 +0000526 bb_error_msg("packet size %d, ignored", r);
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000527 continue;
Denis Vlasenko2c916522007-01-12 14:57:37 +0000528 }
Denis Vlasenko081eb712008-03-17 09:02:21 +0000529 if (OPT_verbose)
530 bb_info_msg("Got UDP packet");
531 buf[r] = '\0'; /* paranoia */
Denis Vlasenkoa19e6492009-03-11 14:40:00 +0000532 r = process_packet(conf_data, conf_ttl, buf);
Denis Vlasenkod3bac032007-03-24 12:13:04 +0000533 if (r <= 0)
534 continue;
Denis Vlasenkoe9b76e12008-05-22 17:41:01 +0000535 send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
Denis Vlasenko2c916522007-01-12 14:57:37 +0000536 }
Denis Vlasenkob5b45a92007-03-24 13:09:07 +0000537 return 0;
"Vladimir N. Oleynik"7b4aa6f2006-01-25 14:19:11 +0000538}