dp_dyndns.c revision 04868f1573f4b26ef34610b6d7069172f93bd8ab
/*
SSSD
dp_dyndns.c
Authors:
Stephen Gallagher <sgallagh@redhat.com>
Jakub Hrozek <jhrozek@redhat.com>
Copyright (C) 2013 Red Hat
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
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <ctype.h>
#include "util/util.h"
#include "confdb/confdb.h"
#include "util/child_common.h"
#include "providers/data_provider.h"
#include "providers/dp_backend.h"
#include "providers/dp_dyndns.h"
#include "resolv/async_resolv.h"
#ifndef DYNDNS_TIMEOUT
#define DYNDNS_TIMEOUT 15
#endif /* DYNDNS_TIMEOUT */
struct sss_iface_addr {
struct sss_iface_addr *next;
struct sss_iface_addr *prev;
struct sockaddr_storage *addr;
};
struct sss_iface_addr *
sss_iface_addr_add(TALLOC_CTX *mem_ctx, struct sss_iface_addr **list,
struct sockaddr_storage *ss)
{
struct sss_iface_addr *address;
address = talloc(mem_ctx, struct sss_iface_addr);
if (address == NULL) {
return NULL;
}
address->addr = talloc_memdup(address, ss,
sizeof(struct sockaddr_storage));
if(address->addr == NULL) {
talloc_zfree(address);
return NULL;
}
DLIST_ADD(*list, address);
return address;
}
errno_t
sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx,
struct sss_iface_addr *ifaddr_list,
char ***_straddrs)
{
struct sss_iface_addr *ifaddr;
size_t count;
int ai;
char **straddrs;
const char *ip;
char ip_addr[INET6_ADDRSTRLEN];
errno_t ret;
count = 0;
DLIST_FOR_EACH(ifaddr, ifaddr_list) {
count++;
}
straddrs = talloc_array(mem_ctx, char *, count+1);
if (straddrs == NULL) {
return ENOMEM;
}
ai = 0;
DLIST_FOR_EACH(ifaddr, ifaddr_list) {
switch(ifaddr->addr->ss_family) {
case AF_INET:
errno = 0;
ip = inet_ntop(ifaddr->addr->ss_family,
&(((struct sockaddr_in *)ifaddr->addr)->sin_addr),
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
goto fail;
}
break;
case AF_INET6:
errno = 0;
ip = inet_ntop(ifaddr->addr->ss_family,
&(((struct sockaddr_in6 *)ifaddr->addr)->sin6_addr),
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
goto fail;
}
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
continue;
}
straddrs[ai] = talloc_strdup(straddrs, ip);
if (straddrs[ai] == NULL) {
ret = ENOMEM;
goto fail;
}
ai++;
}
straddrs[count] = NULL;
*_straddrs = straddrs;
return EOK;
fail:
talloc_free(straddrs);
return ret;
}
static bool
ok_for_dns(struct sockaddr *sa)
{
char straddr[INET6_ADDRSTRLEN];
struct in6_addr *addr6;
struct in_addr *addr;
switch (sa->sa_family) {
case AF_INET6:
addr6 = &((struct sockaddr_in6 *) sa)->sin6_addr;
if (inet_ntop(AF_INET6, addr6, straddr, INET6_ADDRSTRLEN) == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE,
("inet_ntop failed, won't log IP addresses\n"));
snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
}
if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
DEBUG(SSSDBG_FUNC_DATA, ("Link local IPv6 address %s\n", straddr));
return false;
} else if (IN6_IS_ADDR_LOOPBACK(addr6)) {
DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv6 address %s\n", straddr));
return false;
} else if (IN6_IS_ADDR_MULTICAST(addr6)) {
DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv6 address %s\n", straddr));
return false;
}
break;
case AF_INET:
addr = &((struct sockaddr_in *) sa)->sin_addr;
if (inet_ntop(AF_INET, addr, straddr, INET6_ADDRSTRLEN) == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE,
("inet_ntop failed, won't log IP addresses\n"));
snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
}
if (IN_MULTICAST(ntohl(addr->s_addr))) {
DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv4 address %s\n", straddr));
return false;
} else if (inet_netof(*addr) == IN_LOOPBACKNET) {
DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv4 address %s\n", straddr));
return false;
} else if ((addr->s_addr & 0xffff0000) == 0xa9fe0000) {
/* 169.254.0.0/16 */
DEBUG(SSSDBG_FUNC_DATA, ("Link-local IPv4 address %s\n", straddr));
return false;
} else if (addr->s_addr == htonl(INADDR_BROADCAST)) {
DEBUG(SSSDBG_FUNC_DATA, ("Broadcast IPv4 address %s\n", straddr));
return false;
}
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
return false;
}
return true;
}
/* Collect IP addresses associated with an interface */
errno_t
sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname,
struct sss_iface_addr **_addrlist)
{
struct ifaddrs *ifaces = NULL;
struct ifaddrs *ifa;
errno_t ret;
size_t addrsize;
struct sss_iface_addr *address;
struct sss_iface_addr *addrlist = NULL;
/* Get the IP addresses associated with the
* specified interface
*/
errno = 0;
ret = getifaddrs(&ifaces);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_OP_FAILURE,
("Could not read interfaces [%d][%s]\n", ret, strerror(ret)));
goto done;
}
for (ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) {
/* Some interfaces don't have an ifa_addr */
if (!ifa->ifa_addr) continue;
/* Add IP addresses to the list */
if ((ifa->ifa_addr->sa_family == AF_INET ||
ifa->ifa_addr->sa_family == AF_INET6) &&
strcasecmp(ifa->ifa_name, ifname) == 0 &&
ok_for_dns(ifa->ifa_addr)) {
/* Add this address to the IP address list */
address = talloc_zero(mem_ctx, struct sss_iface_addr);
if (!address) {
goto done;
}
addrsize = ifa->ifa_addr->sa_family == AF_INET ? \
sizeof(struct sockaddr_in) : \
sizeof(struct sockaddr_in6);
address->addr = talloc_memdup(address, ifa->ifa_addr,
addrsize);
if (address->addr == NULL) {
ret = ENOMEM;
goto done;
}
DLIST_ADD(addrlist, address);
}
}
ret = EOK;
*_addrlist = addrlist;
done:
freeifaddrs(ifaces);
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)
{
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;
}
/* Remove existing entries as needed */
if (remove_af & DYNDNS_REMOVE_A) {
update_msg = talloc_asprintf_append(update_msg,
"update delete %s. in A\nsend\n",
hostname);
if (update_msg == NULL) {
ret = ENOMEM;
goto done;
}
}
if (remove_af & DYNDNS_REMOVE_AAAA) {
update_msg = talloc_asprintf_append(update_msg,
"update delete %s. in AAAA\nsend\n",
hostname);
if (update_msg == NULL) {
ret = ENOMEM;
goto done;
}
}
DLIST_FOR_EACH(new_record, addresses) {
switch(new_record->addr->ss_family) {
case AF_INET:
ip = inet_ntop(new_record->addr->ss_family,
&(((struct sockaddr_in *)new_record->addr)->sin_addr),
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
goto done;
}
break;
case AF_INET6:
ip = inet_ntop(new_record->addr->ss_family,
&(((struct sockaddr_in6 *)new_record->addr)->sin6_addr),
ip_addr, INET6_ADDRSTRLEN);
if (ip == NULL) {
ret = errno;
goto done;
}
break;
default:
DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
ret = EINVAL;
goto done;
}
/* Format the record update */
update_msg = talloc_asprintf_append(update_msg,
"update add %s. %d in %s %s\n",
hostname, ttl,
new_record->addr->ss_family == AF_INET ? "A" : "AAAA",
ip_addr);
if (update_msg == NULL) {
ret = ENOMEM;
goto done;
}
}
update_msg = talloc_asprintf_append(update_msg, "send\n");
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:
talloc_free(tmp_ctx);
return ret;
}
struct nsupdate_get_addrs_state {
struct tevent_context *ev;
struct be_resolv_ctx *be_res;
enum host_database *db;
const char *hostname;
/* Use sss_addr in this request */
char **addrlist;
size_t count;
};
static void nsupdate_get_addrs_done(struct tevent_req *subreq);
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 ret;
struct tevent_req *req;
struct tevent_req *subreq;
struct nsupdate_get_addrs_state *state;
req = tevent_req_create(mem_ctx, &state, struct nsupdate_get_addrs_state);
if (req == NULL) {
return NULL;
}
state->be_res = be_res;
state->ev = ev;
state->hostname = talloc_strdup(state, hostname);
if (state->hostname == NULL) {
ret = ENOMEM;
goto done;
}
state->db = talloc_array(state, enum host_database, 2);
if (state->db == NULL) {
ret = ENOMEM;
goto done;
}
state->db[0] = DB_DNS;
state->db[1] = DB_SENTINEL;
subreq = resolv_gethostbyname_send(state, ev, be_res->resolv, hostname,
state->be_res->family_order,
state->db);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, nsupdate_get_addrs_done, req);
ret = ERR_OK;
done:
if (ret != ERR_OK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void
nsupdate_get_addrs_done(struct tevent_req *subreq)
{
errno_t ret;
size_t count;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct nsupdate_get_addrs_state *state = tevent_req_data(req,
struct nsupdate_get_addrs_state);
struct resolv_hostent *rhostent;
int i;
int resolv_status;
ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL,
&rhostent);
talloc_zfree(subreq);
/* If the retry did not match, simply quit */
if (ret == ENOENT) {
/* If the resolver is set to honor both address families
* it automatically retries the other one internally, so ENOENT
* means neither matched and we can simply quit.
*/
ret = EOK;
goto done;
} else if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
("Could not resolve address for this machine, error [%d]: %s, "
"resolver returned: [%d]: %s\n", ret, sss_strerror(ret),
resolv_status, resolv_strerror(resolv_status)));
goto done;
}
/* EOK */
if (rhostent->addr_list) {
for (count=0; rhostent->addr_list[count]; count++);
} else {
/* The address list is NULL. This is probably a bug in
* c-ares, but we need to handle it gracefully.
*/
DEBUG(SSSDBG_MINOR_FAILURE,
("Lookup of [%s] returned no addresses. Skipping.\n",
rhostent->name));
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);
if (state->addrlist[state->count + i] == NULL) {
ret = ENOMEM;
goto done;
}
}
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
* get the complete list.
*/
if (((state->be_res->family_order == IPV4_FIRST &&
rhostent->family == AF_INET) ||
(state->be_res->family_order == IPV6_FIRST &&
rhostent->family == AF_INET6))) {
state->be_res->family_order = \
(state->be_res->family_order == IPV4_FIRST) ? \
IPV6_ONLY : \
IPV4_ONLY;
subreq = resolv_gethostbyname_send(state, state->ev,
state->be_res->resolv,
state->hostname,
state->be_res->family_order,
state->db);
if (!subreq) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, nsupdate_get_addrs_done, req);
return;
}
/* The second address matched either immediatelly or after a retry.
* No need to retry again. */
ret = EOK;
done:
if (ret == EOK) {
/* All done */
tevent_req_done(req);
} else if (ret != EAGAIN) {
DEBUG(SSSDBG_OP_FAILURE,
("nsupdate_get_addrs_done failed: [%d]: [%s]\n",
sss_strerror(ret)));
tevent_req_error(req, ret);
}
/* EAGAIN - another lookup in progress */
}
errno_t
nsupdate_get_addrs_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
char ***_addrlist)
{
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);
return EOK;
}
/* Write the nsupdate_msg into the already forked child, wait until
* the child finishes
*
* This is not a typical tevent_req styled request as it ends either after
* a timeout or when the child finishes operation.
*/
struct nsupdate_child_state {
int pipefd_to_child;
struct tevent_timer *timeout_handler;
int child_status;
};
static void
nsupdate_child_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt);
static void
nsupdate_child_handler(int child_status,
struct tevent_signal *sige,
void *pvt);
static void nsupdate_child_stdin_done(struct tevent_req *subreq);
static struct tevent_req *
nsupdate_child_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
int pipefd_to_child,
pid_t child_pid,
char *child_stdin)
{
errno_t ret;
struct tevent_req *req;
struct tevent_req *subreq;
struct nsupdate_child_state *state;
struct timeval tv;
req = tevent_req_create(mem_ctx, &state, struct nsupdate_child_state);
if (req == NULL) {
return NULL;
}
state->pipefd_to_child = pipefd_to_child;
/* Set up SIGCHLD handler */
ret = child_handler_setup(ev, child_pid, nsupdate_child_handler, req);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Could not set up child handlers [%d]: %s\n",
ret, sss_strerror(ret)));
ret = ERR_DYNDNS_FAILED;
goto done;
}
/* Set up timeout handler */
tv = tevent_timeval_current_ofs(DYNDNS_TIMEOUT, 0);
state->timeout_handler = tevent_add_timer(ev, req, tv,
nsupdate_child_timeout, req);
if(state->timeout_handler == NULL) {
ret = ERR_DYNDNS_FAILED;
goto done;
}
/* Write the update message to the nsupdate child */
subreq = write_pipe_send(req, ev,
(uint8_t *) child_stdin,
strlen(child_stdin)+1,
state->pipefd_to_child);
if (subreq == NULL) {
ret = ERR_DYNDNS_FAILED;
goto done;
}
tevent_req_set_callback(subreq, nsupdate_child_stdin_done, req);
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void
nsupdate_child_timeout(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval tv, void *pvt)
{
struct tevent_req *req =
talloc_get_type(pvt, struct tevent_req);
struct nsupdate_child_state *state =
tevent_req_data(req, struct nsupdate_child_state);
DEBUG(SSSDBG_CRIT_FAILURE, ("Timeout reached for dynamic DNS update\n"));
state->child_status = ETIMEDOUT;
tevent_req_error(req, ERR_DYNDNS_TIMEOUT);
}
static void
nsupdate_child_stdin_done(struct tevent_req *subreq)
{
errno_t ret;
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct nsupdate_child_state *state =
tevent_req_data(req, struct nsupdate_child_state);
/* Verify that the buffer was sent, then return
* and wait for the sigchld handler to finish.
*/
DEBUG(SSSDBG_TRACE_LIBS, ("Sending nsupdate data complete\n"));
ret = write_pipe_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Sending nsupdate data failed [%d]: %s\n",
ret, sss_strerror(ret)));
tevent_req_error(req, ERR_DYNDNS_FAILED);
return;
}
close(state->pipefd_to_child);
state->pipefd_to_child = -1;
/* Now either wait for the timeout to fire or the child
* to finish
*/
}
static void
nsupdate_child_handler(int child_status,
struct tevent_signal *sige,
void *pvt)
{
struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
struct nsupdate_child_state *state =
tevent_req_data(req, struct nsupdate_child_state);
state->child_status = child_status;
if (WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
DEBUG(SSSDBG_OP_FAILURE,
("Dynamic DNS child failed with status [%d]\n", child_status));
tevent_req_error(req, ERR_DYNDNS_FAILED);
return;
}
if (WIFSIGNALED(child_status)) {
DEBUG(SSSDBG_OP_FAILURE,
("Dynamic DNS child was terminated by signal [%d]\n",
WTERMSIG(child_status)));
tevent_req_error(req, ERR_DYNDNS_FAILED);
return;
}
tevent_req_done(req);
}
static errno_t
nsupdate_child_recv(struct tevent_req *req, int *child_status)
{
struct nsupdate_child_state *state =
tevent_req_data(req, struct nsupdate_child_state);
*child_status = state->child_status;
TEVENT_REQ_RETURN_ON_ERROR(req);
return ERR_OK;
}
/* Fork a nsupdate child, write the nsupdate_msg into stdin and wait for the child
* to finish one way or another
*/
struct be_nsupdate_state {
int child_status;
};
static void be_nsupdate_done(struct tevent_req *subreq);
struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
char *nsupdate_msg)
{
int pipefd_to_child[2];
pid_t child_pid;
errno_t ret;
struct tevent_req *req = NULL;
struct tevent_req *subreq = NULL;
struct be_nsupdate_state *state;
char *args[3];
req = tevent_req_create(mem_ctx, &state, struct be_nsupdate_state);
if (req == NULL) {
return NULL;
}
state->child_status = 0;
ret = pipe(pipefd_to_child);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
("pipe failed [%d][%s].\n", ret, strerror(ret)));
goto done;
}
child_pid = fork();
if (child_pid == 0) { /* child */
args[0] = talloc_strdup(state, NSUPDATE_PATH);
args[1] = talloc_strdup(state, "-g");
args[2] = NULL;
if (args[0] == NULL || args[1] == NULL) {
ret = ENOMEM;
goto done;
}
close(pipefd_to_child[1]);
ret = dup2(pipefd_to_child[0], STDIN_FILENO);
if (ret == -1) {
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
("dup2 failed [%d][%s].\n", ret, strerror(ret)));
goto done;
}
errno = 0;
execv(NSUPDATE_PATH, args);
/* The child should never end up here */
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE, ("execv failed [%d][%s].\n", ret, strerror(ret)));
goto done;
} else if (child_pid > 0) { /* parent */
close(pipefd_to_child[0]);
subreq = nsupdate_child_send(state, ev, pipefd_to_child[1],
child_pid, nsupdate_msg);
if (subreq == NULL) {
ret = ERR_DYNDNS_FAILED;
goto done;
}
tevent_req_set_callback(subreq, be_nsupdate_done, req);
} else { /* error */
ret = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
("fork failed [%d][%s].\n", ret, strerror(ret)));
goto done;
}
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void
be_nsupdate_done(struct tevent_req *subreq)
{
struct tevent_req *req =
tevent_req_callback_data(subreq, struct tevent_req);
struct be_nsupdate_state *state =
tevent_req_data(req, struct be_nsupdate_state);
errno_t ret;
ret = nsupdate_child_recv(subreq, &state->child_status);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("nsupdate child execution failed [%d]: %s\n",
ret, sss_strerror(ret)));
tevent_req_error(req, ret);
return;
}
DEBUG(SSSDBG_FUNC_DATA,
("nsupdate child status: %d\n", state->child_status));
tevent_req_done(req);
}
errno_t
be_nsupdate_recv(struct tevent_req *req, int *child_status)
{
struct be_nsupdate_state *state =
tevent_req_data(req, struct be_nsupdate_state);
*child_status = state->child_status;
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
errno_t
be_nsupdate_check(void)
{
errno_t ret;
struct stat stat_buf;
/* Ensure that nsupdate exists */
errno = 0;
ret = stat(NSUPDATE_PATH, &stat_buf);
if (ret == -1) {
ret = errno;
if (ret == ENOENT) {
DEBUG(SSSDBG_MINOR_FAILURE,
("%s does not exist. Dynamic DNS updates disabled\n",
NSUPDATE_PATH));
} else {
DEBUG(SSSDBG_OP_FAILURE,
("Could not set up dynamic DNS updates: [%d][%s]\n",
ret, strerror(ret)));
}
}
return ret;
}
static struct dp_option default_dyndns_opts[] = {
{ "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
{ "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
DP_OPTION_TERMINATOR
};
errno_t
be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
struct dp_option *defopts, struct be_nsupdate_ctx **_ctx)
{
errno_t ret;
struct dp_option *src_opts;
struct be_nsupdate_ctx *ctx;
ctx = talloc(mem_ctx, struct be_nsupdate_ctx);
if (ctx == NULL) return ENOMEM;
src_opts = defopts ? defopts : default_dyndns_opts;
ret = dp_get_options(ctx, be_ctx->cdb, be_ctx->conf_path,
src_opts, DP_OPT_DYNDNS, &ctx->opts);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, ("Cannot retrieve dynamic DNS options\n"));
return ret;
}
*_ctx = ctx;
return EOK;
}