When we register our A record, dynamically register a PTR record
for the first IPv4 address we find. This helps interoperability
with (naive) services that expect forward and reverse DNS to match.
We attempt multiple registrations in widening zones since we don't
have a way to predict how the DSN admin has configured the PTR
record zones.
---
source/libaddns/dns.h | 18 +++-
source/libaddns/dnserr.h | 1 +
source/libaddns/dnsrecord.c | 89 +++++++++++++++-
source/libaddns/dnsutils.c | 2 +
source/utils/net_ads.c | 11 +--
source/utils/net_dns.c | 246 ++++++++++++++++++++++++++++++++++
+++++---
6 files changed, 337 insertions(+), 30 deletions(-)

diff --git a/source/libaddns/dns.h b/source/libaddns/dns.h
index cf842f4..735d84d 100644
--- a/source/libaddns/dns.h
+++ b/source/libaddns/dns.h
@@ -3,6 +3,7 @@

Copyright (C) 2006 Krishna Ganugapati
Copyright (C) 2006 Gerald Carter
+ Copyright (C) 2008 James Peach

** NOTE! The following LGPL license applies to the libaddns
** library. This does NOT imply that all of Samba is released
@@ -243,6 +244,7 @@ void *talloc_zeronull(const void *context, size_t
size, const char *name);
#define QTYPE_MD 3
#define QTYPE_CNAME 5
#define QTYPE_SOA 6
+#define QTYPE_PTR 12
#define QTYPE_ANY 255
#define QTYPE_TKEY 249
#define QTYPE_TSIG 250
@@ -307,6 +309,11 @@ TXT 16 text strings
#define DNS_NAME_ERROR 3
#define DNS_NOT_IMPLEMENTED 4
#define DNS_REFUSED 5
+#define DNS_YXDOMAIN 6
+#define DNS_YYRRSET 7
+#define DNS_NXRRSET 8
+#define DNS_NOTAUTH 9
+#define DNS_NOTZONE 10

typedef long HANDLE;

@@ -523,13 +530,22 @@ DNS_ERROR dns_sign_update(struct
dns_update_request *req,
const char *keyname,
const char *algorithmname,
time_t time_signed, uint16 fudge);
-DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx,
+
+/* Create an A record update request. */
+DNS_ERROR dns_create_update_request_a(TALLOC_CTX *mem_ctx,
const char *domainname,
const char *hostname,
const struct sockaddr_storage *ip_addr,
size_t num_adds,
struct dns_update_request **preq);

+/* Create a PTR record update request. */
+DNS_ERROR dns_create_update_request_ptr(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *zone_name,
+ const struct in_addr ip,
+ struct dns_update_request **preq);
+
#endif /* HAVE_GSSAPI_SUPPORT */

#endif /* _DNS_H */
diff --git a/source/libaddns/dnserr.h b/source/libaddns/dnserr.h
index 8859420..bb4df70 100644
--- a/source/libaddns/dnserr.h
+++ b/source/libaddns/dnserr.h
@@ -64,6 +64,7 @@ typedef uint32 DNS_ERROR;
#define ERROR_DNS_INVALID_MESSAGE ERROR_DNS(9)
#define ERROR_DNS_SOCKET_ERROR ERROR_DNS(10)
#define ERROR_DNS_UPDATE_FAILED ERROR_DNS(11)
+#define ERROR_DNS_WRONG_ZONE ERROR_DNS(12)

