ipa_dyndns.c revision 62826f0052c1d6b71f62c1149c894d40549828ad
/*
SSSD
Authors:
Stephen Gallagher <sgallagh@redhat.com>
Copyright (C) 2010 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 "providers/ipa/ipa_common.h"
#include "providers/ipa/ipa_dyndns.h"
#include "util/child_common.h"
#include "providers/data_provider.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async_private.h"
#include "resolv/async_resolv.h"
#define IPA_DYNDNS_TIMEOUT 15
#define IPA_DYNDNS_REMOVE_A 0x1
#define IPA_DYNDNS_REMOVE_AAAA 0x2
struct ipa_ipaddress {
struct ipa_ipaddress *next;
struct ipa_ipaddress *prev;
struct sockaddr_storage *addr;
bool matched;
};
struct ipa_dyndns_ctx {
struct ipa_options *ipa_ctx;
struct sdap_id_op* sdap_op;
char *hostname;
struct ipa_ipaddress *addresses;
bool use_server_with_nsupdate;
enum restrict_family family_order;
};
static errno_t
struct ipa_ipaddress *ipa_addr_list,
char ***_straddrs)
{
struct ipa_ipaddress *ipa_addr;
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:
DEBUG(0, ("Unknown address family\n"));
continue;
}
goto fail;
}
ai++;
}
return EOK;
fail:
return ret;
}
struct ipa_options *ctx)
{
int resolv_timeout;
return ret;
}
return ret;
}
return ret;
}
return EOK;
}
void ipa_dyndns_update(void *pvt)
{
return;
}
}
{
char straddr[INET6_ADDRSTRLEN];
("inet_ntop failed, won't log IP addresses\n"));
}
if (IN6_IS_ADDR_LINKLOCAL(addr)) {
return false;
} else if (IN6_IS_ADDR_LOOPBACK(addr)) {
return false;
} else if (IN6_IS_ADDR_MULTICAST(addr)) {
return false;
}
("inet_ntop failed, won't log IP addresses\n"));
}
return false;
return false;
/* 169.254.0.0/16 */
return false;
return false;
}
} else {
return false;
}
return true;
}
struct sdap_handle *sh);
static struct tevent_req *
static struct tevent_req *
{
int ret;
char *iface;
struct ipa_dyndns_ctx *state;
struct ipa_ipaddress *address;
return NULL;
}
state->use_server_with_nsupdate = false;
if (iface) {
/* Get the IP addresses associated with the
* specified interface
*/
errno = 0;
if (ret == -1) {
DEBUG(0, ("Could not read interfaces [%d][%s]\n",
goto failed;
}
/* 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 failed;
}
sizeof(struct sockaddr_in) : \
sizeof(struct sockaddr_in6);
addrsize);
goto failed;
}
}
}
goto failed;
}
}
else {
/* Detect DYNDNS interface from LDAP connection */
goto failed;
}
if (!subreq) {
goto failed;
}
}
return req;
return NULL;
}
{
if (dp_error == DP_ERR_OFFLINE) {
} else {
}
goto failed;
}
goto failed;
}
goto failed;
}
return;
}
struct sdap_handle *sh)
{
int ret;
int fd;
struct ipa_ipaddress *address;
struct sockaddr_storage ss;
if (!sh) {
return EINVAL;
}
/* Get the file descriptor for the primary LDAP connection */
return ret;
}
errno = 0;
if (ret == -1) {
return ret;
}
case AF_INET:
case AF_INET6:
if (!address) {
return ENOMEM;
}
sizeof(struct sockaddr_storage));
return ENOMEM;
}
break;
default:
return EIO;
}
return EOK;
}
static struct tevent_req *
struct ipa_dyndns_ctx *ctx,
enum restrict_family family_order);
static errno_t
char ***_addrlist);
static errno_t
static void
{
char *ipa_hostname;
struct tevent_req *subreq;
/* Get the IPA hostname */
if (!ipa_hostname) {
/* This should never happen, but we'll protect
* against it anyway.
*/
return EINVAL;
}
return ENOMEM;
}
return ret;
}
return ENOMEM;
}
req);
return EOK;
}
static errno_t
{
&state->family_order);
return ret;
}
/* Unless one family is restricted, just replace all
* address families during the update
*/
switch (state->family_order) {
case IPV4_ONLY:
break;
case IPV6_ONLY:
break;
case IPV4_FIRST:
case IPV6_FIRST:
break;
}
} else {
/* If the interface isn't specified, we ONLY want to have the address
* that's connected to the LDAP server stored, so we need to check
* (and later remove) both address families.
*/
}
return EOK;
}
static void
{
struct tevent_req *req =
struct ipa_dyndns_ctx);
bool do_update = false;
int i;
if (!tmp_ctx) {
goto fail;
}
goto fail;
}
goto fail;
}
/* Compare the lists */
goto fail;
}
if (dns_only) {
for (i=0; dns_only[i]; i++) {
do_update = true;
}
}
if (local_only) {
for (i=0; local_only[i]; i++) {
do_update = true;
}
}
if (do_update) {
goto fail;
}
req);
return;
}
return;
fail:
}
struct ipa_dyndns_update_get_addrs_state {
struct ipa_dyndns_ctx *dctx;
enum host_database *db;
enum restrict_family family_order;
char **addrlist;
};
static struct tevent_req *
struct ipa_dyndns_ctx *ctx,
enum restrict_family family_order)
{
struct tevent_req *req;
struct ipa_dyndns_update_get_addrs_state *state;
struct ipa_dyndns_update_get_addrs_state);
return NULL;
}
goto immediate;
}
goto immediate;
}
}
return req;
}
static errno_t
{
struct tevent_req *subreq;
struct ipa_dyndns_update_get_addrs_state);
if (!subreq) {
return ENOMEM;
}
return EOK;
}
static void
{
int ret;
struct tevent_req *req =
struct ipa_dyndns_update_get_addrs_state);
struct resolv_hostent *rhostent;
int i;
int resolv_status;
&rhostent);
/* If the retry did not match, simply quit */
/* If the resolver is set to honor both address families
* retry the second one
*/
}
return;
}
/* Nothing to retry, simply quit */
return;
("Could not resolve address for this machine, error [%d]: %s, "
return;
}
/* EOK */
return;
}
for (i=0; i < count; i++) {
rhostent, i);
return;
}
}
/* If the resolver is set to honor both address families
* and the first one matched, retry the second one to
* get the complete list.
*/
}
return;
}
/* The second address matched either immediatelly or after a retry.
* No need to retry again. */
return;
}
static errno_t
char ***_addrlist)
{
struct ipa_dyndns_update_get_addrs_state);
return EOK;
}
struct ipa_nsupdate_ctx {
char *update_msg;
struct ipa_dyndns_ctx *dyndns_ctx;
int pipefd_to_child;
struct tevent_timer *timeout_handler;
int child_status;
};
bool use_server_with_nsupdate);
static struct tevent_req *
static struct tevent_req *
{
int ret;
struct ipa_nsupdate_ctx *state;
struct tevent_req *req;
struct tevent_req *subreq;
return NULL;
}
state->child_status = 0;
/* Format the message to pass to the nsupdate command */
goto failed;
}
/* Fork a child process to perform the DNS update */
goto failed;
}
return req;
return NULL;
}
struct nsupdate_send_ctx {
struct ipa_nsupdate_ctx *nsupdate_ctx;
int child_status;
};
bool use_server_with_nsupdate)
{
int ret, i;
char *servername = NULL;
char *realm;
char *realm_directive;
char *zone;
char ip_addr[INET6_ADDRSTRLEN];
const char *ip;
struct ipa_ipaddress *new_record;
if (!realm) {
goto done;
}
#ifdef HAVE_NSUPDATE_REALM
#else
#endif
if (!realm_directive) {
goto done;
}
if (!zone) {
goto done;
}
/* The DNS zone for IPA is the lower-case
* version of the IPA domain
*/
for(i = 0; zone[i] != '\0'; i++) {
}
if (use_server_with_nsupdate) {
"ldap://", 7) != 0) {
goto done;
}
if (!servername) {
goto done;
}
("Creating update message for server [%s], realm [%s] "
/* Add the server, realm and zone headers */
zone);
} else {
("Creating update message for realm [%s] and zone [%s].\n",
/* Add the realm and zone headers */
}
goto done;
}
/* Remove existing entries as needed */
if (remove_af & IPA_DYNDNS_REMOVE_A) {
"update delete %s. in A\nsend\n",
goto done;
}
}
if (remove_af & IPA_DYNDNS_REMOVE_AAAA) {
"update delete %s. in AAAA\nsend\n",
goto done;
}
}
case AF_INET:
goto done;
}
break;
case AF_INET6:
goto done;
}
break;
default:
DEBUG(0, ("Unknown address family\n"));
goto done;
}
/* Format the record update */
"update add %s. 86400 in %s %s\n",
ip_addr);
goto done;
}
}
goto done;
}
(" -- Begin nsupdate message -- \n%s",
ctx->update_msg));
(" -- End nsupdate message -- \n"));
done:
return ret;
}
static void ipa_dyndns_child_handler(int child_status,
struct tevent_signal *sige,
void *pvt);
struct tevent_timer *te,
static struct tevent_req *
{
int pipefd_to_child[2];
int ret;
struct nsupdate_send_ctx *state;
char *args[3];
return NULL;
}
state->child_status = 0;
if (ret == -1) {
return NULL;
}
if (pid == 0) { /* child */
return NULL;
}
if (ret == -1) {
return NULL;
}
errno = 0;
return NULL;
}
else if (pid > 0) { /* parent */
close(pipefd_to_child[0]);
/* Write the update message to the nsupdate child */
return NULL;
}
/* Set up SIGCHLD handler */
return NULL;
}
/* Set up timeout handler */
return NULL;
}
}
else { /* error */
return NULL;
}
return req;
}
struct tevent_timer *te,
{
struct tevent_req *req =
}
{
/* Verify that the buffer was sent, then return
* and wait for the sigchld handler to finish.
*/
int ret;
struct tevent_req *req =
struct nsupdate_send_ctx *state =
return;
}
}
static void ipa_dyndns_child_handler(int child_status,
struct tevent_signal *sige,
void *pvt)
{
struct nsupdate_send_ctx *state =
child_status));
return;
}
if WIFSIGNALED(child_status) {
WTERMSIG(child_status)));
return;
}
}
{
struct nsupdate_send_ctx *state =
return EOK;
}
{
return EOK;
}
{
int ret;
struct tevent_req *req =
struct ipa_nsupdate_ctx);
return;
}
}
{
struct ipa_nsupdate_ctx *state =
return EOK;
}
{
/* Check the return code from the sigchld handler
* and return it to the parent request.
*/
int ret;
int child_status;
struct tevent_req *req =
if (state->use_server_with_nsupdate == false &&
state->use_server_with_nsupdate = true;
}
return;
} else {
return;
}
}
}
{
return;
}
}