diff --git a/.codespellignore b/.codespellignore deleted file mode 100644 index 5530bfc9..00000000 --- a/.codespellignore +++ /dev/null @@ -1,4 +0,0 @@ -ede -edn -nd -tre \ No newline at end of file diff --git a/.github/.codespellignore b/.github/.codespellignore new file mode 100644 index 00000000..48b6bd42 --- /dev/null +++ b/.github/.codespellignore @@ -0,0 +1,4 @@ +ssudo +tre +ede +nd diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9da5551..848960a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.0 - name: "Calculate required variables" id: variables @@ -53,7 +53,7 @@ jobs: needs: smoke-tests - container: ghcr.io/pi-hole/ftl-build:v1.25-${{ matrix.arch }} + container: ghcr.io/pi-hole/ftl-build:v1.26-${{ matrix.arch }} strategy: fail-fast: false @@ -81,6 +81,8 @@ jobs: bin_name: pihole-FTL-armv8-linux-gnueabihf - arch: aarch64 bin_name: pihole-FTL-aarch64-linux-gnu + - arch: riscv64 + bin_name: pihole-FTL-riscv64-linux-gnu env: CI_ARCH: ${{ matrix.arch }}${{ matrix.arch_extra }} @@ -88,7 +90,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.0 - name: "Fix ownership of repository" run: chown -R root . @@ -142,7 +144,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.0 - name: Get Binaries and documentation built in previous jobs uses: actions/download-artifact@v3.0.2 diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7b0553a6..a04908f3 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -10,10 +10,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.0 - name: Spell-Checking uses: codespell-project/actions-codespell@master with: - ignore_words_file: .codespellignore - skip: ./src/database/sqlite3.c,./src/database/sqlite3.h,./src/database/shell.c,./src/lua,./src/dnsmasq,./src/tre-regex + ignore_words_file: .github/.codespellignore + skip: ./src/database/sqlite3.c,./src/database/sqlite3.h,./src/database/shell.c,./src/lua,./src/dnsmasq,./src/tre-regex,./.git,./test/libs diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9c3c9829..e183ce17 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: issues: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/stale_pr.yml b/.github/workflows/stale_pr.yml index 7c753d7d..e9230b2c 100644 --- a/.github/workflows/stale_pr.yml +++ b/.github/workflows/stale_pr.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # Do not automatically mark PR/issue as stale diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index 89b6323f..79111ddd 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.0 - name: Opening pull request run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' env: diff --git a/CMakeLists.txt b/CMakeLists.txt index d468618a..6bf4dedd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,6 @@ cmake_minimum_required(VERSION 2.8.12) project(PIHOLE_FTL C) -set(DNSMASQ_VERSION pi-hole-v2.89) +set(DNSMASQ_VERSION pi-hole-v2.89-9461807) add_subdirectory(src) diff --git a/src/api/docs/content/specs/info.yaml b/src/api/docs/content/specs/info.yaml index 9745d95a..6a222776 100644 --- a/src/api/docs/content/specs/info.yaml +++ b/src/api/docs/content/specs/info.yaml @@ -926,9 +926,6 @@ components: cname: type: integer description: Number of valid CNAME entries - srv: - type: integer - description: Number of valid SRV entries ds: type: integer description: Number of valid DS entries @@ -1044,7 +1041,6 @@ components: a: 212 aaaa: 61 cname: 14 - srv: 0 ds: 60 dnskey: 35 other: 14 diff --git a/src/api/info.c b/src/api/info.c index d03de5ff..7b8f385a 100644 --- a/src/api/info.c +++ b/src/api/info.c @@ -924,7 +924,6 @@ int api_info_metrics(struct ftl_conn *api) JSON_ADD_NUMBER_TO_OBJECT(content, "a", metrics.dns.cache.content.a); JSON_ADD_NUMBER_TO_OBJECT(content, "aaaa", metrics.dns.cache.content.aaaa); JSON_ADD_NUMBER_TO_OBJECT(content, "cname", metrics.dns.cache.content.cname); - JSON_ADD_NUMBER_TO_OBJECT(content, "srv", metrics.dns.cache.content.srv); JSON_ADD_NUMBER_TO_OBJECT(content, "ds", metrics.dns.cache.content.ds); JSON_ADD_NUMBER_TO_OBJECT(content, "dnskey", metrics.dns.cache.content.dnskey); JSON_ADD_NUMBER_TO_OBJECT(content, "other", metrics.dns.cache.content.other); diff --git a/src/dnsmasq/blockdata.c b/src/dnsmasq/blockdata.c index 4c26155f..56698c70 100644 --- a/src/dnsmasq/blockdata.c +++ b/src/dnsmasq/blockdata.c @@ -19,7 +19,7 @@ static struct blockdata *keyblock_free; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; -static void blockdata_expand(int n) +static void add_blocks(int n) { struct blockdata *new = whine_malloc(n * sizeof(struct blockdata)); @@ -47,7 +47,7 @@ void blockdata_init(void) /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */ if (option_bool(OPT_DNSSEC_VALID)) - blockdata_expand(daemon->cachesize); + add_blocks(daemon->cachesize); } void blockdata_report(void) @@ -58,50 +58,61 @@ void blockdata_report(void) blockdata_alloced * sizeof(struct blockdata)); } +static struct blockdata *new_block(void) +{ + struct blockdata *block; + + if (!keyblock_free) + add_blocks(50); + + if (keyblock_free) + { + block = keyblock_free; + keyblock_free = block->next; + blockdata_count++; + if (blockdata_hwm < blockdata_count) + blockdata_hwm = blockdata_count; + block->next = NULL; + return block; + } + + return NULL; +} + static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) { struct blockdata *block, *ret = NULL; struct blockdata **prev = &ret; size_t blen; - while (len > 0) + do { - if (!keyblock_free) - blockdata_expand(50); - - if (keyblock_free) - { - block = keyblock_free; - keyblock_free = block->next; - blockdata_count++; - } - else + if (!(block = new_block())) { /* failed to alloc, free partial chain */ blockdata_free(ret); return NULL; } - - if (blockdata_hwm < blockdata_count) - blockdata_hwm = blockdata_count; + + if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0) + { + if (data) + { + memcpy(block->key, data, blen); + data += blen; + } + else if (!read_write(fd, block->key, blen, 1)) + { + /* failed read free partial chain */ + blockdata_free(ret); + return NULL; + } + } - blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; - if (data) - { - memcpy(block->key, data, blen); - data += blen; - } - else if (!read_write(fd, block->key, blen, 1)) - { - /* failed read free partial chain */ - blockdata_free(ret); - return NULL; - } len -= blen; *prev = block; prev = &block->next; - block->next = NULL; - } + } while (len != 0); return ret; } @@ -111,6 +122,58 @@ struct blockdata *blockdata_alloc(char *data, size_t len) return blockdata_alloc_real(0, data, len); } +/* Add data to the end of the block. + newlen is length of new data, NOT total new length. + Use blockdata_alloc(NULL, 0) to make empty block to add to. */ +int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen) +{ + struct blockdata *b; + + /* find size of current final block */ + for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN); + + /* chain to short for length, something is broken */ + if (oldlen > KEYBLOCK_LEN) + { + blockdata_free(block); + return 0; + } + + while (1) + { + struct blockdata *new; + size_t blocksize = KEYBLOCK_LEN - oldlen; + size_t size = (newlen <= blocksize) ? newlen : blocksize; + + if (size != 0) + { + memcpy(&b->key[oldlen], data, size); + data += size; + newlen -= size; + } + + /* full blocks from now on. */ + oldlen = 0; + + if (newlen == 0) + break; + + if ((new = new_block())) + { + b->next = new; + b = new; + } + else + { + /* failed to alloc, free partial chain */ + blockdata_free(block); + return 0; + } + } + + return 1; +} + void blockdata_free(struct blockdata *blocks) { struct blockdata *tmp; diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index a93b6168..c81d2092 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -30,6 +30,7 @@ static void make_non_terminals(struct crec *source); static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); static void dump_cache_entry(struct crec *cache, time_t now); +char *querystr(char *desc, unsigned short type); /* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */ @@ -134,6 +135,17 @@ static void cache_link(struct crec *crecp); void rehash(int size); static void cache_hash(struct crec *crecp); +unsigned short rrtype(char *in) +{ + unsigned int i; + + for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) + if (strcasecmp(in, typestr[i].name) == 0) + return typestr[i].type; + + return 0; +} + void next_uid(struct crec *crecp) { static unsigned int uid = 0; @@ -264,8 +276,8 @@ static void cache_blockdata_free(struct crec *crecp) { if (!(crecp->flags & F_NEG)) { - if (crecp->flags & F_SRV) - blockdata_free(crecp->addr.srv.target); + if (crecp->flags & F_RR) + blockdata_free(crecp->addr.rr.rrdata); #ifdef HAVE_DNSSEC else if (crecp->flags & F_DNSKEY) blockdata_free(crecp->addr.key.keydata); @@ -459,8 +471,9 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ - if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || - (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) + if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_RR | F_NXDOMAIN)) || + (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) || + ((crecp->flags & flags & F_RR) && addr->rr.rrtype == crecp->addr.rr.rrtype)) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) return crecp; @@ -777,13 +790,14 @@ void cache_end_insert(void) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR)) read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); - if (flags & F_SRV) + + if (flags & F_RR) { - /* A negative SRV entry is possible and has no data, obviously. */ + /* A negative RR entry is possible and has no data, obviously. */ if (!(flags & F_NEG)) - blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent); + blockdata_write(new_chain->addr.rr.rrdata, new_chain->addr.rr.datalen, daemon->pipe_to_parent); } #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) @@ -849,16 +863,15 @@ int cache_recv_insert(time_t now, int fd) ttl = difftime(ttd, now); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) + if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_RR)) { unsigned short class = C_IN; - + if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) return 0; - - if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen))) + + if ((flags & F_RR) && !(flags & F_NEG) && !(addr.rr.rrdata = blockdata_read(fd, addr.rr.datalen))) return 0; - #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) { @@ -1588,7 +1601,7 @@ static void make_non_terminals(struct crec *source) if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && - !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) && + !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_RR)) && hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; @@ -1645,7 +1658,7 @@ static void make_non_terminals(struct crec *source) if (crecp) { - crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE); + crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) crecp->ttd = source->ttd; crecp->name.namep = name; @@ -1788,17 +1801,8 @@ static void dump_cache_entry(struct crec *cache, time_t now) p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) a = sanitise(cache_get_cname_target(cache)); - else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG)) - { - int targetlen = cache->addr.srv.targetlen; - ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority, - cache->addr.srv.weight, cache->addr.srv.srvport); - - if (targetlen > (40 - len)) - targetlen = 40 - len; - blockdata_retrieve(cache->addr.srv.target, targetlen, a + len); - a[len + targetlen] = 0; - } + else if (cache->flags & F_RR) + sprintf(a, "%s", querystr(NULL, cache->addr.rr.rrtype)); #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) { @@ -1825,8 +1829,8 @@ static void dump_cache_entry(struct crec *cache, time_t now) t = "6"; else if (cache->flags & F_CNAME) t = "C"; - else if (cache->flags & F_SRV) - t = "V"; + else if (cache->flags & F_RR) + t = "T"; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) t = "S"; @@ -1880,8 +1884,6 @@ void get_dnsmasq_metrics(struct metrics *ci) ci->dns.cache.content.aaaa++; else if (cache->flags & F_CNAME) ci->dns.cache.content.cname++; - else if (cache->flags & F_SRV) - ci->dns.cache.content.srv++; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) ci->dns.cache.content.ds++; @@ -1890,6 +1892,8 @@ void get_dnsmasq_metrics(struct metrics *ci) #endif else ci->dns.cache.content.other++; + // cache->flags & F_RR --> cache->addr.rr.rrtype + // Needs (USHRT_MAX*sizeof(int)) bytes of memory if(cache->flags & F_IMMORTAL) ci->dns.cache.immortal++; @@ -1897,17 +1901,17 @@ void get_dnsmasq_metrics(struct metrics *ci) else ci->dns.cache.expired++; - ci->dhcp.bootp = daemon->metrics[METRIC_BOOTP]; - ci->dhcp.pxe = daemon->metrics[METRIC_PXE]; - ci->dhcp.ack = daemon->metrics[METRIC_DHCPACK]; - ci->dhcp.decline = daemon->metrics[METRIC_DHCPDECLINE]; - ci->dhcp.discover = daemon->metrics[METRIC_DHCPDISCOVER]; - ci->dhcp.inform = daemon->metrics[METRIC_DHCPINFORM]; - ci->dhcp.nak = daemon->metrics[METRIC_DHCPNAK]; - ci->dhcp.offer = daemon->metrics[METRIC_DHCPOFFER]; - ci->dhcp.release = daemon->metrics[METRIC_DHCPRELEASE]; - ci->dhcp.request = daemon->metrics[METRIC_DHCPREQUEST]; - ci->dhcp.noanswer = daemon->metrics[METRIC_NOANSWER]; + ci->dhcp.bootp = daemon->metrics[METRIC_BOOTP]; + ci->dhcp.pxe = daemon->metrics[METRIC_PXE]; + ci->dhcp.ack = daemon->metrics[METRIC_DHCPACK]; + ci->dhcp.decline = daemon->metrics[METRIC_DHCPDECLINE]; + ci->dhcp.discover = daemon->metrics[METRIC_DHCPDISCOVER]; + ci->dhcp.inform = daemon->metrics[METRIC_DHCPINFORM]; + ci->dhcp.nak = daemon->metrics[METRIC_DHCPNAK]; + ci->dhcp.offer = daemon->metrics[METRIC_DHCPOFFER]; + ci->dhcp.release = daemon->metrics[METRIC_DHCPRELEASE]; + ci->dhcp.request = daemon->metrics[METRIC_DHCPREQUEST]; + ci->dhcp.noanswer = daemon->metrics[METRIC_NOANSWER]; ci->dhcp.leases.allocated_4 = daemon->metrics[METRIC_LEASES_ALLOCATED_4]; ci->dhcp.leases.pruned_4 = daemon->metrics[METRIC_LEASES_PRUNED_4]; ci->dhcp.leases.allocated_6 = daemon->metrics[METRIC_LEASES_ALLOCATED_6]; @@ -2148,6 +2152,8 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, sprintf(portstring, "#%u", type); } } + else if (flags & F_RR) + dest = querystr(NULL, addr->rr.rrtype); else dest = arg; } @@ -2174,8 +2180,6 @@ void _log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, } else if (flags & F_CNAME) dest = ""; - else if (flags & F_SRV) - dest = ""; else if (flags & F_RRNAME) dest = arg; diff --git a/src/dnsmasq/config.h b/src/dnsmasq/config.h index 3ee9be1f..12562901 100644 --- a/src/dnsmasq/config.h +++ b/src/dnsmasq/config.h @@ -19,7 +19,7 @@ #define CHILD_LIFETIME 300 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ -#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */ +#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ #define DNSSEC_WORK 50 /* Max number of queries to validate one question */ diff --git a/src/dnsmasq/dbus.c b/src/dnsmasq/dbus.c index fd5d1ca6..4512b8ec 100644 --- a/src/dnsmasq/dbus.c +++ b/src/dnsmasq/dbus.c @@ -52,6 +52,12 @@ const char* introspection_xml_template = " \n" " \n" " \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -817,6 +823,28 @@ DBusHandlerResult message_handler(DBusConnection *connection, { reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k"); } + else if (strcmp(method, "SetFilterA") == 0) + { + static int done = 0; + static struct rrlist list = { T_A, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } + } + else if (strcmp(method, "SetFilterAAAA") == 0) + { + static int done = 0; + static struct rrlist list = { T_AAAA, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } + } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries"); diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index 9d403d25..bac888cc 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -132,17 +132,11 @@ int main_dnsmasq (int argc, char **argv) { /* Note that both /000 and '.' are allowed within labels. These get represented in presentation format using NAME_ESCAPE as an escape - character when in DNSSEC mode. - In theory, if all the characters in a name were /000 or + character. In theory, if all the characters in a name were /000 or '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec. - - daemon->namebuff was previously allocated by the option-reading - code before we knew if we're in DNSSEC mode, so reallocate here. */ - free(daemon->namebuff); - daemon->namebuff = safe_malloc(MAXDNAME * 2); - daemon->keyname = safe_malloc(MAXDNAME * 2); - daemon->workspacename = safe_malloc(MAXDNAME * 2); + presentation format would be twice as long as the spec. */ + daemon->keyname = safe_malloc((MAXDNAME * 2) + 1); + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); /* one char flag per possible RR in answer section (may get extended). */ daemon->rr_status_sz = 64; daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz); @@ -153,7 +147,7 @@ int main_dnsmasq (int argc, char **argv) /* CONNTRACK UBUS code uses this buffer, so if not allocated above, we need to allocate it here. */ if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename) - daemon->workspacename = safe_malloc(MAXDNAME); + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); #endif #ifdef HAVE_DHCP diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 1f976316..01f46554 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -276,13 +276,12 @@ struct event_desc { #define OPT_UMBRELLA_DEVID 64 #define OPT_CMARK_ALST_EN 65 #define OPT_QUIET_TFTP 66 -#define OPT_FILTER_A 67 -#define OPT_FILTER_AAAA 68 -#define OPT_STRIP_ECS 69 -#define OPT_STRIP_MAC 70 -#define OPT_NORR 71 -#define OPT_NO_IDENT 72 -#define OPT_LAST 73 +#define OPT_STRIP_ECS 67 +#define OPT_STRIP_MAC 68 +#define OPT_NORR 69 +#define OPT_NO_IDENT 70 +#define OPT_CACHE_RR 71 +#define OPT_LAST 72 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) @@ -325,15 +324,16 @@ union all_addr { unsigned char algo; unsigned char digest; } ds; - struct { - struct blockdata *target; - unsigned short targetlen, srvport, priority, weight; - } srv; /* for log_query */ struct { unsigned short keytag, algo, digest, rcode; int ede; } log; + /* for arbitrary RR record. */ + struct { + struct blockdata *rrdata; + unsigned short rrtype, datalen; + } rr; }; @@ -515,7 +515,7 @@ struct crec { #define F_NOEXTRA (1u<<27) #define F_DOMAINSRV (1u<<28) #define F_RCODE (1u<<29) -#define F_SRV (1u<<30) +#define F_RR (1u<<30) #define F_STALE (1u<<31) #define UID_NONE 0 @@ -663,6 +663,11 @@ struct iname { struct iname *next; }; +struct rrlist { + unsigned short rr; + struct rrlist *next; +}; + /* subnet parameters from command line */ struct mysubnet { union mysockaddr addr; @@ -1128,6 +1133,7 @@ extern struct daemon { struct naptr *naptr; struct txt_record *txt, *rr; struct ptr_record *ptr; + struct rrlist *cache_rr, *filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; @@ -1309,6 +1315,7 @@ struct server_details { /* cache.c */ void cache_init(void); +unsigned short rrtype(char *in); void next_uid(struct crec *crecp); /********************************************* Pi-hole modification ***********************************************/ #define log_query(flags,name,addr,arg,type) _log_query(flags, name, addr, arg, type, __FILE__, __LINE__) @@ -1346,6 +1353,8 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, void blockdata_init(void); void blockdata_report(void); struct blockdata *blockdata_alloc(char *data, size_t len); +int blockdata_expand(struct blockdata *block, size_t oldlen, + char *data, size_t newlen); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data); struct blockdata *blockdata_read(int fd, size_t len); void blockdata_write(struct blockdata *block, size_t len, int fd); @@ -1375,7 +1384,7 @@ void report_addresses(struct dns_header *header, size_t len, u32 mark); size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale); + int *stale, int *filtered); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now); int check_for_ignored_address(struct dns_header *header, size_t qlen); @@ -1427,6 +1436,7 @@ void rand_init(void); unsigned short rand16(void); u32 rand32(void); u64 rand64(void); +int rr_on_list(struct rrlist *list, unsigned short rr); int legal_hostname(char *name); char *canonicalise(char *in, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); @@ -1831,14 +1841,16 @@ void poll_listen(int fd, short event); int do_poll(int timeout); /* rrfilter.c */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode); -u16 *rrfilter_desc(int type); +size_t rrfilter(struct dns_header *header, size_t *plen, int mode); +short *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); +int to_wire(char *name); +void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 -#define RRFILTER_A 2 -#define RRFILTER_AAAA 3 +#define RRFILTER_CONF 2 + /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); diff --git a/src/dnsmasq/dnssec.c b/src/dnsmasq/dnssec.c index 219ba9af..aa196ebf 100644 --- a/src/dnsmasq/dnssec.c +++ b/src/dnsmasq/dnssec.c @@ -24,81 +24,6 @@ #define SERIAL_LT -1 #define SERIAL_GT 1 -/* Convert from presentation format to wire format, in place. - Also map UC -> LC. - Note that using extract_name to get presentation format - then calling to_wire() removes compression and maps case, - thus generating names in canonical form. - Calling to_wire followed by from_wire is almost an identity, - except that the UC remains mapped to LC. - - Note that both /000 and '.' are allowed within labels. These get - represented in presentation format using NAME_ESCAPE as an escape - character. In theory, if all the characters in a name were /000 or - '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec (1024). - The buffers are all declared as 2049 (allowing for the trailing zero) - for this reason. -*/ -static int to_wire(char *name) -{ - unsigned char *l, *p, *q, term; - int len; - - for (l = (unsigned char*)name; *l != 0; l = p) - { - for (p = l; *p != '.' && *p != 0; p++) - if (*p >= 'A' && *p <= 'Z') - *p = *p - 'A' + 'a'; - else if (*p == NAME_ESCAPE) - { - for (q = p; *q; q++) - *q = *(q+1); - (*p)--; - } - term = *p; - - if ((len = p - l) != 0) - memmove(l+1, l, len); - *l = len; - - p++; - - if (term == 0) - *p = 0; - } - - return l + 1 - (unsigned char *)name; -} - -/* Note: no compression allowed in input. */ -static void from_wire(char *name) -{ - unsigned char *l, *p, *last; - int len; - - for (last = (unsigned char *)name; *last != 0; last += *last+1); - - for (l = (unsigned char *)name; *l != 0; l += len+1) - { - len = *l; - memmove(l, l+1, len); - for (p = l; p < l + len; p++) - if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) - { - memmove(p+1, p, 1 + last - p); - len++; - *p++ = NAME_ESCAPE; - (*p)++; - } - - l[len] = '.'; - } - - if ((char *)l != name) - *(l-1) = 0; -} - /* Input in presentation format */ static int count_labels(char *name) { @@ -225,7 +150,7 @@ static int is_check_date(unsigned long curtime) On returning 0, the end has been reached. */ struct rdata_state { - u16 *desc; + short *desc; size_t c; unsigned char *end, *ip, *op; char *buff; @@ -246,7 +171,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state { d = *(state->desc); - if (d == (u16)-1) + if (d == -1) { /* all the bytes to the end. */ if ((state->c = state->end - state->ip) != 0) @@ -294,7 +219,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state /* Bubble sort the RRset into the canonical order. */ -static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, +static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx, unsigned char **rrset, char *buff1, char *buff2) { int swap, i, j; @@ -331,7 +256,7 @@ static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int is the identity function and we can compare the RRs directly. If not we compare the canonicalised RRs one byte at a time. */ - if (*rr_desc == (u16)-1) + if (*rr_desc == -1) { int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1; int cmp = memcmp(state1.ip, state2.ip, rdmin); @@ -524,7 +449,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in unsigned char *p; int rdlen, j, name_labels, algo, labels, key_tag; struct crec *crecp = NULL; - u16 *rr_desc = rrfilter_desc(type); + short *rr_desc = rrfilter_desc(type); u32 sig_expiration, sig_inception; int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP; @@ -671,7 +596,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in If canonicalisation is not needed, a simple insertion into the hash works. */ - if (*rr_desc == (u16)-1) + if (*rr_desc == -1) { len = htons(rdlen); hash->update(ctx, 2, (unsigned char *)&len); diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index fe8e25a3..9cc51e68 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -253,9 +253,10 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout) if (highout) *highout = nhigh; - if (nlow == nhigh) + /* qlen == -1 when we failed to match even an empty query, if there are no default servers. */ + if (nlow == nhigh || qlen == -1) return 0; - + return 1; } diff --git a/src/dnsmasq/edns0.c b/src/dnsmasq/edns0.c index c498eb12..567101b5 100644 --- a/src/dnsmasq/edns0.c +++ b/src/dnsmasq/edns0.c @@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l memcpy(buff, datap, rdlen); /* now, delete OPT RR */ - plen = rrfilter(header, plen, RRFILTER_EDNS0); + rrfilter(header, &plen, RRFILTER_EDNS0); /* Now, force addition of a new one */ p = NULL; diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index c1070067..45de97b1 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -734,7 +734,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server if (added_pheader) { /* client didn't send EDNS0, we added one, strip it off before returning answer. */ - n = rrfilter(header, n, RRFILTER_EDNS0); + rrfilter(header, &n, RRFILTER_EDNS0); pheader = NULL; } else @@ -826,16 +826,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server } } - /* Before extract_addresses() */ - if (rcode == NOERROR) - { - if (option_bool(OPT_FILTER_A)) - n = rrfilter(header, n, RRFILTER_A); - - if (option_bool(OPT_FILTER_AAAA)) - n = rrfilter(header, n, RRFILTER_AAAA); - } - switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) { case 1: @@ -872,6 +862,9 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server break; } + if (rcode == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) + ede = EDE_FILTERED; + if (doctored) cache_secure = 0; } @@ -893,7 +886,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ if (!do_bit) - n = rrfilter(header, n, RRFILTER_DNSSEC); + rrfilter(header, &n, RRFILTER_DNSSEC); } #endif @@ -1867,7 +1860,7 @@ void receive_query(struct listener *listen, time_t now) #endif else { - int stale; + int stale, filtered; int ad_reqd = do_bit; u16 hb3 = header->hb3, hb4 = header->hb4; int fd = listen->fd; @@ -1907,17 +1900,28 @@ void receive_query(struct listener *listen, time_t now) /**********************************************/ m = answer_request(header, ((char *) header) + udp_size, (size_t)n, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); if (m >= 1) { - if (stale && have_pseudoheader) + if (have_pseudoheader) { - u16 swap = htons(EDE_STALE); + int ede = EDE_UNSET; - m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, - EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons(ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, + EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } } + #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd); #endif @@ -2187,7 +2191,7 @@ unsigned char *tcp_request(int confd, time_t now, unsigned char *pheader; unsigned int mark = 0; int have_mark = 0; - int first, last, stale, do_stale = 0; + int first, last, filtered, stale, do_stale = 0; unsigned int flags = 0; u16 hb3, hb4; @@ -2420,7 +2424,7 @@ unsigned char *tcp_request(int confd, time_t now, else /* m > 0 if answered from cache */ m = answer_request(header, ((char *) header) + 65536, (size_t)size, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); /* Do this by steam now we're not in the select() loop */ check_log_writer(1); @@ -2562,13 +2566,23 @@ unsigned char *tcp_request(int confd, time_t now, m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); } } - else if (stale) - { - u16 swap = htons((u16)EDE_STALE); - - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); - } - + else + { + ede = EDE_UNSET; + + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons((u16)ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } + } + check_log_writer(1); *length = htons(m); diff --git a/src/dnsmasq/network.c b/src/dnsmasq/network.c index c71dea5e..a557d75f 100644 --- a/src/dnsmasq/network.c +++ b/src/dnsmasq/network.c @@ -361,13 +361,8 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, struct in_addr newaddr = addr->in.sin_addr; if (int_name->flags & INP4) - { - if (netmask.s_addr == 0xffffffff) - continue; - - newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | - (int_name->proto4.s_addr & ~netmask.s_addr); - } + newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | + (int_name->proto4.s_addr & ~netmask.s_addr); /* check for duplicates. */ for (lp = int_name->addr; lp; lp = lp->next) @@ -400,10 +395,6 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, { int i; - /* No sense in doing /128. */ - if (prefixlen == 128) - continue; - for (i = 0; i < 16; i++) { int bits = ((i+1)*8) - prefixlen; diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 51a538fa..2a74942f 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -190,6 +190,8 @@ struct myoption { #define LOPT_STALE_CACHE 377 #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 +#define LOPT_CACHE_RR 380 +#define LOPT_FILTER_RR 381 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -229,6 +231,7 @@ static const struct myoption opts[] = { "filterwin2k", 0, 0, 'f' }, { "filter-A", 0, 0, LOPT_FILTER_A }, { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, + { "filter-rr", 1, 0, LOPT_FILTER_RR }, { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, @@ -243,6 +246,7 @@ static const struct myoption opts[] = { "local-ttl", 1, 0, 'T' }, { "no-negcache", 0, 0, 'N' }, { "no-round-robin", 0, 0, LOPT_NORR }, + { "cache-rr", 1, 0, LOPT_CACHE_RR }, { "addn-hosts", 1, 0, 'H' }, { "hostsdir", 1, 0, LOPT_HOST_INOTIFY }, { "query-port", 1, 0, 'Q' }, @@ -407,8 +411,9 @@ static struct { { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL }, { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL }, { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, - { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, - { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_A, ARG_DUP, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, + { LOPT_FILTER_AAAA, ARG_DUP, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_RR, ARG_DUP, "", gettext_noop("Don't include resource records of the given type in DNS answers."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, @@ -570,13 +575,14 @@ static struct { { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, - { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, - { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file."), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump."), NULL }, { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { LOPT_UMBRELLA, ARG_ONE, "[=]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL }, { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, + { LOPT_CACHE_RR, ARG_DUP, "", gettext_noop("Cache this DNS resource record type."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -1163,6 +1169,9 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.in-addr.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) @@ -1248,6 +1257,9 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.ip6.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) @@ -3441,7 +3453,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; } - case LOPT_FAST_RETRY: + case LOPT_FAST_RETRY: /* --fast-dns-retry */ daemon->fast_retry_timeout = TIMEOUT; if (!arg) @@ -3463,6 +3475,47 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } break; + + case LOPT_CACHE_RR: /* --cache-rr */ + case LOPT_FILTER_RR: /* --filter-rr */ + case LOPT_FILTER_A: /* --filter-A */ + case LOPT_FILTER_AAAA: /* --filter-AAAA */ + while (1) { + int type; + struct rrlist *new; + + comma = NULL; + + if (option == LOPT_FILTER_A) + type = T_A; + else if (option == LOPT_FILTER_AAAA) + type = T_AAAA; + else + { + comma = split(arg); + if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) + ret_err(_("bad RR type")); + } + + new = opt_malloc(sizeof(struct rrlist)); + new->rr = type; + + if (option == LOPT_CACHE_RR) + { + new->next = daemon->cache_rr; + daemon->cache_rr = new; + } + else + { + new->next = daemon->filter_rr; + daemon->filter_rr = new; + } + + if (!comma) break; + arg = comma; + } + break; + #ifdef HAVE_DHCP case 'X': /* --dhcp-lease-max */ @@ -5158,7 +5211,7 @@ err: break; } - case LOPT_STALE_CACHE: + case LOPT_STALE_CACHE: /* --use-stale-cache */ { int max_expiry = STALE_CACHE_EXPIRY; if (arg) @@ -5731,10 +5784,15 @@ void read_opts(int argc, char **argv, char *compile_opts) { size_t argbuf_size = MAXDNAME; char *argbuf = opt_malloc(argbuf_size); - char *buff = opt_malloc(MAXDNAME); + /* Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec. */ + char *buff = opt_malloc((MAXDNAME * 2) + 1); int option, testmode = 0; char *arg, *conffile = NULL; - + opterr = 0; daemon = opt_malloc(sizeof(struct daemon)); @@ -5879,12 +5937,10 @@ void read_opts(int argc, char **argv, char *compile_opts) add_txt("servers.bind", NULL, TXT_STAT_SERVERS); /* Pi-hole modification */ add_txt("privacylevel.pihole", NULL, TXT_PRIVACYLEVEL); + add_txt("version.FTL", (char*)get_FTL_version(), 0 ); /************************/ } #endif - /******** Pi-hole modification ********/ - add_txt("version.FTL", (char*)get_FTL_version(), 0 ); - /**************************************/ /* port might not be known when the address is parsed - fill in here */ if (daemon->servers) diff --git a/src/dnsmasq/rfc1035.c b/src/dnsmasq/rfc1035.c index 09f9cc4c..f497b0c0 100644 --- a/src/dnsmasq/rfc1035.c +++ b/src/dnsmasq/rfc1035.c @@ -90,23 +90,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, if (isExtract) { unsigned char c = *p; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) + + if (c == 0 || c == '.' || c == NAME_ESCAPE) { - if (c == 0 || c == '.' || c == NAME_ESCAPE) - { - *cp++ = NAME_ESCAPE; - *cp++ = c+1; - } - else - *cp++ = c; + *cp++ = NAME_ESCAPE; + *cp++ = c+1; } else -#endif - if (c != 0 && c != '.') - *cp++ = c; - else - return 0; + *cp++ = c; } else { @@ -119,10 +110,9 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, cp++; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE) + + if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; -#endif if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; @@ -503,12 +493,10 @@ static int find_soa(struct dns_header *header, size_t qlen, int *doctored) } /* Print TXT reply to log */ -static int print_txt(struct dns_header *header, const size_t qlen, char *name, - unsigned char *p, const int ardlen, int secflag) +static int log_txt(char *name, unsigned char *p, const int ardlen, int secflag) { unsigned char *p1 = p; - if (!CHECK_LEN(header, p1, qlen, ardlen)) - return 0; + /* Loop over TXT payload */ while ((p1 - p) < ardlen) { @@ -527,7 +515,7 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name, } *p3 = 0; - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0); + log_query(secflag | F_FORWARD, name, NULL, (char*)p1, 0); /* restore */ memmove(p1 + 1, p1, i); *p1 = len; @@ -718,8 +706,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t addrlen = IN6ADDRSZ; flags |= F_IPV6; } - else if (qtype == T_SRV) - flags |= F_SRV; + else if (qtype != T_CNAME && (qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype))) + flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ @@ -817,31 +805,70 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t #ifdef HAVE_DNSSEC if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) #endif - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype); + log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); } else if (!(flags & F_NXDOMAIN)) { found = 1; - if (flags & F_SRV) + if (flags & F_RR) { + short desc, *rrdesc = rrfilter_desc(aqtype); unsigned char *tmp = namep; - if (!CHECK_LEN(header, p1, qlen, 6)) + if (!CHECK_LEN(header, p1, qlen, ardlen)) return 2; /* bad packet */ - GETSHORT(addr.srv.priority, p1); - GETSHORT(addr.srv.weight, p1); - GETSHORT(addr.srv.srvport, p1); - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 2; - addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */ - if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen))) + addr.rr.rrtype = aqtype; + addr.rr.datalen = 0; + + /* The RR data may include names, and those names may include + compression, which will be rendered meaningless when + copied into another packet. + Here we go through a description of the packet type to + find the names, and extract them to a c-string and then + re-encode them to standalone DNS format without compression. */ + if (!(addr.rr.rrdata = blockdata_alloc(NULL, 0))) return 0; - + do + { + desc = *rrdesc++; + + if (desc == -1) + { + /* Copy the rest of the RR and end. */ + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, endrr - p1)) + return 0; + addr.rr.datalen += endrr - p1; + } + else if (desc == 0) + { + /* Name, extract it then re-encode. */ + int len; + + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 2; + + len = to_wire(name); + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, name, len)) + return 0; + addr.rr.datalen += len; + } + else + { + /* desc is length of a block of data to be used as-is */ + if (desc > endrr - p1) + desc = endrr - p1; + if (!blockdata_expand(addr.rr.rrdata, addr.rr.datalen, (char *)p1, desc)) + return 0; + addr.rr.datalen += desc; + p1 += desc; + } + } while (desc != -1); + /* we overwrote the original name, so get it back here. */ if (!extract_name(header, qlen, &tmp, name, 1, 0)) return 2; - } + } else if (flags & (F_IPV4 | F_IPV6)) { /* copy address into aligned storage */ @@ -889,11 +916,24 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (aqtype == T_TXT) { - if (!print_txt(header, qlen, name, p1, ardlen, secflag)) - return 2; + if (!CHECK_LEN(header, p1, qlen, ardlen)) + return 2; + + log_txt(name, p1, ardlen, secflag | F_UPSTREAM); } else - log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype); + { + int negflag = F_UPSTREAM; + + /* We're filtering this RRtype. It will be removed from the + returned packet in process_reply() but gets cached here anyway + and will be filtered again on the way out of the cache. Here, + we just need to alter the logging. */ + if (rr_on_list(daemon->filter_rr, qtype)) + negflag = F_NEG | F_CONFIG; + + log_query(negflag | flags | F_FORWARD | secflag, name, &addr, NULL, aqtype); + } } p1 = endrr; @@ -905,11 +945,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t { if (flags & F_NXDOMAIN) { - flags &= ~(F_IPV4 | F_IPV6 | F_SRV); + flags &= ~(F_IPV4 | F_IPV6 | F_RR); - /* Can store NXDOMAIN reply to CNAME or ANY query. */ - if (qtype == T_CNAME || qtype == T_ANY) - insert = 1; + /* Can store NXDOMAIN reply for any qtype. */ + insert = 1; } log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0); @@ -927,7 +966,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t if (ttl == 0) ttl = cttl; - newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + if (flags & F_RR) + addr.rr.rrtype = qtype; + + newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc); @@ -1422,7 +1464,7 @@ static int cache_validated(const struct crec *crecp) size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale) + int *stale, int *filtered) { char *name = daemon->namebuff; unsigned char *p, *ansp; @@ -1440,6 +1482,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (stale) *stale = 0; + + if (filtered) + *filtered = 0; /* never answer queries with RD unset, to avoid cache snooping. */ if (ntohs(header->ancount) != 0 || @@ -1708,8 +1753,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) continue; - - + if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; @@ -1877,8 +1921,24 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!(crecp->flags & F_DNSSECOK)) sec_data = 0; - - if (crecp->flags & F_NEG) + + if (!(crecp->flags & (F_HOSTS | F_DHCP))) + auth = 0; + + if (rr_on_list(daemon->filter_rr, qtype) && + !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) + { + /* We have a cached answer but we're filtering it. */ + ans = 1; + sec_data = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; + } + else if (crecp->flags & F_NEG) { ans = 1; auth = 0; @@ -1898,9 +1958,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, !is_same_net(crecp->addr.addr4, local_addr, local_netmask)) continue; - if (!(crecp->flags & (F_HOSTS | F_DHCP))) - auth = 0; - ans = 1; if (!dryrun) { @@ -1949,6 +2006,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, { ans = found = 1; sec_data = 0; + if (!dryrun) { int offset; @@ -1981,13 +2039,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (qtype == T_SRV || qtype == T_ANY) { - int found = 0; struct mx_srv_record *move = NULL, **up = &daemon->mxnames; for (rec = daemon->mxnames; rec; rec = rec->next) if (rec->issrv && hostname_isequal(name, rec->name)) { - found = ans = 1; + ans = 1; sec_data = 0; if (!dryrun) { @@ -2021,60 +2078,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, *up = move; move->next = NULL; } - - if (!found) - { - if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && - rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) - do - { - int stale_flag = 0; - - if (crec_isstale(crecp, now)) - { - if (stale) - *stale = 1; - - stale_flag = F_STALE; - } - /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */ - if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN))) - break; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - auth = 0; - found = ans = 1; - - if (crecp->flags & F_NEG) - { - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - if (!dryrun) - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - } - else if (!dryrun) - { - char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL); - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd", - crecp->addr.srv.priority, crecp->addr.srv.weight, crecp->addr.srv.srvport, - target)) - anscount++; - } - } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV))); - } - - if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) - { - ans = 1; - sec_data = 0; - if (!dryrun) - log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); - } } if (qtype == T_NAPTR || qtype == T_ANY) @@ -2106,8 +2109,92 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, if (!dryrun) log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); } - } + if (!ans) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && + rd_bit && (!do_bit || cache_validated(crecp))) + do + { + int flags = crecp->flags; + + if ((flags & F_NXDOMAIN) || crecp->addr.rr.rrtype == qtype) + { + if (crec_isstale(crecp, now)) + { + if (stale) + *stale = 1; + + flags |= F_STALE; + } + + if (!(flags & F_DNSSECOK)) + sec_data = 0; + + if (flags & F_NXDOMAIN) + nxdomain = 1; + else if (rr_on_list(daemon->filter_rr, qtype)) + flags |= F_NEG | F_CONFIG; + + auth = 0; + ans = 1; + + if (!dryrun) + { + char *rrdata = NULL; + + if (!(flags & F_NEG)) + { + rrdata = blockdata_retrieve(crecp->addr.rr.rrdata, crecp->addr.rr.datalen, NULL); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, qtype, C_IN, "t", + crecp->addr.rr.datalen, rrdata)) + anscount++; + } + + /* log after cache insertion as log_txt mangles rrdata */ + if (qtype == T_TXT && !(crecp->flags & F_NEG)) + log_txt(name, (unsigned char *)rrdata, crecp->addr.rr.datalen, crecp->flags & F_DNSSECOK); + else + log_query(flags, name, &crecp->addr, NULL, 0); + } + } + } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); + } + + if (!ans && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) + { + ans = 1; + sec_data = 0; + if (!dryrun) + log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); + } + + + if (!ans && rr_on_list(daemon->filter_rr, qtype)) + { + /* We don't have a cached answer and when we get an answer from upstream we're going to + filter it anyway. If we have a cached answer for the domain for another RRtype then + that may be enough to tell us if the answer should be NODATA and save the round trip. + Cached NXDOMAIN has already been handled, so here we look for any record for the domain, + since its existence allows us to return a NODATA answer. Note that we never set the AD flag, + since we didn't authenticate the record. */ + + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME)) + { + ans = 1; + sec_data = auth = 0; + + if (!dryrun) + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; + } + } + } + if (!ans) return 0; /* failed to answer a question */ } diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index 87544816..477df91c 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -353,7 +353,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu put_opt6_short(DHCP6USEMULTI); put_opt6_string("Use multicast"); end_opt6(o1); - return 1; + goto done; } /* match vendor and user class options */ @@ -1277,12 +1277,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu } + log_tags(tagif, state->xid); + + done: /* Fill in the message type. Note that we store the offset, not a direct pointer, since the packet memory may have been reallocated. */ ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype; - log_tags(tagif, state->xid); log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1)); return 1; diff --git a/src/dnsmasq/rrfilter.c b/src/dnsmasq/rrfilter.c index 42d9c210..d380e148 100644 --- a/src/dnsmasq/rrfilter.c +++ b/src/dnsmasq/rrfilter.c @@ -136,9 +136,9 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i if (class == C_IN) { - u16 *d; + short *d; - for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++) + for (pp = p, d = rrfilter_desc(type); *d != -1; d++) { if (*d != 0) pp += *d; @@ -156,41 +156,46 @@ static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, i } -/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode) +/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. + * returns number of modified records. */ +size_t rrfilter(struct dns_header *header, size_t *plen, int mode) { static unsigned char **rrs = NULL; static int rr_sz = 0; unsigned char *p = (unsigned char *)(header+1); - int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar; + size_t rr_found = 0; + int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar; + if (mode == RRFILTER_CONF && !daemon->filter_rr) + return 0; + if (ntohs(header->qdcount) != 1 || - !(p = skip_name(p, header, plen, 4))) - return plen; + !(p = skip_name(p, header, *plen, 4))) + return 0; GETSHORT(qtype, p); GETSHORT(qclass, p); /* First pass, find pointers to start and end of all the records we wish to elide: records added for DNSSEC, unless explicitly queried for */ - for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; + for (chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) { unsigned char *pstart = p; int type, class; - if (!(p = skip_name(p, header, plen, 10))) - return plen; + if (!(p = skip_name(p, header, *plen, 10))) + return rr_found; GETSHORT(type, p); GETSHORT(class, p); p += 4; /* TTL */ GETSHORT(rdlen, p); - if (!ADD_RDLEN(header, p, plen, rdlen)) - return plen; + if (!ADD_RDLEN(header, p, *plen, rdlen)) + return rr_found; if (mode == RRFILTER_EDNS0) /* EDNS */ { @@ -217,15 +222,12 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) if (class != C_IN) continue; - if (mode == RRFILTER_A && type != T_A) - continue; - - if (mode == RRFILTER_AAAA && type != T_AAAA) + if (!rr_on_list(daemon->filter_rr, type)) continue; } if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) - return plen; + return rr_found; rrs[rr_found++] = pstart; rrs[rr_found++] = p; @@ -240,7 +242,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) /* Nothing to do. */ if (rr_found == 0) - return plen; + return rr_found; /* Second pass, look for pointers in names in the records we're keeping and make sure they don't point to records we're going to elide. This is theoretically possible, but unlikely. If @@ -248,42 +250,42 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode) p = (unsigned char *)(header+1); /* question first */ - if (!check_name(&p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_name(&p, header, *plen, 0, rrs, rr_found)) + return rr_found; p += 4; /* qclass, qtype */ /* Now answers and NS */ - if (!check_rrs(p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_rrs(p, header, *plen, 0, rrs, rr_found)) + return rr_found; /* Third pass, actually fix up pointers in the records */ p = (unsigned char *)(header+1); - check_name(&p, header, plen, 1, rrs, rr_found); + check_name(&p, header, *plen, 1, rrs, rr_found); p += 4; /* qclass, qtype */ - check_rrs(p, header, plen, 1, rrs, rr_found); + check_rrs(p, header, *plen, 1, rrs, rr_found); /* Fourth pass, elide records */ - for (p = rrs[0], i = 1; i < rr_found; i += 2) + for (p = rrs[0], i = 1; (unsigned)i < rr_found; i += 2) { unsigned char *start = rrs[i]; - unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen; + unsigned char *end = ((unsigned)i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + *plen; memmove(p, start, end-start); p += end-start; } - plen = p - (unsigned char *)header; + *plen = p - (unsigned char *)header; header->ancount = htons(ntohs(header->ancount) - chop_an); header->nscount = htons(ntohs(header->nscount) - chop_ns); header->arcount = htons(ntohs(header->arcount) - chop_ar); - return plen; + return rr_found; } /* This is used in the DNSSEC code too, hence it's exported */ -u16 *rrfilter_desc(int type) +short *rrfilter_desc(int type) { /* List of RRtypes which include domains in the data. 0 -> domain @@ -294,7 +296,7 @@ u16 *rrfilter_desc(int type) anything which needs no mangling. */ - static u16 rr_desc[] = + static short rr_desc[] = { T_NS, 0, -1, T_MD, 0, -1, @@ -319,10 +321,10 @@ u16 *rrfilter_desc(int type) 0, -1 /* wildcard/catchall */ }; - u16 *p = rr_desc; + short *p = rr_desc; while (*p != type && *p != 0) - while (*p++ != (u16)-1); + while (*p++ != -1); return p+1; } @@ -350,3 +352,78 @@ int expand_workspace(unsigned char ***wkspc, int *szp, int new) return 1; } + +/* Convert from presentation format to wire format, in place. + Also map UC -> LC. + Note that using extract_name to get presentation format + then calling to_wire() removes compression and maps case, + thus generating names in canonical form. + Calling to_wire followed by from_wire is almost an identity, + except that the UC remains mapped to LC. + + Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec (1024). + The buffers are all declared as 2049 (allowing for the trailing zero) + for this reason. +*/ +int to_wire(char *name) +{ + unsigned char *l, *p, *q, term; + int len; + + for (l = (unsigned char*)name; *l != 0; l = p) + { + for (p = l; *p != '.' && *p != 0; p++) + if (*p >= 'A' && *p <= 'Z') + *p = *p - 'A' + 'a'; + else if (*p == NAME_ESCAPE) + { + for (q = p; *q; q++) + *q = *(q+1); + (*p)--; + } + term = *p; + + if ((len = p - l) != 0) + memmove(l+1, l, len); + *l = len; + + p++; + + if (term == 0) + *p = 0; + } + + return l + 1 - (unsigned char *)name; +} + +/* Note: no compression allowed in input. */ +void from_wire(char *name) +{ + unsigned char *l, *p, *last; + int len; + + for (last = (unsigned char *)name; *last != 0; last += *last+1); + + for (l = (unsigned char *)name; *l != 0; l += len+1) + { + len = *l; + memmove(l, l+1, len); + for (p = l; p < l + len; p++) + if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) + { + memmove(p+1, p, 1 + last - p); + len++; + *p++ = NAME_ESCAPE; + (*p)++; + } + + l[len] = '.'; + } + + if ((char *)l != name) + *(l-1) = 0; +} diff --git a/src/dnsmasq/util.c b/src/dnsmasq/util.c index e0ce67d3..073d7ada 100644 --- a/src/dnsmasq/util.c +++ b/src/dnsmasq/util.c @@ -115,6 +115,19 @@ u64 rand64(void) return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } +int rr_on_list(struct rrlist *list, unsigned short rr) +{ + while (list) + { + if (list->rr == rr) + return 1; + + list = list->next; + } + + return 0; +} + /* returns 1 if name is OK and ascii printable * returns 2 if name should be processed by IDN */ static int check_name(char *in) @@ -280,11 +293,9 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit) if (limit && p + 1 > (unsigned char*)limit) return NULL; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE) + if (*sval == NAME_ESCAPE) *p++ = (*(++sval))-1; else -#endif *p++ = *sval; } diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index d7c3dbd0..324b3465 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -65,7 +65,7 @@ static void _query_set_reply(const unsigned int flags, const enum reply_type rep #define FTL_check_blocking(queryID, domainID, clientID) _FTL_check_blocking(queryID, domainID, clientID, __FILE__, __LINE__) static bool _FTL_check_blocking(int queryID, int domainID, int clientID, const char* file, const int line); static enum query_status detect_blocked_IP(const unsigned short flags, const union all_addr *addr, const queriesData *query, const domainsData *domain); -static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const unsigned char new_status); +static void query_blocked(queriesData* query, domainsData* domain, clientsData* client, const enum query_status new_status); static void FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, unsigned short port, const int id, const char* file, const int line); static void FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const char* arg, const int id, const char* file, const int line); static void FTL_upstream_error(const union all_addr *addr, const unsigned int flags, const int id, const char* file, const int line); @@ -101,13 +101,16 @@ static struct { static union mysockaddr last_server = {{ 0 }}; unsigned char* pihole_privacylevel = &config.misc.privacylevel.v.privacy_level; -const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_SERVFAIL", "F_RCODE", "F_SRV", "F_STALE" }; +const char *flagnames[] = {"F_IMMORTAL ", "F_NAMEP ", "F_REVERSE ", "F_FORWARD ", "F_DHCP ", "F_NEG ", "F_HOSTS ", "F_IPV4 ", "F_IPV6 ", "F_BIGNAME ", "F_NXDOMAIN ", "F_CNAME ", "F_DNSKEY ", "F_CONFIG ", "F_DS ", "F_DNSSECOK ", "F_UPSTREAM ", "F_RRNAME ", "F_SERVER ", "F_QUERY ", "F_NOERR ", "F_AUTH ", "F_DNSSEC ", "F_KEYTAG ", "F_SECSTAT ", "F_NO_RR ", "F_IPSET ", "F_NOEXTRA ", "F_DOMAINSRV", "F_RCODE", "F_RR", "F_STALE" }; -void FTL_hook(unsigned int flags, const char *name, union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line) +void FTL_hook(unsigned int flags, +const char *name, +union all_addr *addr, char *arg, int id, unsigned short type, const char* file, const int line) { // Extract filename from path const char *path = short_path(file); - log_debug(DEBUG_FLAGS, "Processing FTL hook from %s:%d (name: \"%s\")...", path, line, name); + const char *types = flags & F_RR ? querystr(arg, type) : "?"; + log_debug(DEBUG_FLAGS, "Processing FTL hook from %s:%d (type: %s, name: \"%s\", id: %i)...", path, line, types, name, id); print_flags(flags); // Check domain name received from dnsmasq diff --git a/src/edns0.c b/src/edns0.c index e0054e59..79a1cadb 100644 --- a/src/edns0.c +++ b/src/edns0.c @@ -21,7 +21,7 @@ // EDNS(0) Client Subnet [Optional, RFC7871] #define EDNS0_ECS EDNS0_OPTION_CLIENT_SUBNET -// EDN(0) COOKIE [Standard, RFC7873] +// EDNS(0) COOKIE [Standard, RFC7873] #define EDNS0_COOKIE 10 // EDNS(0) MAC address [NOT STANDARDIZED] diff --git a/src/log.c b/src/log.c index ec7c7881..03b5594c 100644 --- a/src/log.c +++ b/src/log.c @@ -588,13 +588,13 @@ const char __attribute__ ((const)) *get_ordinal_suffix(unsigned int number) // If the tens digit is not equal to 1, then the following table could be used: switch (number % 10) { - case 1: // If the units digit is 1: This is written after the number "st" + case 1: // If the units digit is 1: This is written after the number "1st" return "st"; - case 2: // If the units digit is 2: This is written after the number "nd" + case 2: // If the units digit is 2: This is written after the number "2nd" return "nd"; - case 3: // If the units digit is 3: This is written after the number "rd" + case 3: // If the units digit is 3: This is written after the number "3rd" return "rd"; - default: // If the units digit is 0 or 4-9: This is written after the number "th" + default: // If the units digit is 0 or 4-9: This is written after the number "9th" return "th"; } // For example: 2nd, 7th, 20th, 23rd, 52nd, 135th, 301st BUT 311th (covered above) diff --git a/src/metrics.h b/src/metrics.h index 3cd6c9a7..bd036bc4 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -38,7 +38,6 @@ struct metrics int a; int aaaa; int cname; - int srv; int ds; int dnskey; int other; diff --git a/src/regex_r.h b/src/regex_r.h index a2f87b0d..62e53740 100644 --- a/src/regex_r.h +++ b/src/regex_r.h @@ -16,7 +16,7 @@ extern const char *regextype[]; -// Use TRE instead of GNU regex library (compiled into FTL itself) +// Use TRE-Engine instead of GNU/MUSL regex libraries #define USE_TRE_REGEX #ifdef USE_TRE_REGEX diff --git a/src/struct_size.c b/src/struct_size.c index ae640c14..fd7f6fb8 100644 --- a/src/struct_size.c +++ b/src/struct_size.c @@ -29,6 +29,9 @@ int check_one_struct(const char *struct_name, const size_t found_size, const siz #elif defined(__arm__) const size_t expected_size = size32; const char *arch = "arm"; +#elif defined(__riscv) && __riscv_xlen == 64 + const size_t expected_size = size64; + const char *arch = "riscv64"; #else const size_t expected_size = 0; const char *arch = NULL; diff --git a/test/arch_test.sh b/test/arch_test.sh index 6a8ef0cd..3ec0046e 100644 --- a/test/arch_test.sh +++ b/test/arch_test.sh @@ -147,6 +147,12 @@ elif [[ "${CI_ARCH}" == "armv8a" ]]; then check_CPU_arch "v8" check_FP_arch "VFPv3-D16" +elif [[ "${CI_ARCH}" == "riscv64" ]]; then + + check_machine "ELF64" "RISC-V" + check_libs "[libm.so.6] [libc.so.6] [ld-linux-riscv64-lp64d.so.1]" + check_file "ELF 64-bit LSB pie executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, with debug_info, not stripped" + else echo "Invalid job ${CI_ARCH}" diff --git a/test/test_suite.bats b/test/test_suite.bats index b443a2b9..a70237d6 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -426,6 +426,17 @@ [[ ${lines[@]} == *"status: SERVFAIL"* ]] } +@test "ABP-style matching working as expected" { + run bash -c "dig A special.gravity.ftl @127.0.0.1 +short" + printf "%s\n" "${lines[@]}" + [[ ${lines[0]} == "0.0.0.0" ]] + [[ ${lines[1]} == "" ]] + run bash -c "dig A a.b.c.d.special.gravity.ftl @127.0.0.1 +short" + printf "%s\n" "${lines[@]}" + [[ ${lines[0]} == "0.0.0.0" ]] + [[ ${lines[1]} == "" ]] +} + @test "pihole-FTL.db schema is as expected" { run bash -c './pihole-FTL sqlite3 /etc/pihole/pihole-FTL.db .dump' printf "%s\n" "${lines[@]}"