24 |
#define DEBUG 0 |
#define DEBUG 0 |
25 |
|
|
26 |
enum { |
enum { |
27 |
MAX_HOST_LEN = 16, // longest host name allowed is 15 |
/* can tweak this */ |
28 |
IP_STRING_LEN = 18, // .xxx.xxx.xxx.xxx\0 |
DEFAULT_TTL = 120, |
29 |
|
|
30 |
//must be strlen('.in-addr.arpa') larger than IP_STRING_LEN |
/* cannot get bigger packets than 512 per RFC1035. */ |
|
MAX_NAME_LEN = (IP_STRING_LEN + 13), |
|
|
|
|
|
/* Cannot get bigger packets than 512 per RFC1035 |
|
|
In practice this can be set considerably smaller: |
|
|
Length of response packet is header (12B) + 2*type(4B) + 2*class(4B) + |
|
|
ttl(4B) + rlen(2B) + r (MAX_NAME_LEN =21B) + |
|
|
2*querystring (2 MAX_NAME_LEN= 42B), all together 90 Byte |
|
|
*/ |
|
31 |
MAX_PACK_LEN = 512, |
MAX_PACK_LEN = 512, |
32 |
|
IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"), |
33 |
DEFAULT_TTL = 30, // increase this when not testing? |
MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"), |
|
|
|
34 |
REQ_A = 1, |
REQ_A = 1, |
35 |
REQ_PTR = 12 |
REQ_PTR = 12, |
36 |
}; |
}; |
37 |
|
|
38 |
struct dns_head { // the message from client and first part of response mag |
/* the message from client and first part of response msg */ |
39 |
|
struct dns_head { |
40 |
uint16_t id; |
uint16_t id; |
41 |
uint16_t flags; |
uint16_t flags; |
42 |
uint16_t nquer; // accepts 0 |
uint16_t nquer; |
43 |
uint16_t nansw; // 1 in response |
uint16_t nansw; |
44 |
uint16_t nauth; // 0 |
uint16_t nauth; |
45 |
uint16_t nadd; // 0 |
uint16_t nadd; |
46 |
}; |
}; |
47 |
struct dns_prop { |
struct dns_prop { |
48 |
uint16_t type; |
uint16_t type; |
49 |
uint16_t class; |
uint16_t class; |
50 |
}; |
}; |
51 |
struct dns_entry { // element of known name, ip address and reversed ip address |
/* element of known name, ip address and reversed ip address */ |
52 |
|
struct dns_entry { |
53 |
struct dns_entry *next; |
struct dns_entry *next; |
54 |
char ip[IP_STRING_LEN]; // dotted decimal IP |
uint32_t ip; |
55 |
char rip[IP_STRING_LEN]; // length decimal reversed IP |
char rip[IP_STRING_LEN]; /* length decimal reversed IP */ |
56 |
char name[MAX_HOST_LEN]; |
char name[1]; |
57 |
}; |
}; |
58 |
|
|
59 |
static struct dns_entry *dnsentry; |
#define OPT_verbose (option_mask32) |
|
static uint32_t ttl = DEFAULT_TTL; |
|
|
|
|
|
static const char *fileconf = "/etc/dnsd.conf"; |
|
|
|
|
|
// Must match getopt32 call |
|
|
#define OPT_daemon (option_mask32 & 0x10) |
|
|
#define OPT_verbose (option_mask32 & 0x20) |
|
60 |
|
|
61 |
|
|
62 |
/* |
/* |
|
* Convert host name from C-string to dns length/string. |
|
|
*/ |
|
|
static void convname(char *a, uint8_t *q) |
|
|
{ |
|
|
int i = (q[0] == '.') ? 0 : 1; |
|
|
for (; i < MAX_HOST_LEN-1 && *q; i++, q++) |
|
|
a[i] = tolower(*q); |
|
|
a[0] = i - 1; |
|
|
a[i] = 0; |
|
|
} |
|
|
|
|
|
/* |
|
63 |
* Insert length of substrings instead of dots |
* Insert length of substrings instead of dots |
64 |
*/ |
*/ |
65 |
static void undot(uint8_t * rip) |
static void undot(char *rip) |
66 |
{ |
{ |
67 |
int i = 0, s = 0; |
int i = 0; |
68 |
|
int s = 0; |
69 |
|
|
70 |
while (rip[i]) |
while (rip[i]) |
71 |
i++; |
i++; |
72 |
for (--i; i >= 0; i--) { |
for (--i; i >= 0; i--) { |
73 |
if (rip[i] == '.') { |
if (rip[i] == '.') { |
74 |
rip[i] = s; |
rip[i] = s; |
75 |
s = 0; |
s = 0; |
76 |
} else s++; |
} else { |
77 |
|
s++; |
78 |
|
} |
79 |
} |
} |
80 |
} |
} |
81 |
|
|
82 |
/* |
/* |
83 |
* Read hostname/IP records from file |
* Read hostname/IP records from file |
84 |
*/ |
*/ |
85 |
static void dnsentryinit(void) |
static struct dns_entry *parse_conf_file(const char *fileconf) |
86 |
{ |
{ |
87 |
char *token[2]; |
char *token[2]; |
88 |
parser_t *parser; |
parser_t *parser; |
89 |
struct dns_entry *m, *prev; |
struct dns_entry *m, *conf_data; |
90 |
|
struct dns_entry **nextp; |
91 |
|
|
92 |
|
conf_data = NULL; |
93 |
|
nextp = &conf_data; |
94 |
|
|
|
prev = dnsentry = NULL; |
|
95 |
parser = config_open(fileconf); |
parser = config_open(fileconf); |
96 |
while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { |
while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { |
97 |
unsigned a, b, c, d; |
struct in_addr ip; |
98 |
/* |
uint32_t v32; |
|
* Assumes all host names are lower case only |
|
|
* Hostnames with more than one label are not handled correctly. |
|
|
* Presently the dot is copied into name without |
|
|
* converting to a length/string substring for that label. |
|
|
*/ |
|
|
// if (!token[1] || sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4) |
|
|
if (sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4) |
|
|
continue; |
|
99 |
|
|
100 |
m = xzalloc(sizeof(*m)); |
if (inet_aton(token[1], &ip) == 0) { |
101 |
/*m->next = NULL;*/ |
bb_error_msg("error at line %u, skipping", parser->lineno); |
102 |
sprintf(m->ip, ".%u.%u.%u.%u"+1, a, b, c, d); |
continue; |
103 |
sprintf(m->rip, ".%u.%u.%u.%u", d, c, b, a); |
} |
|
undot((uint8_t*)m->rip); |
|
|
convname(m->name, (uint8_t*)token[0]); |
|
104 |
|
|
105 |
if (OPT_verbose) |
if (OPT_verbose) |
106 |
fprintf(stderr, "\tname:%s, ip:%s\n", &(m->name[1]), m->ip); |
bb_error_msg("name:%s, ip:%s", token[0], token[1]); |
107 |
|
|
108 |
|
/* sizeof(*m) includes 1 byte for m->name[0] */ |
109 |
|
m = xzalloc(sizeof(*m) + strlen(token[0]) + 1); |
110 |
|
/*m->next = NULL;*/ |
111 |
|
*nextp = m; |
112 |
|
nextp = &m->next; |
113 |
|
|
114 |
if (prev == NULL) |
m->name[0] = '.'; |
115 |
dnsentry = m; |
strcpy(m->name + 1, token[0]); |
116 |
else |
undot(m->name); |
117 |
prev->next = m; |
m->ip = ip.s_addr; /* in network order */ |
118 |
prev = m; |
v32 = ntohl(m->ip); |
119 |
|
/* inverted order */ |
120 |
|
sprintf(m->rip, ".%u.%u.%u.%u", |
121 |
|
(uint8_t)(v32), |
122 |
|
(uint8_t)(v32 >> 8), |
123 |
|
(uint8_t)(v32 >> 16), |
124 |
|
(v32 >> 24) |
125 |
|
); |
126 |
|
undot(m->rip); |
127 |
} |
} |
128 |
config_close(parser); |
config_close(parser); |
129 |
|
return conf_data; |
130 |
} |
} |
131 |
|
|
132 |
/* |
/* |
133 |
* Look query up in dns records and return answer if found |
* Look query up in dns records and return answer if found. |
|
* qs is the query string, first byte the string length |
|
134 |
*/ |
*/ |
135 |
static int table_lookup(uint16_t type, uint8_t * as, uint8_t * qs) |
static char *table_lookup(struct dns_entry *d, |
136 |
|
uint16_t type, |
137 |
|
char* query_string) |
138 |
{ |
{ |
139 |
int i; |
while (d) { |
140 |
struct dns_entry *d = dnsentry; |
unsigned len = d->name[0]; |
141 |
|
/* d->name[len] is the last (non NUL) char */ |
|
do { |
|
142 |
#if DEBUG |
#if DEBUG |
143 |
char *p,*q; |
char *p, *q; |
144 |
q = (char *)&(qs[1]); |
q = query_string + 1; |
145 |
p = &(d->name[1]); |
p = d->name + 1; |
146 |
fprintf(stderr, "\n%s: %d/%d p:%s q:%s %d", |
fprintf(stderr, "%d/%d p:%s q:%s %d\n", |
147 |
__FUNCTION__, (int)strlen(p), (int)(d->name[0]), |
(int)strlen(p), len, |
148 |
p, q, (int)strlen(q)); |
p, q, (int)strlen(q) |
149 |
|
); |
150 |
#endif |
#endif |
151 |
if (type == REQ_A) { /* search by host name */ |
if (type == htons(REQ_A)) { |
152 |
for (i = 1; i <= (int)(d->name[0]); i++) |
/* search by host name */ |
153 |
if (tolower(qs[i]) != d->name[i]) |
if (len != 1 || d->name[1] != '*') { |
154 |
break; |
/* we are lax, hope no name component is ever >64 so that length |
155 |
if (i > (int)(d->name[0]) || |
* (which will be represented as 'A','B'...) matches a lowercase letter. |
156 |
(d->name[0] == 1 && d->name[1] == '*')) { |
* Actually, I think false matches are hard to construct. |
157 |
strcpy((char *)as, d->ip); |
* Example. |
158 |
|
* [31] len is represented as '1', [65] as 'A', [65+32] as 'a'. |
159 |
|
* [65] <65 same chars>[31]<31 same chars>NUL |
160 |
|
* [65+32]<65 same chars>1 <31 same chars>NUL |
161 |
|
* This example seems to be the minimal case when false match occurs. |
162 |
|
*/ |
163 |
|
if (strcasecmp(d->name, query_string) != 0) |
164 |
|
goto next; |
165 |
|
} |
166 |
|
return (char *)&d->ip; |
167 |
#if DEBUG |
#if DEBUG |
168 |
fprintf(stderr, " OK as:%s\n", as); |
fprintf(stderr, "Found IP:%x\n", (int)d->ip); |
169 |
#endif |
#endif |
170 |
return 0; |
return 0; |
|
} |
|
|
} else if (type == REQ_PTR) { /* search by IP-address */ |
|
|
if ((d->name[0] != 1 || d->name[1] != '*') && |
|
|
!strncmp((char*)&d->rip[1], (char*)&qs[1], strlen(d->rip)-1)) { |
|
|
strcpy((char *)as, d->name); |
|
|
return 0; |
|
|
} |
|
171 |
} |
} |
172 |
|
/* search by IP-address */ |
173 |
|
if ((len != 1 || d->name[1] != '*') |
174 |
|
/* we assume (do not check) that query_string |
175 |
|
* ends in ".in-addr.arpa" */ |
176 |
|
&& strncmp(d->rip, query_string, strlen(d->rip)) == 0 |
177 |
|
) { |
178 |
|
#if DEBUG |
179 |
|
fprintf(stderr, "Found name:%s\n", d->name); |
180 |
|
#endif |
181 |
|
return d->name; |
182 |
|
} |
183 |
|
next: |
184 |
d = d->next; |
d = d->next; |
185 |
} while (d); |
} |
186 |
return -1; |
|
187 |
|
return NULL; |
188 |
} |
} |
189 |
|
|
190 |
/* |
/* |
191 |
* Decode message and generate answer |
* Decode message and generate answer |
192 |
*/ |
*/ |
193 |
static int process_packet(uint8_t *buf) |
/* RFC 1035 |
194 |
|
... |
195 |
|
Whenever an octet represents a numeric quantity, the left most bit |
196 |
|
in the diagram is the high order or most significant bit. |
197 |
|
That is, the bit labeled 0 is the most significant bit. |
198 |
|
... |
199 |
|
|
200 |
|
4.1.1. Header section format |
201 |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
202 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
203 |
|
| ID | |
204 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
205 |
|
|QR| OPCODE |AA|TC|RD|RA| 0 0 0| RCODE | |
206 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
207 |
|
| QDCOUNT | |
208 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
209 |
|
| ANCOUNT | |
210 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
211 |
|
| NSCOUNT | |
212 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
213 |
|
| ARCOUNT | |
214 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
215 |
|
ID 16 bit random identifier assigned by querying peer. |
216 |
|
Used to match query/response. |
217 |
|
QR message is a query (0), or a response (1). |
218 |
|
OPCODE 0 standard query (QUERY) |
219 |
|
1 inverse query (IQUERY) |
220 |
|
2 server status request (STATUS) |
221 |
|
AA Authoritative Answer - this bit is valid in responses. |
222 |
|
Responding name server is an authority for the domain name |
223 |
|
in question section. Answer section may have multiple owner names |
224 |
|
because of aliases. The AA bit corresponds to the name which matches |
225 |
|
the query name, or the first owner name in the answer section. |
226 |
|
TC TrunCation - this message was truncated. |
227 |
|
RD Recursion Desired - this bit may be set in a query and |
228 |
|
is copied into the response. If RD is set, it directs |
229 |
|
the name server to pursue the query recursively. |
230 |
|
Recursive query support is optional. |
231 |
|
RA Recursion Available - this be is set or cleared in a |
232 |
|
response, and denotes whether recursive query support is |
233 |
|
available in the name server. |
234 |
|
RCODE Response code. |
235 |
|
0 No error condition |
236 |
|
1 Format error |
237 |
|
2 Server failure - server was unable to process the query |
238 |
|
due to a problem with the name server. |
239 |
|
3 Name Error - meaningful only for responses from |
240 |
|
an authoritative name server. The referenced domain name |
241 |
|
does not exist. |
242 |
|
4 Not Implemented. |
243 |
|
5 Refused. |
244 |
|
QDCOUNT number of entries in the question section. |
245 |
|
ANCOUNT number of records in the answer section. |
246 |
|
NSCOUNT number of records in the authority records section. |
247 |
|
ARCOUNT number of records in the additional records section. |
248 |
|
|
249 |
|
4.1.2. Question section format |
250 |
|
|
251 |
|
The section contains QDCOUNT (usually 1) entries, each of this format: |
252 |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
253 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
254 |
|
/ QNAME / |
255 |
|
/ / |
256 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
257 |
|
| QTYPE | |
258 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
259 |
|
| QCLASS | |
260 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
261 |
|
QNAME a domain name represented as a sequence of labels, where |
262 |
|
each label consists of a length octet followed by that |
263 |
|
number of octets. The domain name terminates with the |
264 |
|
zero length octet for the null label of the root. Note |
265 |
|
that this field may be an odd number of octets; no |
266 |
|
padding is used. |
267 |
|
QTYPE a two octet type of the query. |
268 |
|
1 a host address [REQ_A const] |
269 |
|
2 an authoritative name server |
270 |
|
3 a mail destination (Obsolete - use MX) |
271 |
|
4 a mail forwarder (Obsolete - use MX) |
272 |
|
5 the canonical name for an alias |
273 |
|
6 marks the start of a zone of authority |
274 |
|
7 a mailbox domain name (EXPERIMENTAL) |
275 |
|
8 a mail group member (EXPERIMENTAL) |
276 |
|
9 a mail rename domain name (EXPERIMENTAL) |
277 |
|
10 a null RR (EXPERIMENTAL) |
278 |
|
11 a well known service description |
279 |
|
12 a domain name pointer [REQ_PTR const] |
280 |
|
13 host information |
281 |
|
14 mailbox or mail list information |
282 |
|
15 mail exchange |
283 |
|
16 text strings |
284 |
|
0x1c IPv6? |
285 |
|
252 a request for a transfer of an entire zone |
286 |
|
253 a request for mailbox-related records (MB, MG or MR) |
287 |
|
254 a request for mail agent RRs (Obsolete - see MX) |
288 |
|
255 a request for all records |
289 |
|
QCLASS a two octet code that specifies the class of the query. |
290 |
|
1 the Internet |
291 |
|
(others are historic only) |
292 |
|
255 any class |
293 |
|
|
294 |
|
4.1.3. Resource record format |
295 |
|
|
296 |
|
The answer, authority, and additional sections all share the same format: |
297 |
|
a variable number of resource records, where the number of records |
298 |
|
is specified in the corresponding count field in the header. |
299 |
|
Each resource record has this format: |
300 |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
301 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
302 |
|
/ / |
303 |
|
/ NAME / |
304 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
305 |
|
| TYPE | |
306 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
307 |
|
| CLASS | |
308 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
309 |
|
| TTL | |
310 |
|
| | |
311 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
312 |
|
| RDLENGTH | |
313 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| |
314 |
|
/ RDATA / |
315 |
|
/ / |
316 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
317 |
|
NAME a domain name to which this resource record pertains. |
318 |
|
TYPE two octets containing one of the RR type codes. This |
319 |
|
field specifies the meaning of the data in the RDATA field. |
320 |
|
CLASS two octets which specify the class of the data in the RDATA field. |
321 |
|
TTL a 32 bit unsigned integer that specifies the time interval |
322 |
|
(in seconds) that the record may be cached. |
323 |
|
RDLENGTH a 16 bit integer, length in octets of the RDATA field. |
324 |
|
RDATA a variable length string of octets that describes the resource. |
325 |
|
The format of this information varies according to the TYPE |
326 |
|
and CLASS of the resource record. |
327 |
|
If the TYPE is A and the CLASS is IN, it's a 4 octet IP address. |
328 |
|
|
329 |
|
4.1.4. Message compression |
330 |
|
|
331 |
|
In order to reduce the size of messages, domain names coan be compressed. |
332 |
|
An entire domain name or a list of labels at the end of a domain name |
333 |
|
is replaced with a pointer to a prior occurance of the same name. |
334 |
|
|
335 |
|
The pointer takes the form of a two octet sequence: |
336 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
337 |
|
| 1 1| OFFSET | |
338 |
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
339 |
|
The first two bits are ones. This allows a pointer to be distinguished |
340 |
|
from a label, since the label must begin with two zero bits because |
341 |
|
labels are restricted to 63 octets or less. The OFFSET field specifies |
342 |
|
an offset from the start of the message (i.e., the first octet |
343 |
|
of the ID field in the domain header). |
344 |
|
A zero offset specifies the first byte of the ID field, etc. |
345 |
|
Domain name in a message can be represented as either: |
346 |
|
- a sequence of labels ending in a zero octet |
347 |
|
- a pointer |
348 |
|
- a sequence of labels ending with a pointer |
349 |
|
*/ |
350 |
|
static int process_packet(struct dns_entry *conf_data, |
351 |
|
uint32_t conf_ttl, |
352 |
|
uint8_t *buf) |
353 |
{ |
{ |
354 |
uint8_t answstr[MAX_NAME_LEN + 1]; |
char *answstr; |
355 |
struct dns_head *head; |
struct dns_head *head; |
356 |
struct dns_prop *qprop; |
struct dns_prop *unaligned_qprop; |
357 |
uint8_t *from, *answb; |
char *query_string; |
358 |
|
uint8_t *answb; |
359 |
uint16_t outr_rlen; |
uint16_t outr_rlen; |
360 |
uint16_t outr_flags; |
uint16_t outr_flags; |
361 |
uint16_t flags; |
uint16_t type; |
362 |
int lookup_result, type, packet_len; |
uint16_t class; |
363 |
int querystr_len; |
int querystr_len; |
364 |
|
|
|
answstr[0] = '\0'; |
|
|
|
|
365 |
head = (struct dns_head *)buf; |
head = (struct dns_head *)buf; |
366 |
if (head->nquer == 0) { |
if (head->nquer == 0) { |
367 |
bb_error_msg("no queries"); |
bb_error_msg("packet has 0 queries, ignored"); |
368 |
return -1; |
return -1; |
369 |
} |
} |
370 |
|
|
371 |
if (head->flags & 0x8000) { |
if (head->flags & htons(0x8000)) { /* QR bit */ |
372 |
bb_error_msg("ignoring response packet"); |
bb_error_msg("response packet, ignored"); |
373 |
return -1; |
return -1; |
374 |
} |
} |
375 |
|
|
376 |
from = (void *)&head[1]; // start of query string |
/* start of query string */ |
377 |
//FIXME: strlen of untrusted data??! |
query_string = (void *)(head + 1); |
378 |
querystr_len = strlen((char *)from) + 1 + sizeof(struct dns_prop); |
/* caller guarantees strlen is <= MAX_PACK_LEN */ |
379 |
answb = from + querystr_len; // where to append answer block |
querystr_len = strlen(query_string) + 1; |
380 |
|
/* may be unaligned! */ |
381 |
outr_rlen = 0; |
unaligned_qprop = (void *)(query_string + querystr_len); |
382 |
outr_flags = 0; |
querystr_len += sizeof(unaligned_qprop); |
383 |
|
/* where to append answer block */ |
384 |
qprop = (struct dns_prop *)(answb - 4); |
answb = (void *)(unaligned_qprop + 1); |
385 |
type = ntohs(qprop->type); |
|
386 |
|
/* QR = 1 "response", RCODE = 4 "Not Implemented" */ |
387 |
// only let REQ_A and REQ_PTR pass |
outr_flags = htons(0x8000 | 4); |
388 |
if (!(type == REQ_A || type == REQ_PTR)) { |
|
389 |
goto empty_packet; /* we can't handle the query type */ |
move_from_unaligned16(type, &unaligned_qprop->type); |
390 |
|
if (type != htons(REQ_A) && type != htons(REQ_PTR)) { |
391 |
|
/* we can't handle the query type */ |
392 |
|
goto empty_packet; |
393 |
} |
} |
394 |
|
move_from_unaligned16(class, &unaligned_qprop->class); |
395 |
if (ntohs(qprop->class) != 1 /* class INET */ ) { |
if (class != htons(1)) { /* not class INET? */ |
|
outr_flags = 4; /* not supported */ |
|
396 |
goto empty_packet; |
goto empty_packet; |
397 |
} |
} |
398 |
/* we only support standard queries */ |
/* OPCODE != 0 "standard query" ? */ |
399 |
|
if ((head->flags & htons(0x7800)) != 0) { |
|
if ((ntohs(head->flags) & 0x7800) != 0) |
|
400 |
goto empty_packet; |
goto empty_packet; |
401 |
|
} |
402 |
|
|
403 |
// We have a standard query |
/* look up the name */ |
404 |
bb_info_msg("%s", (char *)from); |
#if DEBUG |
405 |
lookup_result = table_lookup(type, answstr, from); |
/* need to convert lengths to dots before we can use it in non-debug */ |
406 |
if (lookup_result != 0) { |
bb_info_msg("%s", query_string); |
407 |
outr_flags = 3 | 0x0400; // name do not exist and auth |
#endif |
408 |
|
answstr = table_lookup(conf_data, type, query_string); |
409 |
|
outr_rlen = 4; |
410 |
|
if (answstr && type == htons(REQ_PTR)) { |
411 |
|
/* return a host name */ |
412 |
|
outr_rlen = strlen(answstr) + 1; |
413 |
|
} |
414 |
|
if (!answstr |
415 |
|
|| (unsigned)(answb - buf) + querystr_len + 4 + 2 + outr_rlen > MAX_PACK_LEN |
416 |
|
) { |
417 |
|
/* QR = 1 "response" |
418 |
|
* AA = 1 "Authoritative Answer" |
419 |
|
* RCODE = 3 "Name Error" */ |
420 |
|
outr_flags = htons(0x8000 | 0x0400 | 3); |
421 |
goto empty_packet; |
goto empty_packet; |
422 |
} |
} |
|
if (type == REQ_A) { // return an address |
|
|
struct in_addr a; // NB! its "struct { unsigned __long__ s_addr; }" |
|
|
uint32_t v32; |
|
|
if (!inet_aton((char*)answstr, &a)) { //dotted dec to long conv |
|
|
outr_flags = 1; /* Frmt err */ |
|
|
goto empty_packet; |
|
|
} |
|
|
v32 = a.s_addr; /* in case long != int */ |
|
|
memcpy(answstr, &v32, 4); |
|
|
outr_rlen = 4; // uint32_t IP |
|
|
} else |
|
|
outr_rlen = strlen((char *)answstr) + 1; // a host name |
|
|
outr_flags |= 0x0400; /* authority-bit */ |
|
|
// we have an answer |
|
|
head->nansw = htons(1); |
|
423 |
|
|
424 |
// copy query block to answer block |
/* copy query block to answer block */ |
425 |
memcpy(answb, from, querystr_len); |
memcpy(answb, query_string, querystr_len); |
426 |
answb += querystr_len; |
answb += querystr_len; |
427 |
|
/* append answer Resource Record */ |
428 |
// and append answer rr |
move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl)); |
|
// FIXME: unaligned accesses?? |
|
|
*(uint32_t *) answb = htonl(ttl); |
|
429 |
answb += 4; |
answb += 4; |
430 |
*(uint16_t *) answb = htons(outr_rlen); |
move_to_unaligned32((uint16_t *)answb, htons(outr_rlen)); |
431 |
answb += 2; |
answb += 2; |
432 |
memcpy(answb, answstr, outr_rlen); |
memcpy(answb, answstr, outr_rlen); |
433 |
answb += outr_rlen; |
answb += outr_rlen; |
434 |
|
|
435 |
empty_packet: |
/* QR = 1 "response", |
436 |
|
* AA = 1 "Authoritative Answer", |
437 |
|
* RCODE = 0 "success" */ |
438 |
|
outr_flags = htons(0x8000 | 0x0400 | 0); |
439 |
|
/* we have one answer */ |
440 |
|
head->nansw = htons(1); |
441 |
|
|
442 |
flags = ntohs(head->flags); |
empty_packet: |
443 |
// clear rcode and RA, set responsebit and our new flags |
head->flags |= outr_flags; |
|
flags |= (outr_flags & 0xff80) | 0x8000; |
|
|
head->flags = htons(flags); |
|
444 |
head->nauth = head->nadd = 0; |
head->nauth = head->nadd = 0; |
445 |
head->nquer = htons(1); |
head->nquer = htons(1); // why??? |
|
|
|
|
packet_len = answb - buf; |
|
|
return packet_len; |
|
|
} |
|
446 |
|
|
447 |
/* |
return answb - buf; |
|
* Exit on signal |
|
|
*/ |
|
|
static void interrupt(int sig) |
|
|
{ |
|
|
/* unlink("/var/run/dnsd.lock"); */ |
|
|
bb_error_msg("interrupt, exiting\n"); |
|
|
kill_myself_with_sig(sig); |
|
448 |
} |
} |
449 |
|
|
450 |
int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
451 |
int dnsd_main(int argc UNUSED_PARAM, char **argv) |
int dnsd_main(int argc UNUSED_PARAM, char **argv) |
452 |
{ |
{ |
453 |
const char *listen_interface = "0.0.0.0"; |
const char *listen_interface = "0.0.0.0"; |
454 |
|
const char *fileconf = "/etc/dnsd.conf"; |
455 |
|
struct dns_entry *conf_data; |
456 |
|
uint32_t conf_ttl = DEFAULT_TTL; |
457 |
char *sttl, *sport; |
char *sttl, *sport; |
458 |
len_and_sockaddr *lsa, *from, *to; |
len_and_sockaddr *lsa, *from, *to; |
459 |
unsigned lsa_size; |
unsigned lsa_size; |
460 |
int udps; |
int udps, opts; |
461 |
uint16_t port = 53; |
uint16_t port = 53; |
462 |
/* Paranoid sizing: querystring x2 + ttl + outr_rlen + answstr */ |
uint8_t buf[MAX_PACK_LEN + 1]; |
|
/* I'd rather see process_packet() fixed instead... */ |
|
|
uint8_t buf[MAX_PACK_LEN * 2 + 4 + 2 + (MAX_NAME_LEN+1)]; |
|
|
|
|
|
getopt32(argv, "i:c:t:p:dv", &listen_interface, &fileconf, &sttl, &sport); |
|
|
//if (option_mask32 & 0x1) // -i |
|
|
//if (option_mask32 & 0x2) // -c |
|
|
if (option_mask32 & 0x4) // -t |
|
|
ttl = xatou_range(sttl, 1, 0xffffffff); |
|
|
if (option_mask32 & 0x8) // -p |
|
|
port = xatou_range(sport, 1, 0xffff); |
|
|
|
|
|
if (OPT_verbose) { |
|
|
bb_info_msg("listen_interface: %s", listen_interface); |
|
|
bb_info_msg("ttl: %d, port: %d", ttl, port); |
|
|
bb_info_msg("fileconf: %s", fileconf); |
|
|
} |
|
463 |
|
|
464 |
if (OPT_daemon) { |
opts = getopt32(argv, "vi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport); |
465 |
|
//if (opts & 0x1) // -v |
466 |
|
//if (opts & 0x2) // -i |
467 |
|
//if (opts & 0x4) // -c |
468 |
|
if (opts & 0x8) // -t |
469 |
|
conf_ttl = xatou_range(sttl, 1, 0xffffffff); |
470 |
|
if (opts & 0x10) // -p |
471 |
|
port = xatou_range(sport, 1, 0xffff); |
472 |
|
if (opts & 0x20) { // -d |
473 |
bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
474 |
openlog(applet_name, LOG_PID, LOG_DAEMON); |
openlog(applet_name, LOG_PID, LOG_DAEMON); |
475 |
logmode = LOGMODE_SYSLOG; |
logmode = LOGMODE_SYSLOG; |
476 |
} |
} |
477 |
|
/* Clear all except "verbose" bit */ |
478 |
|
option_mask32 &= 1; |
479 |
|
|
480 |
dnsentryinit(); |
conf_data = parse_conf_file(fileconf); |
|
|
|
|
signal(SIGINT, interrupt); |
|
|
bb_signals(0 |
|
|
/* why? + (1 << SIGPIPE) */ |
|
|
+ (1 << SIGHUP) |
|
|
#ifdef SIGTSTP |
|
|
+ (1 << SIGTSTP) |
|
|
#endif |
|
|
#ifdef SIGURG |
|
|
+ (1 << SIGURG) |
|
|
#endif |
|
|
, SIG_IGN); |
|
481 |
|
|
482 |
lsa = xdotted2sockaddr(listen_interface, port); |
lsa = xdotted2sockaddr(listen_interface, port); |
483 |
udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
487 |
from = xzalloc(lsa_size); |
from = xzalloc(lsa_size); |
488 |
to = xzalloc(lsa_size); |
to = xzalloc(lsa_size); |
489 |
|
|
490 |
bb_info_msg("Accepting UDP packets on %s", |
{ |
491 |
xmalloc_sockaddr2dotted(&lsa->u.sa)); |
char *p = xmalloc_sockaddr2dotted(&lsa->u.sa); |
492 |
|
bb_info_msg("Accepting UDP packets on %s", p); |
493 |
|
free(p); |
494 |
|
} |
495 |
|
|
496 |
while (1) { |
while (1) { |
497 |
int r; |
int r; |
503 |
memcpy(to, lsa, lsa_size); |
memcpy(to, lsa, lsa_size); |
504 |
r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len); |
r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len); |
505 |
if (r < 12 || r > MAX_PACK_LEN) { |
if (r < 12 || r > MAX_PACK_LEN) { |
506 |
bb_error_msg("invalid packet size"); |
bb_error_msg("packet size %d, ignored", r); |
507 |
continue; |
continue; |
508 |
} |
} |
509 |
if (OPT_verbose) |
if (OPT_verbose) |
510 |
bb_info_msg("Got UDP packet"); |
bb_info_msg("Got UDP packet"); |
511 |
buf[r] = '\0'; /* paranoia */ |
buf[r] = '\0'; /* paranoia */ |
512 |
r = process_packet(buf); |
r = process_packet(conf_data, conf_ttl, buf); |
513 |
if (r <= 0) |
if (r <= 0) |
514 |
continue; |
continue; |
515 |
send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len); |
send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len); |