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