s4-dns-ex: use autoclose on the dns child pipe
[ira/wip.git] / source4 / libcli / resolve / dns_ex.c
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
47 struct 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 */
65 static 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 */
81 static 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         /* this is the blocking call we are going to lots of trouble
96            to avoid in the parent */
97         reply = rk_dns_lookup(state->name.name, do_srv?"SRV":"A");
98         if (!reply) {
99                 goto done;
100         }
101
102         if (do_srv) {
103                 rk_dns_srv_order(reply);
104         }
105
106         /* Loop over all returned records and pick the "srv" records */
107         for (rr=reply->head; rr; rr=rr->next) {
108                 /* we are only interested in the IN class */
109                 if (rr->class != rk_ns_c_in) {
110                         continue;
111                 }
112
113                 if (do_srv) {
114                         /* we are only interested in SRV records */
115                         if (rr->type != rk_ns_t_srv) {
116                                 continue;
117                         }
118
119                         /* verify we actually have a SRV record here */
120                         if (!rr->u.srv) {
121                                 continue;
122                         }
123
124                         /* Verify we got a port */
125                         if (rr->u.srv->port == 0) {
126                                 continue;
127                         }
128                 } else {
129                         /* we are only interested in A records */
130                         /* TODO: add AAAA support */
131                         if (rr->type != rk_ns_t_a) {
132                                 continue;
133                         }
134
135                         /* verify we actually have a A record here */
136                         if (!rr->u.a) {
137                                 continue;
138                         }
139                 }
140                 count++;
141         }
142
143         if (count == 0) {
144                 goto done;
145         }
146
147         srv_rr = talloc_zero_array(state,
148                                    struct rk_resource_record *,
149                                    count);
150         if (!srv_rr) {
151                 goto done;
152         }
153
154         addrs_rr = talloc_zero_array(state,
155                                      struct rk_resource_record *,
156                                      count);
157         if (!addrs_rr) {
158                 goto done;
159         }
160
161         /* Loop over all returned records and pick the records */
162         for (rr=reply->head;rr;rr=rr->next) {
163                 /* we are only interested in the IN class */
164                 if (rr->class != rk_ns_c_in) {
165                         continue;
166                 }
167
168                 if (do_srv) {
169                         /* we are only interested in SRV records */
170                         if (rr->type != rk_ns_c_in) {
171                                 continue;
172                         }
173
174                         /* verify we actually have a srv record here */
175                         if (!rr->u.srv) {
176                                 continue;
177                         }
178
179                         /* Verify we got a port */
180                         if (rr->u.srv->port == 0) {
181                                 continue;
182                         }
183
184                         srv_rr[srv_valid] = rr;
185                         srv_valid++;
186                 } else {
187                         /* we are only interested in A records */
188                         /* TODO: add AAAA support */
189                         if (rr->type != rk_ns_t_a) {
190                                 continue;
191                         }
192
193                         /* verify we actually have a A record here */
194                         if (!rr->u.a) {
195                                 continue;
196                         }
197
198                         addrs_rr[addrs_valid] = rr;
199                         addrs_valid++;
200                 }
201         }
202
203         for (i=0; i < srv_valid; i++) {
204                 for (rr=reply->head;rr;rr=rr->next) {
205
206                         if (rr->class != rk_ns_c_in) {
207                                 continue;
208                         }
209
210                         /* we are only interested in SRV records */
211                         if (rr->type != rk_ns_t_a) {
212                                 continue;
213                         }
214
215                         /* verify we actually have a srv record here */
216                         if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
217                                 continue;
218                         }
219
220                         addrs_rr[i] = rr;
221                         addrs_valid++;
222                         break;
223                 }
224         }
225
226         if (addrs_valid == 0) {
227                 goto done;
228         }
229
230         addrs = talloc_strdup(state, "");
231         if (!addrs) {
232                 goto done;
233         }
234         first = true;
235         for (i=0; i < count; i++) {
236                 uint16_t port;
237                 if (!addrs_rr[i]) {
238                         continue;
239                 }
240
241                 if (srv_rr[i] &&
242                     (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
243                         port = srv_rr[i]->u.srv->port;
244                 } else {
245                         port = state->port;
246                 }
247
248                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
249                                                       first?"":",",
250                                                       inet_ntoa(*addrs_rr[i]->u.a),
251                                                       port,
252                                                       addrs_rr[i]->domain);
253                 if (!addrs) {
254                         goto done;
255                 }
256                 first = false;
257         }
258
259         if (addrs) {
260                 write(fd, addrs, talloc_get_size(addrs));
261         }
262
263 done:
264         close(fd);
265 }
266
267 /*
268   the blocking child
269 */
270 static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
271 {
272         int ret;
273         struct addrinfo hints;
274         struct addrinfo *res;
275         struct addrinfo *res_list = NULL;
276         char *addrs;
277         bool first;
278
279         ZERO_STRUCT(hints);
280         hints.ai_socktype = SOCK_STREAM;
281         hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
282         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
283
284         ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
285         /* try to fallback in case of error */
286         if (state->do_fallback) {
287                 switch (ret) {
288 #ifdef EAI_NODATA
289                 case EAI_NODATA:
290 #endif
291                 case EAI_NONAME:
292                         /* getaddrinfo() doesn't handle CNAME records */
293                         run_child_dns_lookup(state, fd);
294                         return;
295                 default:
296                         break;
297                 }
298         }
299         if (ret != 0) {
300                 goto done;
301         }
302
303         addrs = talloc_strdup(state, "");
304         if (!addrs) {
305                 goto done;
306         }
307         first = true;
308         for (res = res_list; res; res = res->ai_next) {
309                 struct sockaddr_in *in;
310
311                 if (res->ai_family != AF_INET) {
312                         continue;
313                 }
314                 in = (struct sockaddr_in *)res->ai_addr;
315
316                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
317                                                       first?"":",",
318                                                       inet_ntoa(in->sin_addr),
319                                                       state->port,
320                                                       state->name.name);
321                 if (!addrs) {
322                         goto done;
323                 }
324                 first = false;
325         }
326
327         if (addrs) {
328                 write(fd, addrs, talloc_get_size(addrs));
329         }
330 done:
331         if (res_list) {
332                 freeaddrinfo(res_list);
333         }
334         close(fd);
335 }
336
337 /*
338   handle a read event on the pipe
339 */
340 static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, 
341                          uint16_t flags, void *private_data)
342 {
343         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
344         struct dns_ex_state *state = talloc_get_type(c->private_data,
345                                      struct dns_ex_state);
346         char *address;
347         uint32_t num_addrs, i;
348         char **addrs;
349         int ret;
350         int status;
351         int value = 0;
352
353         /* if we get any event from the child then we know that we
354            won't need to kill it off */
355         talloc_set_destructor(state, NULL);
356
357         if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
358                 value = 8192;
359         }
360
361         address = talloc_array(state, char, value+1);
362         if (address) {
363                 /* yes, we don't care about EAGAIN or other niceities
364                    here. They just can't happen with this parent/child
365                    relationship, and even if they did then giving an error is
366                    the right thing to do */
367                 ret = read(state->child_fd, address, value);
368         } else {
369                 ret = -1;
370         }
371         if (waitpid(state->child, &status, WNOHANG) == 0) {
372                 kill(state->child, SIGKILL);
373                 waitpid(state->child, &status, 0);
374         }
375
376         if (ret <= 0) {
377                 DEBUG(3,("dns child failed to find name '%s' of type %s\n",
378                          state->name.name, (state->flags & RESOLVE_NAME_FLAG_DNS_SRV)?"SRV":"A"));
379                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
380                 return;
381         }
382
383         /* enusre the address looks good */
384         address[ret] = 0;
385
386         addrs = str_list_make(state, address, ",");
387         if (composite_nomem(addrs, c)) return;
388
389         num_addrs = str_list_length((const char * const *)addrs);
390
391         state->addrs = talloc_array(state, struct socket_address *,
392                                     num_addrs+1);
393         if (composite_nomem(state->addrs, c)) return;
394
395         state->names = talloc_array(state, char *, num_addrs+1);
396         if (composite_nomem(state->names, c)) return;
397
398         for (i=0; i < num_addrs; i++) {
399                 uint32_t port = 0;
400                 char *p = strrchr(addrs[i], ':');
401                 char *n;
402
403                 if (!p) {
404                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
405                         return;
406                 }
407
408                 *p = '\0';
409                 p++;
410
411                 n = strrchr(p, '/');
412                 if (!n) {
413                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
414                         return;
415                 }
416
417                 *n = '\0';
418                 n++;
419
420                 if (strcmp(addrs[i], "0.0.0.0") == 0 ||
421                     inet_addr(addrs[i]) == INADDR_NONE) {
422                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
423                         return;
424                 }
425                 port = strtoul(p, NULL, 10);
426                 if (port > UINT16_MAX) {
427                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
428                         return;
429                 }
430                 state->addrs[i] = socket_address_from_strings(state->addrs,
431                                                               "ipv4",
432                                                               addrs[i],
433                                                               port);
434                 if (composite_nomem(state->addrs[i], c)) return;
435
436                 state->names[i] = talloc_strdup(state->names, n);
437                 if (composite_nomem(state->names[i], c)) return;
438         }
439         state->addrs[i] = NULL;
440         state->names[i] = NULL;
441
442         composite_done(c);
443 }
444
445 /*
446   getaddrinfo() or dns_lookup() name resolution method - async send
447  */
448 struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
449                                                    struct tevent_context *event_ctx,
450                                                    void *privdata,
451                                                    uint32_t flags,
452                                                    uint16_t port,
453                                                    struct nbt_name *name,
454                                                    bool do_fallback)
455 {
456         struct composite_context *c;
457         struct dns_ex_state *state;
458         int fd[2] = { -1, -1 };
459         int ret;
460
461         c = composite_create(mem_ctx, event_ctx);
462         if (c == NULL) return NULL;
463
464         if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
465                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
466                 return c;
467         }
468
469         state = talloc_zero(c, struct dns_ex_state);
470         if (composite_nomem(state, c)) return c;
471         c->private_data = state;
472
473         c->status = nbt_name_dup(state, name, &state->name);
474         if (!composite_is_ok(c)) return c;
475
476         /* setup a pipe to chat to our child */
477         ret = pipe(fd);
478         if (ret == -1) {
479                 composite_error(c, map_nt_error_from_unix(errno));
480                 return c;
481         }
482
483         state->do_fallback = do_fallback;
484         state->flags = flags;
485         state->port = port;
486
487         state->child_fd = fd[0];
488         state->event_ctx = c->event_ctx;
489
490         /* we need to put the child in our event context so
491            we know when the dns_lookup() has finished */
492         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
493                                   pipe_handler, c);
494         if (composite_nomem(state->fde, c)) {
495                 close(fd[0]);
496                 close(fd[1]);
497                 return c;
498         }
499         tevent_fd_set_auto_close(state->fde);
500
501         state->child = fork();
502         if (state->child == (pid_t)-1) {
503                 composite_error(c, map_nt_error_from_unix(errno));
504                 return c;
505         }
506
507         if (state->child == 0) {
508                 close(fd[0]);
509                 if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
510                         run_child_dns_lookup(state, fd[1]);
511                 } else {
512                         run_child_getaddrinfo(state, fd[1]);
513                 }
514                 _exit(0);
515         }
516         close(fd[1]);
517
518         /* cleanup wayward children */
519         talloc_set_destructor(state, dns_ex_destructor);
520
521         return c;
522 }
523
524 /*
525   getaddrinfo() or dns_lookup() name resolution method - recv side
526 */
527 NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, 
528                                   TALLOC_CTX *mem_ctx,
529                                   struct socket_address ***addrs,
530                                   char ***names)
531 {
532         NTSTATUS status;
533
534         status = composite_wait(c);
535
536         if (NT_STATUS_IS_OK(status)) {
537                 struct dns_ex_state *state = talloc_get_type(c->private_data,
538                                              struct dns_ex_state);
539                 *addrs = talloc_steal(mem_ctx, state->addrs);
540                 if (names) {
541                         *names = talloc_steal(mem_ctx, state->names);
542                 }
543         }
544
545         talloc_free(c);
546         return status;
547 }