diff options
author | Kai Blin <kai@samba.org> | 2010-09-29 17:24:53 -0700 |
---|---|---|
committer | Kai Blin <kai@samba.org> | 2010-10-23 10:17:05 +0000 |
commit | a7b833ec7e19bc3251ade69af101631013d60419 (patch) | |
tree | 6d0c44eb1e889f4168667107efd16f4602fafeda | |
parent | 719a6bbfede5b124c96f7c84d27e68ac285b073a (diff) | |
download | samba-a7b833ec7e19bc3251ade69af101631013d60419.tar.gz samba-a7b833ec7e19bc3251ade69af101631013d60419.tar.bz2 samba-a7b833ec7e19bc3251ade69af101631013d60419.zip |
s4 dns: Reply to a name request with an A record.
The first real answer to a DNS request. Still uses hardcoded reply.
-rw-r--r-- | librpc/idl/dns.idl | 43 | ||||
-rw-r--r-- | librpc/ndr/ndr_dns.c | 98 | ||||
-rw-r--r-- | librpc/ndr/ndr_dns.h | 2 | ||||
-rw-r--r-- | source4/dns_server/dns_server.c | 133 |
4 files changed, 247 insertions, 29 deletions
diff --git a/librpc/idl/dns.idl b/librpc/idl/dns.idl index 16dcf52929..b4075365ab 100644 --- a/librpc/idl/dns.idl +++ b/librpc/idl/dns.idl @@ -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; /* diff --git a/librpc/ndr/ndr_dns.c b/librpc/ndr/ndr_dns.c index 18dde2bd1a..638220af4e 100644 --- a/librpc/ndr/ndr_dns.c +++ b/librpc/ndr/ndr_dns.c @@ -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; +} diff --git a/librpc/ndr/ndr_dns.h b/librpc/ndr/ndr_dns.h index d0b6ab36f3..acdb7bb03a 100644 --- a/librpc/ndr/ndr_dns.h +++ b/librpc/ndr/ndr_dns.h @@ -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); diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index 5d9a5086ba..cc43108bfa 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -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; } |