diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/providers/fail_over.c | 2 | ||||
-rw-r--r-- | src/resolv/async_resolv.c | 113 | ||||
-rw-r--r-- | src/resolv/async_resolv.h | 3 | ||||
-rw-r--r-- | src/tests/resolv-tests.c | 2 |
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, |