Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC 7314 expire option #379

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions difffile.c
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,11 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint32_t serialno,
uint32_t seq_nr, uint32_t seq_total,
int* is_axfr, int* delete_mode, int* rr_count,
struct zone* zone, int* bytes,
int* softfail, struct ixfr_store* ixfr_store)
int* softfail, struct ixfr_store* ixfr_store,
char* expire_option_seen, uint32_t* expire_option_value)
{
uint32_t msglen, checklen, pkttype;
int qcount, ancount;
int qcount, ancount, nscount, arcount;
buffer_type* packet;
region_type* region;

Expand Down Expand Up @@ -1054,6 +1055,8 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint32_t serialno,
authority section RRs are skipped */
qcount = QDCOUNT(packet);
ancount = ANCOUNT(packet);
nscount = NSCOUNT(packet);
arcount = ARCOUNT(packet);
buffer_skip(packet, QHEADERSZ);
/* qcount should be 0 or 1 really, ancount limited by 64k packet */
if(qcount > 64 || ancount > 65530) {
Expand Down Expand Up @@ -1245,6 +1248,15 @@ apply_ixfr(nsd_type* nsd, FILE *in, uint32_t serialno,
}
}
}
if(arcount > 0) {
for(; nscount > 0; nscount--) {
if(!packet_skip_rr(packet, 0))
break;
}
if(nscount == 0)
*expire_option_seen = process_expire_option(
packet, expire_option_value);
}
region_destroy(region);
return 1;
}
Expand Down Expand Up @@ -1356,6 +1368,8 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in,
{
int is_axfr=0, delete_mode=0, rr_count=0, softfail=0;
struct ixfr_store* ixfr_store = NULL, ixfr_store_mem;
char expire_option_seen = 0;
uint32_t expire_option_value = 0;

DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", zone_buf));
if(zone_is_ixfr_enabled(zone))
Expand All @@ -1367,7 +1381,8 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in,
ret = apply_ixfr(nsd, in, new_serial,
i, num_parts, &is_axfr, &delete_mode,
&rr_count, zone,
&num_bytes, &softfail, ixfr_store);
&num_bytes, &softfail, ixfr_store,
&expire_option_seen, &expire_option_value);
if(ret == 0) {
log_msg(LOG_ERR, "bad ixfr packet part %d in diff file for %s", (int)i, zone_buf);
diff_update_commit(
Expand All @@ -1392,6 +1407,20 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in,
zone->is_checked = (committed == DIFF_VERIFIED);
zone->mtime.tv_sec = time_end_0;
zone->mtime.tv_nsec = time_end_1*1000;
if(expire_option_seen && zone->soa_rrset && zone->soa_rrset->rrs
&& zone->soa_rrset->rrs->type == TYPE_SOA
&& zone->soa_rrset->rrs->rdata_count == 7) {
uint32_t expire_value;

memcpy(&expire_value, rdata_atom_data(
zone->soa_rrset->rrs->rdatas[5])
, sizeof(uint32_t));
expire_value = ntohl(expire_value);
if(expire_option_value < expire_value) {
zone->mtime.tv_sec -=
(expire_value - expire_option_value);
}
}
if(zone->logstr)
region_recycle(nsd->db->region, zone->logstr,
strlen(zone->logstr)+1);
Expand Down Expand Up @@ -1851,6 +1880,24 @@ task_new_apply_xfr(udb_base* udb, udb_ptr* last, const dname_type* dname,
return 1;
}

void
task_new_refresh(udb_base* udb, udb_ptr* last, const dname_type* dname,
uint64_t acquired)
{
udb_ptr e;

DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task refresh %s %d",
dname_to_string(dname, 0), (int)acquired));
if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d)
+dname_total_size(dname), dname)) {
log_msg(LOG_ERR, "tasklist: out of space, cannot add refresh");
return;
}
TASKLIST(&e)->task_type = task_refresh;
TASKLIST(&e)->yesno = acquired;
udb_ptr_unlink(&e, udb);
}

void
task_process_expire(namedb_type* db, struct task_list_d* task)
{
Expand Down Expand Up @@ -2133,6 +2180,20 @@ task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
fclose(df);
}

static void
task_process_refresh(struct nsd* nsd, struct task_list_d* task)
{
zone_type* zone;

DEBUG(DEBUG_IPC,1, (LOG_INFO, "refresh task %s %d", dname_to_string(
task->zname, NULL), (int)task->yesno));
zone = namedb_find_zone(nsd->db, task->zname);
if(!zone)
return;

zone->mtime.tv_sec = task->yesno;
zone->mtime.tv_nsec = 0;
}

void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
udb_ptr* task)
Expand Down Expand Up @@ -2188,6 +2249,9 @@ void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
case task_activate_cookie_secret:
task_process_activate_cookie_secret(nsd, TASKLIST(task));
break;
case task_refresh:
task_process_refresh(nsd, TASKLIST(task));
break;
default:
log_msg(LOG_WARNING, "unhandled task in reload type %d",
(int)TASKLIST(task)->task_type);
Expand Down
4 changes: 4 additions & 0 deletions difffile.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ struct task_list_d {
task_drop_cookie_secret,
/** make staging cookie secret active */
task_activate_cookie_secret,
/** refresh a zone (set mtime to value of yesno) */
task_refresh,
} task_type;
uint32_t size; /* size of this struct */

