]>
Commit | Line | Data |
---|---|---|
1136f709 | 1 | /* sar.h - srvx asynchronous resolver |
2 | * Copyright 2005, 2007 Michael Poole <mdpoole@troilus.org> | |
3 | * | |
4 | * This file is part of srvx. | |
5 | * | |
6 | * srvx is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
be2c97a5 | 8 | * the Free Software Foundation; either version 3 of the License, or |
1136f709 | 9 | * (at your option) any later version. |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with srvx; if not, write to the Free Software Foundation, | |
18 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
19 | */ | |
20 | ||
21 | #include "sar.h" | |
22 | #include "conf.h" | |
23 | #include "ioset.h" | |
24 | #include "log.h" | |
25 | #include "timeq.h" | |
26 | ||
27 | #if defined(HAVE_NETINET_IN_H) | |
28 | # include <netinet/in.h> /* sockaddr_in6 on some BSDs */ | |
29 | #endif | |
30 | ||
31 | static const char hexdigits[] = "0123456789abcdef"; | |
32 | ||
33 | struct dns_rr; | |
34 | struct sar_getaddr_state; | |
35 | struct sar_getname_state; | |
36 | ||
37 | struct sar_family_helper { | |
38 | const char *localhost_addr; | |
39 | const char *unspec_addr; | |
40 | unsigned int socklen; | |
41 | unsigned int family; | |
42 | ||
43 | unsigned int (*ntop)(char *output, unsigned int out_size, const struct sockaddr *sa, unsigned int socklen); | |
44 | unsigned int (*pton)(struct sockaddr *sa, unsigned int socklen, unsigned int *bits, const char *input); | |
45 | int (*get_port)(const struct sockaddr *sa, unsigned int socklen); | |
46 | int (*set_port)(struct sockaddr *sa, unsigned int socklen, unsigned short port); | |
47 | unsigned int (*build_addr_request)(struct sar_request *req, const char *node, const char *srv_node, unsigned int flags); | |
48 | void (*build_ptr_name)(struct sar_getname_state *state, const struct sockaddr *sa, unsigned int socklen); | |
49 | int (*decode_addr)(struct sar_getaddr_state *state, struct dns_rr *rr, unsigned char *raw, unsigned int raw_size); | |
50 | ||
51 | struct sar_family_helper *next; | |
52 | }; | |
53 | ||
54 | #define MAX_FAMILY AF_INET | |
55 | static struct sar_family_helper sar_ipv4_helper; | |
56 | ||
57 | #if defined(AF_INET6) | |
58 | # if AF_INET6 > MAX_FAMILY | |
59 | # undef MAX_FAMILY | |
60 | # define MAX_FAMILY AF_INET6 | |
61 | # endif | |
62 | static struct sar_family_helper sar_ipv6_helper; | |
63 | #endif | |
64 | ||
65 | static struct sar_family_helper *sar_helpers[MAX_FAMILY+1]; | |
66 | static struct sar_family_helper *sar_first_helper; | |
67 | ||
68 | unsigned int | |
69 | sar_ntop(char *output, unsigned int out_size, const struct sockaddr *sa, unsigned int socklen) | |
70 | { | |
71 | unsigned int pos; | |
72 | assert(output != NULL); | |
73 | assert(sa != NULL); | |
74 | assert(out_size > 0); | |
75 | ||
76 | if (sa->sa_family <= MAX_FAMILY && sar_helpers[sa->sa_family]) { | |
77 | pos = sar_helpers[sa->sa_family]->ntop(output, out_size, sa, socklen); | |
78 | if (pos) | |
79 | return pos; | |
80 | } | |
81 | *output = '\0'; | |
82 | return 0; | |
83 | } | |
84 | ||
85 | unsigned int | |
86 | sar_pton(struct sockaddr *sa, unsigned int socklen, unsigned int *bits, const char *input) | |
87 | { | |
88 | struct sar_family_helper *helper; | |
89 | unsigned int len; | |
90 | ||
91 | assert(sa != NULL); | |
92 | assert(input != NULL); | |
93 | ||
94 | memset(sa, 0, socklen); | |
95 | if (bits) | |
96 | *bits = ~0; | |
97 | for (helper = sar_first_helper; helper; helper = helper->next) { | |
98 | if (socklen < helper->socklen) | |
99 | continue; | |
100 | len = helper->pton(sa, socklen, bits, input); | |
101 | if (len) { | |
102 | sa->sa_family = helper->family; | |
103 | return len; | |
104 | } | |
105 | } | |
106 | return 0; /* parse failed */ | |
107 | } | |
108 | ||
109 | int | |
110 | sar_get_port(const struct sockaddr *sa, unsigned int socklen) | |
111 | { | |
112 | if (sa->sa_family <= MAX_FAMILY | |
113 | && sar_helpers[sa->sa_family] | |
114 | && socklen >= sar_helpers[sa->sa_family]->socklen) | |
115 | return sar_helpers[sa->sa_family]->get_port(sa, socklen); | |
116 | else return -1; | |
117 | } | |
118 | ||
119 | int | |
120 | sar_set_port(struct sockaddr *sa, unsigned int socklen, unsigned short port) | |
121 | { | |
122 | if (sa->sa_family <= MAX_FAMILY | |
123 | && sar_helpers[sa->sa_family] | |
124 | && socklen >= sar_helpers[sa->sa_family]->socklen) | |
125 | return sar_helpers[sa->sa_family]->set_port(sa, socklen, port); | |
126 | else return 1; | |
127 | } | |
128 | ||
129 | const char * | |
130 | sar_strerror(enum sar_errcode errcode) | |
131 | { | |
132 | switch (errcode) { | |
133 | case SAI_SUCCESS: return "Resolution succeeded."; | |
134 | case SAI_FAMILY: return "The requested address family is not supported."; | |
135 | case SAI_SOCKTYPE: return "The requested socket type is not supported."; | |
136 | case SAI_BADFLAGS: return "Invalid flags value."; | |
137 | case SAI_NONAME: return "Unknown name or service."; | |
138 | case SAI_SERVICE: return "The service is unavailable for that socket type."; | |
139 | case SAI_ADDRFAMILY: return "The host has no address in the requested family."; | |
140 | case SAI_NODATA: return "The host has no addresses at all."; | |
141 | case SAI_MEMORY: return "Unable to allocate memory."; | |
142 | case SAI_FAIL: return "The nameserver indicated a permanent error."; | |
143 | case SAI_AGAIN: return "The nameserver indicated a temporary error."; | |
144 | case SAI_MISMATCH: return "Mismatch between reverse and forward resolution."; | |
145 | case SAI_SYSTEM: return strerror(errno); | |
146 | default: return "Unknown resolver error code."; | |
147 | } | |
148 | } | |
149 | ||
150 | void | |
151 | sar_free(struct addrinfo *ai) | |
152 | { | |
153 | struct addrinfo *next; | |
154 | for (; ai; ai = next) { | |
155 | next = ai->ai_next; | |
156 | free(ai); | |
157 | } | |
158 | } | |
159 | ||
160 | /** Global variables to support DNS name resolution. */ | |
161 | static struct { | |
162 | unsigned int sar_timeout; | |
163 | unsigned int sar_retries; | |
164 | unsigned int sar_ndots; | |
165 | unsigned int sar_edns0; | |
166 | char sar_localdomain[MAXLEN]; | |
167 | struct string_list *sar_search; | |
168 | struct string_list *sar_nslist; | |
169 | struct sockaddr_storage sar_bind_address; | |
170 | } conf; | |
171 | static struct log_type *sar_log; | |
172 | ||
173 | /* Except as otherwise noted, constants and formats are from RFC1035. | |
174 | * This resolver is believed to implement the behaviors mandated (and | |
175 | * in many cases those recommended) by these standards: RFC1035, | |
176 | * RFC2671, RFC2782, RFC3596, RFC3597. | |
177 | * | |
178 | * Update queries (including RFC 2136) seems a likely candidate for | |
179 | * future support. | |
180 | * DNSSEC (including RFCs 2535, 3007, 3655, etc) is less likely until | |
181 | * a good application is found. | |
182 | * Caching (RFC 2308) and redirection (RFC 2672) are much less likely, | |
183 | * since most users will have a separate local, caching, recursive | |
184 | * nameserver. | |
185 | * Other DNS extensions (at least through RFC 3755) are believed to be | |
186 | * too rare or insufficiently useful to bother supporting. | |
187 | * | |
188 | * The following are useful Reasons For Concern: | |
189 | * RFC1536, RFC1912, RFC2606, RFC3363, RFC3425, RFC3467 | |
190 | * http://www.iana.org/assignments/dns-parameters | |
191 | * http://www.ietf.org/html.charters/dnsext-charter.html | |
192 | */ | |
193 | ||
194 | struct sar_nameserver { | |
195 | char *name; | |
196 | unsigned int valid; | |
197 | unsigned int req_sent; | |
198 | unsigned int resp_used; | |
199 | unsigned int resp_ignored; | |
200 | unsigned int resp_servfail; | |
201 | unsigned int resp_fallback; | |
202 | unsigned int resp_failures; | |
203 | unsigned int resp_scrambled; | |
204 | unsigned int ss_len; | |
205 | struct sockaddr_storage ss; | |
206 | }; | |
207 | ||
208 | /* EDNS0 uses 12 bit RCODEs, TSIG/TKEY use 16 bit RCODEs. | |
209 | * Declare local RCODE failures here.*/ | |
210 | enum { | |
211 | RCODE_TIMED_OUT = 65536, | |
212 | RCODE_QUERY_TOO_LONG, | |
213 | RCODE_LABEL_TOO_LONG, | |
214 | RCODE_SOCKET_FAILURE, | |
215 | RCODE_DESTROYED, | |
216 | }; | |
217 | ||
218 | #define DNS_NAME_LENGTH 256 | |
219 | ||
220 | #define RES_SIZE_FLAGS 0xc0 | |
221 | #define RES_SF_LABEL 0x00 | |
222 | #define RES_SF_POINTER 0xc0 | |
223 | ||
224 | static dict_t sar_requests; | |
225 | static dict_t sar_nameservers; | |
226 | static struct io_fd *sar_fd; | |
227 | static int sar_fd_fd; | |
228 | ||
229 | const char * | |
230 | sar_rcode_text(unsigned int rcode) | |
231 | { | |
232 | switch (rcode) { | |
233 | case RCODE_NO_ERROR: return "No error"; | |
234 | case RCODE_FORMAT_ERROR: return "Format error"; | |
235 | case RCODE_SERVER_FAILURE: return "Server failure"; | |
236 | case RCODE_NAME_ERROR: return "Name error"; | |
237 | case RCODE_NOT_IMPLEMENTED: return "Feature not implemented"; | |
238 | case RCODE_REFUSED: return "Query refused"; | |
239 | case RCODE_BAD_OPT_VERSION: return "Unsupported EDNS option version"; | |
240 | case RCODE_TIMED_OUT: return "Request timed out"; | |
241 | case RCODE_QUERY_TOO_LONG: return "Query too long"; | |
242 | case RCODE_LABEL_TOO_LONG: return "Label too long"; | |
243 | case RCODE_SOCKET_FAILURE: return "Resolver socket failure"; | |
244 | case RCODE_DESTROYED: return "Request unexpectedly destroyed"; | |
245 | default: return "Unknown rcode"; | |
246 | } | |
247 | } | |
248 | ||
249 | static void | |
250 | sar_request_fail(struct sar_request *req, unsigned int rcode) | |
251 | { | |
252 | log_module(sar_log, LOG_DEBUG, "sar_request_fail({id=%d}, rcode=%d)", req->id, rcode); | |
253 | req->expiry = 0; | |
254 | if (req->cb_fail) { | |
255 | req->cb_fail(req, rcode); | |
256 | if (req->expiry) | |
257 | return; | |
258 | } | |
259 | sar_request_abort(req); | |
260 | } | |
261 | ||
262 | static unsigned long next_sar_timeout; | |
263 | ||
264 | static void | |
265 | sar_timeout_cb(void *data) | |
266 | { | |
267 | dict_iterator_t it; | |
268 | dict_iterator_t next; | |
269 | time_t next_timeout = INT_MAX; | |
270 | ||
271 | for (it = dict_first(sar_requests); it; it = next) { | |
272 | struct sar_request *req; | |
273 | ||
274 | req = iter_data(it); | |
275 | next = iter_next(it); | |
276 | if (req->expiry > next_timeout) | |
277 | continue; | |
278 | else if (req->expiry > now) | |
279 | next_timeout = req->expiry; | |
280 | else if (req->retries >= conf.sar_retries) | |
281 | sar_request_fail(req, RCODE_TIMED_OUT); | |
282 | else | |
283 | sar_request_send(req); | |
284 | } | |
285 | if (next_timeout < INT_MAX) { | |
286 | next_sar_timeout = next_timeout; | |
287 | timeq_add(next_timeout, sar_timeout_cb, data); | |
288 | } | |
289 | } | |
290 | ||
291 | static void | |
292 | sar_check_timeout(unsigned long when) | |
293 | { | |
294 | if (!next_sar_timeout || when < next_sar_timeout) { | |
295 | timeq_del(0, sar_timeout_cb, NULL, TIMEQ_IGNORE_WHEN | TIMEQ_IGNORE_DATA); | |
296 | timeq_add(when, sar_timeout_cb, NULL); | |
297 | next_sar_timeout = when; | |
298 | } | |
299 | } | |
300 | ||
301 | static void | |
302 | sar_request_cleanup(void *d) | |
303 | { | |
304 | struct sar_request *req = d; | |
305 | log_module(sar_log, LOG_DEBUG, "sar_request_cleanup({id=%d})", req->id); | |
306 | free(req->body); | |
307 | if (req->cb_fail) | |
308 | req->cb_fail(req, RCODE_DESTROYED); | |
309 | free(req); | |
310 | } | |
311 | ||
312 | static void | |
313 | sar_dns_init(const char *resolv_conf_path) | |
314 | { | |
315 | struct string_list *ns_sv; | |
316 | struct string_list *ds_sv; | |
317 | FILE *resolv_conf; | |
318 | dict_t node; | |
319 | const char *str; | |
320 | ||
321 | /* Initialize configuration defaults. */ | |
322 | conf.sar_localdomain[0] = '\0'; | |
323 | conf.sar_timeout = 3; | |
324 | conf.sar_retries = 3; | |
325 | conf.sar_ndots = 1; | |
326 | conf.sar_edns0 = 0; | |
327 | ns_sv = alloc_string_list(4); | |
328 | ds_sv = alloc_string_list(4); | |
329 | ||
330 | /* Scan resolver configuration file. */ | |
331 | resolv_conf = fopen(resolv_conf_path, "r"); | |
332 | if (resolv_conf) { | |
333 | char *arg, *opt; | |
334 | unsigned int len; | |
335 | char linebuf[LINE_MAX], ch; | |
336 | ||
337 | while (fgets(linebuf, sizeof(linebuf), resolv_conf)) { | |
338 | ch = linebuf[len = strcspn(linebuf, " \t\r\n")]; | |
339 | linebuf[len] = '\0'; | |
340 | arg = linebuf + len + 1; | |
341 | if (!strcmp(linebuf, "nameserver")) { | |
342 | while (ch == ' ') { | |
343 | ch = arg[len = strcspn(arg, " \t\r\n")]; | |
344 | arg[len] = '\0'; | |
345 | string_list_append(ns_sv, strdup(arg)); | |
346 | arg += len + 1; | |
347 | } | |
348 | } else if (!strcmp(linebuf, "domain")) { | |
349 | if (ch == ' ') { | |
350 | safestrncpy(conf.sar_localdomain, arg, sizeof(conf.sar_localdomain)); | |
351 | } | |
352 | } else if (!strcmp(linebuf, "search")) { | |
353 | while (ch == ' ') { | |
354 | ch = arg[len = strcspn(arg, " \t\r\n")]; | |
355 | arg[len] = '\0'; | |
356 | string_list_append(ds_sv, strdup(arg)); | |
357 | arg += len + 1; | |
358 | } | |
359 | } else if (!strcmp(linebuf, "options")) { | |
360 | while (ch == ' ') { | |
361 | ch = arg[len = strcspn(arg, " \t\r\n")]; | |
362 | arg[len] = '\0'; | |
363 | opt = strchr(arg, ':'); | |
364 | if (opt) { | |
365 | *opt++ = '\0'; | |
366 | if (!strcmp(arg, "timeout")) { | |
367 | conf.sar_timeout = atoi(opt); | |
368 | } else if (!strcmp(arg, "attempts")) { | |
369 | conf.sar_retries = atoi(opt); | |
370 | } else if (!strcmp(arg, "ndots")) { | |
371 | conf.sar_ndots = atoi(opt); | |
372 | } else if (!strcmp(arg, "edns0")) { | |
373 | conf.sar_edns0 = atoi(opt); | |
374 | } | |
375 | } else if (!strcmp(arg, "edns0")) { | |
376 | conf.sar_edns0 = 1440; | |
377 | } | |
378 | arg += len + 1; | |
379 | } | |
380 | } | |
381 | } | |
382 | fclose(resolv_conf); | |
383 | } else { | |
384 | /* This is apparently what BIND defaults to using. */ | |
385 | string_list_append(ns_sv, "127.0.0.1"); | |
386 | } | |
387 | ||
388 | /* Set default search path if domain is set. */ | |
389 | if (conf.sar_localdomain[0] != '\0' && ds_sv->used == 0) | |
390 | string_list_append(ds_sv, strdup(conf.sar_localdomain)); | |
391 | ||
392 | /* Check configuration entries that might override resolv.conf. */ | |
393 | node = conf_get_data("modules/sar", RECDB_OBJECT); | |
394 | if (node) { | |
395 | struct sockaddr *sa; | |
396 | struct string_list *slist; | |
397 | ||
398 | str = database_get_data(node, "timeout", RECDB_QSTRING); | |
399 | if (str) conf.sar_timeout = ParseInterval(str); | |
400 | str = database_get_data(node, "retries", RECDB_QSTRING); | |
401 | if (str) conf.sar_retries = atoi(str); | |
402 | str = database_get_data(node, "ndots", RECDB_QSTRING); | |
403 | if (str) conf.sar_ndots = atoi(str); | |
404 | str = database_get_data(node, "edns0", RECDB_QSTRING); | |
405 | if (str) conf.sar_edns0 = enabled_string(str); | |
406 | str = database_get_data(node, "domain", RECDB_QSTRING); | |
407 | if (str) safestrncpy(conf.sar_localdomain, str, sizeof(conf.sar_localdomain)); | |
408 | slist = database_get_data(node, "search", RECDB_STRING_LIST); | |
409 | if (slist) { | |
410 | free_string_list(ds_sv); | |
411 | ds_sv = string_list_copy(slist); | |
412 | } | |
413 | slist = database_get_data(node, "nameservers", RECDB_STRING_LIST); | |
414 | if (slist) { | |
415 | free_string_list(ns_sv); | |
416 | ns_sv = string_list_copy(slist); | |
417 | } | |
418 | sa = (struct sockaddr*)&conf.sar_bind_address; | |
419 | memset(sa, 0, sizeof(conf.sar_bind_address)); | |
420 | str = database_get_data(node, "bind_address", RECDB_QSTRING); | |
421 | if (str) sar_pton(sa, sizeof(conf.sar_bind_address), NULL, str); | |
422 | str = database_get_data(node, "bind_port", RECDB_QSTRING); | |
423 | if (str != NULL) { | |
424 | if (sa->sa_family == AF_INET) { | |
425 | struct sockaddr_in *sin = (struct sockaddr_in*)sa; | |
426 | sin->sin_port = ntohs(atoi(str)); | |
427 | } | |
428 | #if defined(AF_INET6) | |
429 | else if (sa->sa_family == AF_INET6) { | |
430 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; | |
431 | sin6->sin6_port = ntohs(atoi(str)); | |
432 | } | |
433 | #endif | |
434 | } | |
435 | } | |
436 | ||
437 | /* Replace config lists with their new values. */ | |
438 | free_string_list(conf.sar_search); | |
439 | conf.sar_search = ds_sv; | |
440 | free_string_list(conf.sar_nslist); | |
441 | conf.sar_nslist = ns_sv; | |
442 | } | |
443 | ||
444 | void | |
445 | sar_request_abort(struct sar_request *req) | |
446 | { | |
447 | if (!req) | |
448 | return; | |
449 | assert(dict_find(sar_requests, req->id_text, NULL) == req); | |
450 | log_module(sar_log, LOG_DEBUG, "sar_request_abort({id=%d})", req->id); | |
451 | req->cb_ok = NULL; | |
452 | req->cb_fail = NULL; | |
453 | dict_remove(sar_requests, req->id_text); | |
454 | } | |
455 | ||
456 | static struct sar_nameserver * | |
457 | sar_our_server(const struct sockaddr_storage *ss, unsigned int ss_len) | |
458 | { | |
459 | dict_iterator_t it; | |
460 | ||
461 | for (it = dict_first(sar_nameservers); it; it = iter_next(it)) { | |
462 | struct sar_nameserver *ns; | |
463 | ||
464 | ns = iter_data(it); | |
465 | if (ns->ss_len == ss_len && !memcmp(ss, &ns->ss, ss_len)) | |
466 | return ns; | |
467 | } | |
468 | return NULL; | |
469 | } | |
470 | ||
471 | char * | |
472 | sar_extract_name(const unsigned char *buf, unsigned int size, unsigned int *ppos) | |
473 | { | |
474 | struct string_buffer cv; | |
475 | unsigned int pos, jumped; | |
476 | ||
477 | pos = *ppos; | |
478 | jumped = 0; | |
479 | cv.used = 0; | |
480 | cv.size = 64; | |
481 | cv.list = calloc(1, cv.size); | |
482 | while (1) { | |
483 | if (pos >= size) | |
484 | goto fail; | |
485 | if (!buf[pos]) { | |
486 | if (!jumped) | |
487 | *ppos = pos + 1; | |
488 | if (cv.used) | |
489 | cv.list[cv.used - 1] = '\0'; /* chop off terminating '.' */ | |
490 | else | |
491 | string_buffer_append(&cv, '\0'); | |
492 | return cv.list; | |
493 | } | |
494 | switch (buf[pos] & RES_SIZE_FLAGS) { | |
495 | case RES_SF_LABEL: { | |
496 | unsigned int len = buf[pos]; | |
497 | if (pos + len + 1 >= size) | |
498 | goto fail; | |
499 | string_buffer_append_substring(&cv, (char*)buf + pos + 1, len); | |
500 | string_buffer_append(&cv, '.'); | |
501 | pos += buf[pos] + 1; | |
502 | break; | |
503 | } | |
504 | case RES_SF_POINTER: | |
505 | if ((pos + 1 >= size) || (cv.used >= size)) | |
506 | goto fail; | |
507 | if (!jumped) | |
508 | *ppos = pos + 2; | |
509 | pos = (buf[pos] & ~RES_SIZE_FLAGS) << 8 | buf[pos+1]; | |
510 | jumped = 1; | |
511 | break; | |
512 | default: | |
513 | goto fail; | |
514 | } | |
515 | } | |
516 | fail: | |
517 | free(cv.list); | |
518 | return NULL; | |
519 | } | |
520 | ||
521 | static int | |
522 | sar_decode_answer(struct sar_request *req, struct dns_header *hdr, unsigned char *buf, unsigned int size) | |
523 | { | |
524 | struct dns_rr *rr; | |
525 | unsigned int ii, rr_count, pos; | |
526 | int res; | |
527 | ||
528 | /* Skip over query section. */ | |
529 | for (ii = 0, pos = 12; ii < hdr->qdcount; ++ii) { | |
530 | /* Skip over compressed names. */ | |
531 | while (1) { | |
532 | if (pos >= size) | |
533 | return 2; | |
534 | if (!buf[pos]) | |
535 | break; | |
536 | switch (buf[pos] & RES_SIZE_FLAGS) { | |
537 | case RES_SF_LABEL: | |
538 | pos += buf[pos] + 1; | |
539 | break; | |
540 | case RES_SF_POINTER: | |
541 | if (pos + 1 >= size) | |
542 | return 2; | |
543 | pos = (buf[pos] & ~RES_SIZE_FLAGS) << 8 | buf[pos+1]; | |
544 | if (pos >= size) | |
545 | return 3; | |
546 | break; | |
547 | default: | |
548 | return 4; | |
549 | } | |
550 | } | |
551 | /* Skip over null terminator, type and class part of question. */ | |
552 | pos += 5; | |
553 | } | |
554 | ||
555 | /* Parse each RR in the answer. */ | |
556 | rr_count = hdr->ancount + hdr->nscount + hdr->arcount; | |
557 | rr = calloc(1, rr_count * sizeof(rr[0])); | |
558 | for (ii = 0; ii < rr_count; ++ii) { | |
559 | rr[ii].name = sar_extract_name(buf, size, &pos); | |
560 | if (!rr[ii].name) { | |
561 | res = 5; | |
562 | goto out; | |
563 | } | |
564 | if (pos + 10 > size) { | |
565 | res = 6; | |
566 | goto out; | |
567 | } | |
568 | rr[ii].type = buf[pos+0] << 8 | buf[pos+1]; | |
569 | rr[ii].class = buf[pos+2] << 8 | buf[pos+3]; | |
570 | rr[ii].ttl = buf[pos+4] << 24 | buf[pos+5] << 16 | buf[pos+6] << 8 | buf[pos+7]; | |
571 | rr[ii].rdlength = buf[pos+8] << 8 | buf[pos+9]; | |
572 | rr[ii].rd_start = pos + 10; | |
573 | pos = pos + rr[ii].rdlength + 10; | |
574 | if (pos > size) { | |
575 | res = 7; | |
576 | goto out; | |
577 | } | |
578 | } | |
579 | res = 0; | |
580 | req->expiry = 0; | |
581 | req->cb_ok(req, hdr, rr, buf, size); | |
582 | if (!req->expiry) { | |
583 | req->cb_ok = NULL; | |
584 | req->cb_fail = NULL; | |
585 | dict_remove(sar_requests, req->id_text); | |
586 | } | |
587 | ||
588 | out: | |
589 | while (ii > 0) | |
590 | free(rr[--ii].name); | |
591 | free(rr); | |
592 | return res; | |
593 | } | |
594 | ||
595 | static const unsigned char * | |
596 | sar_extract_rdata(struct dns_rr *rr, unsigned int len, unsigned char *raw, unsigned int raw_size) | |
597 | { | |
598 | if (len > rr->rdlength) | |
599 | return NULL; | |
600 | if (rr->rd_start + len > raw_size) | |
601 | return NULL; | |
602 | return raw + rr->rd_start; | |
603 | } | |
604 | ||
605 | static void | |
606 | sar_fd_readable(struct io_fd *fd) | |
607 | { | |
608 | struct sockaddr_storage ss; | |
609 | struct dns_header hdr; | |
610 | struct sar_nameserver *ns; | |
611 | struct sar_request *req; | |
612 | unsigned char *buf; | |
613 | socklen_t ss_len; | |
614 | int res, rcode, buf_len; | |
615 | char id_text[6]; | |
616 | ||
617 | assert(sar_fd == fd); | |
618 | buf_len = conf.sar_edns0; | |
619 | if (!buf_len) | |
620 | buf_len = 512; | |
621 | buf = alloca(buf_len); | |
622 | ss_len = sizeof(ss); | |
623 | res = recvfrom(sar_fd_fd, buf, buf_len, 0, (struct sockaddr*)&ss, &ss_len); | |
624 | if (res < 12 || !(ns = sar_our_server(&ss, ss_len))) | |
625 | return; | |
626 | hdr.id = buf[0] << 8 | buf[1]; | |
627 | hdr.flags = buf[2] << 8 | buf[3]; | |
628 | hdr.qdcount = buf[4] << 8 | buf[5]; | |
629 | hdr.ancount = buf[6] << 8 | buf[7]; | |
630 | hdr.nscount = buf[8] << 8 | buf[9]; | |
631 | hdr.arcount = buf[10] << 8 | buf[11]; | |
632 | ||
633 | sprintf(id_text, "%d", hdr.id); | |
634 | req = dict_find(sar_requests, id_text, NULL); | |
635 | log_module(sar_log, LOG_DEBUG, "sar_fd_readable(%p): hdr {id=%d, flags=0x%x, qdcount=%d, ancount=%d, nscount=%d, arcount=%d} -> req %p", (void*)fd, hdr.id, hdr.flags, hdr.qdcount, hdr.ancount, hdr.nscount, hdr.arcount, (void*)req); | |
636 | if (!req || !req->retries || !(hdr.flags & REQ_FLAG_QR)) { | |
637 | ns->resp_ignored++; | |
638 | return; | |
639 | } | |
640 | rcode = hdr.flags & REQ_FLAG_RCODE_MASK; | |
641 | if (rcode != RCODE_NO_ERROR) { | |
642 | sar_request_fail(req, rcode); | |
643 | } else if (sar_decode_answer(req, &hdr, (unsigned char*)buf, res)) { | |
644 | ns->resp_scrambled++; | |
645 | sar_request_fail(req, RCODE_FORMAT_ERROR); | |
646 | } | |
647 | } | |
648 | ||
649 | static void | |
650 | sar_build_nslist(struct string_list *nslist) | |
651 | { | |
652 | dict_iterator_t it, next; | |
653 | struct sar_nameserver *ns; | |
654 | unsigned int ii; | |
655 | ||
656 | for (it = dict_first(sar_nameservers); it; it = iter_next(it)) { | |
657 | ns = iter_data(it); | |
658 | ns->valid = 0; | |
659 | } | |
660 | ||
661 | for (ii = 0; ii < nslist->used; ++ii) { | |
662 | const char *name; | |
663 | ||
664 | name = nslist->list[ii]; | |
665 | ns = dict_find(sar_nameservers, name, NULL); | |
666 | if (!ns) { | |
667 | ns = calloc(1, sizeof(*ns) + strlen(name) + 1); | |
668 | ns->name = (char*)(ns + 1); | |
669 | strcpy(ns->name, name); | |
670 | ns->ss_len = sizeof(ns->ss); | |
671 | if (!sar_pton((struct sockaddr*)&ns->ss, sizeof(ns->ss), NULL, name)) { | |
672 | free(it); | |
673 | continue; | |
674 | } | |
675 | sar_set_port((struct sockaddr*)&ns->ss, sizeof(ns->ss), 53); | |
676 | ns->ss_len = sar_helpers[ns->ss.ss_family]->socklen; | |
677 | dict_insert(sar_nameservers, ns->name, ns); | |
678 | } | |
679 | ns->valid = 1; | |
680 | } | |
681 | ||
682 | for (it = dict_first(sar_nameservers); it; it = next) { | |
683 | next = iter_next(it); | |
684 | ns = iter_data(it); | |
685 | if (!ns->valid) | |
686 | dict_remove(sar_nameservers, ns->name); | |
687 | } | |
688 | } | |
689 | ||
690 | static int | |
691 | sar_open_fd(void) | |
692 | { | |
693 | int res; | |
694 | ||
695 | /* Build list of nameservers. */ | |
696 | sar_build_nslist(conf.sar_nslist); | |
697 | ||
698 | if (conf.sar_bind_address.ss_family != 0) { | |
699 | struct addrinfo *ai; | |
700 | ||
701 | ai = (struct addrinfo*)&conf.sar_bind_address; | |
702 | sar_fd_fd = socket(ai->ai_family, SOCK_DGRAM, 0); | |
703 | if (sar_fd_fd < 0) { | |
704 | log_module(sar_log, LOG_FATAL, "Unable to create resolver socket: %s", strerror(errno)); | |
705 | return 1; | |
706 | } | |
707 | ||
708 | res = bind(sar_fd_fd, ai->ai_addr, ai->ai_addrlen); | |
709 | if (res < 0) | |
710 | log_module(sar_log, LOG_ERROR, "Unable to bind resolver socket to address [%s]:%s: %s", (char*)conf_get_data("modules/sar/bind_address", RECDB_QSTRING), (char*)conf_get_data("modules/sar/bind_port", RECDB_QSTRING), strerror(errno)); | |
711 | } else { | |
712 | dict_iterator_t it; | |
713 | struct sar_nameserver *ns; | |
714 | ||
715 | it = dict_first(sar_nameservers); | |
716 | ns = iter_data(it); | |
717 | sar_fd_fd = socket(ns->ss.ss_family, SOCK_DGRAM, 0); | |
718 | if (sar_fd_fd < 0) { | |
719 | log_module(sar_log, LOG_FATAL, "Unable to create resolver socket: %s", strerror(errno)); | |
720 | return 1; | |
721 | } | |
722 | } | |
723 | ||
724 | sar_fd = ioset_add(sar_fd_fd); | |
725 | if (!sar_fd) { | |
726 | log_module(sar_log, LOG_FATAL, "Unable to register resolver socket with event loop."); | |
727 | return 1; | |
728 | } | |
729 | sar_fd->state = IO_CONNECTED; | |
730 | sar_fd->readable_cb = sar_fd_readable; | |
731 | return 0; | |
732 | } | |
733 | ||
734 | struct name_ofs { | |
735 | const char *name; | |
736 | unsigned int ofs; | |
737 | }; | |
738 | ||
739 | static int | |
740 | set_compare_charp(const void *a_, const void *b_) | |
741 | { | |
742 | char * const *a = a_, * const *b = b_; | |
743 | return strcasecmp(*a, *b); | |
744 | } | |
745 | ||
746 | static void | |
747 | string_buffer_reserve(struct string_buffer *cv, unsigned int min_length) | |
748 | { | |
749 | if (cv->size < min_length) { | |
750 | char *new_buffer; | |
751 | new_buffer = realloc(cv->list, min_length); | |
752 | if (new_buffer) { | |
753 | cv->size = min_length; | |
754 | cv->list = new_buffer; | |
755 | } | |
756 | } | |
757 | } | |
758 | ||
759 | /** Append \a name to \a cv in compressed form. */ | |
760 | static int | |
761 | sar_append_name(struct string_buffer *cv, const char *name, struct name_ofs *ofs, unsigned int *used, unsigned int alloc) | |
762 | { | |
763 | struct name_ofs *pofs; | |
764 | unsigned int len; | |
765 | ||
766 | while (1) { | |
767 | pofs = bsearch(&name, ofs, *used, sizeof(ofs[0]), set_compare_charp); | |
768 | if (pofs) { | |
769 | string_buffer_reserve(cv, cv->used + 2); | |
770 | cv->list[cv->used++] = RES_SF_POINTER | (pofs->ofs >> 8); | |
771 | cv->list[cv->used++] = pofs->ofs & 255; | |
772 | return 0; | |
773 | } | |
774 | len = strcspn(name, "."); | |
775 | if (len > 63) | |
776 | return 1; | |
777 | if (*used < alloc) { | |
778 | ofs[*used].name = name; | |
779 | ofs[*used].ofs = cv->used; | |
780 | qsort(ofs, (*used)++, sizeof(ofs[0]), set_compare_charp); | |
781 | } | |
782 | string_buffer_reserve(cv, cv->used + len + 1); | |
783 | cv->list[cv->used] = RES_SF_LABEL | len; | |
784 | memcpy(cv->list + cv->used + 1, name, len); | |
785 | cv->used += len + 1; | |
786 | if (name[len] == '.') | |
787 | name += len + 1; | |
788 | else if (name[len] == '\0') | |
789 | break; | |
790 | } | |
791 | string_buffer_append(cv, '\0'); | |
792 | return 0; | |
793 | } | |
794 | ||
795 | /** Build a DNS question packet from a variable-length argument list. | |
796 | * In \a args, there is at least one pari consisting of const char | |
797 | * *name and unsigned int qtype. A null name argument terminates the | |
798 | * list. | |
799 | */ | |
800 | unsigned int | |
801 | sar_request_vbuild(struct sar_request *req, va_list args) | |
802 | { | |
803 | struct name_ofs suffixes[32]; | |
804 | struct string_buffer cv; | |
805 | const char *name; | |
806 | unsigned int suf_used; | |
807 | unsigned int val; | |
808 | unsigned int qdcount; | |
809 | ||
810 | cv.used = 0; | |
811 | cv.size = 512; | |
812 | cv.list = calloc(1, cv.size); | |
813 | suf_used = 0; | |
814 | val = REQ_OPCODE_QUERY | REQ_FLAG_RD; | |
815 | cv.list[0] = req->id >> 8; | |
816 | cv.list[1] = req->id & 255; | |
817 | cv.list[2] = val >> 8; | |
818 | cv.list[3] = val & 255; | |
819 | cv.list[6] = cv.list[7] = cv.list[8] = cv.list[9] = cv.list[10] = 0; | |
820 | cv.used = 12; | |
821 | for (qdcount = 0; (name = va_arg(args, const char*)); ++qdcount) { | |
822 | if (sar_append_name(&cv, name, suffixes, &suf_used, ArrayLength(suffixes))) { | |
823 | string_buffer_clean(&cv); | |
824 | goto out; | |
825 | } | |
826 | string_buffer_reserve(&cv, cv.used + 4); | |
827 | val = va_arg(args, unsigned int); | |
828 | cv.list[cv.used++] = val >> 8; | |
829 | cv.list[cv.used++] = val & 255; | |
830 | cv.list[cv.used++] = REQ_CLASS_IN >> 8; | |
831 | cv.list[cv.used++] = REQ_CLASS_IN & 255; | |
832 | } | |
833 | cv.list[4] = qdcount >> 8; | |
834 | cv.list[5] = qdcount & 255; | |
835 | val = conf.sar_edns0; | |
836 | if (val) { | |
837 | string_buffer_reserve(&cv, cv.used + 11); | |
838 | cv.list[cv.used + 0] = '\0'; /* empty name */ | |
839 | cv.list[cv.used + 1] = REQ_TYPE_OPT >> 8; | |
840 | cv.list[cv.used + 2] = REQ_TYPE_OPT & 255; | |
841 | cv.list[cv.used + 3] = val >> 8; | |
842 | cv.list[cv.used + 4] = val & 255; | |
843 | cv.list[cv.used + 5] = 0; /* extended-rcode */ | |
844 | cv.list[cv.used + 6] = 0; /* version */ | |
845 | cv.list[cv.used + 7] = 0; /* reserved */ | |
846 | cv.list[cv.used + 8] = 0; /* reserved */ | |
847 | cv.list[cv.used + 9] = 0; /* msb rdlen */ | |
848 | cv.list[cv.used + 10] = 0; /* lsb rdlen */ | |
849 | cv.used += 11; | |
850 | cv.list[11] = 1; /* update arcount */ | |
851 | } else cv.list[11] = 0; | |
852 | ||
853 | out: | |
854 | free(req->body); | |
855 | req->body = (unsigned char*)cv.list; | |
856 | req->body_len = cv.used; | |
857 | return cv.used; | |
858 | } | |
859 | ||
860 | /** Build a DNS question packet. After \a req, there is at least one | |
861 | * pair consisting of const char *name and unsigned int qtype. A null | |
862 | * name argument terminates the list. | |
863 | */ | |
864 | unsigned int | |
865 | sar_request_build(struct sar_request *req, ...) | |
866 | { | |
867 | va_list vargs; | |
868 | unsigned int ret; | |
869 | va_start(vargs, req); | |
870 | ret = sar_request_vbuild(req, vargs); | |
871 | va_end(vargs); | |
872 | return ret; | |
873 | } | |
874 | ||
875 | void | |
876 | sar_request_send(struct sar_request *req) | |
877 | { | |
878 | dict_iterator_t it; | |
879 | ||
880 | /* make sure we have our local socket */ | |
881 | if (!sar_fd && sar_open_fd()) { | |
882 | sar_request_fail(req, RCODE_SOCKET_FAILURE); | |
883 | return; | |
884 | } | |
885 | ||
886 | log_module(sar_log, LOG_DEBUG, "sar_request_send({id=%d})", req->id); | |
887 | ||
888 | /* send query to each configured nameserver */ | |
889 | for (it = dict_first(sar_nameservers); it; it = iter_next(it)) { | |
890 | struct sar_nameserver *ns; | |
891 | int res; | |
892 | ||
893 | ns = iter_data(it); | |
894 | res = sendto(sar_fd_fd, req->body, req->body_len, 0, (struct sockaddr*)&ns->ss, ns->ss_len); | |
895 | if (res > 0) { | |
896 | ns->req_sent++; | |
897 | log_module(sar_log, LOG_DEBUG, "Sent %u bytes to %s.", res, ns->name); | |
898 | } else if (res < 0) | |
899 | log_module(sar_log, LOG_ERROR, "Unable to send %u bytes to nameserver %s: %s", req->body_len, ns->name, strerror(errno)); | |
900 | else /* res == 0 */ | |
901 | assert(0 && "resolver sendto() unexpectedly returned zero"); | |
902 | } | |
903 | ||
904 | /* Check that query timeout is soon enough. */ | |
905 | req->expiry = now + (conf.sar_timeout << ++req->retries); | |
906 | sar_check_timeout(req->expiry); | |
907 | } | |
908 | ||
909 | struct sar_request * | |
910 | sar_request_alloc(unsigned int data_len, sar_request_ok_cb ok_cb, sar_request_fail_cb fail_cb) | |
911 | { | |
912 | struct sar_request *req; | |
913 | ||
914 | req = calloc(1, sizeof(*req) + data_len); | |
915 | req->cb_ok = ok_cb; | |
916 | req->cb_fail = fail_cb; | |
917 | do { | |
918 | req->id = rand() & 0xffff; | |
919 | sprintf(req->id_text, "%d", req->id); | |
920 | } while (dict_find(sar_requests, req->id_text, NULL)); | |
921 | dict_insert(sar_requests, req->id_text, req); | |
922 | log_module(sar_log, LOG_DEBUG, "sar_request_alloc(%d) -> {id=%d}", data_len, req->id); | |
923 | return req; | |
924 | } | |
925 | ||
926 | struct sar_request * | |
927 | sar_request_simple(unsigned int data_len, sar_request_ok_cb ok_cb, sar_request_fail_cb fail_cb, ...) | |
928 | { | |
929 | struct sar_request *req; | |
930 | ||
931 | req = sar_request_alloc(data_len, ok_cb, fail_cb); | |
932 | if (req) { | |
933 | va_list args; | |
934 | ||
935 | va_start(args, fail_cb); | |
936 | sar_request_vbuild(req, args); | |
937 | va_end(args); | |
938 | sar_request_send(req); | |
939 | } | |
940 | return req; | |
941 | } | |
942 | ||
943 | enum service_proto { | |
944 | SERVICE_UDP, | |
945 | SERVICE_TCP, | |
946 | SERVICE_NUM_PROTOS | |
947 | }; | |
948 | ||
949 | struct service_byname { | |
950 | const char *name; /* service name */ | |
951 | struct { | |
952 | /* note: if valid != 0, port == 0, check canonical entry */ | |
953 | struct service_byname *canon; /* if NULL, this is canonical */ | |
954 | uint16_t port; | |
955 | unsigned int valid : 1; | |
956 | unsigned int srv : 1; | |
957 | } protos[SERVICE_NUM_PROTOS]; | |
958 | }; | |
959 | ||
960 | struct service_byport { | |
961 | unsigned int port; | |
962 | char port_text[6]; | |
963 | struct service_byname *byname[SERVICE_NUM_PROTOS]; | |
964 | }; | |
965 | ||
966 | static dict_t services_byname; /* contains struct service_byname */ | |
967 | static dict_t services_byport; /* contains struct service_byport */ | |
968 | ||
969 | static struct service_byname * | |
970 | sar_service_byname(const char *name, int autocreate) | |
971 | { | |
972 | struct service_byname *byname; | |
973 | ||
974 | byname = dict_find(services_byname, name, NULL); | |
975 | if (!byname && autocreate) { | |
976 | byname = calloc(1, sizeof(*byname) + strlen(name) + 1); | |
977 | byname->name = strcpy((char*)(byname + 1), name); | |
978 | dict_insert(services_byname, byname->name, byname); | |
979 | } | |
980 | return byname; | |
981 | } | |
982 | ||
983 | static struct service_byport * | |
984 | sar_service_byport(unsigned int port, int autocreate) | |
985 | { | |
986 | struct service_byport *byport; | |
987 | char port_text[12]; | |
988 | ||
989 | sprintf(port_text, "%d", port); | |
990 | byport = dict_find(services_byport, port_text, NULL); | |
991 | if (!byport && autocreate) { | |
992 | byport = calloc(1, sizeof(*byport)); | |
993 | byport->port = port; | |
994 | sprintf(byport->port_text, "%d", port); | |
995 | dict_insert(services_byport, byport->port_text, byport); | |
996 | } | |
997 | return byport; | |
998 | } | |
999 | ||
1000 | static void | |
1001 | sar_services_load_file(const char *etc_services) | |
1002 | { | |
1003 | static const char *whitespace = " \t\r\n"; | |
1004 | struct service_byname *canon; | |
1005 | struct service_byport *byport; | |
1006 | char *name, *port, *alias, *ptr; | |
1007 | FILE *file; | |
1008 | unsigned int pnum; | |
1009 | enum service_proto proto; | |
1010 | char linebuf[LINE_MAX]; | |
1011 | ||
1012 | file = fopen(etc_services, "r"); | |
1013 | if (!file) | |
1014 | return; | |
1015 | while (fgets(linebuf, sizeof(linebuf), file)) { | |
1016 | ptr = strchr(linebuf, '#'); | |
1017 | if (ptr) | |
1018 | *ptr = '\0'; | |
1019 | /* Tokenize canonical service name and port number. */ | |
1020 | name = strtok_r(linebuf, whitespace, &ptr); | |
1021 | if (name == NULL) | |
1022 | continue; | |
1023 | port = strtok_r(NULL, whitespace, &ptr); | |
1024 | if (port == NULL) | |
1025 | continue; | |
1026 | pnum = strtoul(port, &port, 10); | |
1027 | if (pnum == 0 || *port++ != '/') | |
1028 | continue; | |
1029 | if (!strcmp(port, "udp")) | |
1030 | proto = SERVICE_UDP; | |
1031 | else if (!strcmp(port, "tcp")) | |
1032 | proto = SERVICE_TCP; | |
1033 | else continue; | |
1034 | ||
1035 | /* Set up canonical name-indexed service entry. */ | |
1036 | canon = sar_service_byname(name, 1); | |
1037 | if (canon->protos[proto].valid) { | |
63e4abc0 | 1038 | /* log_module(sar_log, LOG_ERROR, "Service %s/%s listed twice.", name, port); who cares? */ |
1136f709 | 1039 | continue; |
1040 | } | |
1041 | canon->protos[proto].canon = NULL; | |
1042 | canon->protos[proto].port = pnum; | |
1043 | canon->protos[proto].valid = 1; | |
1044 | ||
1045 | /* Set up port-indexed service entry. */ | |
1046 | byport = sar_service_byport(pnum, 1); | |
1047 | if (!byport->byname[proto]) | |
1048 | byport->byname[proto] = canon; | |
1049 | ||
1050 | /* Add alias entries. */ | |
1051 | while ((alias = strtok_r(NULL, whitespace, &ptr))) { | |
1052 | struct service_byname *byname; | |
1053 | ||
1054 | byname = sar_service_byname(alias, 1); | |
1055 | if (byname->protos[proto].valid) { | |
1056 | /* We do not log this since there are a lot of | |
1057 | * duplicate aliases, some only differing in case. */ | |
1058 | continue; | |
1059 | } | |
1060 | byname->protos[proto].canon = canon; | |
1061 | byname->protos[proto].port = pnum; | |
1062 | byname->protos[proto].valid = 1; | |
1063 | } | |
1064 | } | |
1065 | fclose(file); | |
1066 | } | |
1067 | ||
1068 | static void | |
1069 | sar_services_init(const char *etc_services) | |
1070 | { | |
1071 | /* These are a portion of the services listed at | |
1072 | * http://www.dns-sd.org/ServiceTypes.html. | |
1073 | */ | |
1074 | static const char *tcp_srvs[] = { "cvspserver", "distcc", "ftp", "http", | |
1075 | "imap", "ipp", "irc", "ldap", "login", "nfs", "pop3", "postgresql", | |
1076 | "rsync", "sftp-ssh", "soap", "ssh", "telnet", "webdav", "xmpp-client", | |
1077 | "xmpp-server", "xul-http", NULL }; | |
1078 | static const char *udp_srvs[] = { "bootps", "dns-update", "domain", "nfs", | |
1079 | "ntp", "tftp", NULL }; | |
1080 | struct service_byname *byname; | |
1081 | unsigned int ii; | |
1082 | ||
1083 | /* Forget old services dicts and allocate new ones. */ | |
1084 | dict_delete(services_byname); | |
1085 | services_byname = dict_new(); | |
1086 | dict_set_free_data(services_byname, free); | |
1087 | ||
1088 | dict_delete(services_byport); | |
1089 | services_byport = dict_new(); | |
1090 | dict_set_free_data(services_byport, free); | |
1091 | ||
1092 | /* Load the list from the services file. */ | |
1093 | sar_services_load_file(etc_services); | |
1094 | ||
1095 | /* Mark well-known services as using DNS-SD SRV records. */ | |
1096 | for (ii = 0; tcp_srvs[ii]; ++ii) { | |
1097 | byname = sar_service_byname(tcp_srvs[ii], 1); | |
1098 | byname->protos[SERVICE_TCP].srv = 1; | |
1099 | } | |
1100 | ||
1101 | for (ii = 0; udp_srvs[ii]; ++ii) { | |
1102 | byname = sar_service_byname(udp_srvs[ii], 1); | |
1103 | byname->protos[SERVICE_UDP].srv = 1; | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | static void | |
1108 | sar_register_helper(struct sar_family_helper *helper) | |
1109 | { | |
1110 | assert(helper->family <= MAX_FAMILY); | |
1111 | sar_helpers[helper->family] = helper; | |
1112 | helper->next = sar_first_helper; | |
1113 | sar_first_helper = helper; | |
1114 | } | |
1115 | ||
1116 | static unsigned int | |
1117 | sar_addrlen(const struct sockaddr *sa, UNUSED_ARG(unsigned int size)) | |
1118 | { | |
1119 | return sa->sa_family <= MAX_FAMILY && sar_helpers[sa->sa_family] | |
1120 | ? sar_helpers[sa->sa_family]->socklen : 0; | |
1121 | } | |
1122 | ||
1123 | struct sar_getaddr_state { | |
1124 | struct sar_family_helper *helper; | |
1125 | struct addrinfo *ai_head; | |
1126 | struct addrinfo *ai_tail; | |
1127 | sar_addr_cb cb; | |
1128 | void *cb_ctx; | |
1129 | unsigned int search_pos; | |
1130 | unsigned int flags, socktype, protocol, port; | |
1131 | unsigned int srv_ofs; | |
1132 | char full_name[DNS_NAME_LENGTH]; | |
1133 | }; | |
1134 | ||
1135 | static unsigned int | |
1136 | sar_getaddr_append(struct sar_getaddr_state *state, struct addrinfo *ai, int copy) | |
1137 | { | |
1138 | unsigned int count; | |
1139 | ||
1140 | log_module(sar_log, LOG_DEBUG, "sar_getaddr_append({full_name=%s}, ai=%p, copy=%d)", state->full_name, (void*)ai, copy); | |
1141 | ||
1142 | /* Set the appropriate pointer to the new element(s). */ | |
1143 | if (state->ai_tail) | |
1144 | state->ai_tail->ai_next = ai; | |
1145 | else | |
1146 | state->ai_head = ai; | |
1147 | ||
1148 | /* Find the end of the list. */ | |
1149 | if (copy) { | |
1150 | /* Make sure we copy fields for both the first and last entries. */ | |
1151 | count = 1; | |
1152 | while (1) { | |
1153 | if (!ai->ai_addrlen) { | |
1154 | assert(sar_helpers[ai->ai_family]); | |
1155 | ai->ai_addrlen = sar_helpers[ai->ai_family]->socklen; | |
1156 | } | |
1157 | #if defined(HAVE_SOCKADDR_SA_LEN) | |
1158 | ai->ai_addr->sa_len = ai->ai_addrlen; | |
1159 | #endif | |
1160 | ai->ai_addr->sa_family = ai->ai_family; | |
1161 | ai->ai_socktype = state->socktype; | |
1162 | ai->ai_protocol = state->protocol; | |
1163 | if (!ai->ai_next) | |
1164 | break; | |
1165 | count++; | |
1166 | ai = ai->ai_next; | |
1167 | } | |
1168 | } else { | |
1169 | for (count = 1; ai->ai_next; ++count, ai = ai->ai_next) | |
1170 | ; | |
1171 | } | |
1172 | ||
1173 | /* Set the tail pointer and return count of appended items. */ | |
1174 | state->ai_tail = ai; | |
1175 | return count; | |
1176 | } | |
1177 | ||
1178 | static struct sar_request * | |
1179 | sar_getaddr_request(struct sar_request *req) | |
1180 | { | |
1181 | struct sar_getaddr_state *state; | |
1182 | unsigned int len; | |
1183 | char full_name[DNS_NAME_LENGTH]; | |
1184 | ||
1185 | state = (struct sar_getaddr_state*)(req + 1); | |
1186 | ||
1187 | /* If we can and should, append the current search domain. */ | |
1188 | if (state->search_pos < conf.sar_search->used) | |
1189 | snprintf(full_name, sizeof(full_name), "%s.%s", state->full_name, conf.sar_search->list[state->search_pos]); | |
1190 | else if (state->search_pos == conf.sar_search->used) | |
1191 | safestrncpy(full_name, state->full_name, sizeof(full_name)); | |
1192 | else { | |
1193 | log_module(sar_log, LOG_DEBUG, "sar_getaddr_request({id=%d}): failed", req->id); | |
1194 | state->cb(state->cb_ctx, NULL, SAI_NONAME); | |
1195 | return NULL; | |
1196 | } | |
1197 | ||
1198 | /* Build the appropriate request for DNS record(s). */ | |
1199 | if (state->flags & SAI_ALL) | |
1200 | len = sar_request_build(req, full_name + state->srv_ofs, REQ_QTYPE_ALL, NULL); | |
1201 | else if (state->srv_ofs) | |
1202 | len = state->helper->build_addr_request(req, full_name + state->srv_ofs, full_name, state->flags); | |
1203 | else | |
1204 | len = state->helper->build_addr_request(req, full_name, NULL, state->flags); | |
1205 | ||
1206 | log_module(sar_log, LOG_DEBUG, "sar_getaddr_request({id=%d}): full_name=%s, srv_ofs=%d", req->id, full_name, state->srv_ofs); | |
1207 | ||
1208 | /* Check that the request could be built. */ | |
1209 | if (!len) { | |
1210 | state->cb(state->cb_ctx, NULL, SAI_NODATA); | |
1211 | return NULL; | |
1212 | } | |
1213 | ||
1214 | /* Send the request. */ | |
1215 | sar_request_send(req); | |
1216 | return req; | |
1217 | } | |
1218 | ||
1219 | static int | |
1220 | sar_getaddr_decode(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, unsigned char *raw, unsigned int raw_size, unsigned int rr_idx) | |
1221 | { | |
1222 | struct sar_getaddr_state *state; | |
1223 | char *cname; | |
1224 | unsigned int jj, pos, hit; | |
1225 | ||
1226 | log_module(sar_log, LOG_DEBUG, " sar_getaddr_decode(id=%d, <hdr>, {type=%d, rdlength=%d, name=%s}, <data>, %u, <idx>)", hdr->id, rr[rr_idx].type, rr[rr_idx].rdlength, rr[rr_idx].name, raw_size); | |
1227 | state = (struct sar_getaddr_state*)(req + 1); | |
1228 | ||
1229 | switch (rr[rr_idx].type) { | |
1230 | case REQ_TYPE_A: | |
1231 | if (state->flags & SAI_ALL) | |
1232 | return sar_ipv4_helper.decode_addr(state, rr + rr_idx, raw, raw_size); | |
1233 | #if defined(AF_INET6) | |
1234 | else if (state->flags & SAI_V4MAPPED) | |
1235 | return sar_ipv6_helper.decode_addr(state, rr + rr_idx, raw, raw_size); | |
1236 | #endif | |
1237 | return state->helper->decode_addr(state, rr + rr_idx, raw, raw_size); | |
1238 | ||
1239 | case REQ_TYPE_AAAA: | |
1240 | #if defined(AF_INET6) | |
1241 | if (state->flags & SAI_ALL) | |
1242 | return sar_ipv6_helper.decode_addr(state, rr + rr_idx, raw, raw_size); | |
1243 | return state->helper->decode_addr(state, rr + rr_idx, raw, raw_size); | |
1244 | #else | |
1245 | return 0; | |
1246 | #endif | |
1247 | ||
1248 | case REQ_TYPE_CNAME: | |
1249 | /* there should be the canonical name next */ | |
1250 | pos = rr[rr_idx].rd_start; | |
1251 | cname = sar_extract_name(raw, raw_size, &pos); | |
1252 | if (!cname) | |
1253 | return 0; /* XXX: eventually log the unhandled body */ | |
1254 | /* and it should correspond to some other answer in the response */ | |
1255 | for (jj = hit = 0; jj < hdr->ancount; ++jj) { | |
1256 | if (strcasecmp(cname, rr[jj].name)) | |
1257 | continue; | |
1258 | hit += sar_getaddr_decode(req, hdr, rr, raw, raw_size, jj); | |
1259 | } | |
1260 | /* XXX: if (!hit) handle or log the incomplete recursion; */ | |
1261 | return hit; | |
1262 | ||
1263 | case REQ_TYPE_SRV: | |
1264 | /* TODO: decode the SRV record */ | |
1265 | ||
1266 | default: | |
1267 | return 0; | |
1268 | } | |
1269 | } | |
1270 | ||
1271 | static void | |
1272 | sar_getaddr_ok(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, unsigned char *raw, unsigned int raw_size) | |
1273 | { | |
1274 | struct sar_getaddr_state *state; | |
1275 | unsigned int ii; | |
1276 | ||
1277 | state = (struct sar_getaddr_state*)(req + 1); | |
1278 | ||
1279 | log_module(sar_log, LOG_DEBUG, "sar_getaddr_ok({id=%d}, {id=%d}, <rr>, <data>, %u)", req->id, hdr->id, raw_size); | |
1280 | for (ii = 0; ii < hdr->ancount; ++ii) | |
1281 | sar_getaddr_decode(req, hdr, rr, raw, raw_size, ii); | |
1282 | ||
1283 | /* If we found anything, report it, else try again. */ | |
1284 | if (state->ai_head) | |
1285 | state->cb(state->cb_ctx, state->ai_head, SAI_SUCCESS); | |
1286 | else | |
1287 | sar_getaddr_request(req); | |
1288 | } | |
1289 | ||
1290 | static void | |
1291 | sar_getaddr_fail(struct sar_request *req, UNUSED_ARG(unsigned int rcode)) | |
1292 | { | |
1293 | struct sar_getaddr_state *state; | |
1294 | ||
1295 | log_module(sar_log, LOG_DEBUG, "sar_getaddr_fail({id=%d}, rcode=%u)", req->id, rcode); | |
1296 | state = (struct sar_getaddr_state*)(req + 1); | |
1297 | state->cb(state->cb_ctx, NULL, SAI_FAIL); | |
1298 | } | |
1299 | ||
1300 | struct sar_request * | |
1301 | sar_getaddr(const char *node, const char *service, const struct addrinfo *hints_, sar_addr_cb cb, void *cb_ctx) | |
1302 | { | |
1303 | struct sockaddr_storage ss; | |
1304 | struct addrinfo hints; | |
1305 | struct sar_family_helper *helper; | |
1306 | struct service_byname *svc; | |
1307 | char *end; | |
1308 | unsigned int portnum; | |
1309 | unsigned int pos; | |
1310 | enum service_proto proto; | |
1311 | ||
1312 | if (!node && !service) { | |
1313 | cb(cb_ctx, NULL, SAI_NONAME); | |
1314 | return NULL; | |
1315 | } | |
1316 | ||
1317 | /* Initialize local hints structure. */ | |
1318 | if (hints_) | |
1319 | memcpy(&hints, hints_, sizeof(hints)); | |
1320 | else | |
1321 | memset(&hints, 0, sizeof(hints)); | |
1322 | ||
1323 | /* Translate socket type to internal protocol. */ | |
1324 | switch (hints.ai_socktype) { | |
1325 | case 0: hints.ai_socktype = SOCK_STREAM; /* and fall through */ | |
1326 | case SOCK_STREAM: proto = SERVICE_TCP; break; | |
1327 | case SOCK_DGRAM: proto = SERVICE_UDP; break; | |
1328 | default: | |
1329 | cb(cb_ctx, NULL, SAI_SOCKTYPE); | |
1330 | return NULL; | |
1331 | } | |
1332 | ||
1333 | /* Figure out preferred socket size. */ | |
1334 | if (hints.ai_family == AF_UNSPEC) | |
1335 | hints.ai_family = AF_INET; | |
1336 | if (hints.ai_family > MAX_FAMILY | |
1337 | || !(helper = sar_helpers[hints.ai_family])) { | |
1338 | cb(cb_ctx, NULL, SAI_FAMILY); | |
1339 | return NULL; | |
1340 | } | |
1341 | hints.ai_addrlen = helper->socklen; | |
1342 | ||
1343 | /* If \a node is NULL, figure out the correct default from the | |
1344 | * requested family and SAI_PASSIVE flag. | |
1345 | */ | |
1346 | if (node == NULL) | |
1347 | node = (hints.ai_flags & SAI_PASSIVE) ? helper->unspec_addr : helper->localhost_addr; | |
1348 | ||
1349 | /* Try to parse (failing that, look up) \a service. */ | |
1350 | if (!service) | |
1351 | portnum = 0, svc = NULL; | |
1352 | else if ((portnum = strtoul(service, &end, 10)), *end == '\0') | |
1353 | svc = NULL; | |
1354 | else if ((svc = sar_service_byname(service, 0)) != NULL) | |
1355 | portnum = svc->protos[proto].port; | |
1356 | else { | |
1357 | cb(cb_ctx, NULL, SAI_SERVICE); | |
1358 | return NULL; | |
1359 | } | |
1360 | ||
1361 | /* Try to parse \a node as a numeric hostname.*/ | |
1362 | pos = sar_pton((struct sockaddr*)&ss, sizeof(ss), NULL, node); | |
1363 | if (pos && node[pos] == '\0') { | |
1364 | struct addrinfo *ai; | |
1365 | char canonname[SAR_NTOP_MAX]; | |
1366 | ||
1367 | /* we have a valid address; use it */ | |
1368 | sar_set_port((struct sockaddr*)&ss, sizeof(ss), portnum); | |
1369 | hints.ai_addrlen = sar_addrlen((struct sockaddr*)&ss, sizeof(ss)); | |
1370 | if (!hints.ai_addrlen) { | |
1371 | cb(cb_ctx, NULL, SAI_FAMILY); | |
1372 | return NULL; | |
1373 | } | |
1374 | pos = sar_ntop(canonname, sizeof(canonname), (struct sockaddr*)&ss, hints.ai_addrlen); | |
1375 | ||
1376 | /* allocate and fill in the addrinfo response */ | |
1377 | ai = calloc(1, sizeof(*ai) + hints.ai_addrlen + pos + 1); | |
1378 | ai->ai_family = ss.ss_family; | |
1379 | ai->ai_socktype = hints.ai_socktype; | |
1380 | ai->ai_protocol = hints.ai_protocol; | |
1381 | ai->ai_addrlen = hints.ai_addrlen; | |
1382 | ai->ai_addr = memcpy(ai + 1, &ss, ai->ai_addrlen); | |
1383 | ai->ai_canonname = strcpy((char*)ai->ai_addr + ai->ai_addrlen, canonname); | |
1384 | cb(cb_ctx, ai, SAI_SUCCESS); | |
1385 | return NULL; | |
1386 | } else if (hints.ai_flags & SAI_NUMERICHOST) { | |
1387 | cb(cb_ctx, NULL, SAI_NONAME); | |
1388 | return NULL; | |
1389 | } else { | |
1390 | struct sar_request *req; | |
1391 | struct sar_getaddr_state *state; | |
1392 | unsigned int len, ii; | |
1393 | ||
1394 | req = sar_request_alloc(sizeof(*state), sar_getaddr_ok, sar_getaddr_fail); | |
1395 | ||
1396 | state = (struct sar_getaddr_state*)(req + 1); | |
1397 | state->helper = helper; | |
1398 | state->ai_head = state->ai_tail = NULL; | |
1399 | state->cb = cb; | |
1400 | state->cb_ctx = cb_ctx; | |
1401 | state->flags = hints.ai_flags; | |
1402 | state->socktype = hints.ai_socktype; | |
1403 | state->protocol = hints.ai_protocol; | |
1404 | state->port = portnum; | |
1405 | ||
1406 | if ((state->flags & SAI_NOSRV) || !svc) | |
1407 | state->srv_ofs = 0; | |
1408 | else if (svc->protos[proto].srv) | |
1409 | state->srv_ofs = snprintf(state->full_name, sizeof(state->full_name), "_%s._%s.", svc->name, (proto == SERVICE_UDP ? "udp" : "tcp")); | |
1410 | else if (state->flags & SAI_FORCESRV) | |
1411 | state->srv_ofs = snprintf(state->full_name, sizeof(state->full_name), "_%s._%s.", service, (proto == SERVICE_UDP ? "udp" : "tcp")); | |
1412 | else | |
1413 | state->srv_ofs = 0; | |
1414 | ||
1415 | if (state->srv_ofs < sizeof(state->full_name)) | |
1416 | safestrncpy(state->full_name + state->srv_ofs, node, sizeof(state->full_name) - state->srv_ofs); | |
1417 | ||
1418 | for (ii = len = 0; node[ii]; ++ii) | |
1419 | if (node[ii] == '.') | |
1420 | len++; | |
1421 | if (len >= conf.sar_ndots) | |
1422 | state->search_pos = conf.sar_search->used; | |
1423 | else | |
1424 | state->search_pos = 0; | |
1425 | ||
1426 | /* XXX: fill in *state with any other fields needed to parse responses. */ | |
1427 | ||
1428 | if (!sar_getaddr_request(req)) { | |
1429 | free(req); | |
1430 | return NULL; | |
1431 | } | |
1432 | return req; | |
1433 | } | |
1434 | } | |
1435 | ||
1436 | struct sar_getname_state { | |
1437 | sar_name_cb cb; | |
1438 | void *cb_ctx; | |
1439 | char *hostname; | |
1440 | unsigned int flags; | |
1441 | unsigned int family; | |
1442 | enum service_proto proto; | |
1443 | unsigned short port; | |
1444 | unsigned int doing_arpa : 1; /* checking .ip6.arpa vs .ip6.int */ | |
1445 | unsigned char original[16]; /* original address data */ | |
1446 | /* name must be long enough to hold "0.0.<etc>.ip6.arpa" */ | |
1447 | char name[74]; | |
1448 | }; | |
1449 | ||
1450 | static void | |
1451 | sar_getname_fail(struct sar_request *req, UNUSED_ARG(unsigned int rcode)) | |
1452 | { | |
1453 | struct sar_getname_state *state; | |
1454 | unsigned int len; | |
1455 | ||
1456 | state = (struct sar_getname_state*)(req + 1); | |
1457 | if (state->doing_arpa) { | |
1458 | len = strlen(state->name); | |
1459 | assert(len == 73); | |
1460 | strcpy(state->name + len - 4, "int"); | |
1461 | len = sar_request_build(req, state->name, REQ_TYPE_PTR, NULL); | |
1462 | if (len) { | |
1463 | sar_request_send(req); | |
1464 | return; | |
1465 | } | |
1466 | } | |
1467 | state->cb(state->cb_ctx, NULL, NULL, SAI_FAIL); | |
1468 | free(state->hostname); | |
1469 | } | |
1470 | ||
1471 | static const char *sar_getname_port(unsigned int port, unsigned int flags, char *tmpbuf, unsigned int tmpbuf_len) | |
1472 | { | |
1473 | struct service_byport *service; | |
1474 | enum service_proto proto; | |
1475 | char port_text[12]; | |
1476 | ||
1477 | sprintf(port_text, "%d", port); | |
1478 | proto = (flags & SNI_DGRAM) ? SERVICE_UDP : SERVICE_TCP; | |
1479 | if (!(flags & SNI_NUMERICSERV) | |
1480 | && (service = dict_find(services_byport, port_text, NULL)) | |
1481 | && service->byname[proto]) | |
1482 | return service->byname[proto]->name; | |
1483 | snprintf(tmpbuf, tmpbuf_len, "%d", port); | |
1484 | return tmpbuf; | |
1485 | } | |
1486 | ||
1487 | static void | |
1488 | sar_getname_confirm(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, unsigned char *raw, unsigned int raw_size) | |
1489 | { | |
1490 | struct sar_getname_state *state; | |
1491 | const unsigned char *data; | |
1492 | const char *portname; | |
1493 | char servbuf[16]; | |
1494 | unsigned int ii, nbr; | |
1495 | ||
1496 | state = (struct sar_getname_state*)(req + 1); | |
1497 | for (ii = 0; ii < hdr->ancount; ++ii) { | |
1498 | /* Is somebody confused or trying to play games? */ | |
1499 | if (rr[ii].class != REQ_CLASS_IN | |
1500 | || strcasecmp(state->hostname, rr[ii].name)) | |
1501 | continue; | |
1502 | switch (rr[ii].type) { | |
1503 | case REQ_TYPE_A: nbr = 4; break; | |
1504 | case REQ_TYPE_AAAA: nbr = 16; break; | |
1505 | default: continue; | |
1506 | } | |
1507 | data = sar_extract_rdata(rr, nbr, raw, raw_size); | |
1508 | if (data && !memcmp(data, state->original, nbr)) { | |
1509 | portname = sar_getname_port(state->port, state->flags, servbuf, sizeof(servbuf)); | |
1510 | state->cb(state->cb_ctx, state->hostname, portname, SAI_SUCCESS); | |
1511 | free(state->hostname); | |
1512 | return; | |
1513 | } | |
1514 | } | |
1515 | state->cb(state->cb_ctx, NULL, NULL, SAI_MISMATCH); | |
1516 | free(state->hostname); | |
1517 | } | |
1518 | ||
1519 | static void | |
1520 | sar_getname_ok(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, unsigned char *raw, unsigned int raw_size) | |
1521 | { | |
1522 | struct sar_getname_state *state; | |
1523 | const char *portname; | |
1524 | unsigned int ii, pos; | |
1525 | char servbuf[16]; | |
1526 | ||
1527 | state = (struct sar_getname_state*)(req + 1); | |
1528 | for (ii = 0; ii < hdr->ancount; ++ii) { | |
1529 | if (rr[ii].type != REQ_TYPE_PTR | |
1530 | || rr[ii].class != REQ_CLASS_IN | |
1531 | || strcasecmp(rr[ii].name, state->name)) | |
1532 | continue; | |
1533 | pos = rr[ii].rd_start; | |
1534 | state->hostname = sar_extract_name(raw, raw_size, &pos); | |
1535 | break; | |
1536 | } | |
1537 | ||
1538 | if (!state->hostname) { | |
1539 | state->cb(state->cb_ctx, NULL, NULL, SAI_NONAME); | |
1540 | return; | |
1541 | } | |
1542 | ||
1543 | if (state->flags & SNI_PARANOID) { | |
1544 | req->cb_ok = sar_getname_confirm; | |
1545 | pos = sar_helpers[state->family]->build_addr_request(req, state->hostname, NULL, 0); | |
1546 | if (pos) | |
1547 | sar_request_send(req); | |
1548 | else { | |
1549 | free(state->hostname); | |
1550 | state->cb(state->cb_ctx, NULL, NULL, SAI_FAIL); | |
1551 | } | |
1552 | return; | |
1553 | } | |
1554 | ||
1555 | portname = sar_getname_port(state->port, state->flags, servbuf, sizeof(servbuf)); | |
1556 | state->cb(state->cb_ctx, state->hostname, portname, SAI_SUCCESS); | |
1557 | free(state->hostname); | |
1558 | } | |
1559 | ||
1560 | struct sar_request * | |
1561 | sar_getname(const struct sockaddr *sa, unsigned int salen, int flags, sar_name_cb cb, void *cb_ctx) | |
1562 | { | |
1563 | struct sar_family_helper *helper; | |
1564 | struct sar_request *req; | |
1565 | struct sar_getname_state *state; | |
1566 | unsigned int len; | |
1567 | int port; | |
1568 | ||
1569 | if (sa->sa_family > MAX_FAMILY | |
1570 | || !(helper = sar_helpers[sa->sa_family])) { | |
1571 | cb(cb_ctx, NULL, NULL, SAI_FAMILY); | |
1572 | return NULL; | |
1573 | } | |
1574 | ||
1575 | port = helper->get_port(sa, salen); | |
1576 | ||
1577 | if (flags & SNI_NUMERICHOST) { | |
1578 | const char *servname; | |
1579 | char host[SAR_NTOP_MAX], servbuf[16]; | |
1580 | ||
1581 | /* If appropriate, try to look up service name. */ | |
1582 | servname = sar_getname_port(port, flags, servbuf, sizeof(servbuf)); | |
1583 | len = sar_ntop(host, sizeof(host), sa, salen); | |
1584 | assert(len != 0); | |
1585 | cb(cb_ctx, host, servname, SAI_SUCCESS); | |
1586 | return NULL; | |
1587 | } | |
1588 | ||
1589 | req = sar_request_alloc(sizeof(*state), sar_getname_ok, sar_getname_fail); | |
1590 | ||
1591 | state = (struct sar_getname_state*)(req + 1); | |
1592 | state->cb = cb; | |
1593 | state->cb_ctx = cb_ctx; | |
1594 | state->flags = flags; | |
1595 | state->family = sa->sa_family; | |
1596 | state->port = port; | |
1597 | ||
1598 | helper->build_ptr_name(state, sa, salen); | |
1599 | assert(strlen(state->name) < sizeof(state->name)); | |
1600 | len = sar_request_build(req, state->name, REQ_TYPE_PTR, NULL); | |
1601 | if (!len) { | |
1602 | cb(cb_ctx, NULL, NULL, SAI_NODATA); | |
1603 | free(req); | |
1604 | return NULL; | |
1605 | } | |
1606 | ||
1607 | sar_request_send(req); | |
1608 | return req; | |
1609 | } | |
1610 | ||
1611 | static unsigned int | |
1612 | ipv4_ntop(char *output, unsigned int out_size, const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1613 | { | |
1614 | struct sockaddr_in *sin; | |
1615 | unsigned int ip4, pos; | |
1616 | ||
1617 | sin = (struct sockaddr_in*)sa; | |
1618 | ip4 = ntohl(sin->sin_addr.s_addr); | |
1619 | pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255); | |
1620 | return (pos < out_size) ? pos : 0; | |
1621 | } | |
1622 | ||
1623 | static unsigned int | |
1624 | sar_pton_ip4(const char *input, unsigned int *bits, uint32_t *output) | |
1625 | { | |
1626 | unsigned int dots = 0, pos = 0, part = 0, ip = 0; | |
1627 | ||
1628 | /* Intentionally no support for bizarre IPv4 formats (plain | |
1629 | * integers, octal or hex components) -- only vanilla dotted | |
1630 | * decimal quads, optionally with trailing /nn. | |
1631 | */ | |
1632 | if (input[0] == '.') | |
1633 | return 0; | |
1634 | while (1) { | |
1635 | if (isdigit(input[pos])) { | |
1636 | part = part * 10 + input[pos++] - '0'; | |
1637 | if (part > 255) | |
1638 | return 0; | |
1639 | if ((dots == 3) && !isdigit(input[pos])) { | |
1640 | *output = htonl(ip | part); | |
1641 | return pos; | |
1642 | } | |
1643 | } else if (input[pos] == '.') { | |
1644 | if (input[++pos] == '.') | |
1645 | return 0; | |
1646 | ip |= part << (24 - 8 * dots++); | |
1647 | part = 0; | |
1648 | } else if (bits && input[pos] == '/' && isdigit(input[pos + 1])) { | |
1649 | unsigned int len; | |
1650 | char *term; | |
1651 | ||
1652 | len = strtoul(input + pos + 1, &term, 10); | |
1653 | if (term <= input + pos + 1) | |
1654 | return pos; | |
1655 | else if (len > 32) | |
1656 | return 0; | |
1657 | *bits = len; | |
1658 | return term - input; | |
1659 | } else return 0; | |
1660 | } | |
1661 | } | |
1662 | ||
1663 | static unsigned int | |
1664 | ipv4_pton(struct sockaddr *sa, UNUSED_ARG(unsigned int socklen), unsigned int *bits, const char *input) | |
1665 | { | |
1666 | unsigned int pos; | |
1667 | ||
1668 | pos = sar_pton_ip4(input, bits, &((struct sockaddr_in*)sa)->sin_addr.s_addr); | |
1669 | if (!pos) | |
1670 | return 0; | |
1671 | sa->sa_family = AF_INET; | |
e9df2b7d | 1672 | #if defined(HAVE_SOCKADDR_SA_LEN) |
1673 | sa->sa_len = sizeof(struct sockaddr_in); | |
1674 | #endif | |
1136f709 | 1675 | return pos; |
1676 | } | |
1677 | ||
1678 | static int | |
1679 | ipv4_get_port(const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1680 | { | |
1681 | return ntohs(((const struct sockaddr_in*)sa)->sin_port); | |
1682 | } | |
1683 | ||
1684 | static int | |
1685 | ipv4_set_port(struct sockaddr *sa, UNUSED_ARG(unsigned int socklen), unsigned short port) | |
1686 | { | |
1687 | ((struct sockaddr_in*)sa)->sin_port = htons(port); | |
1688 | return 0; | |
1689 | } | |
1690 | ||
1691 | static unsigned int | |
1692 | ipv4_addr_request(struct sar_request *req, const char *node, const char *srv_node, UNUSED_ARG(unsigned int flags)) | |
1693 | { | |
1694 | unsigned int len; | |
1695 | if (srv_node) | |
1696 | len = sar_request_build(req, node, REQ_TYPE_A, srv_node, REQ_TYPE_SRV, NULL); | |
1697 | else | |
1698 | len = sar_request_build(req, node, REQ_TYPE_A, NULL); | |
1699 | return len; | |
1700 | } | |
1701 | ||
1702 | static void | |
1703 | ipv4_ptr_name(struct sar_getname_state *state, const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1704 | { | |
1705 | const uint8_t *bytes; | |
1706 | ||
1707 | bytes = (uint8_t*)&((struct sockaddr_in*)sa)->sin_addr.s_addr; | |
1708 | memcpy(state->original, bytes, 4); | |
1709 | snprintf(state->name, sizeof(state->name), | |
1710 | "%u.%u.%u.%u.in-addr.arpa", | |
1711 | bytes[3], bytes[2], bytes[1], bytes[0]); | |
1712 | } | |
1713 | ||
1714 | static int | |
1715 | ipv4_decode(struct sar_getaddr_state *state, struct dns_rr *rr, unsigned char *raw, UNUSED_ARG(unsigned int raw_size)) | |
1716 | { | |
1717 | struct sockaddr_in *sa; | |
1718 | struct addrinfo *ai; | |
1719 | ||
1720 | if (rr->rdlength != 4) | |
1721 | return 0; | |
1722 | ||
1723 | if (state->flags & SAI_CANONNAME) { | |
1724 | ai = calloc(1, sizeof(*ai) + sizeof(*sa) + strlen(rr->name) + 1); | |
1725 | sa = (struct sockaddr_in*)(ai->ai_addr = (struct sockaddr*)(ai + 1)); | |
1726 | ai->ai_canonname = strcpy((char*)(sa + 1), rr->name); | |
1727 | } else { | |
1728 | ai = calloc(1, sizeof(*ai) + sizeof(*sa)); | |
1729 | sa = (struct sockaddr_in*)(ai->ai_addr = (struct sockaddr*)(ai + 1)); | |
1730 | ai->ai_canonname = NULL; | |
1731 | } | |
1732 | ||
1733 | ai->ai_family = AF_INET; | |
1734 | sa->sin_port = htons(state->port); | |
1735 | memcpy(&sa->sin_addr.s_addr, raw + rr->rd_start, 4); | |
1736 | return sar_getaddr_append(state, ai, 1); | |
1737 | } | |
1738 | ||
1739 | static struct sar_family_helper sar_ipv4_helper = { | |
1740 | "127.0.0.1", | |
1741 | "0.0.0.0", | |
1742 | sizeof(struct sockaddr_in), | |
1743 | AF_INET, | |
1744 | ipv4_ntop, | |
1745 | ipv4_pton, | |
1746 | ipv4_get_port, | |
1747 | ipv4_set_port, | |
1748 | ipv4_addr_request, | |
1749 | ipv4_ptr_name, | |
1750 | ipv4_decode, | |
1751 | NULL | |
1752 | }; | |
1753 | ||
1754 | #if defined(AF_INET6) | |
1755 | ||
1756 | static unsigned int | |
1757 | ipv6_ntop(char *output, unsigned int out_size, const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1758 | { | |
1759 | struct sockaddr_in6 *sin6; | |
1760 | unsigned int pos, part, max_start, max_zeros, curr_zeros, ii; | |
1761 | unsigned short addr16; | |
1762 | ||
1763 | sin6 = (struct sockaddr_in6*)sa; | |
1764 | /* Find longest run of zeros. */ | |
1765 | for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) { | |
1766 | addr16 = (sin6->sin6_addr.s6_addr[ii * 2] << 8) | sin6->sin6_addr.s6_addr[ii * 2 + 1]; | |
1767 | if (!addr16) | |
1768 | curr_zeros++; | |
1769 | else if (curr_zeros > max_zeros) { | |
1770 | max_start = ii - curr_zeros; | |
1771 | max_zeros = curr_zeros; | |
1772 | curr_zeros = 0; | |
1773 | } | |
1774 | } | |
1775 | if (curr_zeros > max_zeros) { | |
1776 | max_start = ii - curr_zeros; | |
1777 | max_zeros = curr_zeros; | |
1778 | } | |
1779 | ||
1780 | /* Print out address. */ | |
1781 | #define APPEND(CH) do { output[pos++] = (CH); if (pos >= out_size) return 0; } while (0) | |
1782 | for (pos = 0, ii = 0; ii < 8; ++ii) { | |
1783 | if ((max_zeros > 0) && (ii == max_start)) { | |
1784 | if (ii == 0) | |
1785 | APPEND(':'); | |
1786 | APPEND(':'); | |
1787 | ii += max_zeros - 1; | |
1788 | continue; | |
1789 | } | |
1790 | part = (sin6->sin6_addr.s6_addr[ii * 2] << 8) | sin6->sin6_addr.s6_addr[ii * 2 + 1]; | |
1791 | if (part >= 0x1000) | |
1792 | APPEND(hexdigits[part >> 12]); | |
1793 | if (part >= 0x100) | |
1794 | APPEND(hexdigits[(part >> 8) & 15]); | |
1795 | if (part >= 0x10) | |
1796 | APPEND(hexdigits[(part >> 4) & 15]); | |
1797 | APPEND(hexdigits[part & 15]); | |
1798 | if (ii < 7) | |
1799 | APPEND(':'); | |
1800 | } | |
1801 | APPEND('\0'); | |
1802 | #undef APPEND | |
1803 | ||
1804 | return pos; | |
1805 | } | |
1806 | ||
1807 | static const unsigned char xdigit_value[256] = { | |
1808 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1809 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1810 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1811 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, | |
1812 | 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1813 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1814 | 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1815 | }; | |
1816 | ||
1817 | static unsigned int | |
1818 | ipv6_pton(struct sockaddr *sa, UNUSED_ARG(unsigned int socklen), unsigned int *bits, const char *input) | |
1819 | { | |
1820 | const char *part_start = NULL; | |
1821 | struct sockaddr_in6 *sin6; | |
1822 | char *colon; | |
1823 | char *dot; | |
1824 | unsigned int part = 0, pos = 0, ii = 0, cpos = 8; | |
1825 | ||
1826 | if (!(colon = strchr(input, ':'))) | |
1827 | return 0; | |
1828 | dot = strchr(input, '.'); | |
1829 | if (dot && dot < colon) | |
1830 | return 0; | |
1831 | sin6 = (struct sockaddr_in6*)sa; | |
1832 | /* Parse IPv6, possibly like ::127.0.0.1. | |
1833 | * This is pretty straightforward; the only trick is borrowed | |
1834 | * from Paul Vixie (BIND): when it sees a "::" continue as if | |
1835 | * it were a single ":", but note where it happened, and fill | |
1836 | * with zeros afterwards. | |
1837 | */ | |
1838 | if (input[pos] == ':') { | |
1839 | if ((input[pos+1] != ':') || (input[pos+2] == ':')) | |
1840 | return 0; | |
1841 | cpos = 0; | |
1842 | pos += 2; | |
1843 | part_start = input + pos; | |
1844 | } | |
1845 | while (ii < 8) { | |
1846 | if (isxdigit(input[pos])) { | |
1847 | part = (part << 4) | xdigit_value[(unsigned char)input[pos]]; | |
1848 | if (part > 0xffff) | |
1849 | return 0; | |
1850 | pos++; | |
1851 | } else if (input[pos] == ':') { | |
1852 | part_start = input + ++pos; | |
1853 | if (input[pos] == '.') | |
1854 | return 0; | |
1855 | sin6->sin6_addr.s6_addr[ii * 2] = part >> 8; | |
1856 | sin6->sin6_addr.s6_addr[ii * 2 + 1] = part & 255; | |
1857 | ii++; | |
1858 | part = 0; | |
1859 | if (input[pos] == ':') { | |
1860 | if (cpos < 8) | |
1861 | return 0; | |
1862 | cpos = ii; | |
1863 | pos++; | |
1864 | } | |
1865 | } else if (input[pos] == '.') { | |
1866 | uint32_t ip4; | |
1867 | unsigned int len; | |
1868 | len = sar_pton_ip4(part_start, bits, &ip4); | |
1869 | if (!len || (ii > 6)) | |
1870 | return 0; | |
1871 | memcpy(sin6->sin6_addr.s6_addr + ii * 2, &ip4, sizeof(ip4)); | |
1872 | if (bits) | |
1873 | *bits += ii * 16; | |
1874 | ii += 2; | |
1875 | pos = part_start + len - input; | |
1876 | break; | |
1877 | } else if (bits && input[pos] == '/' && isdigit(input[pos + 1])) { | |
1878 | unsigned int len; | |
1879 | char *term; | |
1880 | ||
1881 | len = strtoul(input + pos + 1, &term, 10); | |
1882 | if (term <= input + pos + 1) | |
1883 | break; | |
1884 | else if (len > 128) | |
1885 | return 0; | |
1886 | if (bits) | |
1887 | *bits = len; | |
1888 | pos = term - input; | |
1889 | break; | |
1890 | } else if (cpos <= 8) { | |
1891 | sin6->sin6_addr.s6_addr[ii * 2] = part >> 8; | |
1892 | sin6->sin6_addr.s6_addr[ii * 2 + 1] = part & 255; | |
1893 | ii++; | |
1894 | break; | |
1895 | } else return 0; | |
1896 | } | |
1897 | /* Shift stuff after "::" up and fill middle with zeros. */ | |
1898 | if (cpos < 8) { | |
1899 | unsigned int jj; | |
1900 | ii <<= 1; | |
1901 | cpos <<= 1; | |
1902 | for (jj = 0; jj < ii - cpos; jj++) | |
1903 | sin6->sin6_addr.s6_addr[15 - jj] = sin6->sin6_addr.s6_addr[ii - jj - 1]; | |
1904 | for (jj = 0; jj < 16 - ii; jj++) | |
1905 | sin6->sin6_addr.s6_addr[cpos + jj] = 0; | |
1906 | } | |
1907 | sa->sa_family = AF_INET6; | |
e9df2b7d | 1908 | #if defined(HAVE_SOCKADDR_SA_LEN) |
1909 | sa->sa_len = sizeof(struct sockaddr_in6); | |
1910 | #endif | |
1136f709 | 1911 | return pos; |
1912 | } | |
1913 | ||
1914 | static int | |
1915 | ipv6_get_port(const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1916 | { | |
1917 | return ntohs(((const struct sockaddr_in6*)sa)->sin6_port); | |
1918 | } | |
1919 | ||
1920 | static int | |
1921 | ipv6_set_port(struct sockaddr *sa, UNUSED_ARG(unsigned int socklen), unsigned short port) | |
1922 | { | |
1923 | ((struct sockaddr_in6*)sa)->sin6_port = htons(port); | |
1924 | return 0; | |
1925 | } | |
1926 | ||
1927 | static unsigned int | |
1928 | ipv6_addr_request(struct sar_request *req, const char *node, const char *srv_node, unsigned int flags) | |
1929 | { | |
1930 | unsigned int len; | |
1931 | if (flags & SAI_V4MAPPED) { | |
1932 | if (srv_node) | |
1933 | len = sar_request_build(req, node, REQ_TYPE_AAAA, node, REQ_TYPE_A, srv_node, REQ_TYPE_SRV, NULL); | |
1934 | else | |
1935 | len = sar_request_build(req, node, REQ_TYPE_AAAA, node, REQ_TYPE_A, NULL); | |
1936 | } else { | |
1937 | if (srv_node) | |
1938 | len = sar_request_build(req, node, REQ_TYPE_AAAA, srv_node, REQ_TYPE_SRV, NULL); | |
1939 | else | |
1940 | len = sar_request_build(req, node, REQ_TYPE_AAAA, NULL); | |
1941 | } | |
1942 | return len; | |
1943 | } | |
1944 | ||
1945 | static void | |
1946 | ipv6_ptr_name(struct sar_getname_state *state, const struct sockaddr *sa, UNUSED_ARG(unsigned int socklen)) | |
1947 | { | |
1948 | const uint8_t *bytes; | |
1949 | unsigned int ii, jj; | |
1950 | ||
1951 | bytes = ((struct sockaddr_in6*)sa)->sin6_addr.s6_addr; | |
1952 | memcpy(state->original, bytes, 16); | |
1953 | for (jj = 0, ii = 16; ii > 0; ) { | |
1954 | state->name[jj++] = hexdigits[bytes[--ii] & 15]; | |
1955 | state->name[jj++] = hexdigits[bytes[ii] >> 4]; | |
1956 | state->name[jj++] = '.'; | |
1957 | } | |
1958 | strcpy(state->name + jj, ".ip6.arpa"); | |
1959 | state->doing_arpa = 1; | |
1960 | } | |
1961 | ||
1962 | static int | |
1963 | ipv6_decode(struct sar_getaddr_state *state, struct dns_rr *rr, unsigned char *raw, UNUSED_ARG(unsigned int raw_size)) | |
1964 | { | |
1965 | struct sockaddr_in6 *sa; | |
1966 | struct addrinfo *ai; | |
1967 | ||
1968 | if (state->flags & SAI_CANONNAME) { | |
1969 | ai = calloc(1, sizeof(*ai) + sizeof(*sa) + strlen(rr->name) + 1); | |
1970 | sa = (struct sockaddr_in6*)(ai->ai_addr = (struct sockaddr*)(ai + 1)); | |
1971 | ai->ai_canonname = strcpy((char*)(sa + 1), rr->name); | |
1972 | } else { | |
1973 | ai = calloc(1, sizeof(*ai) + sizeof(*sa)); | |
1974 | sa = (struct sockaddr_in6*)(ai->ai_addr = (struct sockaddr*)(ai + 1)); | |
1975 | ai->ai_canonname = NULL; | |
1976 | } | |
1977 | ||
1978 | if (rr->rdlength == 4) { | |
1979 | sa->sin6_addr.s6_addr[10] = sa->sin6_addr.s6_addr[11] = 0xff; | |
1980 | memcpy(sa->sin6_addr.s6_addr + 12, raw + rr->rd_start, 4); | |
1981 | } else if (rr->rdlength == 16) { | |
1982 | memcpy(sa->sin6_addr.s6_addr, raw + rr->rd_start, 16); | |
1983 | } else { | |
1984 | free(ai); | |
1985 | return 0; | |
1986 | } | |
1987 | ||
1988 | ai->ai_family = AF_INET6; | |
1989 | sa->sin6_port = htons(state->port); | |
1990 | return sar_getaddr_append(state, ai, 1); | |
1991 | } | |
1992 | ||
1993 | static struct sar_family_helper sar_ipv6_helper = { | |
1994 | "::1", | |
1995 | "::", | |
1996 | sizeof(struct sockaddr_in6), | |
1997 | AF_INET6, | |
1998 | ipv6_ntop, | |
1999 | ipv6_pton, | |
2000 | ipv6_get_port, | |
2001 | ipv6_set_port, | |
2002 | ipv6_addr_request, | |
2003 | ipv6_ptr_name, | |
2004 | ipv6_decode, | |
2005 | NULL | |
2006 | }; | |
2007 | ||
2008 | #endif /* defined(AF_INET6) */ | |
2009 | ||
2010 | static void | |
2011 | sar_cleanup(void) | |
2012 | { | |
2013 | ioset_close(sar_fd, 1); | |
2014 | dict_delete(services_byname); | |
2015 | dict_delete(services_byport); | |
2016 | dict_delete(sar_nameservers); | |
2017 | dict_delete(sar_requests); | |
2018 | free_string_list(conf.sar_search); | |
2019 | free_string_list(conf.sar_nslist); | |
2020 | } | |
2021 | ||
2022 | static void | |
2023 | sar_conf_reload(void) | |
2024 | { | |
2025 | dict_t node; | |
2026 | const char *resolv_conf = "/etc/resolv.conf"; | |
2027 | const char *services = "/etc/services"; | |
2028 | const char *str; | |
2029 | ||
2030 | node = conf_get_data("modules/sar", RECDB_OBJECT); | |
2031 | if (node != NULL) { | |
2032 | str = database_get_data(node, "resolv_conf", RECDB_QSTRING); | |
2033 | if (str) resolv_conf = str; | |
2034 | str = database_get_data(node, "services", RECDB_QSTRING); | |
2035 | if (str) services = str; | |
2036 | } | |
2037 | sar_dns_init(resolv_conf); | |
2038 | sar_services_init(services); | |
2039 | } | |
2040 | ||
2041 | void | |
2042 | sar_init(void) | |
2043 | { | |
2044 | reg_exit_func(sar_cleanup); | |
2045 | sar_log = log_register_type("sar", NULL); | |
2046 | ||
2047 | sar_requests = dict_new(); | |
2048 | dict_set_free_data(sar_requests, sar_request_cleanup); | |
2049 | ||
2050 | sar_nameservers = dict_new(); | |
2051 | dict_set_free_data(sar_nameservers, free); | |
2052 | ||
2053 | sar_register_helper(&sar_ipv4_helper); | |
2054 | #if defined(AF_INET6) | |
2055 | sar_register_helper(&sar_ipv6_helper); | |
2056 | #endif | |
2057 | ||
2058 | conf_register_reload(sar_conf_reload); | |
2059 | } |