libcli/util Rename common map_nt_error_from_unix to avoid duplicate symbol
[samba.git] / source4 / libcli / resolve / dns_ex.c
index 4bcf50a01474f357544506035f080c11db1007f0..592121257937707833fc14d0116d2a31d3e232d2 100644 (file)
 #include "libcli/composite/composite.h"
 #include "librpc/gen_ndr/ndr_nbt.h"
 #include "libcli/resolve/resolve.h"
+#include "lib/util/util_net.h"
+
+#ifdef class
+#undef class
+#endif
+
 #include "heimdal/lib/roken/resolve.h"
 
 struct dns_ex_state {
-       bool do_getaddrinfo;
        bool do_fallback;
-       bool do_srv;
+       uint32_t flags;
+       uint16_t port;
        struct nbt_name name;
        struct socket_address **addrs;
+       char **names;
        pid_t child;
        int child_fd;
-       struct fd_event *fde;
-       struct event_context *event_ctx;
+       struct tevent_fd *fde;
+       struct tevent_context *event_ctx;
 };
 
 /*
@@ -61,7 +68,6 @@ static int dns_ex_destructor(struct dns_ex_state *state)
        int status;
 
        kill(state->child, SIGTERM);
-       close(state->child_fd);
        if (waitpid(state->child, &status, WNOHANG) == 0) {
                kill(state->child, SIGKILL);
                waitpid(state->child, &status, 0);
@@ -75,38 +81,49 @@ static int dns_ex_destructor(struct dns_ex_state *state)
 */
 static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
 {
-       struct dns_reply *reply;
-       struct resource_record *rr;
+       struct rk_dns_reply *reply;
+       struct rk_resource_record *rr;
        uint32_t count = 0;
        uint32_t srv_valid = 0;
-       struct resource_record **srv_rr;
+       struct rk_resource_record **srv_rr;
        uint32_t addrs_valid = 0;
-       struct resource_record **addrs_rr;
+       struct rk_resource_record **addrs_rr;
+       struct rk_dns_reply **srv_replies = NULL;
        char *addrs;
        bool first;
        uint32_t i;
+       bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
+
+       if (strchr(state->name.name, '.') && state->name.name[strlen(state->name.name)-1] != '.') {
+               /* we are asking for a fully qualified name, but the
+                  name doesn't end in a '.'. We need to prevent the
+                  DNS library trying the search domains configured in
+                  resolv.conf */
+               state->name.name = talloc_strdup_append(discard_const_p(char, state->name.name),
+                                                       ".");
+       }
 
        /* this is the blocking call we are going to lots of trouble
           to avoid in the parent */
-       reply = dns_lookup(state->name.name, state->do_srv?"SRV":"A");
+       reply = rk_dns_lookup(state->name.name, do_srv?"SRV":"A");
        if (!reply) {
                goto done;
        }
 
-       if (state->do_srv) {
-               dns_srv_order(reply);
+       if (do_srv) {
+               rk_dns_srv_order(reply);
        }
 
        /* Loop over all returned records and pick the "srv" records */
        for (rr=reply->head; rr; rr=rr->next) {
                /* we are only interested in the IN class */
-               if (rr->class != C_IN) {
+               if (rr->class != rk_ns_c_in) {
                        continue;
                }
 
-               if (state->do_srv) {
+               if (do_srv) {
                        /* we are only interested in SRV records */
-                       if (rr->type != T_SRV) {
+                       if (rr->type != rk_ns_t_srv) {
                                continue;
                        }
 
@@ -120,14 +137,13 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
                                continue;
                        }
                } else {
-                       /* we are only interested in A records */
-                       /* TODO: add AAAA support */
-                       if (rr->type != T_A) {
+                       /* we are only interested in A or AAAA records */
+                       if (rr->type != rk_ns_t_a && rr->type != rk_ns_t_aaaa) {
                                continue;
                        }
 
-                       /* verify we actually have a record here */
-                       if (!rr->u.a) {
+                       /* verify we actually have a record here */
+                       if (!rr->u.data) {
                                continue;
                        }
                }
@@ -139,29 +155,36 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
        }
 
        srv_rr = talloc_zero_array(state,
-                                  struct resource_record *,
+                                  struct rk_resource_record *,
                                   count);
        if (!srv_rr) {
                goto done;
        }
 
        addrs_rr = talloc_zero_array(state,
-                                    struct resource_record *,
+                                    struct rk_resource_record *,
                                     count);
        if (!addrs_rr) {
                goto done;
        }
 
+       srv_replies = talloc_zero_array(state,
+                                       struct rk_dns_reply *,
+                                       count);
+       if (!srv_replies) {
+               goto done;
+       }
+
        /* Loop over all returned records and pick the records */
        for (rr=reply->head;rr;rr=rr->next) {
                /* we are only interested in the IN class */
-               if (rr->class != C_IN) {
+               if (rr->class != rk_ns_c_in) {
                        continue;
                }
 
-               if (state->do_srv) {
+               if (do_srv) {
                        /* we are only interested in SRV records */
-                       if (rr->type != T_SRV) {
+                       if (rr->type != rk_ns_t_srv) {
                                continue;
                        }
 
@@ -178,14 +201,13 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
                        srv_rr[srv_valid] = rr;
                        srv_valid++;
                } else {
-                       /* we are only interested in A records */
-                       /* TODO: add AAAA support */
-                       if (rr->type != T_A) {
+                       /* we are only interested in A or AAAA records */
+                       if (rr->type != rk_ns_t_a && rr->type != rk_ns_t_aaaa) {
                                continue;
                        }
 
-                       /* verify we actually have a A record here */
-                       if (!rr->u.a) {
+                       /* verify we actually have a record record here */
+                       if (!rr->u.data) {
                                continue;
                        }
 
@@ -195,19 +217,23 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
        }
 
        for (i=0; i < srv_valid; i++) {
-               for (rr=reply->head;rr;rr=rr->next) {
+               srv_replies[i] = rk_dns_lookup(srv_rr[i]->u.srv->target, "A");
+               if (srv_replies[i] == NULL)
+                       continue;
 
-                       if (rr->class != C_IN) {
+               /* Add first A record to addrs_rr */
+               for (rr=srv_replies[i]->head;rr;rr=rr->next) {
+                       if (rr->class != rk_ns_c_in) {
                                continue;
                        }
 
-                       /* we are only interested in SRV records */
-                       if (rr->type != T_A) {
+                       /* we are only interested in A or AAAA records */
+                       if (rr->type != rk_ns_t_a && rr->type != rk_ns_t_aaaa) {
                                continue;
                        }
 
-                       /* verify we actually have a srv record here */
-                       if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
+                       /* verify we actually have a record here */
+                       if (!rr->u.data) {
                                continue;
                        }
 
@@ -226,14 +252,45 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
                goto done;
        }
        first = true;
-       for (i=0; i < count; i++) {
+       for (i=0; i < addrs_valid; i++) {
+               uint16_t port;
+               char addrstr[INET6_ADDRSTRLEN];
+
                if (!addrs_rr[i]) {
                        continue;
                }
-               addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u",
+
+               if (srv_rr[i] &&
+                   (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
+                       port = srv_rr[i]->u.srv->port;
+               } else {
+                       port = state->port;
+               }
+
+               switch (addrs_rr[i]->type) {
+               case rk_ns_t_a:
+                       if (inet_ntop(AF_INET, addrs_rr[i]->u.a,
+                                     addrstr, sizeof(addrstr)) == NULL) {
+                               continue;
+                       }
+                       break;
+#ifdef HAVE_IPV6
+               case rk_ns_t_aaaa:
+                       if (inet_ntop(AF_INET6, (struct in6_addr *)addrs_rr[i]->u.data,
+                                     addrstr, sizeof(addrstr)) == NULL) {
+                               continue;
+                       }
+                       break;
+#endif
+               default:
+                       continue;
+               }
+
+               addrs = talloc_asprintf_append_buffer(addrs, "%s%s@%u/%s",
                                                      first?"":",",
-                                                     inet_ntoa(*addrs_rr[i]->u.a),
-                                                     srv_rr[i]?srv_rr[i]->u.srv->port:0);
+                                                     addrstr,
+                                                     port,
+                                                     addrs_rr[i]->domain);
                if (!addrs) {
                        goto done;
                }
@@ -245,6 +302,12 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
        }
 
 done:
+       if (reply != NULL)
+               rk_dns_free_data(reply);
+       for (i=0; i < srv_valid; i++) {
+               if (srv_replies[i] != NULL)
+                       rk_dns_free_data(srv_replies[i]);
+       }
        close(fd);
 }
 
@@ -262,14 +325,22 @@ static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
 
        ZERO_STRUCT(hints);
        hints.ai_socktype = SOCK_STREAM;
-       hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
        hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
 
        ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
-       if (ret == EAI_NODATA && state->do_fallback) {
-               /* getaddrinfo() doesn't handle CNAME records */
-               run_child_dns_lookup(state, fd);
-               return;
+       /* try to fallback in case of error */
+       if (state->do_fallback) {
+               switch (ret) {
+#ifdef EAI_NODATA
+               case EAI_NODATA:
+#endif
+               case EAI_NONAME:
+                       /* getaddrinfo() doesn't handle CNAME records */
+                       run_child_dns_lookup(state, fd);
+                       return;
+               default:
+                       break;
+               }
        }
        if (ret != 0) {
                goto done;
@@ -281,17 +352,15 @@ static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
        }
        first = true;
        for (res = res_list; res; res = res->ai_next) {
-               struct sockaddr_in *in;
-
-               if (res->ai_family != AF_INET) {
+               char addrstr[INET6_ADDRSTRLEN];
+               if (!print_sockaddr_len(addrstr, sizeof(addrstr), (struct sockaddr *)res->ai_addr, res->ai_addrlen)) {
                        continue;
                }
-               in = (struct sockaddr_in *)res->ai_addr;
-
-               addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u",
+               addrs = talloc_asprintf_append_buffer(addrs, "%s%s@%u/%s",
                                                      first?"":",",
-                                                     inet_ntoa(in->sin_addr),
-                                                     0);
+                                                     addrstr,
+                                                     state->port,
+                                                     state->name.name);
                if (!addrs) {
                        goto done;
                }
@@ -311,34 +380,45 @@ done:
 /*
   handle a read event on the pipe
 */
-static void pipe_handler(struct event_context *ev, struct fd_event *fde, 
+static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, 
                         uint16_t flags, void *private_data)
 {
        struct composite_context *c = talloc_get_type(private_data, struct composite_context);
        struct dns_ex_state *state = talloc_get_type(c->private_data,
                                     struct dns_ex_state);
-       char address[2048];
+       char *address;
        uint32_t num_addrs, i;
        char **addrs;
        int ret;
        int status;
+       int value = 0;
 
        /* if we get any event from the child then we know that we
           won't need to kill it off */
        talloc_set_destructor(state, NULL);
 
-       /* yes, we don't care about EAGAIN or other niceities
-          here. They just can't happen with this parent/child
-          relationship, and even if they did then giving an error is
-          the right thing to do */
-       ret = read(state->child_fd, address, sizeof(address)-1);
-       close(state->child_fd);
+       if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
+               value = 8192;
+       }
+
+       address = talloc_array(state, char, value+1);
+       if (address) {
+               /* yes, we don't care about EAGAIN or other niceities
+                  here. They just can't happen with this parent/child
+                  relationship, and even if they did then giving an error is
+                  the right thing to do */
+               ret = read(state->child_fd, address, value);
+       } else {
+               ret = -1;
+       }
        if (waitpid(state->child, &status, WNOHANG) == 0) {
                kill(state->child, SIGKILL);
                waitpid(state->child, &status, 0);
        }
 
        if (ret <= 0) {
+               DEBUG(3,("dns child failed to find name '%s' of type %s\n",
+                        state->name.name, (state->flags & RESOLVE_NAME_FLAG_DNS_SRV)?"SRV":"A"));
                composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
                return;
        }
@@ -355,9 +435,13 @@ static void pipe_handler(struct event_context *ev, struct fd_event *fde,
                                    num_addrs+1);
        if (composite_nomem(state->addrs, c)) return;
 
+       state->names = talloc_array(state, char *, num_addrs+1);
+       if (composite_nomem(state->names, c)) return;
+
        for (i=0; i < num_addrs; i++) {
                uint32_t port = 0;
-               char *p = strrchr(addrs[i], ':');
+               char *p = strrchr(addrs[i], '@');
+               char *n;
 
                if (!p) {
                        composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
@@ -367,22 +451,35 @@ static void pipe_handler(struct event_context *ev, struct fd_event *fde,
                *p = '\0';
                p++;
 
-               if (strcmp(addrs[i], "0.0.0.0") == 0 ||
-                   inet_addr(addrs[i]) == INADDR_NONE) {
+               n = strrchr(p, '/');
+               if (!n) {
+                       composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+                       return;
+               }
+
+               *n = '\0';
+               n++;
+
+               if (strcmp(addrs[i], "0.0.0.0") == 0) {
                        composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
                        return;
                }
                port = strtoul(p, NULL, 10);
                if (port > UINT16_MAX) {
-                       port = 0;
+                       composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+                       return;
                }
                state->addrs[i] = socket_address_from_strings(state->addrs,
-                                                             "ipv4",
+                                                             "ip",
                                                              addrs[i],
                                                              port);
                if (composite_nomem(state->addrs[i], c)) return;
+
+               state->names[i] = talloc_strdup(state->names, n);
+               if (composite_nomem(state->names[i], c)) return;
        }
        state->addrs[i] = NULL;
+       state->names[i] = NULL;
 
        composite_done(c);
 }
@@ -391,12 +488,12 @@ static void pipe_handler(struct event_context *ev, struct fd_event *fde,
   getaddrinfo() or dns_lookup() name resolution method - async send
  */
 struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
-                                                  struct event_context *event_ctx,
+                                                  struct tevent_context *event_ctx,
                                                   void *privdata,
+                                                  uint32_t flags,
+                                                  uint16_t port,
                                                   struct nbt_name *name,
-                                                  bool do_getaddrinfo,
-                                                  bool do_fallback,
-                                                  bool do_srv)
+                                                  bool do_fallback)
 {
        struct composite_context *c;
        struct dns_ex_state *state;
@@ -406,7 +503,10 @@ struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
        c = composite_create(mem_ctx, event_ctx);
        if (c == NULL) return NULL;
 
-       if (composite_nomem(c->event_ctx, c)) return c;
+       if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
+               composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+               return c;
+       }
 
        state = talloc_zero(c, struct dns_ex_state);
        if (composite_nomem(state, c)) return c;
@@ -418,13 +518,13 @@ struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
        /* setup a pipe to chat to our child */
        ret = pipe(fd);
        if (ret == -1) {
-               composite_error(c, map_nt_error_from_unix(errno));
+               composite_error(c, map_nt_error_from_unix_common(errno));
                return c;
        }
 
-       state->do_getaddrinfo = do_getaddrinfo;
        state->do_fallback = do_fallback;
-       state->do_srv = do_srv;
+       state->flags = flags;
+       state->port = port;
 
        state->child_fd = fd[0];
        state->event_ctx = c->event_ctx;
@@ -438,19 +538,20 @@ struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
                close(fd[1]);
                return c;
        }
+       tevent_fd_set_auto_close(state->fde);
 
        state->child = fork();
        if (state->child == (pid_t)-1) {
-               composite_error(c, map_nt_error_from_unix(errno));
+               composite_error(c, map_nt_error_from_unix_common(errno));
                return c;
        }
 
        if (state->child == 0) {
                close(fd[0]);
-               if (state->do_getaddrinfo) {
-                       run_child_getaddrinfo(state, fd[1]);
-               } else {
+               if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
                        run_child_dns_lookup(state, fd[1]);
+               } else {
+                       run_child_getaddrinfo(state, fd[1]);
                }
                _exit(0);
        }
@@ -467,7 +568,8 @@ struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
 */
 NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, 
                                  TALLOC_CTX *mem_ctx,
-                                 struct socket_address ***addrs)
+                                 struct socket_address ***addrs,
+                                 char ***names)
 {
        NTSTATUS status;
 
@@ -477,6 +579,9 @@ NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c,
                struct dns_ex_state *state = talloc_get_type(c->private_data,
                                             struct dns_ex_state);
                *addrs = talloc_steal(mem_ctx, state->addrs);
+               if (names) {
+                       *names = talloc_steal(mem_ctx, state->names);
+               }
        }
 
        talloc_free(c);