Expand Down Expand Up @@ -157,6 +159,8 @@ void task_new_drop_cookie_secret(udb_base* udb, udb_ptr* last);
void task_new_activate_cookie_secret(udb_base* udb, udb_ptr* last);
int task_new_apply_xfr(udb_base* udb, udb_ptr* last, const dname_type* zone,
uint32_t old_serial, uint32_t new_serial, uint64_t filenumber);
void task_new_refresh(udb_base* udb, udb_ptr* last, const dname_type* zone,
uint64_t acquired);
void task_process_in_reload(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
udb_ptr* task);
void task_process_expire(namedb_type* db, struct task_list_d* task);
Expand Down
53 changes: 53 additions & 0 deletions edns.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ edns_init_record(edns_record_type *edns)
edns->ede = -1; /* -1 means no Extended DNS Error */
edns->ede_text = NULL;
edns->ede_text_len = 0;
edns->expire_option_seen = 0;
edns->expire_option_value = 0;
}

/** handle a single edns option in the query */
Expand All @@ -98,6 +100,13 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
buffer_skip(packet, optlen);
}
break;
case EXPIRE_CODE:
edns->expire_option_seen = 1;
/* we have to check optlen, and move the buffer along */
buffer_skip(packet, optlen);
/* in the reply we need space for optcode+optlen+time*/
edns->opt_reserved_space += OPT_HDR + sizeof(uint32_t);
break;
case COOKIE_CODE:
/* Cookies enabled? */
if(nsd->do_answer_cookie) {
Expand Down Expand Up @@ -339,3 +348,47 @@ void cookie_create(query_type *q, struct nsd* nsd, uint32_t *now_p)
memcpy(q->edns.cookie + 16, hash, 8);
}

int
process_expire_option(buffer_type* packet, uint32_t* expire_option_value)
{
size_t mempos = buffer_position(packet);
uint16_t rdlen;

/* len(dname) + len(type) + len(class) + len(ttl) + len(rdlen) =
* 1 + 2 + 2 + 4 + 2 = 11
*
* len(option_code) + len(option_len) + len(expire_option) =
* 2 + 2 + 4 = 8
*/
if(!buffer_available(packet, 19)
|| buffer_read_u8(packet) != 0
|| buffer_read_u16(packet) != TYPE_OPT) {
buffer_set_position(packet, mempos);
return 0;
}
/* skip UDP payload, rcode, version, flags (or class and ttl) */
buffer_skip(packet, 6);
rdlen = buffer_read_u16(packet);
while(rdlen >= 8) {
uint16_t option_code, option_len;

if(!buffer_available(packet, 8))
break;
option_code = buffer_read_u16(packet);
option_len = buffer_read_u16(packet);
rdlen -= 4;
if(option_code == EXPIRE_CODE && option_len == 4) {
*expire_option_value = buffer_read_u32(packet);
return 1;
}
/* skip option */
if (option_len > rdlen
|| !buffer_available(packet, option_len))
break;
buffer_skip(packet, option_len);
rdlen -= option_len;
}
buffer_set_position(packet, mempos);
return 0;
}

8 changes: 8 additions & 0 deletions edns.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct query;
#define OPT_RDATA 2 /* holds the rdata length comes after OPT_LEN */
#define OPT_HDR 4U /* NSID opt header length */
#define NSID_CODE 3 /* nsid option code */
#define EXPIRE_CODE 9 /* RFC 7314 */
#define COOKIE_CODE 10 /* COOKIE option code */
#define EDE_CODE 15 /* Extended DNS Errors option code */
#define DNSSEC_OK_MASK 0x8000U /* do bit mask */
Expand Down Expand Up @@ -65,6 +66,8 @@ struct edns_record
int ede; /* RFC 8914 - Extended DNS Errors */
char* ede_text; /* RFC 8914 - Extended DNS Errors text*/
uint16_t ede_text_len;
char expire_option_seen;
uint32_t expire_option_value;
};
typedef struct edns_record edns_record_type;

Expand Down Expand Up @@ -103,4 +106,9 @@ void edns_init_nsid(edns_data_type *data, uint16_t nsid_len);
void cookie_verify(struct query *q, struct nsd* nsd, uint32_t *now_p);
void cookie_create(struct query *q, struct nsd* nsd, uint32_t *now_p);

