dp_dyndns.c revision 4f2a07c422fa357ef6651bca8c48b8005280fa1d
/*
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 */
/* MASK represents special value for matching all interfaces */
#define MASK "*"
struct sss_iface_addr {
struct sss_iface_addr *next;
struct sss_iface_addr *prev;
struct sockaddr_storage *addr;
};
struct sss_iface_addr *list2)
{
}
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
{
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
case AF_INET6:
case AF_INET:
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;
}
/* steal old dlist to the new head */
}
}
/* OK, some result was found */
} else {
/* No result was found */
"No IPs usable for DNS was found for interface: %s.\n", ifname);
}
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"
"send\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"
"send\n",
if (update_msg == NULL) {
return NULL;
}
}
return update_msg;
}
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 *servername,
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 (update_msg == NULL) {
goto done;
}
" -- Begin nsupdate message -- \n"
"%s"
" -- 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"
" -- 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;
}
}
/* steal old dlist to the new head */
}
/* 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",
}
/* 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;
struct sss_child_ctx_old *child_ctx;
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",
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;
};
enum be_nsupdate_auth auth_type,
bool force_tcp);
struct tevent_context *ev,
enum be_nsupdate_auth auth_type,
char *nsupdate_msg,
bool force_tcp)
{
int pipefd_to_child[2];
struct be_nsupdate_state *state;
char **args;
int debug_fd;
return NULL;
}
state->child_status = 0;
if (ret == -1) {
goto done;
}
if (child_pid == 0) { /* child */
if (ret == -1) {
goto done;
}
if (debug_level >= SSSDBG_TRACE_LIBS) {
if (ret == -1) {
/* stderr is not fatal */
}
}
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 char **
enum be_nsupdate_auth auth_type,
bool force_tcp)
{
char **argv;
int argc = 0;
return NULL;
}
goto fail;
}
argc++;
switch (auth_type) {
case BE_NSUPDATE_AUTH_NONE:
break;
goto fail;
}
argc++;
break;
default:
goto fail;
}
if (force_tcp) {
goto fail;
}
argc++;
}
if (debug_level >= SSSDBG_TRACE_LIBS) {
goto fail;
}
argc++;
}
if (debug_level >= SSSDBG_TRACE_INTERNAL) {
goto fail;
}
argc++;
}
return argv;
fail:
return NULL;
}
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[] = {
};
struct be_nsupdate_ctx **_ctx)
{
struct be_nsupdate_ctx *ctx;
char *strauth;
return ret;
}
} else {
return EINVAL;
}
return ERR_OK;
}
struct tevent_context *ev,
void *timer_pvt)
{
return ERR_OK;
}
{
bool res;
const void *addr_a;
const void *addr_b;
} else {
res = false;
goto done;
}
res = false;
goto done;
}
done:
return res;
}
const char **_iface_name)
{
if (ret == -1) {
goto done;
}
/* Some interfaces don't have an ifa_addr */
const char *iface_name;
if (iface_name == NULL) {
} else {
}
goto done;
}
}
done:
return ret;
}
struct sss_iface_addr **_iface_addrs)
{
struct sss_iface_addr *iface_addrs;
const char *iface_name = NULL;
goto done;
}
goto done;
}
"sss_iface_addr_list_get failed: %d:[%s]\n",
goto done;
}
done:
return ret;
}