diff options
Diffstat (limited to 'source4/dns_server')
-rw-r--r-- | source4/dns_server/dns_server.c | 135 | ||||
-rw-r--r-- | source4/dns_server/dns_server.h | 7 |
2 files changed, 132 insertions, 10 deletions
diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index 5a6673c267..c53612c07a 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -39,7 +39,9 @@ #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" /* hold information about one dns socket */ struct dns_socket { @@ -89,6 +91,47 @@ static void dns_tcp_send(struct stream_connection *conn, uint16_t flags) dns_tcp_terminate_connection(dnsconn, "dns_tcp_send: called"); } +static bool dns_name_match(const char *zone, const char *name, size_t *host_part_len) +{ + size_t zl = strlen(zone); + size_t nl = strlen(name); + ssize_t zi, ni; + static const size_t fixup = 'a' - 'A'; + + if (zl > nl) { + return false; + } + + for (zi = zl, ni = nl; zi >= 0; zi--, ni--) { + char zc = zone[zi]; + char nc = name[ni]; + + /* convert to lower case */ + if (zc >= 'A' && zc <= 'Z') { + zc += fixup; + } + if (nc >= 'A' && nc <= 'Z') { + nc += fixup; + } + + if (zc != nc) { + return false; + } + } + + if (ni >= 0) { + if (name[ni] != '.') { + return false; + } + + ni--; + } + + *host_part_len = ni+1; + + return true; +} + static NTSTATUS dns_name2dn(struct dns_server *dns, TALLOC_CTX *mem_ctx, const char *name, @@ -96,7 +139,8 @@ static NTSTATUS dns_name2dn(struct dns_server *dns, { struct ldb_dn *base; struct ldb_dn *dn; - const char *p; + const struct dns_server_zone *z; + size_t host_part_len = 0; if (name == NULL) { return NT_STATUS_INVALID_PARAMETER; @@ -112,16 +156,30 @@ static NTSTATUS dns_name2dn(struct dns_server *dns, return NT_STATUS_OK; } - p = strcasestr(name, "root-servers.net"); - if (p != NULL) { - base = ldb_get_default_basedn(dns->samdb); - dn = ldb_dn_copy(mem_ctx, base); - ldb_dn_add_child_fmt(dn, "DC=%s,DC=RootDNSServers,CN=MicrosoftDNS,CN=System", name); + for (z = dns->zones; z != NULL; z = z->next) { + bool match; + + match = dns_name_match(z->name, name, &host_part_len); + if (match) { + break; + } + } + + if (z == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (host_part_len == 0) { + dn = ldb_dn_copy(mem_ctx, z->dn); + ldb_dn_add_child_fmt(dn, "DC=@"); *_dn = dn; return NT_STATUS_OK; } - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + dn = ldb_dn_copy(mem_ctx, z->dn); + ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name); + *_dn = dn; + return NT_STATUS_OK; } static NTSTATUS handle_question(struct dns_server *dns, @@ -714,11 +772,39 @@ 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}; + int i; + switch (lpcfg_server_role(task->lp_ctx)) { case ROLE_STANDALONE: @@ -741,7 +827,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; @@ -749,8 +835,6 @@ static void dns_task_init(struct task_server *task) dns->task = task; - /* Connect to a SAMDB with system privileges for fetching the old pw - * hashes. */ dns->samdb = samdb_connect(dns, dns->task->event_ctx, dns->task->lp_ctx, system_session(dns->task->lp_ctx), 0); if (!dns->samdb) { @@ -758,6 +842,37 @@ static void dns_task_init(struct task_server *task) 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); diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index 8d7880702f..0377a2cc86 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -26,9 +26,16 @@ struct tsocket_address; +struct dns_server_zone { + struct dns_server_zone *prev, *next; + const char *name; + struct ldb_dn *dn; +}; + struct dns_server { struct task_server *task; struct ldb_context *samdb; + struct dns_server_zone *zones; }; #endif /* __DNS_SERVER_H__ */ |