dp_dyndns.c revision 38ebc764eeb7693e0c4f0894d6687e54fbba871b
/*
SSSD
Authors:
Stephen Gallagher <sgallagh@redhat.com>
Jakub Hrozek <jhrozek@redhat.com>
Copyright (C) 2013 Red Hat
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 <ifaddrs.h>
#include <ctype.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 *
struct sockaddr_storage *ss)
{
struct sss_iface_addr *address;
return NULL;
}
sizeof(struct sockaddr_storage));
return NULL;
}
return address;
}
struct sss_iface_addr *ifaddr_list,
char ***_straddrs)
{
struct sss_iface_addr *ifaddr;
int ai;
char **straddrs;
const char *ip;
char ip_addr[INET6_ADDRSTRLEN];
count = 0;
count++;
}
return ENOMEM;
}
ai = 0;
case AF_INET:
errno = 0;
goto fail;
}
break;
case AF_INET6:
errno = 0;
goto fail;
}
break;
default:
continue;
}
goto fail;
}
ai++;
}
return EOK;
fail:
return ret;
}
static bool
{
char straddr[INET6_ADDRSTRLEN];
case AF_INET6:
("inet_ntop failed, won't log IP addresses\n"));
}
if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
return false;
} else if (IN6_IS_ADDR_LOOPBACK(addr6)) {
return false;
} else if (IN6_IS_ADDR_MULTICAST(addr6)) {
return false;
}
break;
case AF_INET:
("inet_ntop failed, won't log IP addresses\n"));
}
return false;
return false;
/* 169.254.0.0/16 */
return false;
return false;
}
break;
default:
return false;
}
return true;
}
/* Collect IP addresses associated with an interface */
struct sss_iface_addr **_addrlist)
{
struct sss_iface_addr *address;
/* Get the IP addresses associated with the
* specified interface
*/
errno = 0;
if (ret == -1) {
goto done;
}
/* Some interfaces don't have an ifa_addr */
/* Add IP addresses to the list */
/* Add this address to the IP address list */
if (!address) {
goto done;
}
sizeof(struct sockaddr_in) : \
sizeof(struct sockaddr_in6);
addrsize);
goto done;
}
}
}
done:
return ret;
}
static char *
{
struct sss_iface_addr *new_record;
char ip_addr[INET6_ADDRSTRLEN];
const char *ip;
/* Remove existing entries as needed */
if (remove_af & DYNDNS_REMOVE_A) {
"update delete %s. in A\nsend\n",
hostname);
if (update_msg == NULL) {
return NULL;
}
}
if (remove_af & DYNDNS_REMOVE_AAAA) {
"update delete %s. in AAAA\nsend\n",
hostname);
if (update_msg == NULL) {
return NULL;
}
}
case AF_INET:
return NULL;
}
break;
case AF_INET6:
return NULL;
}
break;
default:
return NULL;
}
/* Format the record update */
"update add %s. %d in %s %s\n",
ip_addr);
if (update_msg == NULL) {
return NULL;
}
}
}
static char *
struct sss_iface_addr *old_addresses)
{
char *strptr;
case AF_INET:
if (!(remove_af & DYNDNS_REMOVE_A)) {
continue;
}
break;
case AF_INET6:
if (!(remove_af & DYNDNS_REMOVE_AAAA)) {
continue;
}
break;
default:
return NULL;
}
addr);
return NULL;
}
/* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
"update delete %s in PTR\n", strptr);
if (update_msg == NULL) {
return NULL;
}
}
/* example: update add 11.78.16.10.in-addr.arpa. 85000 in PTR testvm.example.com */
case AF_INET:
break;
case AF_INET6:
break;
default:
return NULL;
}
addr);
return NULL;
}
/* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
"update add %s %d in PTR %s.\n",
if (update_msg == NULL) {
return NULL;
}
}
}
static char *
const char *servername)
{
char *realm_directive;
char *update_msg;
#ifdef HAVE_NSUPDATE_REALM
#else
#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) {
("Creating update message for server [%s] and realm [%s]\n.",
servername, realm));
/* Add the server, realm and headers */
} else {
("Creating update message for realm [%s].\n", realm));
/* Add the realm headers */
}
if (update_msg == NULL) {
goto fail;
}
return update_msg;
fail:
return NULL;
}
const char *zone, const char *servername,
struct sss_iface_addr *old_addresses,
char **_update_msg)
{
int ret;
char *update_msg;
/* in some cases realm could have been NULL if we weren't using TSIG */
return EINVAL;
}
if (update_msg == NULL) {
goto done;
}
if (zone) {
("Setting the zone explicitly to [%s].\n", zone));
if (update_msg == NULL) {
goto done;
}
}
if (update_msg == NULL) {
goto done;
}
(" -- Begin nsupdate message -- \n%s",
update_msg));
(" -- End nsupdate message -- \n"));
done:
return ret;
}
const char *servername, const char *hostname,
struct sss_iface_addr *addresses,
struct sss_iface_addr *old_addresses,
char **_update_msg)
{
char *update_msg;
/* in some cases realm could have been NULL if we weren't using TSIG */
return EINVAL;
}
if (update_msg == NULL) {
goto done;
}
if (update_msg == NULL) {
goto done;
}
(" -- Begin nsupdate message -- \n%s",
update_msg));
(" -- End nsupdate message -- \n"));
done:
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 */
struct sss_iface_addr *addrlist;
};
struct tevent_req *
struct tevent_context *ev,
struct be_resolv_ctx *be_res,
const char *hostname)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct nsupdate_get_addrs_state *state;
return NULL;
}
goto done;
}
goto done;
}
goto done;
}
done:
}
return req;
}
static void
{
struct tevent_req *req =
struct nsupdate_get_addrs_state);
struct resolv_hostent *rhostent;
struct sss_iface_addr *addr;
int i;
int resolv_status;
&rhostent);
/* If the retry did not match, simply quit */
/* 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.
*/
goto done;
("Could not resolve address for this machine, error [%d]: %s, "
goto done;
}
/* EOK */
} else {
/* The address list is NULL. This is probably a bug in
* c-ares, but we need to handle it gracefully.
*/
("Lookup of [%s] returned no addresses. Skipping.\n",
count = 0;
}
for (i=0; i < count; i++) {
goto done;
}
goto done;
}
}
}
/* If the resolver is set to honor both address families
* and the first one matched, retry the second one to
* get the complete list.
*/
IPV6_ONLY : \
if (!subreq) {
goto done;
}
return;
}
/* The second address matched either immediatelly or after a retry.
* No need to retry again. */
done:
/* All done */
("nsupdate_get_addrs_done failed: [%d]: [%s]\n",
sss_strerror(ret)));
}
/* EAGAIN - another lookup in progress */
}
struct sss_iface_addr **_addrlist,
{
struct nsupdate_get_addrs_state);
if (_addrlist) {
}
if (_count) {
}
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
struct tevent_timer *te,
static void
struct tevent_signal *sige,
void *pvt);
static struct tevent_req *
struct tevent_context *ev,
int pipefd_to_child,
char *child_stdin)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct nsupdate_child_state *state;
return NULL;
}
/* Set up SIGCHLD handler */
goto done;
}
/* Set up timeout handler */
goto done;
}
/* Write the update message to the nsupdate child */
(uint8_t *) child_stdin,
goto done;
}
done:
}
return req;
}
static void
struct tevent_timer *te,
{
struct tevent_req *req =
struct nsupdate_child_state *state =
}
static void
{
struct tevent_req *req =
struct nsupdate_child_state *state =
/* Verify that the buffer was sent, then return
* and wait for the sigchld handler to finish.
*/
return;
}
/* Now either wait for the timeout to fire or the child
* to finish
*/
}
static void
struct tevent_signal *sige,
void *pvt)
{
struct nsupdate_child_state *state =
("Dynamic DNS child failed with status [%d]\n", child_status));
return;
}
if (WIFSIGNALED(child_status)) {
("Dynamic DNS child was terminated by signal [%d]\n",
WTERMSIG(child_status)));
return;
}
}
static errno_t
{
struct nsupdate_child_state *state =
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;
};
struct tevent_context *ev,
char *nsupdate_msg)
{
int pipefd_to_child[2];
struct be_nsupdate_state *state;
char *args[3];
return NULL;
}
state->child_status = 0;
if (ret == -1) {
goto done;
}
if (child_pid == 0) { /* child */
goto done;
}
if (ret == -1) {
goto done;
}
errno = 0;
/* The child should never end up here */
goto done;
} else if (child_pid > 0) { /* parent */
close(pipefd_to_child[0]);
goto done;
}
} else { /* error */
goto done;
}
done:
}
return req;
}
static void
{
struct tevent_req *req =
struct be_nsupdate_state *state =
return;
}
}
{
struct be_nsupdate_state *state =
return EOK;
}
struct tevent_timer *te,
struct timeval current_time,
void *pvt)
{
/* timer_callback is responsible for calling be_nsupdate_timer_schedule
* again */
}
struct be_nsupdate_ctx *ctx)
{
int refresh;
if (ctx->refresh_timer) {
return;
}
if (refresh == 0) return;
if (!ctx->refresh_timer) {
("Failed to add dyndns refresh timer event\n"));
}
}
be_nsupdate_check(void)
{
/* Ensure that nsupdate exists */
errno = 0;
if (ret == -1) {
("%s does not exist. Dynamic DNS updates disabled\n",
} else {
("Could not set up dynamic DNS updates: [%d][%s]\n",
}
}
return ret;
}
static struct dp_option default_dyndns_opts[] = {
};
void *timer_pvt,
struct be_nsupdate_ctx **_ctx)
{
struct be_nsupdate_ctx *ctx;
return ret;
}
return ERR_OK;
}