>From 0c112afce8a697eddbfa54b8ab1d1e46b5b46af4 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 30 Apr 2013 16:40:09 +0200 Subject: [PATCH 3/5] 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 --- src/config/SSSDConfig/__init__.py.in | 1 + src/config/SSSDConfigTest.py | 2 + src/config/etc/sssd.api.conf | 1 + src/man/sssd-ipa.5.xml | 20 +++ src/providers/dp_dyndns.c | 329 ++++++++++++++++++++++++++--------- src/providers/dp_dyndns.h | 28 ++- src/providers/ipa/ipa_dyndns.c | 4 +- src/providers/ipa/ipa_opts.h | 1 + src/providers/ldap/sdap_dyndns.c | 217 ++++++++++++++++++----- src/providers/ldap/sdap_dyndns.h | 1 + src/resolv/async_resolv.c | 4 +- src/resolv/async_resolv.h | 7 +- 12 files changed, 476 insertions(+), 139 deletions(-) diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in index 3b3f2619d6e5765653c333906f9ca52798bf916d..720cbdb5c0b31ef25425f3ca6f90de7ac81f9ca0 100644 --- a/src/config/SSSDConfig/__init__.py.in +++ b/src/config/SSSDConfig/__init__.py.in @@ -129,6 +129,7 @@ option_strings = { 'dyndns_ttl' : _("The TTL to apply to the client's DNS entry after updating it"), 'dyndns_iface' : _("The interface whose IP should be used for dynamic DNS updates"), 'dyndns_refresh_interval' : _("How often to periodically update the client's DNS entry"), + 'dyndns_update_ptr' : _("Whether the provider should explicitly update the PTR record as well"), # [provider/ipa] 'ipa_domain' : _('IPA domain'), diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index d3bd9f35575aa154490292833002d31eb2477517..86ff29df1f8225a846903ad6c61c2a6147c9bc69 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -511,6 +511,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'dyndns_ttl', 'dyndns_iface', 'dyndns_refresh_interval', + 'dyndns_update_ptr', 'override_gid', 'case_sensitive', 'override_homedir', @@ -858,6 +859,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): 'dyndns_ttl', 'dyndns_iface', 'dyndns_refresh_interval', + 'dyndns_update_ptr', 'override_gid', 'case_sensitive', 'override_homedir', diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf index b09cbd18768c1ba9bbc2aa1d632df8de400f6f77..396063b4fd9fe08e65860bd24a68350401a0d7f0 100644 --- a/src/config/etc/sssd.api.conf +++ b/src/config/etc/sssd.api.conf @@ -126,6 +126,7 @@ dyndns_update = bool, None, false dyndns_ttl = int, None, false dyndns_iface = str, None, false dyndns_refresh_interval = int, None, false +dyndns_update_ptr = bool, None, false # Special providers [provider/permit] diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml index 426e9a5bbee76c7da62c8100fec5834e89befd0c..21a28047969043e1abb8f54a708fddc7727d8073 100644 --- a/src/man/sssd-ipa.5.xml +++ b/src/man/sssd-ipa.5.xml @@ -220,6 +220,26 @@ + dyndns_update_ptr (bool) + + + Whether the PTR record should also be explicitly + updated when updating the client's DNS records. + Applicable only when dyndns_update is true. + + + This options should be False in most IPA + deployments as the IPA server generates the + PTR records automatically when forward records + are changed. + + + Default: False (disabled) + + + + + ipa_hbac_search_base (string) diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c index 6ca8bdb2eaec2662b31efc7ab19c7753b2809d1e..6f143dae1cc6585d00c7c95de012c3e260d9fb7f 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; - 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; - } + char ip_addr[INET6_ADDRSTRLEN]; + const char *ip; + 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 }; diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h index e49ab8f0309bda5da6e6b00f1cb53016b88ae22c..8fdbe487b5a857cb02b13e099176ce07b27da0c4 100644 --- a/src/providers/dp_dyndns.h +++ b/src/providers/dp_dyndns.h @@ -46,6 +46,7 @@ enum dp_dyndns_opts { DP_OPT_DYNDNS_REFRESH_INTERVAL, DP_OPT_DYNDNS_IFACE, DP_OPT_DYNDNS_TTL, + DP_OPT_DYNDNS_UPDATE_PTR, DP_OPT_DYNDNS /* attrs counter */ }; @@ -79,11 +80,20 @@ sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx, char ***_straddrs); 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); +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); + +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); /* Returns: * * ERR_OK - on success @@ -100,8 +110,10 @@ struct tevent_req * nsupdate_get_addrs_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct be_resolv_ctx *be_res, const char *hostname); -errno_t nsupdate_get_addrs_recv(struct tevent_req *req, - TALLOC_CTX *mem_ctx, - char ***_addrlist); +errno_t +nsupdate_get_addrs_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct sss_iface_addr **_addrlist, + size_t *_count); #endif /* DP_DYNDNS_H_ */ diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c index 52b8051b0ab1b2b91b3972ab8673517b01cfff0d..bb38d105f41b46cf26f0f11c399a23f2a9642a92 100644 --- a/src/providers/ipa/ipa_dyndns.c +++ b/src/providers/ipa/ipa_dyndns.c @@ -242,7 +242,9 @@ ipa_dyndns_update_send(struct ipa_options *ctx) } subreq = sdap_dyndns_update_send(state, sdap_ctx->be->ev, - sdap_ctx->be, sdap_ctx, + sdap_ctx->be, + ctx->dyndns_ctx->opts, + sdap_ctx, dp_opt_get_string(ctx->dyndns_ctx->opts, DP_OPT_DYNDNS_IFACE), dp_opt_get_string(ctx->basic, diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h index 57911082ad6a89f09becdee30f99a89a74e7d12e..bfb09e36cb3562b61c5bdb1dd928c5b124bdd0ce 100644 --- a/src/providers/ipa/ipa_opts.h +++ b/src/providers/ipa/ipa_opts.h @@ -56,6 +56,7 @@ struct dp_option ipa_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_FALSE, BOOL_FALSE }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c index d1d96904fcda3a3b4e07e9407990dbf97a555af6..59dccfbbcedc5cfd20ec80237d70cd4466b0a569 100644 --- a/src/providers/ldap/sdap_dyndns.c +++ b/src/providers/ldap/sdap_dyndns.c @@ -43,6 +43,7 @@ sdap_dyndns_get_addrs_recv(struct tevent_req *req, struct sdap_dyndns_update_state { struct tevent_context *ev; struct be_resolv_ctx *be_res; + struct dp_option *opts; const char *hostname; const char *dns_zone; @@ -51,22 +52,29 @@ struct sdap_dyndns_update_state { int ttl; struct sss_iface_addr *addresses; + struct sss_iface_addr *dns_addrlist; uint8_t remove_af; + bool update_ptr; bool check_diff; bool use_server_with_nsupdate; char *update_msg; }; static void sdap_dyndns_update_addrs_done(struct tevent_req *subreq); -static void sdap_dyndns_addrs_check_done(struct tevent_req *subreq); +static void sdap_dyndns_dns_addrs_done(struct tevent_req *subreq); +static errno_t sdap_dyndns_addrs_diff(struct sdap_dyndns_update_state *state, + bool *_do_update); static errno_t sdap_dyndns_update_step(struct tevent_req *req); +static errno_t sdap_dyndns_update_ptr_step(struct tevent_req *req); static void sdap_dyndns_update_done(struct tevent_req *subreq); +static void sdap_dyndns_update_ptr_done(struct tevent_req *subreq); struct tevent_req * sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct be_ctx *be_ctx, + struct dp_option *opts, struct sdap_id_ctx *sdap_ctx, const char *ifname, const char *hostname, @@ -86,6 +94,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, return NULL; } state->check_diff = check_diff; + state->update_ptr = dp_opt_get_bool(opts, DP_OPT_DYNDNS_UPDATE_PTR); state->hostname = hostname; state->dns_zone = dns_zone; state->realm = realm; @@ -94,6 +103,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, state->ttl = ttl; state->be_res = be_ctx->be_res; state->ev = ev; + state->opts = opts; if (ifname) { /* Unless one family is restricted, just replace all @@ -155,8 +165,10 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq) return; } - if (state->check_diff) { - /* Check if we need the update at all */ + if (state->check_diff || state->update_ptr) { + /* Check if we need the update at all. In case we are updating the PTR + * records as well, we need to know the old addresses to be able to + * reliably delete the PTR records */ subreq = nsupdate_get_addrs_send(state, state->ev, state->be_res, state->hostname); if (subreq == NULL) { @@ -164,7 +176,7 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq) tevent_req_error(req, ret); return; } - tevent_req_set_callback(subreq, sdap_dyndns_addrs_check_done, req); + tevent_req_set_callback(subreq, sdap_dyndns_dns_addrs_done, req); return; } @@ -178,37 +190,81 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq) } static void -sdap_dyndns_addrs_check_done(struct tevent_req *subreq) +sdap_dyndns_dns_addrs_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + struct sdap_dyndns_update_state *state; + errno_t ret; + bool do_update; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_dyndns_update_state); + + ret = nsupdate_get_addrs_recv(subreq, state, &state->dns_addrlist, NULL); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not receive list of current addresses [%d]: %s\n", + ret, sss_strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (state->check_diff) { + ret = sdap_dyndns_addrs_diff(state, &do_update); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not check the diff between DNS " + "and current addresses [%d]: %s\n", ret, strerror(ret))); + tevent_req_error(req, ret); + return; + } + + if (do_update == false) { + DEBUG(SSSDBG_TRACE_FUNC, + ("No DNS update needed, addresses did not change\n")); + tevent_req_done(req); + return; + } + DEBUG(SSSDBG_TRACE_FUNC, + ("Detected IP addresses change, will perform an update\n")); + } + + /* Either we needed the addresses for updating PTR records only or + * the addresses have changed (or both) */ + ret = sdap_dyndns_update_step(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not start the update [%d]: %s\n", + ret, sss_strerror(ret))); + tevent_req_error(req, ret); + } + return; +} + +static errno_t +sdap_dyndns_addrs_diff(struct sdap_dyndns_update_state *state, bool *_do_update) { errno_t ret; int i; - struct tevent_req *req; - struct sdap_dyndns_update_state *state; char **str_dnslist = NULL, **str_local_list = NULL; char **dns_only = NULL, **local_only = NULL; bool do_update; - req = tevent_req_callback_data(subreq, struct tevent_req); - state = tevent_req_data(req, struct sdap_dyndns_update_state); - - ret = nsupdate_get_addrs_recv(subreq, state, &str_dnslist); - talloc_zfree(subreq); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, - ("Could not receive list of current addresses [%d]: %s\n", - ret, sss_strerror(ret))); - tevent_req_error(req, ret); - return; - } - ret = sss_iface_addr_list_as_str_list(state, - state->addresses, &str_local_list); + state->dns_addrlist, &str_dnslist); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Converting DNS IP addresses to strings failed: [%d]: %s\n", ret, sss_strerror(ret))); - tevent_req_error(req, ret); - return; + return ret; + } + + ret = sss_iface_addr_list_as_str_list(state, + state->addresses, &str_local_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Converting local IP addresses to strings failed: [%d]: %s\n", + ret, sss_strerror(ret))); + return ret; } /* Compare the lists */ @@ -217,8 +273,7 @@ sdap_dyndns_addrs_check_done(struct tevent_req *subreq) if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("diff_string_lists failed: [%d]: %s\n", ret, sss_strerror(ret))); - tevent_req_error(req, ret); - return; + return ret; } if (dns_only) { @@ -237,22 +292,8 @@ sdap_dyndns_addrs_check_done(struct tevent_req *subreq) } } - if (do_update) { - DEBUG(SSSDBG_TRACE_FUNC, - ("Detected IP addresses change, will perform an update\n")); - ret = sdap_dyndns_update_step(req); - if (ret != EOK) { - DEBUG(SSSDBG_OP_FAILURE, ("Could not start the update [%d]: %s\n", - ret, sss_strerror(ret))); - tevent_req_error(req, ret); - } - return; - } - - DEBUG(SSSDBG_TRACE_FUNC, - ("No DNS update needed, addresses did not change\n")); - tevent_req_done(req); - return; + *_do_update = do_update; + return EOK; } static errno_t @@ -271,11 +312,11 @@ sdap_dyndns_update_step(struct tevent_req *req) servername = state->servername; } - ret = be_nsupdate_create_msg(state, state->realm, state->dns_zone, - servername, state->hostname, - state->ttl, state->remove_af, - state->addresses, - &state->update_msg); + ret = be_nsupdate_create_fwd_msg(state, state->realm, state->dns_zone, + servername, state->hostname, + state->ttl, state->remove_af, + state->addresses, state->dns_addrlist, + &state->update_msg); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n")); return ret; @@ -321,6 +362,90 @@ sdap_dyndns_update_done(struct tevent_req *subreq) return; } + if (state->update_ptr == false) { + DEBUG(SSSDBG_TRACE_FUNC, ("No PTR update requested, done\n")); + tevent_req_done(req); + return; + } + + talloc_free(state->update_msg); + ret = sdap_dyndns_update_ptr_step(req); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + /* Execution will resume in sdap_dyndns_update_ptr_done */ +} + +static errno_t +sdap_dyndns_update_ptr_step(struct tevent_req *req) +{ + errno_t ret; + struct sdap_dyndns_update_state *state; + const char *servername; + struct tevent_req *subreq; + + state = tevent_req_data(req, struct sdap_dyndns_update_state); + + servername = NULL; + if (state->use_server_with_nsupdate == true && + state->servername) { + servername = state->servername; + } + + ret = be_nsupdate_create_ptr_msg(state, state->realm, + servername, state->hostname, + state->ttl, state->remove_af, + state->addresses, state->dns_addrlist, + &state->update_msg); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n")); + return ret; + } + + /* Fork a child process to perform the DNS update */ + subreq = be_nsupdate_send(state, state->ev, state->auth_type, + state->update_msg, + dp_opt_get_bool(state->opts, + DP_OPT_DYNDNS_USE_TCP)); + if (subreq == NULL) { + return EIO; + } + + tevent_req_set_callback(subreq, sdap_dyndns_update_ptr_done, req); + return EOK; +} + +static void +sdap_dyndns_update_ptr_done(struct tevent_req *subreq) +{ + errno_t ret; + int child_status; + struct tevent_req *req; + struct sdap_dyndns_update_state *state; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct sdap_dyndns_update_state); + + ret = be_nsupdate_recv(subreq, &child_status); + talloc_zfree(subreq); + if (ret != EOK) { + /* If the update didn't succeed, we can retry using the server name */ + if (state->use_server_with_nsupdate == false && state->servername && + WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) { + state->use_server_with_nsupdate = true; + DEBUG(SSSDBG_MINOR_FAILURE, + ("nsupdate failed, retrying with server name\n")); + ret = sdap_dyndns_update_ptr_step(req); + if (ret == EOK) { + return; + } + } + + tevent_req_error(req, ret); + return; + } + tevent_req_done(req); } diff --git a/src/providers/ldap/sdap_dyndns.h b/src/providers/ldap/sdap_dyndns.h index 1602938e17d01f30a99bae37a6d496643281a680..a18a71fc9d62d2de8f9cc574cf376a959d7a4c99 100644 --- a/src/providers/ldap/sdap_dyndns.h +++ b/src/providers/ldap/sdap_dyndns.h @@ -33,6 +33,7 @@ struct tevent_req * sdap_dyndns_update_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct be_ctx *be_ctx, + struct dp_option *opts, struct sdap_id_ctx *sdap_ctx, const char *ifname, const char *hostname, diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c index b56a68da79fd25b775da8e19702999b1c3cfb57b..290a1258e7a33fd552adeb6eb2d293108936e79f 100644 --- a/src/resolv/async_resolv.c +++ b/src/resolv/async_resolv.c @@ -1457,8 +1457,8 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx, } struct sockaddr_storage * -resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, - int port) +resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, + int port, int index) { struct sockaddr_storage *sockaddr; diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h index d759a82f37fa8bebc5d6b4b6d9a65e935e4cbb52..9bf5e0c40e44c385858fa4d85adebe6e022ca006 100644 --- a/src/resolv/async_resolv.h +++ b/src/resolv/async_resolv.h @@ -129,8 +129,11 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx, resolv_get_string_address_index(mem_ctx, hostent, 0) struct sockaddr_storage * -resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, - int port); +resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, + int port, int index); + +#define resolv_get_sockaddr_address(mem_ctx, rhostent, port) \ + resolv_get_sockaddr_address_index(mem_ctx, rhostent, port, 0) /** Get SRV record **/ struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx, -- 1.8.2.1