s4 dns: Reply to a name request with an A record.
authorKai Blin <kai@samba.org>
Thu, 30 Sep 2010 00:24:53 +0000 (17:24 -0700)
committerKai Blin <kai@samba.org>
Sat, 23 Oct 2010 10:17:05 +0000 (10:17 +0000)
The first real answer to a DNS request. Still uses hardcoded reply.

librpc/idl/dns.idl
librpc/ndr/ndr_dns.c
librpc/ndr/ndr_dns.h
source4/dns_server/dns_server.c

index 16dcf529295bc89b48e83838f3323a9d3b03e58b..b4075365ab0f6046d46dc7c185b2d11a58155f78 100644 (file)
@@ -119,18 +119,48 @@ interface dns
                uint8  data[length];
        } dns_rdata_data;
 
-       typedef [nodiscriminant,public] union {
-               [case(DNS_QTYPE_A),subcontext(2)]       ipv4address      ipv4_address;
-               [case(DNS_QTYPE_AAAA),subcontext(2)]    ipv6address      ipv6_address;
-               [default]                 dns_rdata_data   data;
+       typedef struct {
+               dns_string mname;
+               dns_string rname;
+               uint32     serial;
+               uint32     refresh;
+               uint32     retry;
+               uint32     expire;
+               uint32     minimum;
+       } dns_soa_record;
+
+       typedef [public] struct {
+               uint16     priority;
+               uint16     weight;
+               uint16     port;
+               dns_string target;
+       } dns_srv_record;
+
+       typedef [public] struct {
+               uint16     preference;
+               dns_string exchange;
+       } dns_mx_record;
+
+       typedef [nodiscriminant,public,flag(NDR_NOALIGN)] union {
+               [case(DNS_QTYPE_A)]     ipv4address      ipv4_record;
+               [case(DNS_QTYPE_NS)]    dns_string       ns_record;
+               [case(DNS_QTYPE_CNAME)] dns_string       cname_record;
+               [case(DNS_QTYPE_SOA)]   dns_soa_record   soa_record;
+               [case(DNS_QTYPE_PTR)]   dns_string       ptr_record;
+               [case(DNS_QTYPE_MX)]    dns_mx_record    mx_record;
+               [case(DNS_QTYPE_AAAA)]  ipv6address      ipv6_record;
+               [case(DNS_QTYPE_SRV)]   dns_srv_record   srv_record;
+               [default];
        } dns_rdata;
 
-       typedef [flag(LIBNDR_PRINT_ARRAY_HEX),public] struct {
-               dns_string   name;
+       typedef [flag(LIBNDR_PRINT_ARRAY_HEX|NDR_NOALIGN),nopush,nopull] struct {
+               dns_string name;
                dns_qtype  rr_type;
                dns_qclass rr_class;
                uint32     ttl;
+               uint16     length;
                [switch_is(rr_type)] dns_rdata rdata;
+               DATA_BLOB  unexpected;
        } dns_res_rec;
 
        typedef [flag(NDR_NOALIGN|NDR_BIG_ENDIAN|NDR_PAHEX),public] struct {
@@ -144,7 +174,6 @@ interface dns
                dns_res_rec       answers[ancount];
                dns_res_rec       nsrecs[nscount];
                dns_res_rec       additional[arcount];
-               [flag(NDR_REMAINING)] DATA_BLOB padding;
        } dns_name_packet;
 
         /*
index 18dde2bd1a00f562cf433ea28bc87af49079073a..638220af4ec5898c48373dcd50521b19bddd062b 100644 (file)
@@ -57,7 +57,7 @@ static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr,
        while (loops < 5) {
                if (*offset >= ndr->data_size) {
                        return ndr_pull_error(ndr, NDR_ERR_STRING,
-                                             "BAD DNS NAME component");
+                                             "BAD DNS NAME component, bad offset");
                }
                len = ndr->data[*offset];
                if (len == 0) {
@@ -70,7 +70,7 @@ static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr,
                        /* its a label pointer */
                        if (1 + *offset >= ndr->data_size) {
                                return ndr_pull_error(ndr, NDR_ERR_STRING,
-                                                     "BAD DNS NAME component");
+                                                     "BAD DNS NAME component, bad label offset");
                        }
                        *max_offset = MAX(*max_offset, *offset + 2);
                        *offset = ((len&0x3F)<<8) | ndr->data[1 + *offset];
@@ -81,11 +81,11 @@ static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr,
                if ((len & 0xC0) != 0) {
                        /* its a reserved length field */
                        return ndr_pull_error(ndr, NDR_ERR_STRING,
-                                             "BAD DNS NAME component");
+                                             "BAD DNS NAME component, reserved lenght field: 0x%02x", (len &0xC));
                }
                if (*offset + len + 2 > ndr->data_size) {
                        return ndr_pull_error(ndr, NDR_ERR_STRING,
-                                             "BAD DNS NAME component");
+                                             "BAD DNS NAME component, length too long");
                }
                *component = (uint8_t*)talloc_strndup(ndr, (const char *)&ndr->data[1 + *offset], len);
                NDR_ERR_HAVE_NO_MEMORY(*component);
