#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 {
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 {
struct dns_tcp_connection);
struct dns_tcp_call *call;
NTSTATUS status;
- bool ok;
call = talloc(dns_conn, struct dns_tcp_call);
if (call == NULL) {
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;
uint8_t *buf;
ssize_t len;
int sys_errno;
- bool ok;
+ NTSTATUS status;
call = talloc(sock, struct dns_udp_call);
if (call == NULL) {
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;
}
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,
/* 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;
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:
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;
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);