source4/: Fix prototypes for all functions in various subsystems.
[sfrench/samba-autobuild/.git] / source4 / dns_server / dns_server.c
index 5d9a5086ba8c5921d61cf7a2a164a212c4e0fd25..0e5def15c00a6964eee7eb4bd42718a690bdf29d 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"
 #include "param/param.h"
 #include "librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include <ldb.h>
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "auth/session.h"
+#include "lib/util/dlinklist.h"
+
+NTSTATUS server_service_dns_init(void);
 
 /* hold information about one dns socket */
 struct dns_socket {
@@ -84,27 +93,98 @@ 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 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;
+       WERROR ret;
+       struct dns_name_packet *in_packet;
+       struct dns_name_packet *out_packet;
+       struct dns_res_rec *answers = NULL, *nsrecs = NULL, *additional = NULL;
+       uint16_t num_answers = 0 , num_nsrecs = 0, num_additional = 0;
+
+       if (in->length < 12) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       in_packet = talloc_zero(mem_ctx, struct dns_name_packet);
+       /* TODO: We don't really need an out_packet. */
+       out_packet = talloc_zero(mem_ctx, struct dns_name_packet);
+
+       if (in_packet == NULL) return NT_STATUS_NO_MEMORY;
+       if (out_packet == NULL) return NT_STATUS_NO_MEMORY;
 
-       dump_data(0, in->data, in->length);
+       dump_data(2, 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;
+               *out = *in;
+
+               out->data[2] |= 0x80; /* Toggle DNS_FLAG_REPLY */
+               out->data[3] |= DNS_RCODE_FORMERR;
+
+               return NT_STATUS_OK;
+       }
+
+       NDR_PRINT_DEBUG(dns_name_packet, in_packet);
+       *out_packet = *in_packet;
+       out_packet->operation |= DNS_FLAG_REPLY;
+
+       switch (in_packet->operation & DNS_OPCODE) {
+       case DNS_OPCODE_QUERY:
+
+               ret = dns_server_process_query(dns, out_packet, in_packet,
+                                              &answers, &num_answers,
+                                              &nsrecs,  &num_nsrecs,
+                                              &additional, &num_additional);
+
+               break;
+       case DNS_OPCODE_REGISTER:
+               ret = dns_server_process_update(dns, out_packet, in_packet,
+                                               answers, num_answers,
+                                               &nsrecs,  &num_nsrecs,
+                                               &additional, &num_additional);
+               break;
+       default:
+               ret = WERR_DNS_ERROR_RCODE_NOT_IMPLEMENTED;
+               break;
        }
 
-       NDR_PRINT_DEBUG(dns_name_packet, packet);
-       return true;
+       if (W_ERROR_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;
+       } else {
+               out_packet->operation |= werr_to_dns_err(ret);
+       }
+
+       NDR_PRINT_DEBUG(dns_name_packet, out_packet);
+       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));
+               *out = *in;
+
+               out->data[2] |= 0x80; /* Toggle DNS_FLAG_REPLY */
+               out->data[3] |= DNS_RCODE_SERVFAIL;
+
+               return NT_STATUS_OK;
+       }
+
+       dump_data(2, out->data, out->length);
+       return NT_STATUS_OK;
 }
 
 struct dns_tcp_call {
@@ -123,7 +203,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 +239,9 @@ 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)) {
+               DEBUG(0, ("dns_process returned %s\n", nt_errstr(status)));
                dns_tcp_terminate_connection(dns_conn,
                                "dns_tcp_call_loop: process function failed");
                return;
@@ -315,7 +395,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,9 +419,10 @@ 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);
+               DEBUG(0, ("dns_process returned %s\n", nt_errstr(status)));
                goto done;
        }
 
@@ -413,7 +494,8 @@ static NTSTATUS dns_add_socket(struct dns_server *dns,
                return status;
        }
 
-       status = stream_setup_socket(dns->task->event_ctx,
+       status = stream_setup_socket(dns->task,
+                                    dns->task->event_ctx,
                                     dns->task->lp_ctx,
                                     model_ops,
                                     &dns_tcp_stream_ops,
@@ -471,7 +553,7 @@ static NTSTATUS dns_startup_interfaces(struct dns_server *dns, struct loadparm_c
        /* within the dns task we want to be a single process, so
           ask for the single process model ops and pass these to the
           stream_setup_socket() call. */
-       model_ops = process_model_startup(dns->task->event_ctx, "single");
+       model_ops = process_model_startup("single");
        if (!model_ops) {
                DEBUG(0,("Can't find 'single' process model_ops\n"));
                return NT_STATUS_INTERNAL_ERROR;
@@ -490,11 +572,38 @@ static NTSTATUS dns_startup_interfaces(struct dns_server *dns, struct loadparm_c
 
        return NT_STATUS_OK;
 }
+
+static int dns_server_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
+{
+       const char *n1, *n2;
+       size_t l1, l2;
+
+       n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
+       n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
+
+       l1 = strlen(n1);
+       l2 = strlen(n2);
+
+       /* If the string lengths are not equal just sort by length */
+       if (l1 != l2) {
+               /* If m1 is the larger zone name, return it first */
+               return l2 - l1;
+       }
+
+       /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
+       return 0;
+}
+
 static void dns_task_init(struct task_server *task)
 {
        struct dns_server *dns;
        NTSTATUS status;
        struct interface *ifaces;
+       int ret;
+       struct ldb_result *res;
+       struct ldb_dn *rootdn;
+       static const char * const attrs[] = { "name", NULL};
+       unsigned int i;
 
        switch (lpcfg_server_role(task->lp_ctx)) {
        case ROLE_STANDALONE:
@@ -517,7 +626,7 @@ static void dns_task_init(struct task_server *task)
 
        task_server_set_title(task, "task[dns]");
 
-       dns = talloc(task, struct dns_server);
+       dns = talloc_zero(task, struct dns_server);
        if (dns == NULL) {
                task_server_terminate(task, "dns: out of memory", true);
                return;
@@ -525,6 +634,44 @@ static void dns_task_init(struct task_server *task)
 
        dns->task = task;
 
+       dns->samdb = samdb_connect(dns, dns->task->event_ctx, dns->task->lp_ctx,
+                             system_session(dns->task->lp_ctx), 0);
+       if (!dns->samdb) {
+               task_server_terminate(task, "dns: samdb_connect failed", true);
+               return;
+       }
+
+       rootdn = ldb_dn_new(dns, dns->samdb, "");
+       if (rootdn == NULL) {
+               task_server_terminate(task, "dns: out of memory", true);
+               return;
+       }
+
+       // TODO: this search does not work against windows
+       ret = dsdb_search(dns->samdb, dns, &res, rootdn, LDB_SCOPE_SUBTREE,
+                         attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)");
+       if (ret != LDB_SUCCESS) {
+               task_server_terminate(task,
+                                     "dns: failed to look up root DNS zones",
+                                     true);
+               return;
+       }
+
+       TYPESAFE_QSORT(res->msgs, res->count, dns_server_sort_zones);
+
+       for (i=0; i < res->count; i++) {
+               struct dns_server_zone *z;
+
+               z = talloc_zero(dns, struct dns_server_zone);
+               if (z == NULL) {
+               }
+
+               z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
+               z->dn = talloc_move(z, &res->msgs[i]->dn);
+
+               DLIST_ADD_END(dns->zones, z, NULL);
+       }
+
        status = dns_startup_interfaces(dns, task->lp_ctx, ifaces);
        if (!NT_STATUS_IS_OK(status)) {
                task_server_terminate(task, "dns failed to setup interfaces", true);