/*
* About to be removed, transitional error
diff --git a/source/libaddns/dnsrecord.c b/source/libaddns/dnsrecord.c
index 500cbd6..ff1d6a2 100644
--- a/source/libaddns/dnsrecord.c
+++ b/source/libaddns/dnsrecord.c
@@ -2,6 +2,7 @@
Linux DNS client library implementation
Copyright (C) 2006 Krishna Ganugapati
Copyright (C) 2006 Gerald Carter
+ Copyright (C) 2008 James Peach

** NOTE! The following LGPL license applies to the libaddns
** library. This does NOT imply that all of Samba is released
@@ -118,6 +119,37 @@ DNS_ERROR dns_create_rrec(TALLOC_CTX *mem_ctx,
const char *name,
return ERROR_DNS_SUCCESS;
}

+DNS_ERROR dns_create_ptr_record(TALLOC_CTX *mem_ctx,
+ const char * host,
+ uint32 ttl, struct in_addr ip,
+ struct dns_rrec **prec)
+{
+ DNS_ERROR err;
+ char * ptr;
+
+ struct dns_domain_name * name;
+ struct dns_buffer * buf;
+
+ buf = dns_create_buffer(mem_ctx);
+
+ ptr = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.",
+ (ntohl(ip.s_addr) & 0x000000ff),
+ (ntohl(ip.s_addr) & 0x0000ff00) >> 8,
+ (ntohl(ip.s_addr) & 0x00ff0000) >> 16,
+ (ntohl(ip.s_addr) & 0xff000000) >> 24);
+
+ dns_domain_name_from_string(mem_ctx, host, &name);
+ dns_marshall_domain_name(buf, name);
+
+ /* For a PTR record, x.x.x.x.in-addr.arpa is the record name, and the
+ * canonical hostname is the record data.
+ */
+ err = dns_create_rrec(mem_ctx, ptr, QTYPE_PTR, DNS_CLASS_IN, ttl,
+ buf->offset, (uint8_t *)buf->data, prec);
+
+ return err;
+}
+
DNS_ERROR dns_create_a_record(TALLOC_CTX *mem_ctx, const char *host,
uint32 ttl, const struct sockaddr_storage *pss,
struct dns_rrec **prec)
@@ -361,8 +393,61 @@ DNS_ERROR dns_create_probe(TALLOC_CTX *mem_ctx,
const char *zone,
TALLOC_FREE(req);
return err;
}
-
-DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx,
+
+/* Create a PTR-record update request. */
+DNS_ERROR dns_create_update_request_ptr(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *zone_name,
+ const struct in_addr ip,
+ struct dns_update_request **preq)
+{
+ struct dns_update_request *req;
+ struct dns_rrec *rec;
+ DNS_ERROR err;
+
+ char * ptr_name;
+
+ ptr_name = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.",
+ (ntohl(ip.s_addr) & 0x000000ff),
+ (ntohl(ip.s_addr) & 0x0000ff00) >> 8,
+ (ntohl(ip.s_addr) & 0x00ff0000) >> 16,
+ (ntohl(ip.s_addr) & 0xff000000) >> 24);
+
+ err = dns_create_update(mem_ctx, zone_name, &req);
+ if (!ERR_DNS_IS_OK(err)) return err;
+
+ err = dns_create_rrec(req, zone_name, QTYPE_ANY,
+ DNS_CLASS_ANY, 0, 0, NULL, &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_preqs, &req->preqs);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /* Delete any PTR records. */
+ err = dns_create_delete_record(req, ptr_name, QTYPE_PTR,
DNS_CLASS_ANY,
+ &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_updates, &req->updates);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ /* Add the corresponding PTR record. */
+ err = dns_create_ptr_record(req, hostname, 3600, ip, &rec);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_add_rrec(req, rec, &req->num_updates, &req->updates);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ *preq = req;
+ return ERROR_DNS_SUCCESS;
+
+ error:
+ TALLOC_FREE(req);
+ return err;
+}
+
+/* Create an A-record update request. */
+DNS_ERROR dns_create_update_request_a(TALLOC_CTX *mem_ctx,
const char *domainname,
const char *hostname,
const struct sockaddr_storage *ss_addrs,
diff --git a/source/libaddns/dnsutils.c b/source/libaddns/dnsutils.c
index 8201ce4..0394600 100644
--- a/source/libaddns/dnsutils.c
+++ b/source/libaddns/dnsutils.c
@@ -178,6 +178,8 @@ const char * dns_errstr(DNS_ERROR err)
return "socket error";
} else if (ERR_DNS_EQUAL(err, ERROR_DNS_UPDATE_FAILED)) {
return "DNS update failed";
+ } else if (ERR_DNS_EQUAL(err, ERROR_DNS_WRONG_ZONE)) {
+ return "wrong DNS zone";
} else {
return "invalid DNS error code";
}
diff --git a/source/utils/net_ads.c b/source/utils/net_ads.c
index 1add13f..9e2c318 100644
--- a/source/utils/net_ads.c
+++ b/source/utils/net_ads.c
@@ -1077,15 +1077,8 @@ static NTSTATUS
net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
done:

if (!NT_STATUS_IS_OK(status)) {
- if (!ERR_DNS_IS_OK(dns_err)) {
- d_printf("DNS update for %s failed: %s\n",
- machine_name,
- dns_errstr(dns_err));
- } else {
- d_printf("DNS update for %s failed: %s\n",
- machine_name,
- get_friendly_nt_error_msg(status));
- }
+ d_printf("DNS update for %s failed: %s\n",
+ machine_name, get_friendly_nt_error_msg(status));
}


diff --git a/source/utils/net_dns.c b/source/utils/net_dns.c
index 14d45e2..7f075b1 100644
--- a/source/utils/net_dns.c
+++ b/source/utils/net_dns.c
@@ -5,6 +5,7 @@

Copyright (C) Krishna Ganugapati (krishnag@centeris.com)
2006
Copyright (C) Gerald Carter
2006
+ Copyright (C) 2008 James Peach

