summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-12-12 17:10:25 +0100
committerJakub Hrozek <jhrozek@redhat.com>2015-06-24 12:47:28 +0200
commit33b5fe170b5cfa7e0f6ce4defbac943843c4bebd (patch)
treee641ab9d4aac1486951c16196327da6b836847fd
parenta0e03d61a8664a151c7acb928d5bf3f8d9d3d348 (diff)
downloadsssd-33b5fe170b5cfa7e0f6ce4defbac943843c4bebd.tar.gz
sssd-33b5fe170b5cfa7e0f6ce4defbac943843c4bebd.tar.xz
sssd-33b5fe170b5cfa7e0f6ce4defbac943843c4bebd.zip
RESOLV: Add an internal function to read TTL from a DNS packet
Related: https://fedorahosted.org/sssd/ticket/1884 Adds an internal resolver function that reads the TTL for SRV records as specified by RFC-2181. Several internal c-ares definitions are used until c-ares contains a function that exposes all this information via a parsing function. Reviewed-by: Pavel Březina <pbrezina@redhat.com> (cherry picked from commit bf54fbed126ec3d459af40ea370ffadacd31c76d)
-rw-r--r--src/providers/fail_over.c2
-rw-r--r--src/resolv/async_resolv.c113
-rw-r--r--src/resolv/async_resolv.h3
-rw-r--r--src/tests/resolv-tests.c2
4 files changed, 116 insertions, 4 deletions
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 2b47e55cb..eb16d9319 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -1082,7 +1082,7 @@ resolve_srv_done(struct tevent_req *subreq)
int resolv_status;
ret = resolv_getsrv_recv(state, subreq,
- &resolv_status, NULL, &reply_list);
+ &resolv_status, NULL, &reply_list, NULL);
talloc_free(subreq);
if (ret != EOK) {
DEBUG(1, ("SRV query failed: [%s]\n",
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index dcbdd5188..c7f04d1b6 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -62,7 +62,21 @@
#endif
#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
+
+/*
+ * Macro DNS__32BIT reads a network long (32 bit) given in network
+ * byte order, and returns its value as an unsigned int. Copied
+ * from c-ares source code.
+ */
+#define DNS__32BIT(p) ((unsigned int) \
+ (((unsigned int)((unsigned char)(p)[0]) << 24U) | \
+ ((unsigned int)((unsigned char)(p)[1]) << 16U) | \
+ ((unsigned int)((unsigned char)(p)[2]) << 8U) | \
+ ((unsigned int)((unsigned char)(p)[3]))))
+
#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
+#define DNS_RR_LEN(r) DNS__16BIT((r) + 8)
+#define DNS_RR_TTL(r) DNS__32BIT((r) + 4)
enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL };
@@ -1402,6 +1416,7 @@ struct getsrv_state {
/* parsed data returned by ares */
struct ares_srv_reply *reply_list;
+ uint32_t ttl;
int status;
int timeouts;
int retrying;
@@ -1432,6 +1447,7 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
state->resolv_ctx = ctx;
state->query = query;
state->reply_list = NULL;
+ state->ttl = 0;
state->status = 0;
state->timeouts = 0;
state->retrying = 0;
@@ -1448,12 +1464,99 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
return req;
}
+/*
+ * Implemented based on http://tools.ietf.org/html/rfc2181#section-5
+ *
+ * Especially:
+ * 5.2. TTLs of RRs in an RRSet
+ * Consequently the use of differing TTLs in an RRSet is hereby
+ * deprecated, the TTLs of all RRs in an RRSet must be the same.
+ * ...
+ * Should an authoritative source send such a malformed RRSet, the
+ * client should treat the RRs for all purposes as if all TTLs in the
+ * RRSet had been set to the value of the lowest TTL in the RRSet.
+ *
+ * On success, returns true and sets the TTL in the _ttl parameter. On
+ * failure, returns false and _ttl is undefined.
+ */
+static bool
+resolv_get_ttl(const unsigned char *abuf, const int alen, uint32_t *_ttl)
+{
+ const unsigned char *aptr;
+ int ret;
+ char *name = NULL;
+ long len;
+ uint32_t ttl = 0;
+ uint32_t rr_ttl;
+ unsigned int rr_len;
+ unsigned int ancount;
+ unsigned int i;
+
+ /* Read the number of RRs and then skip past the header */
+ if (alen < NS_HFIXEDSZ) {
+ return false;
+ }
+
+ ancount = DNS_HEADER_ANCOUNT(abuf);
+ if (ancount == 0) {
+ return false;
+ }
+
+ aptr = abuf + NS_HFIXEDSZ;
+
+ /* We only care about len from the question data,
+ * so that we can move past hostname */
+ ret = ares_expand_name(aptr, abuf, alen, &name, &len);
+ ares_free_string(name);
+ if (ret != ARES_SUCCESS) {
+ return false;
+ }
+
+ /* Skip past the question */
+ aptr += len + NS_QFIXEDSZ;
+ if (aptr > abuf + alen) {
+ return false;
+ }
+
+ /* Examine each RR in turn and read the lowest TTL */
+ for (i = 0; i < ancount; i++) {
+ /* Decode the RR up to the data field. */
+ ret = ares_expand_name(aptr, abuf, alen, &name, &len);
+ ares_free_string(name);
+ if (ret != ARES_SUCCESS) {
+ return false;
+ }
+
+ aptr += len;
+ if (aptr + NS_RRFIXEDSZ > abuf + alen) {
+ return false;
+ }
+
+ rr_len = DNS_RR_LEN(aptr);
+ rr_ttl = DNS_RR_TTL(aptr);
+ if (aptr + rr_len > abuf + alen) {
+ return false;
+ }
+ aptr += NS_RRFIXEDSZ + rr_len;
+
+ if (ttl > 0) {
+ ttl = MIN(ttl, rr_ttl);
+ } else {
+ ttl = rr_ttl; /* special-case for first TTL */
+ }
+ }
+
+ *_ttl = ttl;
+ return true;
+}
+
static void
resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
{
struct tevent_req *req = talloc_get_type(arg, struct tevent_req);
struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
int ret;
+ bool ok;
struct ares_srv_reply *reply_list;
if (state->retrying == 0 && status == ARES_EDESTRUCTION
@@ -1485,6 +1588,10 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int
goto fail;
}
state->reply_list = reply_list;
+ ok = resolv_get_ttl(abuf, alen, &state->ttl);
+ if (ok == false) {
+ state->ttl = RESOLV_DEFAULT_TTL;
+ }
tevent_req_done(req);
return;
@@ -1496,7 +1603,8 @@ fail:
int
resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
- int *timeouts, struct ares_srv_reply **reply_list)
+ int *timeouts, struct ares_srv_reply **reply_list,
+ uint32_t *ttl)
{
struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
@@ -1506,6 +1614,9 @@ resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
*timeouts = state->timeouts;
if (reply_list)
*reply_list = talloc_steal(mem_ctx, state->reply_list);
+ if (ttl) {
+ *ttl = state->ttl;
+ }
TEVENT_REQ_RETURN_ON_ERROR(req);
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index b5547e5df..94ac92bb7 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -128,7 +128,8 @@ int resolv_getsrv_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
int *status,
int *timeouts,
- struct ares_srv_reply **reply_list);
+ struct ares_srv_reply **reply_list,
+ uint32_t *ttl);
/* This is an implementation of section "Usage rules" of RFC 2782 */
int
diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c
index 5c1a487f4..c0ef9d680 100644
--- a/src/tests/resolv-tests.c
+++ b/src/tests/resolv-tests.c
@@ -391,7 +391,7 @@ static void test_internet(struct tevent_req *req)
break;
case TESTING_SRV:
recv_status = resolv_getsrv_recv(tmp_ctx, req, &status, NULL,
- &srv_replies);
+ &srv_replies, NULL);
test_ctx->error = (srv_replies == NULL) ? ENOENT : EOK;
for (srvptr = srv_replies; srvptr != NULL; srvptr = srvptr->next) {
DEBUG(2, ("SRV Record: %d %d %d %s\n", srvptr->weight,