@@ -95,7 +95,7 @@ static enum ndr_err_code ndr_pull_component(struct ndr_pull *ndr,
        }
 
        /* too many pointers */
-       return ndr_pull_error(ndr, NDR_ERR_STRING, "BAD DNS NAME component");
+       return ndr_pull_error(ndr, NDR_ERR_STRING, "BAD DNS NAME component, too many pointers");
 }
 
 /**
@@ -208,3 +208,91 @@ _PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr, int ndr_fla
         */
        return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
 }
+
+_PUBLIC_ enum ndr_err_code ndr_push_dns_res_rec(struct ndr_push *ndr, int ndr_flags, const struct dns_res_rec *r)
+{
+       {
+               uint32_t _flags_save_STRUCT = ndr->flags;
+               uint32_t _saved_offset1, _saved_offset2;
+               uint16_t length;
+               ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_NOALIGN);
+               if (ndr_flags & NDR_SCALARS) {
+                       NDR_CHECK(ndr_push_align(ndr, 4));
+                       NDR_CHECK(ndr_push_dns_string(ndr, NDR_SCALARS, r->name));
+                       NDR_CHECK(ndr_push_dns_qtype(ndr, NDR_SCALARS, r->rr_type));
+                       NDR_CHECK(ndr_push_dns_qclass(ndr, NDR_SCALARS, r->rr_class));
+                       NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->ttl));
+                       _saved_offset1 = ndr->offset;
+                       NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, 0));
+                       NDR_CHECK(ndr_push_set_switch_value(ndr, &r->rdata, r->rr_type));
+                       NDR_CHECK(ndr_push_dns_rdata(ndr, NDR_SCALARS, &r->rdata));
+
+                       if (r->unexpected.length > UINT16_MAX) {
+                               return ndr_push_error(ndr, NDR_ERR_LENGTH,
+                                                     "Unexpected blob lenght is too large");
+                       }
+
+                       NDR_CHECK(ndr_push_bytes(ndr, r->unexpected.data, r->unexpected.length));
+                       NDR_CHECK(ndr_push_trailer_align(ndr, 4));
+                       length = ndr->offset - (_saved_offset1 + 2);
+                       _saved_offset2 = ndr->offset;
+                       ndr->offset = _saved_offset1;
+                       NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, length));
+                       ndr->offset = _saved_offset2;
+               }
+               if (ndr_flags & NDR_BUFFERS) {
+                       NDR_CHECK(ndr_push_dns_rdata(ndr, NDR_BUFFERS, &r->rdata));
+               }
+               ndr->flags = _flags_save_STRUCT;
+       }
+       return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_dns_res_rec(struct ndr_pull *ndr, int ndr_flags, struct dns_res_rec *r)
+{
+       {
+               uint32_t _flags_save_STRUCT = ndr->flags;
+               uint32_t _saved_offset1;
+               uint32_t pad, length;
+
+               ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX|LIBNDR_FLAG_NOALIGN);
+               if (ndr_flags & NDR_SCALARS) {
+                       NDR_CHECK(ndr_pull_align(ndr, 4));
+                       NDR_CHECK(ndr_pull_dns_string(ndr, NDR_SCALARS, &r->name));
+                       NDR_CHECK(ndr_pull_dns_qtype(ndr, NDR_SCALARS, &r->rr_type));
+                       NDR_CHECK(ndr_pull_dns_qclass(ndr, NDR_SCALARS, &r->rr_class));
+                       NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->ttl));
+                       NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->length));
+                       _saved_offset1 = ndr->offset;
+                       NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->rdata, r->rr_type));
+                       NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_SCALARS, &r->rdata));
+                       length = ndr->offset - _saved_offset1;
+                       if (length > r->length) {
+                               return ndr_pull_error(ndr, NDR_ERR_LENGTH,
+                                                     "TODO");
+                       }
+
+                       r->unexpected = data_blob_null;
+                       pad = r->length - length;
+                       if (pad > 0) {
+                               NDR_PULL_NEED_BYTES(ndr, pad);
+                               r->unexpected = data_blob_talloc(ndr->current_mem_ctx,
+                                                                ndr->data + ndr->offset,
+                                                                pad);
+                               if (r->unexpected.data == NULL) {
+                                       return ndr_pull_error(ndr, NDR_ERR_ALLOC,
+                                                             "Failed to allocate a data blob");
+                               }
+                               ndr->offset += pad;
+                       }
+
+
+                       NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
+               }
+               if (ndr_flags & NDR_BUFFERS) {
+                       NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_BUFFERS, &r->rdata));
+               }
+               ndr->flags = _flags_save_STRUCT;
+       }
+       return NDR_ERR_SUCCESS;
+}
index d0b6ab36f38bf8bd746189a06d1a0ee7e1d46ed7..acdb7bb03a56d24a6673a0942bccf8c463af66fa 100644 (file)
@@ -1,3 +1,5 @@
 _PUBLIC_ void ndr_print_dns_string(struct ndr_print *ndr, const char *name, const char *s);
 _PUBLIC_ enum ndr_err_code ndr_pull_dns_string(struct ndr_pull *ndr, int ndr_flags, const char **s);
 _PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr, int ndr_flags, const char *s);
