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