source: trunk/server/source4/libcli/resolve/dns_ex.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 12.9 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 async getaddrinfo()/dns_lookup() name resolution module
5
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2008
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23/*
24 this module uses a fork() per getaddrinfo() or dns_looup() call.
25 At first that might seem crazy, but it is actually very fast,
26 and solves many of the tricky problems of keeping a child
27 hanging around in a librar (like what happens when the parent forks).
28 We use a talloc destructor to ensure that the child is cleaned up
29 when we have finished with this name resolution.
30*/
31
32#include "includes.h"
33#include "lib/events/events.h"
34#include "system/network.h"
35#include "system/filesys.h"
36#include "lib/socket/socket.h"
37#include "libcli/composite/composite.h"
38#include "librpc/gen_ndr/ndr_nbt.h"
39#include "libcli/resolve/resolve.h"
40
41#ifdef class
42#undef class
43#endif
44
45#include "heimdal/lib/roken/resolve.h"
46
47struct dns_ex_state {
48 bool do_fallback;
49 uint32_t flags;
50 uint16_t port;
51 struct nbt_name name;
52 struct socket_address **addrs;
53 char **names;
54 pid_t child;
55 int child_fd;
56 struct tevent_fd *fde;
57 struct tevent_context *event_ctx;
58};
59
60/*
61 kill off a wayward child if needed. This allows us to stop an async
62 name resolution without leaving a potentially blocking call running
63 in a child
64*/
65static int dns_ex_destructor(struct dns_ex_state *state)
66{
67 int status;
68
69 kill(state->child, SIGTERM);
70 if (waitpid(state->child, &status, WNOHANG) == 0) {
71 kill(state->child, SIGKILL);
72 waitpid(state->child, &status, 0);
73 }
74
75 return 0;
76}
77
78/*
79 the blocking child
80*/
81static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
82{
83 struct rk_dns_reply *reply;
84 struct rk_resource_record *rr;
85 uint32_t count = 0;
86 uint32_t srv_valid = 0;
87 struct rk_resource_record **srv_rr;
88 uint32_t addrs_valid = 0;
89 struct rk_resource_record **addrs_rr;
90 char *addrs;
91 bool first;
92 uint32_t i;
93 bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
94
95 if (strchr(state->name.name, '.') && state->name.name[strlen(state->name.name)-1] != '.') {
96 /* we are asking for a fully qualified name, but the
97 name doesn't end in a '.'. We need to prevent the
98 DNS library trying the search domains configured in
99 resolv.conf */
100 state->name.name = talloc_strdup_append(discard_const_p(char, state->name.name),
101 ".");
102 }
103
104 /* this is the blocking call we are going to lots of trouble
105 to avoid in the parent */
106 reply = rk_dns_lookup(state->name.name, do_srv?"SRV":"A");
107 if (!reply) {
108 goto done;
109 }
110
111 if (do_srv) {
112 rk_dns_srv_order(reply);
113 }
114
115 /* Loop over all returned records and pick the "srv" records */
116 for (rr=reply->head; rr; rr=rr->next) {
117 /* we are only interested in the IN class */
118 if (rr->class != rk_ns_c_in) {
119 continue;
120 }
121
122 if (do_srv) {
123 /* we are only interested in SRV records */
124 if (rr->type != rk_ns_t_srv) {
125 continue;
126 }
127
128 /* verify we actually have a SRV record here */
129 if (!rr->u.srv) {
130 continue;
131 }
132
133 /* Verify we got a port */
134 if (rr->u.srv->port == 0) {
135 continue;
136 }
137 } else {
138 /* we are only interested in A records */
139 /* TODO: add AAAA support */
140 if (rr->type != rk_ns_t_a) {
141 continue;
142 }
143
144 /* verify we actually have a A record here */
145 if (!rr->u.a) {
146 continue;
147 }
148 }
149 count++;
150 }
151
152 if (count == 0) {
153 goto done;
154 }
155
156 srv_rr = talloc_zero_array(state,
157 struct rk_resource_record *,
158 count);
159 if (!srv_rr) {
160 goto done;
161 }
162
163 addrs_rr = talloc_zero_array(state,
164 struct rk_resource_record *,
165 count);
166 if (!addrs_rr) {
167 goto done;
168 }
169
170 /* Loop over all returned records and pick the records */
171 for (rr=reply->head;rr;rr=rr->next) {
172 /* we are only interested in the IN class */
173 if (rr->class != rk_ns_c_in) {
174 continue;
175 }
176
177 if (do_srv) {
178 /* we are only interested in SRV records */
179 if (rr->type != rk_ns_t_srv) {
180 continue;
181 }
182
183 /* verify we actually have a srv record here */
184 if (!rr->u.srv) {
185 continue;
186 }
187
188 /* Verify we got a port */
189 if (rr->u.srv->port == 0) {
190 continue;
191 }
192
193 srv_rr[srv_valid] = rr;
194 srv_valid++;
195 } else {
196 /* we are only interested in A records */
197 /* TODO: add AAAA support */
198 if (rr->type != rk_ns_t_a) {
199 continue;
200 }
201
202 /* verify we actually have a A record here */
203 if (!rr->u.a) {
204 continue;
205 }
206
207 addrs_rr[addrs_valid] = rr;
208 addrs_valid++;
209 }
210 }
211
212 for (i=0; i < srv_valid; i++) {
213 for (rr=reply->head;rr;rr=rr->next) {
214
215 if (rr->class != rk_ns_c_in) {
216 continue;
217 }
218
219 /* we are only interested in A records */
220 if (rr->type != rk_ns_t_a) {
221 continue;
222 }
223
224 /* verify we actually have a srv record here */
225 if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
226 continue;
227 }
228
229 addrs_rr[i] = rr;
230 addrs_valid++;
231 break;
232 }
233 }
234
235 if (addrs_valid == 0) {
236 goto done;
237 }
238
239 addrs = talloc_strdup(state, "");
240 if (!addrs) {
241 goto done;
242 }
243 first = true;
244 for (i=0; i < count; i++) {
245 uint16_t port;
246 if (!addrs_rr[i]) {
247 continue;
248 }
249
250 if (srv_rr[i] &&
251 (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
252 port = srv_rr[i]->u.srv->port;
253 } else {
254 port = state->port;
255 }
256
257 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
258 first?"":",",
259 inet_ntoa(*addrs_rr[i]->u.a),
260 port,
261 addrs_rr[i]->domain);
262 if (!addrs) {
263 goto done;
264 }
265 first = false;
266 }
267
268 if (addrs) {
269 write(fd, addrs, talloc_get_size(addrs));
270 }
271
272done:
273 close(fd);
274}
275
276/*
277 the blocking child
278*/
279static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
280{
281 int ret;
282 struct addrinfo hints;
283 struct addrinfo *res;
284 struct addrinfo *res_list = NULL;
285 char *addrs;
286 bool first;
287
288 ZERO_STRUCT(hints);
289 hints.ai_socktype = SOCK_STREAM;
290 hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
291 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
292
293 ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
294 /* try to fallback in case of error */
295 if (state->do_fallback) {
296 switch (ret) {
297#ifdef EAI_NODATA
298 case EAI_NODATA:
299#endif
300 case EAI_NONAME:
301 /* getaddrinfo() doesn't handle CNAME records */
302 run_child_dns_lookup(state, fd);
303 return;
304 default:
305 break;
306 }
307 }
308 if (ret != 0) {
309 goto done;
310 }
311
312 addrs = talloc_strdup(state, "");
313 if (!addrs) {
314 goto done;
315 }
316 first = true;
317 for (res = res_list; res; res = res->ai_next) {
318 struct sockaddr_in *in;
319
320 if (res->ai_family != AF_INET) {
321 continue;
322 }
323 in = (struct sockaddr_in *)res->ai_addr;
324
325 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
326 first?"":",",
327 inet_ntoa(in->sin_addr),
328 state->port,
329 state->name.name);
330 if (!addrs) {
331 goto done;
332 }
333 first = false;
334 }
335
336 if (addrs) {
337 write(fd, addrs, talloc_get_size(addrs));
338 }
339done:
340 if (res_list) {
341 freeaddrinfo(res_list);
342 }
343 close(fd);
344}
345
346/*
347 handle a read event on the pipe
348*/
349static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde,
350 uint16_t flags, void *private_data)
351{
352 struct composite_context *c = talloc_get_type(private_data, struct composite_context);
353 struct dns_ex_state *state = talloc_get_type(c->private_data,
354 struct dns_ex_state);
355 char *address;
356 uint32_t num_addrs, i;
357 char **addrs;
358 int ret;
359 int status;
360 int value = 0;
361
362 /* if we get any event from the child then we know that we
363 won't need to kill it off */
364 talloc_set_destructor(state, NULL);
365
366 if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
367 value = 8192;
368 }
369
370 address = talloc_array(state, char, value+1);
371 if (address) {
372 /* yes, we don't care about EAGAIN or other niceities
373 here. They just can't happen with this parent/child
374 relationship, and even if they did then giving an error is
375 the right thing to do */
376 ret = read(state->child_fd, address, value);
377 } else {
378 ret = -1;
379 }
380 if (waitpid(state->child, &status, WNOHANG) == 0) {
381 kill(state->child, SIGKILL);
382 waitpid(state->child, &status, 0);
383 }
384
385 if (ret <= 0) {
386 DEBUG(3,("dns child failed to find name '%s' of type %s\n",
387 state->name.name, (state->flags & RESOLVE_NAME_FLAG_DNS_SRV)?"SRV":"A"));
388 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
389 return;
390 }
391
392 /* enusre the address looks good */
393 address[ret] = 0;
394
395 addrs = str_list_make(state, address, ",");
396 if (composite_nomem(addrs, c)) return;
397
398 num_addrs = str_list_length((const char * const *)addrs);
399
400 state->addrs = talloc_array(state, struct socket_address *,
401 num_addrs+1);
402 if (composite_nomem(state->addrs, c)) return;
403
404 state->names = talloc_array(state, char *, num_addrs+1);
405 if (composite_nomem(state->names, c)) return;
406
407 for (i=0; i < num_addrs; i++) {
408 uint32_t port = 0;
409 char *p = strrchr(addrs[i], ':');
410 char *n;
411
412 if (!p) {
413 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
414 return;
415 }
416
417 *p = '\0';
418 p++;
419
420 n = strrchr(p, '/');
421 if (!n) {
422 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
423 return;
424 }
425
426 *n = '\0';
427 n++;
428
429 if (strcmp(addrs[i], "0.0.0.0") == 0 ||
430 inet_addr(addrs[i]) == INADDR_NONE) {
431 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
432 return;
433 }
434 port = strtoul(p, NULL, 10);
435 if (port > UINT16_MAX) {
436 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
437 return;
438 }
439 state->addrs[i] = socket_address_from_strings(state->addrs,
440 "ipv4",
441 addrs[i],
442 port);
443 if (composite_nomem(state->addrs[i], c)) return;
444
445 state->names[i] = talloc_strdup(state->names, n);
446 if (composite_nomem(state->names[i], c)) return;
447 }
448 state->addrs[i] = NULL;
449 state->names[i] = NULL;
450
451 composite_done(c);
452}
453
454/*
455 getaddrinfo() or dns_lookup() name resolution method - async send
456 */
457struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
458 struct tevent_context *event_ctx,
459 void *privdata,
460 uint32_t flags,
461 uint16_t port,
462 struct nbt_name *name,
463 bool do_fallback)
464{
465 struct composite_context *c;
466 struct dns_ex_state *state;
467 int fd[2] = { -1, -1 };
468 int ret;
469
470 c = composite_create(mem_ctx, event_ctx);
471 if (c == NULL) return NULL;
472
473 if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
474 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
475 return c;
476 }
477
478 state = talloc_zero(c, struct dns_ex_state);
479 if (composite_nomem(state, c)) return c;
480 c->private_data = state;
481
482 c->status = nbt_name_dup(state, name, &state->name);
483 if (!composite_is_ok(c)) return c;
484
485 /* setup a pipe to chat to our child */
486 ret = pipe(fd);
487 if (ret == -1) {
488 composite_error(c, map_nt_error_from_unix(errno));
489 return c;
490 }
491
492 state->do_fallback = do_fallback;
493 state->flags = flags;
494 state->port = port;
495
496 state->child_fd = fd[0];
497 state->event_ctx = c->event_ctx;
498
499 /* we need to put the child in our event context so
500 we know when the dns_lookup() has finished */
501 state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ,
502 pipe_handler, c);
503 if (composite_nomem(state->fde, c)) {
504 close(fd[0]);
505 close(fd[1]);
506 return c;
507 }
508 tevent_fd_set_auto_close(state->fde);
509
510 state->child = fork();
511 if (state->child == (pid_t)-1) {
512 composite_error(c, map_nt_error_from_unix(errno));
513 return c;
514 }
515
516 if (state->child == 0) {
517 close(fd[0]);
518 if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
519 run_child_dns_lookup(state, fd[1]);
520 } else {
521 run_child_getaddrinfo(state, fd[1]);
522 }
523 _exit(0);
524 }
525 close(fd[1]);
526
527 /* cleanup wayward children */
528 talloc_set_destructor(state, dns_ex_destructor);
529
530 return c;
531}
532
533/*
534 getaddrinfo() or dns_lookup() name resolution method - recv side
535*/
536NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c,
537 TALLOC_CTX *mem_ctx,
538 struct socket_address ***addrs,
539 char ***names)
540{
541 NTSTATUS status;
542
543 status = composite_wait(c);
544
545 if (NT_STATUS_IS_OK(status)) {
546 struct dns_ex_state *state = talloc_get_type(c->private_data,
547 struct dns_ex_state);
548 *addrs = talloc_steal(mem_ctx, state->addrs);
549 if (names) {
550 *names = talloc_steal(mem_ctx, state->names);
551 }
552 }
553
554 talloc_free(c);
555 return status;
556}
Note: See TracBrowser for help on using the repository browser.