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