+_PUBLIC_ enum ndr_err_code ndr_push_dns_res_rec(struct ndr_push *ndr, int ndr_flags, const struct dns_res_rec *r);
+_PUBLIC_ enum ndr_err_code ndr_pull_dns_res_rec(struct ndr_pull *ndr, int ndr_flags, struct dns_res_rec *r);
index 5d9a5086ba8c5921d61cf7a2a164a212c4e0fd25..cc43108bfabc47da521843a5db7cbb85f0c4dfa6 100644 (file)
@@ -28,6 +28,7 @@
 #include "lib/socket/socket.h"
 #include "lib/tsocket/tsocket.h"
 #include "libcli/util/tstream.h"
+#include "libcli/util/ntstatus.h"
 #include "system/network.h"
 #include "lib/stream/packet.h"
 #include "lib/socket/netif.h"
@@ -84,27 +85,126 @@ static void dns_tcp_send(struct stream_connection *conn, uint16_t flags)
        dns_tcp_terminate_connection(dnsconn, "dns_tcp_send: called");
 }
 
-bool dns_process(struct dns_server *dns,
-                TALLOC_CTX *mem_ctx,
-                DATA_BLOB *in,
-                DATA_BLOB *out)
+static NTSTATUS handle_question(TALLOC_CTX *mem_ctx,
+                               struct dns_name_question *question,
+                               struct dns_res_rec **answers, uint16_t *ancount)
+{
+       struct dns_res_rec *ans;
+       uint16_t count = *ancount;
+       count += 1;
+       ans = talloc_realloc(NULL, *answers, struct dns_res_rec, count);
+       NT_STATUS_HAVE_NO_MEMORY(ans);
+
+       ans[0].name = talloc_strdup(ans, "example.com");
+       ans[0].rr_type = DNS_QTYPE_A;
+       ans[0].rr_class = DNS_QCLASS_IP;
+       ans[0].ttl = 0;
+       ans[0].rdata.ipv4_record = talloc_strdup(ans, "127.0.0.1");
+
+       *ancount = count;
+       *answers = ans;
+
+       return NT_STATUS_OK;
+
+}
+
+static NTSTATUS compute_reply(TALLOC_CTX *mem_ctx,
+                             struct dns_name_packet *in,
+                             struct dns_res_rec **answers,    uint16_t *ancount,
+                             struct dns_res_rec **nsrecs,     uint16_t *nscount,
+                             struct dns_res_rec **additional, uint16_t *arcount)
+{
+       uint16_t num_answers=0, num_nsrecs=0, num_additional=0;
+       struct dns_res_rec *ans=NULL, *ns=NULL, *add=NULL;
+       int i;
+       NTSTATUS status;
+
+       ans = talloc_array(mem_ctx, struct dns_res_rec, 0);
+       if (answers == NULL) return NT_STATUS_NO_MEMORY;
+
+       for (i = 0; i < in->qdcount; ++i) {
+               status = handle_question(mem_ctx, &in->questions[i], &ans, &num_answers);
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
+       *answers = ans;
+       *ancount = num_answers;
+
+       /*FIXME: Do something for these */
+       *nsrecs  = NULL;
+       *nscount = 0;
+
+       *additional = NULL;
+       *arcount    = 0;
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS dns_process(struct dns_server *dns,
+                           TALLOC_CTX *mem_ctx,
+                           DATA_BLOB *in,
+                           DATA_BLOB *out)
 {
        enum ndr_err_code ndr_err;
-       struct dns_name_packet *packet = talloc(mem_ctx, struct dns_name_packet);
-       if (packet == NULL) return false;
+       NTSTATUS ret;
+       struct dns_name_packet *in_packet = talloc(mem_ctx, struct dns_name_packet);
+       struct dns_name_packet *out_packet = talloc(mem_ctx, struct dns_name_packet);
+       struct dns_res_rec *answers, *nsrecs, *additional;
+       uint16_t num_answers, num_nsrecs, num_additional;
+
+       if (in_packet == NULL) return NT_STATUS_INVALID_PARAMETER;
 
        dump_data(0, in->data, in->length);
 
-       ndr_err = ndr_pull_struct_blob(in, packet, packet,
+       ndr_err = ndr_pull_struct_blob(in, in_packet, in_packet,
                        (ndr_pull_flags_fn_t)ndr_pull_dns_name_packet);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               TALLOC_FREE(packet);
+               TALLOC_FREE(in_packet);
                DEBUG(0, ("Failed to parse packet %d!\n", ndr_err));
-               return false;
+               return NT_STATUS_COULD_NOT_INTERPRET;
+       }
+
+       NDR_PRINT_DEBUG(dns_name_packet, in_packet);
+       out_packet->id = in_packet->id;
+       out_packet->operation = DNS_FLAG_REPLY | DNS_FLAG_AUTHORITATIVE;
+                               /* TODO: DNS_FLAG_RECURSION_DESIRED | DNS_FLAG_RECURSION_AVAIL; */
+
+       out_packet->qdcount = in_packet->qdcount;
+       out_packet->questions = in_packet->questions;
+
+       out_packet->ancount = 0;
+       out_packet->answers = NULL;
+
+       out_packet->nscount = 0;
+       out_packet->nsrecs  = NULL;
+
+       out_packet->arcount = 0;
+       out_packet->additional = NULL;
+
+       ret = compute_reply(out_packet, in_packet, &answers, &num_answers,
+                           &nsrecs, &num_nsrecs, &additional, &num_additional);
+
+       if (NT_STATUS_IS_OK(ret)) {
+               out_packet->ancount = num_answers;
+               out_packet->answers = answers;
+
+               out_packet->nscount = num_nsrecs;
+               out_packet->nsrecs  = nsrecs;
+
+               out_packet->arcount = num_additional;
+               out_packet->additional = additional;
+       }
+
+       ndr_err = ndr_push_struct_blob(out, out_packet, out_packet,
+                       (ndr_push_flags_fn_t)ndr_push_dns_name_packet);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               TALLOC_FREE(in_packet);
+               TALLOC_FREE(out_packet);
+               DEBUG(0, ("Failed to push packet %d!\n", ndr_err));
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       NDR_PRINT_DEBUG(dns_name_packet, packet);
-       return true;
+       return NT_STATUS_OK;
 }
 
 struct dns_tcp_call {
@@ -123,7 +223,6 @@ static void dns_tcp_call_loop(struct tevent_req *subreq)
                                      struct dns_tcp_connection);
        struct dns_tcp_call *call;
        NTSTATUS status;
-       bool ok;
 
        call = talloc(dns_conn, struct dns_tcp_call);
        if (call == NULL) {
@@ -160,8 +259,8 @@ static void dns_tcp_call_loop(struct tevent_req *subreq)
        call->in.length -= 4;
 
        /* Call dns */
-       ok = dns_process(dns_conn->dns_socket->dns, call, &call->in, &call->out);
-       if (!ok) {
+       status = dns_process(dns_conn->dns_socket->dns, call, &call->in, &call->out);
+       if (!NT_STATUS_IS_OK(status)) {
                dns_tcp_terminate_connection(dns_conn,
                                "dns_tcp_call_loop: process function failed");
                return;
@@ -315,7 +414,7 @@ static void dns_udp_call_loop(struct tevent_req *subreq)
        uint8_t *buf;
        ssize_t len;
        int sys_errno;
-       bool ok;
+       NTSTATUS status;
 
        call = talloc(sock, struct dns_udp_call);
        if (call == NULL) {
@@ -339,8 +438,8 @@ static void dns_udp_call_loop(struct tevent_req *subreq)
                 tsocket_address_string(call->src, call)));
 
        /* Call krb5 */
-       ok = dns_process(sock->dns_socket->dns, call, &call->in, &call->out);
-       if (!ok) {
+       status = dns_process(sock->dns_socket->dns, call, &call->in, &call->out);
+       if (!NT_STATUS_IS_OK(status)) {
                talloc_free(call);
                goto done;
        }