s4:libcli/resolve/dns_ex.c - fix "const" warning
[obnox/samba/samba-obnox.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         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
272 done:
273         close(fd);
274 }
275
276 /*
277   the blocking child
278 */
279 static 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         }
339 done:
340         if (res_list) {
341                 freeaddrinfo(res_list);
342         }
343         close(fd);
344 }
345
346 /*
347   handle a read event on the pipe
348 */
349 static 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  */
457 struct 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 */
536 NTSTATUS 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 }