summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Blin <kai@samba.org>2010-09-29 17:24:53 -0700
committerKai Blin <kai@samba.org>2010-10-23 10:17:05 +0000
commita7b833ec7e19bc3251ade69af101631013d60419 (patch)
tree6d0c44eb1e889f4168667107efd16f4602fafeda
parent719a6bbfede5b124c96f7c84d27e68ac285b073a (diff)
downloadsamba-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.idl43
-rw-r--r--librpc/ndr/ndr_dns.c98
-rw-r--r--librpc/ndr/ndr_dns.h2
-rw-r--r--source4/dns_server/dns_server.c133
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;
}