/* packet position must point to the additional section (and the OPT RR)
* Returns 1 when the OPT RR contains an EXPIRE option, 0 otherwise.
* When an EXPIRE option is seen, expire_option_value is filled with the value
*/
int process_expire_option(buffer_type* packet, uint32_t* expire_option_value);
#endif /* EDNS_H */
45 changes: 45 additions & 0 deletions query.c
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,8 @@ query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p)
/* fill with NULLs */
buffer_write(q->packet, edns->rdata_none, OPT_RDATA);
} else {
zone_type* expire_zone = NULL;

/* rdata length */
buffer_write_u16(q->packet, q->edns.opt_reserved_space);
/* edns options */
Expand All @@ -1802,6 +1804,49 @@ query_add_optional(query_type *q, nsd_type *nsd, uint32_t *now_p)
cookie_create(q, nsd, now_p);
buffer_write(q->packet, q->edns.cookie, 24);
}
if(q->edns.expire_option_seen) {
if(q->qtype == TYPE_AXFR)
expire_zone = q->axfr_zone;
else
expire_zone = q->zone;
}
/* Append EXPIRE (RFC 7314) option if needed */
if(q->edns.expire_option_seen && expire_zone
&& expire_zone->soa_rrset && expire_zone->soa_rrset->rrs
&& expire_zone->soa_rrset->rrs->type == TYPE_SOA
&& expire_zone->soa_rrset->rrs->rdata_count == 7) {
uint32_t expire_value;

/* OPTION-CODE */
buffer_write_u16(q->packet, EXPIRE_CODE);
/* OPTION-LENGTH */
buffer_write_u16(q->packet, sizeof(uint32_t));
memcpy(&expire_value, rdata_atom_data(
expire_zone->soa_rrset->rrs->rdatas[5])
, sizeof(uint32_t));
expire_value = ntohl(expire_value);
if(zone_is_slave(expire_zone->opts)) {
uint32_t now = *now_p ? *now_p
: (*now_p = (uint32_t)time(NULL));

if(expire_zone->mtime.tv_sec < now) {
uint32_t passed = now - expire_zone->mtime.tv_sec;
if(passed < expire_value)
expire_value -= passed;
else
expire_value = 0;
}
}
buffer_write_u32(q->packet, expire_value);
} else if(q->edns.expire_option_seen) {
/* OPTION-CODE */
buffer_write_u16(q->packet, EXPIRE_CODE);
/* OPTION-LENGTH */
buffer_write_u16(q->packet, sizeof(uint32_t));
/* OPTION-VALE */
buffer_write_u32(q->packet, 0);
}

/* Append Extended DNS Error (RFC8914) option if needed */
if (q->edns.ede >= 0) { /* < 0 means no EDE */
/* OPTION-CODE */
Expand Down
21 changes: 21 additions & 0 deletions xfrd-tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,25 @@ xfrd_acl_sockaddr_frm(acl_options_type* acl, struct sockaddr_in *frm)
#endif /* INET6 */
}

void
xfrd_write_request_edns_expire_option(struct buffer* packet, uint16_t sz)
{
/* TODO: Track whether a transfer failed with this option (for an
* upstream), and then retry without the option.
*/
/* . OPT */
ARCOUNT_SET(packet, 1);
buffer_write_u8(packet, 0);
buffer_write_u16(packet, TYPE_OPT);
buffer_write_u16(packet, sz);
buffer_write_u8(packet, 0); /* rcode */
buffer_write_u8(packet, 0); /* version */
buffer_write_u16(packet, 0x8000); /* DO flag */
buffer_write_u16(packet, 2 * sizeof(uint16_t)); /* rdlength */
buffer_write_u16(packet, EXPIRE_CODE); /* option code */
buffer_write_u16(packet, 0); /* option length */
}

void
xfrd_write_soa_buffer(struct buffer* packet,
const dname_type* apex, struct xfrd_soa* soa, int apex_compress)
Expand Down Expand Up @@ -1022,6 +1041,7 @@ xfrd_tcp_setup_write_packet(struct xfrd_tcp_pipeline* tp, xfrd_zone_type* zone)
xfrd_setup_packet(tcp->packet, TYPE_AXFR, CLASS_IN, zone->apex,
zone->query_id, NULL);
xfrd_prepare_zone_xfr(zone, TYPE_AXFR);
xfrd_write_request_edns_expire_option(tcp->packet, 65535);
} else {
int apex_compress = 0;
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "request incremental zone "
Expand All @@ -1034,6 +1054,7 @@ xfrd_tcp_setup_write_packet(struct xfrd_tcp_pipeline* tp, xfrd_zone_type* zone)
NSCOUNT_SET(tcp->packet, 1);
xfrd_write_soa_buffer(tcp->packet, zone->apex, &zone->soa_disk,
apex_compress);
xfrd_write_request_edns_expire_option(tcp->packet, 65535);
}
if(zone->master->key_options && zone->master->key_options->tsig_key) {
xfrd_tsig_sign_request(
Expand Down
2 changes: 2 additions & 0 deletions xfrd-tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ void xfrd_setup_packet(struct buffer* packet,
/* write soa in network format to the packet buffer */
void xfrd_write_soa_buffer(struct buffer* packet,
const struct dname* apex, struct xfrd_soa* soa, int apex_compress);
/* write . OPT RR with empty EXPIRE option */
void xfrd_write_request_edns_expire_option(struct buffer* packet, uint16_t sz);
/* use acl address to setup sockaddr struct, returns length of addr. */
socklen_t xfrd_acl_sockaddr_to(struct acl_options* acl,
#ifdef INET6
Expand Down
Loading
Loading