[PATCH 3/4] dns updates: Dynamically register a PTR record.

James Peach jpeach at apple.com
Fri May 30 17:45:22 GMT 2008


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 <krishnag at centeris.com>
    Copyright (C) 2006 Gerald Carter <jerry at samba.org>
+  Copyright (C) 2008 James Peach <jpeach at samba.org>

       ** 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 <krishnag at centeris.com>
    Copyright (C) 2006 Gerald Carter <jerry at samba.org>
+  Copyright (C) 2008 James Peach <jpeach at samba.org>

       ** 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 at centeris.com)          
2006
     Copyright (C) Gerald Carter                                       
2006
+   Copyright (C) 2008 James Peach <jpeach at samba.org>

     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




More information about the samba-technical mailing list