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