From 32124265742d2b32ffc4ea6d3c31470217cf8f92 Mon Sep 17 00:00:00 2001 From: "R. Christian McDonald" Date: Fri, 7 Jun 2024 12:05:24 -0400 Subject: [PATCH] Overload `local_data_remove` to support removing specific records Here we overload the `local_data_remove` control command to support deleting specific records. Curently, this command deletes all records for a given zone. The modification works by attempting to parse the command argument first as a complete record and then as just a domain name, if the first attempt failed. This preserves the command's behavior, while also supporting removing specific records from the zone tree. Signed-off-by: R. Christian McDonald --- daemon/remote.c | 92 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 341e56054..c88ea7e39 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1,6 +1,7 @@ /* * daemon/remote.c - remote control for the unbound daemon. * + * Copyright (c) 2024, Rubicon Communications, LLC ("Netgate"). All rights reserved. * Copyright (c) 2008, NLnet Labs. All rights reserved. * * This software is open source. @@ -1400,18 +1401,95 @@ do_datas_add(struct daemon_remote* rc, RES* ssl, struct worker* worker) (void)ssl_printf(ssl, "added %d datas\n", num); } +static int +perform_data_remove_rr(RES* ssl, struct local_zones* local_zones, + uint8_t* rr, size_t len, size_t dname_len, char *arg) +{ + uint16_t rr_class, rr_type; + int labs, s; + struct local_zone* z; + struct local_data* ld; + uint8_t *rdata; + size_t rdata_len, index; + struct packed_rrset_data* d; + + rdata = sldns_wirerr_get_rdatawl(rr, len, dname_len); + rdata_len = ((size_t)sldns_wirerr_get_rdatalen(rr, len, dname_len))+2; + + labs = dname_count_labels(rr); + + rr_class = sldns_wirerr_get_class(rr, len, dname_len); + rr_type = sldns_wirerr_get_type(rr, len, dname_len); + + z = local_zones_lookup(local_zones, rr, dname_len, + labs, rr_class, rr_type); + if (!z) { + ssl_printf(ssl, "error no zone for rr %s\n", arg); + return 0; + } + + ld = local_zone_find_data(z, rr, dname_len, labs); + if (!ld) { + ssl_printf(ssl, "error no local data for rr %s\n", arg); + return 0; + } + + struct local_rrset* prev=NULL, *p=ld->rrsets; + while (p && ntohs(p->rrset->rk.type) != rr_type) { + prev = p; + p = p->next; + } + + if (!p) { + ssl_printf(ssl, "error no rrset for rr %s\n", arg); + return 0; + } + + d = (struct packed_rrset_data*)p->rrset->entry.data; + if (!packed_rrset_find_rr(d, rdata, rdata_len, &index)) { + ssl_printf(ssl, "error rr %s not found in rrset\n", arg); + return 0; + } + + if (!local_rrset_remove_rr(d, index)) { + ssl_printf(ssl, "error unable to delete rr %s\n", arg); + return 0; + } + + return 1; +} + /** Remove RR data */ static int perform_data_remove(RES* ssl, struct local_zones* zones, char* arg) { - uint8_t* nm; - int nmlabs; - size_t nmlen; - if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) + uint8_t rr[LDNS_RR_BUF_SIZE], *nm; + size_t len = sizeof(rr); + int status, nmlabs; + size_t nmlen, dname_len; + + /* try to parse as a rr first */ + status = sldns_str2wire_rr_buf(arg, rr, &len, &dname_len, 3600, + NULL, 0, NULL, 0); + + /* try to parse as a domain name second */ + if (status != 0) { + if (parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) { + local_zones_del_data(zones, nm, + nmlen, nmlabs, LDNS_RR_CLASS_IN); + free(nm); + return 1; + } + ssl_printf(ssl, "error cannot parse rr %s at %d: %s\n", arg, + LDNS_WIREPARSE_OFFSET(status), + sldns_get_errorstr_parse(status)); return 0; - local_zones_del_data(zones, nm, - nmlen, nmlabs, LDNS_RR_CLASS_IN); - free(nm); + } + + /* handle the rr case */ + if (!perform_data_remove_rr(ssl, zones, rr, len, dname_len, arg)) + return 0; + return 1; }