s4:lib/tevent: rename structs
[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_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 tevent_fd *fde;
52         struct tevent_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 defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
282         if (ret == EAI_NODATA && state->do_fallback) {
283 #else
284         if (ret == EAI_NONAME && state->do_fallback) {
285 #endif
286                 /* getaddrinfo() doesn't handle CNAME records */
287                 run_child_dns_lookup(state, fd);
288                 return;
289         }
290         if (ret != 0) {
291                 goto done;
292         }
293
294         addrs = talloc_strdup(state, "");
295         if (!addrs) {
296                 goto done;
297         }
298         first = true;
299         for (res = res_list; res; res = res->ai_next) {
300                 struct sockaddr_in *in;
301
302                 if (res->ai_family != AF_INET) {
303                         continue;
304                 }
305                 in = (struct sockaddr_in *)res->ai_addr;
306
307                 addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
308                                                       first?"":",",
309                                                       inet_ntoa(in->sin_addr),
310                                                       state->port,
311                                                       state->name.name);
312                 if (!addrs) {
313                         goto done;
314                 }
315                 first = false;
316         }
317
318         if (addrs) {
319                 write(fd, addrs, talloc_get_size(addrs));
320         }
321 done:
322         if (res_list) {
323                 freeaddrinfo(res_list);
324         }
325         close(fd);
326 }
327
328 /*
329   handle a read event on the pipe
330 */
331 static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, 
332                          uint16_t flags, void *private_data)
333 {
334         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
335         struct dns_ex_state *state = talloc_get_type(c->private_data,
336                                      struct dns_ex_state);
337         char *address;
338         uint32_t num_addrs, i;
339         char **addrs;
340         int ret;
341         int status;
342         int value = 0;
343
344         /* if we get any event from the child then we know that we
345            won't need to kill it off */
346         talloc_set_destructor(state, NULL);
347
348         if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
349                 value = 8192;
350         }
351
352         address = talloc_array(state, char, value+1);
353         if (address) {
354                 /* yes, we don't care about EAGAIN or other niceities
355                    here. They just can't happen with this parent/child
356                    relationship, and even if they did then giving an error is
357                    the right thing to do */
358                 ret = read(state->child_fd, address, value);
359         } else {
360                 ret = -1;
361         }
362         close(state->child_fd);
363         if (waitpid(state->child, &status, WNOHANG) == 0) {
364                 kill(state->child, SIGKILL);
365                 waitpid(state->child, &status, 0);
366         }
367
368         if (ret <= 0) {
369                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
370                 return;
371         }
372
373         /* enusre the address looks good */
374         address[ret] = 0;
375
376         addrs = str_list_make(state, address, ",");
377         if (composite_nomem(addrs, c)) return;
378
379         num_addrs = str_list_length((const char * const *)addrs);
380
381         state->addrs = talloc_array(state, struct socket_address *,
382                                     num_addrs+1);
383         if (composite_nomem(state->addrs, c)) return;
384
385         state->names = talloc_array(state, char *, num_addrs+1);
386         if (composite_nomem(state->names, c)) return;
387
388         for (i=0; i < num_addrs; i++) {
389                 uint32_t port = 0;
390                 char *p = strrchr(addrs[i], ':');
391                 char *n;
392
393                 if (!p) {
394                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
395                         return;
396                 }
397
398                 *p = '\0';
399                 p++;
400
401                 n = strrchr(p, '/');
402                 if (!n) {
403                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
404                         return;
405                 }
406
407                 *n = '\0';
408                 n++;
409
410                 if (strcmp(addrs[i], "0.0.0.0") == 0 ||
411                     inet_addr(addrs[i]) == INADDR_NONE) {
412                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
413                         return;
414                 }
415                 port = strtoul(p, NULL, 10);
416                 if (port > UINT16_MAX) {
417                         composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
418                         return;
419                 }
420                 state->addrs[i] = socket_address_from_strings(state->addrs,
421                                                               "ipv4",
422                                                               addrs[i],
423                                                               port);
424                 if (composite_nomem(state->addrs[i], c)) return;
425
426                 state->names[i] = talloc_strdup(state->names, n);
427                 if (composite_nomem(state->names[i], c)) return;
428         }
429         state->addrs[i] = NULL;
430         state->names[i] = NULL;
431
432         composite_done(c);
433 }
434
435 /*
436   getaddrinfo() or dns_lookup() name resolution method - async send
437  */
438 struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
439                                                    struct tevent_context *event_ctx,
440                                                    void *privdata,
441                                                    uint32_t flags,
442                                                    uint16_t port,
443                                                    struct nbt_name *name,
444                                                    bool do_fallback)
445 {
446         struct composite_context *c;
447         struct dns_ex_state *state;
448         int fd[2] = { -1, -1 };
449         int ret;
450
451         c = composite_create(mem_ctx, event_ctx);
452         if (c == NULL) return NULL;
453
454         if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
455                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
456                 return c;
457         }
458
459         state = talloc_zero(c, struct dns_ex_state);
460         if (composite_nomem(state, c)) return c;
461         c->private_data = state;
462
463         c->status = nbt_name_dup(state, name, &state->name);
464         if (!composite_is_ok(c)) return c;
465
466         /* setup a pipe to chat to our child */
467         ret = pipe(fd);
468         if (ret == -1) {
469                 composite_error(c, map_nt_error_from_unix(errno));
470                 return c;
471         }
472
473         state->do_fallback = do_fallback;
474         state->flags = flags;
475         state->port = port;
476
477         state->child_fd = fd[0];
478         state->event_ctx = c->event_ctx;
479
480         /* we need to put the child in our event context so
481            we know when the dns_lookup() has finished */
482         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
483                                   pipe_handler, c);
484         if (composite_nomem(state->fde, c)) {
485                 close(fd[0]);
486                 close(fd[1]);
487                 return c;
488         }
489
490         state->child = fork();
491         if (state->child == (pid_t)-1) {
492                 composite_error(c, map_nt_error_from_unix(errno));
493                 return c;
494         }
495
496         if (state->child == 0) {
497                 close(fd[0]);
498                 if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
499                         run_child_dns_lookup(state, fd[1]);
500                 } else {
501                         run_child_getaddrinfo(state, fd[1]);
502                 }
503                 _exit(0);
504         }
505         close(fd[1]);
506
507         /* cleanup wayward children */
508         talloc_set_destructor(state, dns_ex_destructor);
509
510         return c;
511 }
512
513 /*
514   getaddrinfo() or dns_lookup() name resolution method - recv side
515 */
516 NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, 
517                                   TALLOC_CTX *mem_ctx,
518                                   struct socket_address ***addrs,
519                                   char ***names)
520 {
521         NTSTATUS status;
522
523         status = composite_wait(c);
524
525         if (NT_STATUS_IS_OK(status)) {
526                 struct dns_ex_state *state = talloc_get_type(c->private_data,
527                                              struct dns_ex_state);
528                 *addrs = talloc_steal(mem_ctx, state->addrs);
529                 if (names) {
530                         *names = talloc_steal(mem_ctx, state->names);
531                 }
532         }
533
534         talloc_free(c);
535         return status;
536 }