addns: Remove support for dns_host_file.
[obnox/samba/samba-obnox.git] / source4 / libcli / resolve / dns_ex.c
index 993ef43caabbf94d0fe7ff88e5452789eb5b8fe9..08b693272237ee2dd61927bbb7aa9dbc5b79a342 100644 (file)
 #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"
+#include "lib/addns/dnsquery.h"
+#include "lib/addns/dns.h"
+#include <arpa/nameser.h>
+#include <resolv.h>
 
 struct dns_ex_state {
        bool do_fallback;
@@ -77,243 +75,224 @@ static int dns_ex_destructor(struct dns_ex_state *state)
        return 0;
 }
 
-static uint32_t count_dns_rr(struct rk_resource_record *head, unsigned record_type)
+struct dns_records_container {
+       char **list;
+       uint32_t count;
+};
+
+static int reply_to_addrs(TALLOC_CTX *mem_ctx, uint32_t *a_num,
+                         char ***cur_addrs, uint32_t total,
+                         struct dns_request *reply, int port)
 {
-       uint32_t count = 0;
-       struct rk_resource_record *rr;
+       char addrstr[INET6_ADDRSTRLEN];
+       struct dns_rrec *rr;
+       char **addrs;
+       uint32_t i;
+       const char *addr;
+
+       /* at most we over-allocate here, but not by much */
+       addrs = talloc_realloc(mem_ctx, *cur_addrs, char *,
+                               total + reply->num_answers);
+       if (!addrs) {
+               return 0;
+       }
+       *cur_addrs = addrs;
 
-       for (rr=head; rr; rr=rr->next) {
+       for (i = 0; i < reply->num_answers; i++) {
+               rr = reply->answers[i];
 
                /* we are only interested in the IN class */
-               if (rr->class != rk_ns_c_in) {
+               if (rr->r_class != DNS_CLASS_IN) {
                        continue;
                }
 
-               /* we are only interested by requested record */
-               if (rr->type != record_type) {
-                       continue;
+               if (rr->type == QTYPE_NS) {
+                       /*
+                        * After the record for NS will come the A or AAAA
+                        * record of the NS.
+                        */
+                       break;
                }
 
-               switch(record_type) {
-                       case rk_ns_t_srv:
-
-                               /* verify we actually have a SRV record here */
-                               if (!rr->u.srv) {
-                                       continue;
-                               }
-
-                               /* Verify we got a port */
-                               if (rr->u.srv->port == 0) {
-                                       continue;
-                               }
-                               count++;
-                               break;
-                       case rk_ns_t_a:
-                       case rk_ns_t_aaaa:
-                               /* verify we actually have a record here */
-                               if (!rr->u.data) {
-                                       continue;
-                               }
-                               count++;
-                               break;
-                       default:
-                               count++;
-                               break;
+               /* verify we actually have a record here */
+               if (!rr->data) {
+                       continue;
                }
-       }
-
-       return count;
-}
 
-struct dns_records_container {
-       char **list;
-       uint32_t count;
-};
-
-static char* rr_to_string(TALLOC_CTX *mem_ctx,
-                                       struct rk_resource_record *rr,
-                                       uint16_t port)
-{
-       char addrstr[INET6_ADDRSTRLEN];
-       char *addr;
-
-       switch (rr->type) {
-               case rk_ns_t_a:
-                       if (inet_ntop(AF_INET, rr->u.a,
-                                     addrstr, sizeof(addrstr)) == NULL) {
-                               return NULL;
+               /* we are only interested in A and AAAA records */
+               switch (rr->type) {
+               case QTYPE_A:
+                       addr = inet_ntop(AF_INET,
+                                        (struct in_addr *)rr->data,
+                                        addrstr, sizeof(addrstr));
+                       if (addr == NULL) {
+                               continue;
                        }
                        break;
+               case QTYPE_AAAA:
 #ifdef HAVE_IPV6
-               case rk_ns_t_aaaa:
-                       if (inet_ntop(AF_INET6, (struct in6_addr *)rr->u.data,
-                                     addrstr, sizeof(addrstr)) == NULL) {
-                               return NULL;
+                       addr = inet_ntop(AF_INET6,
+                                        (struct in6_addr *)rr->data,
+                                        addrstr, sizeof(addrstr));
+#else
+                       addr = NULL;
+#endif
+                       if (addr == NULL) {
+                               continue;
                        }
                        break;
-#endif
                default:
-                       return NULL;
+                       continue;
+               }
+
+               addrs[total] = talloc_asprintf(addrs, "%s@%u/%s",
+                                               addrstr, port,
+                                               rr->name->pLabelList->label);
+               if (addrs[total]) {
+                       total++;
+                       if (rr->type == QTYPE_A) {
+                               (*a_num)++;
+                       }
+               }
        }
 
-       addr = talloc_asprintf(mem_ctx, "%s@%u/%s", addrstr,
-                                        port, rr->domain);
+       return total;
+}
 
-       return addr;
+static DNS_ERROR dns_lookup(TALLOC_CTX *mem_ctx, const char* name,
+                           uint16_t q_type, struct dns_request **reply)
+{
+       int len, rlen;
+       uint8_t *answer;
+       bool loop;
+       struct dns_buffer buf;
+       DNS_ERROR err;
+
+       /* give space for a good sized answer by default */
+       answer = NULL;
+       len = 1500;
+       do {
+               answer = talloc_realloc(mem_ctx, answer, uint8_t, len);
+               if (!answer) {
+                       return ERROR_DNS_NO_MEMORY;
+               }
+               rlen = res_search(name, DNS_CLASS_IN, q_type, answer, len);
+               if (rlen == -1) {
+                       if (len >= 65535) {
+                               return ERROR_DNS_SOCKET_ERROR;
+                       }
+                       /* retry once with max packet size */
+                       len = 65535;
+                       loop = true;
+               } else if (rlen > len) {
+                       len = rlen;
+                       loop = true;
+               } else {
+                       loop = false;
+               }
+       } while(loop);
+
+       buf.data = answer;
+       buf.size = rlen;
+       buf.offset = 0;
+       buf.error = ERROR_DNS_SUCCESS;
+
+       err = dns_unmarshall_request(mem_ctx, &buf, reply);
+
+       TALLOC_FREE(answer);
+       return err;
 }
 
 static struct dns_records_container get_a_aaaa_records(TALLOC_CTX *mem_ctx,
                                                        const char* name,
                                                        int port)
 {
-       struct rk_dns_reply *reply, *reply2, *rep, *tmp[3];
-       struct rk_resource_record *rr;
+       struct dns_request *reply;
        struct dns_records_container ret;
        char **addrs = NULL;
-       uint32_t count, count2, total;
-       uint32_t i;
+       uint32_t a_num, total;
+       uint16_t qtype;
+       TALLOC_CTX *tmp_ctx;
+       DNS_ERROR err;
 
        memset(&ret, 0, sizeof(struct dns_records_container));
-       /* this is the blocking call we are going to lots of trouble
-          to avoid them in the parent */
-       reply = rk_dns_lookup(name, "AAAA");
-
-       count = count2 = 0;
-
-       if (reply) {
-
-               count = count_dns_rr(reply->head, rk_ns_t_aaaa);
-               count2 = count_dns_rr(reply->head, rk_ns_t_a);
-
-               if (!count2) {
-                       /*
-                       * DNS server didn't returned A when asked for AAAA records.
-                       * Most of the server do it, let's ask for A specificaly.
-                       */
-                       reply2 = rk_dns_lookup(name, "A");
 
-                       if (!reply2) {
-                               return ret;
-                       }
+       tmp_ctx = talloc_new(mem_ctx);
+       if (!tmp_ctx) {
+               return ret;
+       }
 
-                       count2 = count_dns_rr(reply2->head, rk_ns_t_a);
-               } else {
-                       reply2 = NULL;
-               }
-       } else {
+       qtype = QTYPE_AAAA;
 
-               reply = rk_dns_lookup(name, "A");
-               if (!reply) {
-                       return ret;
+       /* this is the blocking call we are going to lots of trouble
+          to avoid them in the parent */
+       err = dns_lookup(tmp_ctx, name, qtype, &reply);
+       if (!ERR_DNS_IS_OK(err)) {
+               qtype = QTYPE_A;
+               err = dns_lookup(tmp_ctx, name, qtype, &reply);
+               if (!ERR_DNS_IS_OK(err)) {
+                       goto done;
                }
-
-               reply2 = NULL;
-               count = count_dns_rr(reply->head, rk_ns_t_a);
        }
-       count += count2;
 
-       if (count == 0) {
-               goto done;
-       }
+       a_num = total = 0;
+       total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total, reply, port);
 
-       addrs = talloc_zero_array(mem_ctx, char*, count);
-       total = 0;
+       if (qtype == QTYPE_AAAA && a_num == 0) {
+               /*
+               * DNS server didn't returned A when asked for AAAA records.
+               * Most of the server do it, let's ask for A specificaly.
+               */
+               err = dns_lookup(tmp_ctx, name, QTYPE_A, &reply);
+               if (!ERR_DNS_IS_OK(err)) {
+                       goto done;
+               }
 
-       tmp[0] = reply;
-       tmp[1] = reply2;
-       tmp[2] = NULL;
+               total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total,
+                                       reply, port);
 
-       /* Loop over all returned records and pick the records */
-       for (i=0; tmp[i] != NULL; i++) {
-               rep = tmp[i];
-               for (rr=rep->head; rr; rr=rr->next) {
-                       /* we are only interested in the IN class */
-                       if (rr->class != rk_ns_c_in) {
-                               continue;
-                       }
-
-                       /* we are only interested in A and 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.data) {
-                               continue;
-                       }
-                       rr_to_string(mem_ctx, rr, port);
-                       addrs[total] = rr_to_string(mem_ctx, rr, port);
-                       if (addrs[total]) {
-                               total++;
-                       }
-               }
        }
+
        if (total) {
+               talloc_steal(mem_ctx, addrs);
                ret.count = total;
                ret.list = addrs;
        }
 
 done:
-       if (reply != NULL)
-               rk_dns_free_data(reply);
-
-       if (reply2 != NULL)
-               rk_dns_free_data(reply2);
-
+       TALLOC_FREE(tmp_ctx);
        return ret;
 }
 
 static struct dns_records_container get_srv_records(TALLOC_CTX *mem_ctx,
                                                        const char* name)
 {
-       struct rk_dns_reply *reply;
-       struct rk_resource_record *rr;
        struct dns_records_container ret;
        char **addrs = NULL;
-       uint32_t count, total;
+       struct dns_rr_srv *dclist;
+       NTSTATUS status;
+       uint32_t total;
+       unsigned i;
+       int count;
 
        memset(&ret, 0, sizeof(struct dns_records_container));
        /* this is the blocking call we are going to lots of trouble
           to avoid them in the parent */
-       reply = rk_dns_lookup(name, "SRV");
-
-       if (!reply) {
+       status = ads_dns_lookup_srv(mem_ctx, name, &dclist, &count);
+       if (!NT_STATUS_IS_OK(status)) {
                return ret;
        }
-
-       rk_dns_srv_order(reply);
-       count = count_dns_rr(reply->head, rk_ns_t_srv);
-
        total = 0;
        if (count == 0) {
-               goto done;
+               return ret;
        }
 
        /* Loop over all returned records and pick the records */
-       for (rr=reply->head; rr; rr=rr->next) {
+       for (i = 0; i < count; i++) {
                struct dns_records_container c;
-               char* tmp_str;
-               /* we are only interested in the IN class */
-               if (rr->class != rk_ns_c_in) {
-                       continue;
-               }
+               const char* tmp_str;
 
-               /* we are only interested in SRV records */
-               if (rr->type != rk_ns_t_srv) {
-                       continue;
-               }
-
-               /* verify we actually have a srv record here */
-               if (!rr->u.srv) {
-                       continue;
-               }
-
-               /* Verify we got a port */
-               if (rr->u.srv->port == 0) {
-                       continue;
-               }
-
-               tmp_str = rr->u.srv->target;
+               tmp_str = dclist[i].hostname;
                if (strchr(tmp_str, '.') && tmp_str[strlen(tmp_str)-1] != '.') {
                        /* we are asking for a fully qualified name, but the
                        name doesn't end in a '.'. We need to prevent the
@@ -322,7 +301,7 @@ static struct dns_records_container get_srv_records(TALLOC_CTX *mem_ctx,
                        tmp_str = talloc_asprintf(mem_ctx, "%s.", tmp_str);
                }
 
-               c = get_a_aaaa_records(mem_ctx, tmp_str, rr->u.srv->port);
+               c = get_a_aaaa_records(mem_ctx, tmp_str, dclist[i].port);
                total += c.count;
                if (addrs == NULL) {
                        addrs = c.list;
@@ -341,11 +320,6 @@ static struct dns_records_container get_srv_records(TALLOC_CTX *mem_ctx,
                ret.list = addrs;
        }
 
-
-done:
-       if (reply != NULL)
-               rk_dns_free_data(reply);
-
        return ret;
 }
 /*
@@ -375,6 +349,12 @@ static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
                c = get_a_aaaa_records(state, state->name.name, state->port);
        }
 
+       /* This line in critical - if we return without writing to the
+        * pipe, this is the signal that the name did not exist */
+       if (c.count == 0) {
+               goto done;
+       }
+
        addrs = talloc_strdup(state, "");
        if (!addrs) {
                goto done;
@@ -420,8 +400,10 @@ static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
 #ifdef EAI_NODATA
                case EAI_NODATA:
 #endif
+               case EAI_FAIL:
+                       /* Linux returns EAI_NODATA on non-RFC1034-compliant names. FreeBSD returns EAI_FAIL */
                case EAI_NONAME:
-                       /* getaddrinfo() doesn't handle CNAME records */
+                       /* getaddrinfo() doesn't handle CNAME or non-RFC1034 compatible records */
                        run_child_dns_lookup(state, fd);
                        return;
                default:
@@ -503,13 +485,16 @@ static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde,
        }
 
        if (ret <= 0) {
+               /* The check for ret == 0 here is important, if the
+                * name does not exist, then no bytes are written to
+                * the pipe */
                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;
        }
 
-       /* enusre the address looks good */
+       /* ensure the address looks good */
        address[ret] = 0;
 
        addrs = str_list_make(state, address, ",");