summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/confdb/confdb.h1
-rw-r--r--src/config/SSSDConfig.py1
-rwxr-xr-xsrc/config/SSSDConfigTest.py2
-rw-r--r--src/config/etc/sssd.api.conf1
-rw-r--r--src/man/sssd.conf.5.xml28
-rw-r--r--src/providers/data_provider_fo.c52
-rw-r--r--src/providers/fail_over.c25
-rw-r--r--src/providers/fail_over.h22
-rw-r--r--src/resolv/async_resolv.c36
-rw-r--r--src/resolv/async_resolv.h10
-rw-r--r--src/tests/fail_over-tests.c10
-rw-r--r--src/tests/resolv-tests.c18
12 files changed, 172 insertions, 34 deletions
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 28098b805..66576c354 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -100,6 +100,7 @@
#define CONFDB_DOMAIN_MPG "magic_private_groups"
#define CONFDB_DOMAIN_FQ "use_fully_qualified_names"
#define CONFDB_DOMAIN_ENTRY_CACHE_TIMEOUT "entry_cache_timeout"
+#define CONFDB_DOMAIN_FAMILY_ORDER "lookup_family_order"
/* Local Provider */
#define CONFDB_LOCAL_DEFAULT_SHELL "default_shell"
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index a004c33ba..471ecb6c5 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -79,6 +79,7 @@ option_strings = {
'store_legacy_passwords' : _('Store password hashes'),
'use_fully_qualified_names' : _('Display users/groups in fully-qualified form'),
'entry_cache_timeout' : _('Entry cache timeout length (seconds)'),
+ 'lookup_family_order' : _('Restrict or prefer a specific address family when performing DNS lookups'),
# [provider/ipa]
'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 153146f8e..eed1de311 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -414,6 +414,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'store_legacy_passwords',
'use_fully_qualified_names',
'entry_cache_timeout',
+ 'lookup_family_order',
'id_provider',
'auth_provider',
'access_provider',
@@ -724,6 +725,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'store_legacy_passwords',
'use_fully_qualified_names',
'entry_cache_timeout',
+ 'lookup_family_order',
'id_provider',
'auth_provider',
'access_provider',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 190535382..35890acc0 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -54,6 +54,7 @@ cache_credentials = bool, None, true, false
store_legacy_passwords = bool, None, false
use_fully_qualified_names = bool, None, false
entry_cache_timeout = int, None, false
+lookup_family_order = str, None, false
# Special providers
[provider/permit]
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 7b240c8f4..496c1712e 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -589,6 +589,34 @@
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>lookup_family_order (string)</term>
+ <listitem>
+ <para>
+ Provides the ability to select preferred address family
+ to use when performing DNS lookups.
+ </para>
+ <para>
+ Supported values:
+ </para>
+ <para>
+ ipv4_first: Try looking up IPv4 address, if that fails, try IPv6
+ </para>
+ <para>
+ ipv4_only: Only attempt to resolve hostnames to IPv4 addresses.
+ </para>
+ <para>
+ ipv6_first: Try looking up IPv6 address, if that fails, try IPv4
+ </para>
+ <para>
+ ipv6_only: Only attempt to resolve hostnames to IPv6 addresses.
+ </para>
+ <para>
+ Default: ipv4_first
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
index 7d0240489..482f74446 100644
--- a/src/providers/data_provider_fo.c
+++ b/src/providers/data_provider_fo.c
@@ -53,9 +53,47 @@ struct be_failover_ctx {
struct be_svc_data *svcs;
};
+static int be_fo_get_options(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
+ struct fo_options *opts)
+{
+ char *str_opt;
+ int ret;
+
+ /* todo get timeout from configuration */
+ opts->retry_timeout = 30;
+
+ ret = confdb_get_string(ctx->cdb, mem_ctx, ctx->conf_path,
+ CONFDB_DOMAIN_FAMILY_ORDER,
+ "ipv4_first", &str_opt);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ DEBUG(7, ("Lookup order: %s\n", str_opt));
+
+ if (strcasecmp(str_opt, "ipv4_first") == 0) {
+ opts->family_order = IPV4_FIRST;
+ } else if (strcasecmp(str_opt, "ipv4_only") == 0) {
+ opts->family_order = IPV4_ONLY;
+ } else if (strcasecmp(str_opt, "ipv6_first") == 0) {
+ opts->family_order = IPV6_FIRST;
+ } else if (strcasecmp(str_opt, "ipv6_only") == 0) {
+ opts->family_order = IPV6_ONLY;
+ } else {
+ DEBUG(1, ("Unknown value for option %s: %s\n",
+ CONFDB_DOMAIN_FAMILY_ORDER, str_opt));
+ talloc_free(str_opt);
+ return EINVAL;
+ }
+
+ talloc_free(str_opt);
+ return EOK;
+}
+
int be_init_failover(struct be_ctx *ctx)
{
int ret;
+ struct fo_options fopts;
if (ctx->be_fo != NULL) {
return EOK;
@@ -72,8 +110,13 @@ int be_init_failover(struct be_ctx *ctx)
return ret;
}
- /* todo get timeout from configuration */
- ctx->be_fo->fo_ctx = fo_context_init(ctx->be_fo, 30);
+ ret = be_fo_get_options(ctx->be_fo, ctx, &fopts);
+ if (ret != EOK) {
+ talloc_zfree(ctx->be_fo);
+ return ret;
+ }
+
+ ctx->be_fo->fo_ctx = fo_context_init(ctx->be_fo, &fopts);
if (!ctx->be_fo->fo_ctx) {
talloc_zfree(ctx->be_fo);
return ENOMEM;
@@ -250,7 +293,9 @@ struct tevent_req *be_resolve_server_send(TALLOC_CTX *memctx,
state->attempts = 0;
subreq = fo_resolve_service_send(state, ev,
- ctx->be_fo->resolv, svc->fo_service);
+ ctx->be_fo->resolv,
+ ctx->be_fo->fo_ctx,
+ svc->fo_service);
if (!subreq) {
talloc_zfree(req);
return NULL;
@@ -305,6 +350,7 @@ static void be_resolve_server_done(struct tevent_req *subreq)
DEBUG(6, ("Trying with the next one!\n"));
subreq = fo_resolve_service_send(state, state->ev,
state->ctx->be_fo->resolv,
+ state->ctx->be_fo->fo_ctx,
state->svc->fo_service);
if (!subreq) {
tevent_req_error(req, ENOMEM);
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 7560b89ee..54ad0329e 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -45,8 +45,7 @@ struct fo_ctx {
struct fo_service *service_list;
struct server_common *server_common_list;
- /* Settings. */
- time_t retry_timeout;
+ struct fo_options *opts;
};
struct fo_service {
@@ -99,7 +98,7 @@ struct status {
};
struct fo_ctx *
-fo_context_init(TALLOC_CTX *mem_ctx, time_t retry_timeout)
+fo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
{
struct fo_ctx *ctx;
@@ -108,11 +107,17 @@ fo_context_init(TALLOC_CTX *mem_ctx, time_t retry_timeout)
DEBUG(1, ("No memory\n"));
return NULL;
}
+ ctx->opts = talloc_zero(ctx, struct fo_options);
+ if (ctx->opts == NULL) {
+ DEBUG(1, ("No memory\n"));
+ return NULL;
+ }
- ctx->retry_timeout = retry_timeout;
+ ctx->opts->retry_timeout = opts->retry_timeout;
+ ctx->opts->family_order = opts->family_order;
DEBUG(3, ("Created new fail over context, retry timeout is %d\n",
- retry_timeout));
+ ctx->opts->retry_timeout));
return ctx;
}
@@ -166,7 +171,7 @@ get_server_status(struct fo_server *server)
DEBUG(7, ("Status of server '%s' is '%s'\n", SERVER_NAME(server),
str_server_status(server->common->server_status)));
- timeout = server->service->ctx->retry_timeout;
+ timeout = server->service->ctx->opts->retry_timeout;
if (timeout != 0 && server->common->server_status == SERVER_NOT_WORKING) {
gettimeofday(&tv, NULL);
if (STATUS_DIFF(server->common, tv) > timeout) {
@@ -193,7 +198,7 @@ get_port_status(struct fo_server *server)
DEBUG(7, ("Port status of port %d for server '%s' is '%s'\n", server->port,
SERVER_NAME(server), str_port_status(server->port_status)));
- timeout = server->service->ctx->retry_timeout;
+ timeout = server->service->ctx->opts->retry_timeout;
if (timeout != 0 && server->port_status == PORT_NOT_WORKING) {
gettimeofday(&tv, NULL);
if (STATUS_DIFF(server, tv) > timeout) {
@@ -467,7 +472,8 @@ static void fo_resolve_service_done(struct tevent_req *subreq);
struct tevent_req *
fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- struct resolv_ctx *resolv, struct fo_service *service)
+ struct resolv_ctx *resolv, struct fo_ctx *ctx,
+ struct fo_service *service)
{
int ret;
struct fo_server *server;
@@ -498,7 +504,8 @@ fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
switch (get_server_status(server)) {
case SERVER_NAME_NOT_RESOLVED: /* Request name resolution. */
subreq = resolv_gethostbyname_send(server->common, ev, resolv,
- server->common->name);
+ server->common->name,
+ ctx->opts->family_order);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index f11848321..ffcd06877 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -28,9 +28,10 @@
#include <stdbool.h>
#include <talloc.h>
+#include "resolv/async_resolv.h"
+
/* Some forward declarations that don't have to do anything with fail over. */
struct hostent;
-struct resolv_ctx;
struct tevent_context;
struct tevent_req;
@@ -53,12 +54,26 @@ struct fo_service;
struct fo_server;
/*
- * Create a new fail over context. The 'retry_timeout' argument specifies the
+ * Failover settings.
+ *
+ * The 'retry_timeout' member specifies the
* duration in seconds of how long a server or port will be considered
* non-working after being marked as such.
+ *
+ * The family_order member specifies the order of address families to
+ * try when looking up the service.
+ */
+struct fo_options {
+ time_t retry_timeout;
+ enum restrict_family family_order;
+};
+
+/*
+ * Create a new fail over context based on options passed in the
+ * opts parameter
*/
struct fo_ctx *fo_context_init(TALLOC_CTX *mem_ctx,
- time_t retry_timeout);
+ struct fo_options *opts);
/*
* Create a new service structure for 'ctx', saving it to the location pointed
@@ -94,6 +109,7 @@ int fo_add_server(struct fo_service *service,
struct tevent_req *fo_resolve_service_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *resolv,
+ struct fo_ctx *ctx,
struct fo_service *service);
int fo_resolve_service_recv(struct tevent_req *req,
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index 70d8d11e4..d0c5b7a33 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -459,6 +459,8 @@ struct gethostbyname_state {
/* Part of the query. */
const char *name;
int family;
+ /* In which order to use IPv4, or v6 */
+ enum restrict_family family_order;
/* These are returned by ares. The hostent struct will be freed
* when the user callback returns. */
struct hostent *hostent;
@@ -472,14 +474,13 @@ ares_gethostbyname_wakeup(struct tevent_req *req);
struct tevent_req *
resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
- struct resolv_ctx *ctx, const char *name)
+ struct resolv_ctx *ctx, const char *name,
+ enum restrict_family family_order)
{
struct tevent_req *req, *subreq;
struct gethostbyname_state *state;
struct timeval tv = { 0, 0 };
- DEBUG(4, ("Trying to resolve A record of '%s'\n", name));
-
if (ctx->channel == NULL) {
DEBUG(1, ("Invalid ares channel - this is likely a bug\n"));
return NULL;
@@ -491,11 +492,16 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
state->resolv_ctx = ctx;
state->name = name;
- state->family = AF_INET;
state->hostent = NULL;
state->status = 0;
state->timeouts = 0;
state->retrying = 0;
+ state->family_order = family_order;
+ state->family = (family_order < IPV6_ONLY) ? AF_INET : AF_INET6;
+
+ DEBUG(4, ("Trying to resolve %s record of '%s'\n",
+ state->family == AF_INET ? "A" : "AAAA",
+ state->name));
/* We need to have a wrapper around ares_gethostbyname(), because
* ares_gethostbyname() can in some cases call it's callback immediately.
@@ -513,7 +519,7 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
}
static void
-resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent);
+resolv_gethostbyname_next_done(void *arg, int status, int timeouts, struct hostent *hostent);
static void
resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *hostent)
@@ -545,14 +551,22 @@ resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *h
if (status != ARES_SUCCESS) {
if (status == ARES_ENOTFOUND || status == ARES_ENODATA) {
- /* IPv4 failure. Try IPv6 */
- state->family = AF_INET6;
+ if (state->family_order == IPV4_FIRST) {
+ state->family = AF_INET6;
+ } else if (state->family_order == IPV6_FIRST) {
+ state->family = AF_INET;
+ } else {
+ tevent_req_error(req, return_code(status));
+ return;
+ }
+
state->retrying = 0;
state->timeouts = 0;
- DEBUG(4, ("Trying to resolve AAAA record of '%s'\n",
+ DEBUG(4, ("Trying to resolve %s record of '%s'\n",
+ state->family == AF_INET ? "A" : "AAAA",
state->name));
ares_gethostbyname(state->resolv_ctx->channel, state->name,
- state->family, resolv_gethostbyname6_done,
+ state->family, resolv_gethostbyname_next_done,
req);
return;
}
@@ -568,7 +582,7 @@ resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *h
}
static void
-resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *hostent)
+resolv_gethostbyname_next_done(void *arg, int status, int timeouts, struct hostent *hostent)
{
struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
@@ -576,7 +590,7 @@ resolv_gethostbyname6_done(void *arg, int status, int timeouts, struct hostent *
if (state->retrying == 0 && status == ARES_EDESTRUCTION) {
state->retrying = 1;
ares_gethostbyname(state->resolv_ctx->channel, state->name,
- state->family, resolv_gethostbyname6_done, req);
+ state->family, resolv_gethostbyname_next_done, req);
return;
}
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index 2ba6449b2..5f87f12e3 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -57,10 +57,18 @@ struct hostent *resolv_copy_hostent(TALLOC_CTX *mem_ctx,
struct hostent *src);
/** Get host by name **/
+enum restrict_family {
+ IPV4_ONLY,
+ IPV4_FIRST,
+ IPV6_ONLY,
+ IPV6_FIRST
+};
+
struct tevent_req *resolv_gethostbyname_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *ctx,
- const char *name);
+ const char *name,
+ enum restrict_family family_order);
int resolv_gethostbyname_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c
index e794f03b5..4e97a1e2b 100644
--- a/src/tests/fail_over-tests.c
+++ b/src/tests/fail_over-tests.c
@@ -60,6 +60,7 @@ static struct test_ctx *
setup_test(void)
{
struct test_ctx *ctx;
+ struct fo_options fopts;
int ret;
ctx = talloc_zero(global_talloc_context, struct test_ctx);
@@ -77,7 +78,10 @@ setup_test(void)
fail("Could not init resolv context");
}
- ctx->fo_ctx = fo_context_init(ctx, 5 * 60);
+ fopts.retry_timeout = 30;
+ fopts.family_order = IPV4_FIRST;
+
+ ctx->fo_ctx = fo_context_init(ctx, &fopts);
if (ctx->fo_ctx == NULL) {
talloc_free(ctx);
fail("Could not init fail over context");
@@ -198,7 +202,9 @@ _get_request(struct test_ctx *test_ctx, struct fo_service *service,
task->location = location;
test_ctx->tasks++;
- req = fo_resolve_service_send(test_ctx, test_ctx->ev, test_ctx->resolv, service);
+ req = fo_resolve_service_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv,
+ test_ctx->fo_ctx, service);
fail_if(req == NULL, "%s: fo_resolve_service_send() failed", location);
tevent_req_set_callback(req, test_resolve_service_callback, task);
diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c
index 04b9e2e76..e93d0f196 100644
--- a/src/tests/resolv-tests.c
+++ b/src/tests/resolv-tests.c
@@ -178,7 +178,8 @@ START_TEST(test_resolv_localhost)
}
check_leaks_push(test_ctx);
- req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname);
+ req = resolv_gethostbyname_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv, hostname, IPV4_FIRST);
DEBUG(7, ("Sent resolv_gethostbyname\n"));
if (req == NULL) {
ret = ENOMEM;
@@ -232,7 +233,8 @@ START_TEST(test_resolv_negative)
}
check_leaks_push(test_ctx);
- req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname);
+ req = resolv_gethostbyname_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv, hostname, IPV4_FIRST);
DEBUG(7, ("Sent resolv_gethostbyname\n"));
if (req == NULL) {
ret = ENOMEM;
@@ -292,6 +294,9 @@ static void test_internet(struct tevent_req *req)
srvptr->host));
}
break;
+ default:
+ recv_status = EINVAL;
+ break;
}
talloc_zfree(req);
fail_if(recv_status != EOK, "The recv function failed: %d", recv_status);
@@ -322,7 +327,8 @@ START_TEST(test_resolv_internet)
test_ctx->tested_function = TESTING_HOSTNAME;
check_leaks_push(test_ctx);
- req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname);
+ req = resolv_gethostbyname_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv, hostname, IPV4_FIRST);
DEBUG(7, ("Sent resolv_gethostbyname\n"));
if (req == NULL) {
ret = ENOMEM;
@@ -425,7 +431,8 @@ START_TEST(test_resolv_free_context)
return;
}
- req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname);
+ req = resolv_gethostbyname_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv, hostname, IPV4_FIRST);
DEBUG(7, ("Sent resolv_gethostbyname\n"));
if (req == NULL) {
fail("Error calling resolv_gethostbyname_send");
@@ -484,7 +491,8 @@ START_TEST(test_resolv_free_req)
}
check_leaks_push(test_ctx);
- req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname);
+ req = resolv_gethostbyname_send(test_ctx, test_ctx->ev,
+ test_ctx->resolv, hostname, IPV4_FIRST);
DEBUG(7, ("Sent resolv_gethostbyname\n"));
if (req == NULL) {
fail("Error calling resolv_gethostbyname_send");