s4:libcli/resolve: optionally return the name that belongs to the returned address
[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 #include "heimdal/lib/roken/resolve.h"
41
42 struct dns_ex_state {
43         bool do_getaddrinfo;
44         bool do_fallback;
45         bool do_srv;
46         uint32_t flags;
47         struct nbt_name name;
48         struct socket_address **addrs;
49         char **names;
50         pid_t child;
51         int child_fd;
52         struct fd_event *fde;
53         struct event_context *event_ctx;
54 };
55
56 /*
57   kill off a wayward child if needed. This allows us to stop an async
58   name resolution without leaving a potentially blocking call running
59   in a child
60 */
61 static int dns_ex_destructor(struct dns_ex_state *state)
62 {
63         int status;
64
65         kill(state->child, SIGTERM);
66         close(state->child_fd);
67         if (waitpid(state->child, &status, WNOHANG) == 0) {
68                 kill(state->child, SIGKILL);
69                 waitpid(state->child, &status, 0);
70         }
71
72         return 0;
73 }
74
75 /*
76   the blocking child
77 */
78 static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
79 {
80         struct dns_reply *reply;
81         struct resource_record *rr;
82         uint32_t count = 0;
83         uint32_t srv_valid = 0;
84         struct resource_record **srv_rr;
85         uint32_t addrs_valid = 0;
86         struct resource_record **addrs_rr;
87         char *addrs;
88         bool first;
89         uint32_t i;
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, state->do_srv?"SRV":"A");
94         if (!reply) {
95                 goto done;
96         }
97
98         if (state->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 (state->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 (state->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                 if (!addrs_rr[i]) {
233                         continue;
234                 }
235                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
236                                                       first?"":",",
237                                                       inet_ntoa(*addrs_rr[i]->u.a),
238                                                       srv_rr[i]?srv_rr[i]->u.srv->port:0,
239                                                       addrs_rr[i]->domain);
240                 if (!addrs) {
241                         goto done;
242                 }
243                 first = false;
244         }
245
246         if (addrs) {
247                 write(fd, addrs, talloc_get_size(addrs));
248         }
249
250 done:
251         close(fd);
252 }
253
254 /*
255   the blocking child
256 */
257 static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
258 {
259         int ret;
260         struct addrinfo hints;
261         struct addrinfo *res;
262         struct addrinfo *res_list = NULL;
263         char *addrs;
264         bool first;
265
266         ZERO_STRUCT(hints);
267         hints.ai_socktype = SOCK_STREAM;
268         hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
269         hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
270
271         ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
272         if (ret == EAI_NODATA && state->do_fallback) {
273                 /* getaddrinfo() doesn't handle CNAME records */
274                 run_child_dns_lookup(state, fd);
275                 return;
276         }
277         if (ret != 0) {
278                 goto done;
279         }
280
281         addrs = talloc_strdup(state, "");
282         if (!addrs) {
283                 goto done;
284         }
285         first = true;
286         for (res = res_list; res; res = res->ai_next) {
287                 struct sockaddr_in *in;
288
289                 if (res->ai_family != AF_INET) {
290                         continue;
291                 }
292                 in = (struct sockaddr_in *)res->ai_addr;
293
294                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
295                                                       first?"":",",
296                                                       inet_ntoa(in->sin_addr),
297                                                       0, state->name.name);
298                 if (!addrs) {
299                         goto done;
300                 }
301                 first = false;
302         }
303
304         if (addrs) {
305                 write(fd, addrs, talloc_get_size(addrs));
306         }
307 done:
308         if (res_list) {
309                 freeaddrinfo(res_list);
310         }
311         close(fd);
312 }
313
314 /*
315   handle a read event on the pipe
316 */
317 static void pipe_handler(struct event_context *ev, struct fd_event *fde, 
318                          uint16_t flags, void *private_data)
319 {
320         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
321         struct dns_ex_state *state = talloc_get_type(c->private_data,
322                                      struct dns_ex_state);
323         char *address;
324         uint32_t num_addrs, i;
325         char **addrs;
326         int ret;
327         int status;
328         int value = 0;
329
330         /* if we get any event from the child then we know that we
331            won't need to kill it off */
332         talloc_set_destructor(state, NULL);
333
334         if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
335                 value = 8192;
336         }
337
338         address = talloc_array(state, char, value+1);
339         if (address) {
340                 /* yes, we don't care about EAGAIN or other niceities
341                    here. They just can't happen with this parent/child
342                    relationship, and even if they did then giving an error is
343                    the right thing to do */
344                 ret = read(state->child_fd, address, value);
345         } else {
346                 ret = -1;
347         }
348         close(state->child_fd);
349         if (waitpid(state->child, &status, WNOHANG) == 0) {
350                 kill(state->child, SIGKILL);
351                 waitpid(state->child, &status, 0);
352         }
353
354         if (ret <= 0) {
355                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
356                 return;
357         }
358
359         /* enusre the address looks good */
360         address[ret] = 0;
361
362         addrs = str_list_make(state, address, ",");
363         if (composite_nomem(addrs, c)) return;
364
365         num_addrs = str_list_length((const char * const *)addrs);
366
367         state->addrs = talloc_array(state, struct socket_address *,
368                                     num_addrs+1);
369         if (composite_nomem(state->addrs, c)) return;
370
371         state->names = talloc_array(state, char *, num_addrs+1);
372         if (composite_nomem(state->names, c)) return;
373
374         for (i=0; i < num_addrs; i++) {
375                 uint32_t port = 0;
376                 char *p = strrchr(addrs[i], ':');
377                 char *n;
378
379                 if (!p) {
380                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
381                         return;
382                 }
383
384                 *p = '\0';
385                 p++;
386
387                 n = strrchr(p, '/');
388                 if (!n) {
389                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
390                         return;
391                 }
392
393                 *n = '\0';
394                 n++;
395
396                 if (strcmp(addrs[i], "0.0.0.0") == 0 ||
397                     inet_addr(addrs[i]) == INADDR_NONE) {
398                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
399                         return;
400                 }
401                 port = strtoul(p, NULL, 10);
402                 if (port > UINT16_MAX) {
403                         port = 0;
404                 }
405                 state->addrs[i] = socket_address_from_strings(state->addrs,
406                                                               "ipv4",
407                                                               addrs[i],
408                                                               port);
409                 if (composite_nomem(state->addrs[i], c)) return;
410
411                 state->names[i] = talloc_strdup(state->names, n);
412                 if (composite_nomem(state->names[i], c)) return;
413         }
414         state->addrs[i] = NULL;
415         state->names[i] = NULL;
416
417         composite_done(c);
418 }
419
420 /*
421   getaddrinfo() or dns_lookup() name resolution method - async send
422  */
423 struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
424                                                    struct event_context *event_ctx,
425                                                    void *privdata,
426                                                    uint32_t flags,
427                                                    struct nbt_name *name,
428                                                    bool do_getaddrinfo,
429                                                    bool do_fallback,
430                                                    bool do_srv)
431 {
432         struct composite_context *c;
433         struct dns_ex_state *state;
434         int fd[2] = { -1, -1 };
435         int ret;
436
437         c = composite_create(mem_ctx, event_ctx);
438         if (c == NULL) return NULL;
439
440         if (composite_nomem(c->event_ctx, c)) return c;
441
442         state = talloc_zero(c, struct dns_ex_state);
443         if (composite_nomem(state, c)) return c;
444         c->private_data = state;
445
446         c->status = nbt_name_dup(state, name, &state->name);
447         if (!composite_is_ok(c)) return c;
448
449         /* setup a pipe to chat to our child */
450         ret = pipe(fd);
451         if (ret == -1) {
452                 composite_error(c, map_nt_error_from_unix(errno));
453                 return c;
454         }
455
456         state->do_getaddrinfo = do_getaddrinfo;
457         state->do_fallback = do_fallback;
458         state->do_srv = do_srv;
459         state->flags = flags;
460
461         state->child_fd = fd[0];
462         state->event_ctx = c->event_ctx;
463
464         /* we need to put the child in our event context so
465            we know when the dns_lookup() has finished */
466         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
467                                   pipe_handler, c);
468         if (composite_nomem(state->fde, c)) {
469                 close(fd[0]);
470                 close(fd[1]);
471                 return c;
472         }
473
474         state->child = fork();
475         if (state->child == (pid_t)-1) {
476                 composite_error(c, map_nt_error_from_unix(errno));
477                 return c;
478         }
479
480         if (state->child == 0) {
481                 close(fd[0]);
482                 if (state->do_getaddrinfo) {
483                         run_child_getaddrinfo(state, fd[1]);
484                 } else {
485                         run_child_dns_lookup(state, fd[1]);
486                 }
487                 _exit(0);
488         }
489         close(fd[1]);
490
491         /* cleanup wayward children */
492         talloc_set_destructor(state, dns_ex_destructor);
493
494         return c;
495 }
496
497 /*
498   getaddrinfo() or dns_lookup() name resolution method - recv side
499 */
500 NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, 
501                                   TALLOC_CTX *mem_ctx,
502                                   struct socket_address ***addrs,
503                                   char ***names)
504 {
505         NTSTATUS status;
506
507         status = composite_wait(c);
508
509         if (NT_STATUS_IS_OK(status)) {
510                 struct dns_ex_state *state = talloc_get_type(c->private_data,
511                                              struct dns_ex_state);
512                 *addrs = talloc_steal(mem_ctx, state->addrs);
513                 if (names) {
514                         *names = talloc_steal(mem_ctx, state->names);
515                 }
516         }
517
518         talloc_free(c);
519         return status;
520 }