This program is free software; you can redistribute it and/or
modify
it under the terms of the GNU General Public License as published
by
@@ -38,7 +39,36 @@ DNS_ERROR DoDNSUpdate(char *pszServerName,
/************************************************** *******************
************************************************** *******************/

-DNS_ERROR DoDNSUpdate(char *pszServerName,
+static DNS_ERROR
+negotiate_security_context(TALLOC_CTX * mem_ctx,
+ const char * pszDomainName,
+ const char * pszServerName,
+ char ** keyname,
+ gss_ctx_id_t * gss_context)
+{
+ DNS_ERROR err;
+
+ if (!(*keyname = dns_generate_keyname( mem_ctx ))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_negotiate_sec_ctx( pszDomainName, pszServerName,
+ *keyname, gss_context, DNS_SRV_ANY );
+
+ /* retry using the Windows 2000 DNS hack */
+ if (!ERR_DNS_IS_OK(err)) {
+ return dns_negotiate_sec_ctx( pszDomainName, pszServerName,
+ *keyname, gss_context,
+ DNS_SRV_WIN2000 );
+ }
+
+ return ERROR_DNS_SUCCESS;
+}
+
+/************************************************** *******************
+************************************************* ********************/
+
+static DNS_ERROR DoDNSUpdate_A(char *pszServerName,
const char *pszDomainName, const char *pszHostName,
const struct sockaddr_storage *sslist, size_t num_addrs )
{
@@ -81,7 +111,7 @@ DNS_ERROR DoDNSUpdate(char *pszServerName,
* First try without signing
*/

- err = dns_create_update_request(mem_ctx, pszDomainName, pszHostName,
+ err = dns_create_update_request_a(mem_ctx, pszDomainName, pszHostName,
sslist, num_addrs, &req);
if (!ERR_DNS_IS_OK(err)) goto error;

@@ -100,30 +130,17 @@ DNS_ERROR DoDNSUpdate(char *pszServerName,
gss_ctx_id_t gss_context;
char *keyname;

- if (!(keyname = dns_generate_keyname( mem_ctx ))) {
- err = ERROR_DNS_NO_MEMORY;
- goto error;
- }
+ err = negotiate_security_context(mem_ctx,
+ pszDomainName, pszServerName,
+ &keyname, &gss_context);

- err = dns_negotiate_sec_ctx( pszDomainName, pszServerName,
- keyname, &gss_context, DNS_SRV_ANY );
-
- /* retry using the Windows 2000 DNS hack */
- if (!ERR_DNS_IS_OK(err)) {
- err = dns_negotiate_sec_ctx( pszDomainName, pszServerName,
- keyname, &gss_context,
- DNS_SRV_WIN2000 );
- }
-
if (!ERR_DNS_IS_OK(err))
goto error;
-

err = dns_sign_update(req, gss_context, keyname,
"gss.microsoft.com", time(NULL), 3600);

gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER);
-
if (!ERR_DNS_IS_OK(err)) goto error;

err = dns_update_transaction(mem_ctx, conn, req, &resp);
@@ -142,6 +159,199 @@ error:
/************************************************** *******************
************************************************** *******************/

+static DNS_ERROR DoDNSUpdate_PTR_with_zone(TALLOC_CTX *mem_ctx,
+ struct dns_connection *conn,
+ char *pszServerName,
+ const char *pszDomainName,
+ const char *pszHostName,
+ const char * zone_name,
+ const struct in_addr ip)
+{
+ DNS_ERROR err;
+ struct dns_update_request *ptr_req, *resp;
+
+ /* If we choose the wrong zone, then we might get "no such zone", or
+ * "you aren't allowed to update that zone".
+ */
+#define WRONG_ZONE_RESPONSE(code) \
+ (((code) == DNS_NOTZONE) || ((code) == DNS_NOTAUTH))
+
+ /*
+ * First try without signing
+ */
+
+ err = dns_create_update_request_ptr(mem_ctx, pszHostName,
+ zone_name, ip, &ptr_req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_update_transaction(mem_ctx, conn, ptr_req, &resp);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ if (dns_response_code(resp->flags) == DNS_NO_ERROR) {
+ return ERROR_DNS_SUCCESS;
+ } else if (WRONG_ZONE_RESPONSE(dns_response_code(resp->flags))) {
+ return ERROR_DNS_WRONG_ZONE;
+ }
+
+ /*
+ * Okay, we have to try with signing
+ */
+ {
+ OM_uint32 minor;
+ gss_ctx_id_t gss_context;
+ char *keyname;
+
+ err = negotiate_security_context(mem_ctx,
+ pszDomainName, pszServerName,
+ &keyname, &gss_context);
+
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_sign_update(ptr_req, gss_context, keyname,
+ "gss.microsoft.com", time(NULL), 3600);
+
+ gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_update_transaction(mem_ctx, conn, ptr_req, &resp);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ if (dns_response_code(resp->flags) == DNS_NO_ERROR) {
+ err = ERROR_DNS_SUCCESS;
+ } else if (WRONG_ZONE_RESPONSE(dns_response_code(resp->flags))) {
+ err = ERROR_DNS_WRONG_ZONE;
+ } else {
+ err = ERROR_DNS_UPDATE_FAILED;
+ }
+ }
+
+#undef WRONG_ZONE_RESPONSE
+
+error:
+ return err;
+}
+
+/************************************************** *******************
+************************************************* ********************/
+
+static DNS_ERROR DoDNSUpdate_PTR(char *pszServerName,
+ const char *pszDomainName,
+ const char *pszHostName,
+ const struct in_addr ip)
+{
+ DNS_ERROR err;
+ struct dns_connection *conn;
+ char * zone_name;
+ TALLOC_CTX *mem_ctx;
+
+ if (!(mem_ctx = talloc_init(__func__))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ zone_name = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.",
+ (ntohl(ip.s_addr) & 0x000000ff),
+ (ntohl(ip.s_addr) & 0x0000ff00) >> 8,
+ (ntohl(ip.s_addr) & 0x00ff0000) >> 16,
+ (ntohl(ip.s_addr) & 0xff000000) >> 24);
+ if (zone_name == NULL) {
+ err = ERROR_DNS_NO_MEMORY;
+ goto done;
+ }
+
+ err = dns_open_connection( pszServerName, DNS_TCP, mem_ctx, &conn );
+ if (!ERR_DNS_IS_OK(err)) {
+ goto done;
+ }
+ /* Keep knocking off domain components until we find a zone that
+ * we can update out PTR record in.
+ */
+ while ((zone_name = strchr(zone_name, '.'))) {
+ /* Skip the '.' we are pointing at. */
+ zone_name++;
+
+ /* Stop if we hit the end or the root DNS servers. */
+ if (*zone_name == '\0' ||
+ strcmp(zone_name, "in-addr.arpa.") == 0) {
+ err = ERROR_DNS_INVALID_NAME;
+ goto done;
+ }
+
+ err = DoDNSUpdate_PTR_with_zone(mem_ctx, conn,
+ pszServerName, pszDomainName, pszHostName,
+ zone_name, ip);
+
+ DEBUG(6, ("updating PTR for %s in %s zone: %s\n",
+ pszHostName, zone_name, dns_errstr(err)));
+
+ if (ERR_DNS_IS_OK(err)) {
+ goto done;
+ }
+
+ if (!ERR_DNS_EQUAL(err, ERROR_DNS_WRONG_ZONE)) {
+ goto done;
+ }
+ }
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return err;
+}
+
+/************************************************** *******************
+************************************************* ********************/
+
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName, const char *pszHostName,
+ const struct sockaddr_storage *sslist, size_t num_addrs )
+{
+ DNS_ERROR a_err = ERROR_DNS_SUCCESS;
+ DNS_ERROR ptr_err = ERROR_DNS_SUCCESS;
+ unsigned i;
+
+ if ( (num_addrs <= 0) || !sslist ) {
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+
+ a_err = DoDNSUpdate_A(pszServerName, pszDomainName, pszHostName,
+ sslist, num_addrs);
+ if (!ERR_DNS_IS_OK(a_err)) {
+ d_printf("DNS A-record update for %s failed: %s\n",
+ pszHostName, dns_errstr(a_err));
+ }
+
+ /* For now, just try to register the first IPv4 address. We will
+ * need to register the IPv6 address once DNS registration gets
+ * IPv6 support.
+ */
+ for (i = 0; i < num_addrs; ++i) {
+
+ if (sslist[i].ss_family != AF_INET) {
+ continue;
+ }
+
+ ptr_err = DoDNSUpdate_PTR(pszServerName,
+ pszDomainName, pszHostName,
+ ((struct sockaddr_in *)(&sslist[i]))->sin_addr);
+ if (!ERR_DNS_IS_OK(ptr_err)) {
+ d_printf("DNS PTR-record update for %s failed: %s\n",
+ pszHostName, dns_errstr(ptr_err));
+ }
+ }
+
+ if (!ERR_DNS_IS_OK(a_err)) {
+ return a_err;
+ }
+
+ if (!ERR_DNS_IS_OK(ptr_err)) {
+ return ptr_err;
+ }
+
+ return ERROR_DNS_SUCCESS;
+}
+
+/************************************************** *******************
+************************************************* ********************/
+
int get_my_ip_address( struct sockaddr_storage **pp_ss )

{
--
1.5.5.1