summaryrefslogtreecommitdiffstats
path: root/src/providers/dp_dyndns.c
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2013-04-30 16:40:09 +0200
committerJakub Hrozek <jhrozek@redhat.com>2013-05-03 20:25:46 +0200
commit38ebc764eeb7693e0c4f0894d6687e54fbba871b (patch)
treea02094f8985977fc2846b98c385c6169c5864e84 /src/providers/dp_dyndns.c
parenta398adc5b40381fc567a2aee1841b26af78aea17 (diff)
downloadsssd-38ebc764eeb7693e0c4f0894d6687e54fbba871b.tar.gz
sssd-38ebc764eeb7693e0c4f0894d6687e54fbba871b.tar.xz
sssd-38ebc764eeb7693e0c4f0894d6687e54fbba871b.zip
dyndns: New option dyndns_update_ptr
https://fedorahosted.org/sssd/ticket/1832 While some servers, such as FreeIPA allow the PTR record to be synchronized when the forward record is updated, other servers, including Active Directory, require that the PTR record is synchronized manually. This patch adds a new option, dyndns_update_ptr that automatically generates appropriate DNS update message for updating the reverse zone. This option is off by default in the IPA provider. Also renames be_nsupdate_create_msg to be_nsupdate_create_fwd_msg
Diffstat (limited to 'src/providers/dp_dyndns.c')
-rw-r--r--src/providers/dp_dyndns.c327
1 files changed, 248 insertions, 79 deletions
diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c
index 337817fc7..79701c629 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -262,64 +262,14 @@ done:
return ret;
}
-errno_t
-be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
- const char *zone, const char *servername,
- const char *hostname, const unsigned int ttl,
- uint8_t remove_af, struct sss_iface_addr *addresses,
- char **_update_msg)
+static char *
+nsupdate_msg_add_fwd(char *update_msg, struct sss_iface_addr *addresses,
+ const char *hostname, int ttl, uint8_t remove_af)
{
- int ret;
- char *realm_directive;
+ struct sss_iface_addr *new_record;
char ip_addr[INET6_ADDRSTRLEN];
const char *ip;
- struct sss_iface_addr *new_record;
- char *update_msg;
- TALLOC_CTX *tmp_ctx;
-
- /* in some cases realm could have been NULL if we weren't using TSIG */
- if (zone == NULL || hostname == NULL) {
- return EINVAL;
- }
-
- tmp_ctx = talloc_new(NULL);
- if (tmp_ctx == NULL) return ENOMEM;
-
-#ifdef HAVE_NSUPDATE_REALM
- realm_directive = talloc_asprintf(tmp_ctx, "realm %s\n", realm);
-#else
- realm_directive = talloc_asprintf(tmp_ctx, "");
-#endif
- if (!realm_directive) {
- ret = ENOMEM;
- goto done;
- }
-
- /* The realm_directive would now either contain an empty string or be
- * completely empty so we don't need to add another newline here
- */
- if (servername) {
- DEBUG(SSSDBG_FUNC_DATA,
- ("Creating update message for server [%s], realm [%s] "
- "and zone [%s].\n", servername, realm, zone));
-
- /* Add the server, realm and zone headers */
- update_msg = talloc_asprintf(tmp_ctx, "server %s\n%szone %s.\n",
- servername, realm_directive, zone);
- } else {
- DEBUG(SSSDBG_FUNC_DATA,
- ("Creating update message for realm [%s] and zone [%s].\n",
- realm, zone));
-
- /* Add the realm and zone headers */
- update_msg = talloc_asprintf(tmp_ctx, "%szone %s.\n",
- realm_directive, zone);
- }
- talloc_free(realm_directive);
- if (update_msg == NULL) {
- ret = ENOMEM;
- goto done;
- }
+ errno_t ret;
/* Remove existing entries as needed */
if (remove_af & DYNDNS_REMOVE_A) {
@@ -327,8 +277,7 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
"update delete %s. in A\nsend\n",
hostname);
if (update_msg == NULL) {
- ret = ENOMEM;
- goto done;
+ return NULL;
}
}
if (remove_af & DYNDNS_REMOVE_AAAA) {
@@ -336,8 +285,7 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
"update delete %s. in AAAA\nsend\n",
hostname);
if (update_msg == NULL) {
- ret = ENOMEM;
- goto done;
+ return NULL;
}
}
@@ -349,7 +297,9 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
- goto done;
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("inet_ntop failed [%d]: %s\n", ret, strerror(ret)));
+ return NULL;
}
break;
@@ -359,14 +309,15 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
- goto done;
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("inet_ntop failed [%d]: %s\n", ret, strerror(ret)));
+ return NULL;
}
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
- ret = EINVAL;
- goto done;
+ return NULL;
}
/* Format the record update */
@@ -376,12 +327,179 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
new_record->addr->ss_family == AF_INET ? "A" : "AAAA",
ip_addr);
if (update_msg == NULL) {
+ return NULL;
+ }
+
+ }
+
+ return talloc_asprintf_append(update_msg, "send\n");
+}
+
+static char *
+nsupdate_msg_add_ptr(char *update_msg, struct sss_iface_addr *addresses,
+ const char *hostname, int ttl, uint8_t remove_af,
+ struct sss_iface_addr *old_addresses)
+{
+ struct sss_iface_addr *new_record, *old_record;
+ char *strptr;
+ uint8_t *addr;
+
+ DLIST_FOR_EACH(old_record, old_addresses) {
+ switch(old_record->addr->ss_family) {
+ case AF_INET:
+ if (!(remove_af & DYNDNS_REMOVE_A)) {
+ continue;
+ }
+ addr = (uint8_t *) &((struct sockaddr_in *) old_record->addr)->sin_addr;
+ break;
+ case AF_INET6:
+ if (!(remove_af & DYNDNS_REMOVE_AAAA)) {
+ continue;
+ }
+ addr = (uint8_t *) &((struct sockaddr_in6 *) old_record->addr)->sin6_addr;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+ return NULL;
+ }
+
+ strptr = resolv_get_string_ptr_address(update_msg, old_record->addr->ss_family,
+ addr);
+ if (strptr == NULL) {
+ return NULL;
+ }
+
+ /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
+ update_msg = talloc_asprintf_append(update_msg,
+ "update delete %s in PTR\n", strptr);
+ talloc_free(strptr);
+ if (update_msg == NULL) {
+ return NULL;
+ }
+ }
+
+ /* example: update add 11.78.16.10.in-addr.arpa. 85000 in PTR testvm.example.com */
+ DLIST_FOR_EACH(new_record, addresses) {
+ switch(new_record->addr->ss_family) {
+ case AF_INET:
+ addr = (uint8_t *) &((struct sockaddr_in *) new_record->addr)->sin_addr;
+ break;
+ case AF_INET6:
+ addr = (uint8_t *) &((struct sockaddr_in6 *) new_record->addr)->sin6_addr;
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+ return NULL;
+ }
+
+ strptr = resolv_get_string_ptr_address(update_msg, new_record->addr->ss_family,
+ addr);
+ if (strptr == NULL) {
+ return NULL;
+ }
+
+ /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
+ update_msg = talloc_asprintf_append(update_msg,
+ "update add %s %d in PTR %s.\n",
+ strptr, ttl, hostname);
+ talloc_free(strptr);
+ if (update_msg == NULL) {
+ return NULL;
+ }
+ }
+
+ return talloc_asprintf_append(update_msg, "send\n");
+}
+
+static char *
+nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm,
+ const char *servername)
+{
+ char *realm_directive;
+ char *update_msg;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) return NULL;
+
+#ifdef HAVE_NSUPDATE_REALM
+ realm_directive = talloc_asprintf(tmp_ctx, "realm %s\n", realm);
+#else
+ realm_directive = talloc_asprintf(tmp_ctx, "");
+#endif
+ if (!realm_directive) {
+ goto fail;
+ }
+
+ /* The realm_directive would now either contain an empty string or be
+ * completely empty so we don't need to add another newline here
+ */
+ if (servername) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ ("Creating update message for server [%s] and realm [%s]\n.",
+ servername, realm));
+
+ /* Add the server, realm and headers */
+ update_msg = talloc_asprintf(tmp_ctx, "server %s\n%s",
+ servername, realm_directive);
+ } else {
+ DEBUG(SSSDBG_FUNC_DATA,
+ ("Creating update message for realm [%s].\n", realm));
+ /* Add the realm headers */
+ update_msg = talloc_asprintf(tmp_ctx, "%s", realm_directive);
+ }
+ talloc_free(realm_directive);
+ if (update_msg == NULL) {
+ goto fail;
+ }
+
+ update_msg = talloc_steal(mem_ctx, update_msg);
+ talloc_free(tmp_ctx);
+ return update_msg;
+
+fail:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+errno_t
+be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
+ const char *zone, const char *servername,
+ const char *hostname, const unsigned int ttl,
+ uint8_t remove_af, struct sss_iface_addr *addresses,
+ struct sss_iface_addr *old_addresses,
+ char **_update_msg)
+{
+ int ret;
+ char *update_msg;
+ TALLOC_CTX *tmp_ctx;
+
+ /* in some cases realm could have been NULL if we weren't using TSIG */
+ if (hostname == NULL) {
+ return EINVAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) return ENOMEM;
+
+ update_msg = nsupdate_msg_create_common(tmp_ctx, realm, servername);
+ if (update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (zone) {
+ DEBUG(SSSDBG_FUNC_DATA,
+ ("Setting the zone explicitly to [%s].\n", zone));
+ update_msg = talloc_asprintf_append(update_msg, "zone %s.\n", zone);
+ if (update_msg == NULL) {
ret = ENOMEM;
goto done;
}
}
- update_msg = talloc_asprintf_append(update_msg, "send\n");
+ update_msg = nsupdate_msg_add_fwd(update_msg, addresses, hostname,
+ ttl, remove_af);
if (update_msg == NULL) {
ret = ENOMEM;
goto done;
@@ -400,6 +518,47 @@ done:
return ret;
}
+errno_t
+be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
+ const char *servername, const char *hostname,
+ const unsigned int ttl, uint8_t remove_af,
+ struct sss_iface_addr *addresses,
+ struct sss_iface_addr *old_addresses,
+ char **_update_msg)
+{
+ errno_t ret;
+ char *update_msg;
+
+ /* in some cases realm could have been NULL if we weren't using TSIG */
+ if (hostname == NULL) {
+ return EINVAL;
+ }
+
+ update_msg = nsupdate_msg_create_common(mem_ctx, realm, servername);
+ if (update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ update_msg = nsupdate_msg_add_ptr(update_msg, addresses, hostname,
+ ttl, remove_af, old_addresses);
+ if (update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ (" -- Begin nsupdate message -- \n%s",
+ update_msg));
+ DEBUG(SSSDBG_TRACE_FUNC,
+ (" -- End nsupdate message -- \n"));
+
+ ret = ERR_OK;
+ *_update_msg = talloc_steal(mem_ctx, update_msg);
+done:
+ return ret;
+}
+
struct nsupdate_get_addrs_state {
struct tevent_context *ev;
struct be_resolv_ctx *be_res;
@@ -407,7 +566,7 @@ struct nsupdate_get_addrs_state {
const char *hostname;
/* Use sss_addr in this request */
- char **addrlist;
+ struct sss_iface_addr *addrlist;
size_t count;
};
@@ -472,6 +631,7 @@ nsupdate_get_addrs_done(struct tevent_req *subreq)
struct nsupdate_get_addrs_state *state = tevent_req_data(req,
struct nsupdate_get_addrs_state);
struct resolv_hostent *rhostent;
+ struct sss_iface_addr *addr;
int i;
int resolv_status;
@@ -509,25 +669,25 @@ nsupdate_get_addrs_done(struct tevent_req *subreq)
count = 0;
}
- state->addrlist = talloc_realloc(state, state->addrlist, char *,
- state->count + count + 1);
- if (!state->addrlist) {
- ret = ENOMEM;
- goto done;
- }
-
for (i=0; i < count; i++) {
- state->addrlist[state->count + i] = \
- resolv_get_string_address_index(state->addrlist,
- rhostent, i);
+ addr = talloc(state, struct sss_iface_addr);
+ if (addr == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
- if (state->addrlist[state->count + i] == NULL) {
+ addr->addr = resolv_get_sockaddr_address_index(addr, rhostent, 0, i);
+ if (addr == NULL) {
ret = ENOMEM;
goto done;
}
+
+ if (state->addrlist) {
+ talloc_steal(state->addrlist, addr);
+ }
+ DLIST_ADD(state->addrlist, addr);
}
state->count += count;
- state->addrlist[state->count] = NULL;
/* If the resolver is set to honor both address families
* and the first one matched, retry the second one to
@@ -576,14 +736,22 @@ done:
errno_t
nsupdate_get_addrs_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
- char ***_addrlist)
+ struct sss_iface_addr **_addrlist,
+ size_t *_count)
{
struct nsupdate_get_addrs_state *state = tevent_req_data(req,
struct nsupdate_get_addrs_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
- *_addrlist = talloc_steal(mem_ctx, state->addrlist);
+ if (_addrlist) {
+ *_addrlist = talloc_steal(mem_ctx, state->addrlist);
+ }
+
+ if (_count) {
+ *_count = state->count;
+ }
+
return EOK;
}
@@ -948,6 +1116,7 @@ static struct dp_option default_dyndns_opts[] = {
{ "dyndns_refresh_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
{ "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
+ { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE },
DP_OPTION_TERMINATOR
};