From 81fa0bc63909a67cdd6d321617a2e5648a6a94a2 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 4 Sep 2023 11:27:04 +0200 Subject: Adding upstream version 2.2.0. Signed-off-by: Daniel Baumann --- plugins/shared/edns0_ecs.c | 222 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 plugins/shared/edns0_ecs.c (limited to 'plugins/shared/edns0_ecs.c') diff --git a/plugins/shared/edns0_ecs.c b/plugins/shared/edns0_ecs.c new file mode 100644 index 0000000..2b2e1fa --- /dev/null +++ b/plugins/shared/edns0_ecs.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2018-2023, OARC, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define DNS_MSG_HDR_SZ 12 +#define RFC1035_MAXLABELSZ 63 +#define nptohs(p) ((((uint8_t*)(p))[0] << 8) | ((uint8_t*)(p))[1]) + +static int rfc1035NameSkip(const u_char* buf, size_t sz, off_t* off) +{ + unsigned char c; + size_t len; + /* + * loop_detect[] tracks which position in the DNS message it has + * jumped to so it can't jump to the same twice, aka loop + */ + static unsigned char loop_detect[0x3FFF] = { 0 }; + do { + if ((*off) >= sz) + break; + c = *(buf + (*off)); + if (c > 191) { + /* blasted compression */ + int rc; + unsigned short s; + off_t ptr, loop_ptr; + s = nptohs(buf + (*off)); + (*off) += sizeof(s); + /* Sanity check */ + if ((*off) >= sz) + return 1; /* message too short */ + ptr = s & 0x3FFF; + /* Make sure the pointer is inside this message */ + if (ptr >= sz) + return 2; /* bad compression ptr */ + if (ptr < DNS_MSG_HDR_SZ) + return 2; /* bad compression ptr */ + if (loop_detect[ptr]) + return 4; /* compression loop */ + loop_detect[(loop_ptr = ptr)] = 1; + + rc = rfc1035NameSkip(buf, sz, &ptr); + + loop_detect[loop_ptr] = 0; + return rc; + } else if (c > RFC1035_MAXLABELSZ) { + /* + * "(The 10 and 01 combinations are reserved for future use.)" + */ + return 3; /* reserved label/compression flags */ + } else { + (*off)++; + len = (size_t)c; + if (len == 0) + break; + if ((*off) + len > sz) + return 4; /* message is too short */ + (*off) += len; + } + } while (c > 0); + return 0; +} + +static off_t skip_question(const u_char* buf, int len, off_t offset) +{ + if (rfc1035NameSkip(buf, len, &offset)) + return 0; + if (offset + 4 > len) + return 0; + offset += 4; + return offset; +} + +static off_t skip_rr(const u_char* buf, int len, off_t offset) +{ + if (rfc1035NameSkip(buf, len, &offset)) + return 0; + if (offset + 10 > len) + return 0; + unsigned short us = nptohs(buf + offset + 8); + offset += 10; + if (offset + us > len) + return 0; + offset += us; + return offset; +} + +#define EDNS0_TYPE_ECS 8 + +typedef void (*edns0_ecs_cb)(int family, u_char* buf, size_t len); + +static void process_edns0_options(u_char* buf, int len, edns0_ecs_cb cb) +{ + unsigned short edns0_type; + unsigned short edns0_len; + off_t offset = 0; + + while (len >= 4) { + edns0_type = nptohs(buf + offset); + edns0_len = nptohs(buf + offset + 2); + if (len < 4 + edns0_len) + break; + if (edns0_type == EDNS0_TYPE_ECS) { + if (edns0_len < 5) + break; + if (cb) + cb(nptohs(buf + offset + 4), buf + offset + 8, edns0_len - 4); + } + offset += 4 + edns0_len; + len -= 4 + edns0_len; + } +} + +#define T_OPT 41 + +static off_t grok_additional_for_opt_rr(u_char* buf, int len, off_t offset, edns0_ecs_cb cb) +{ + unsigned short us; + /* + * OPT RR for EDNS0 MUST be 0 (root domain), so if the first byte of + * the name is anything it can't be a valid EDNS0 record. + */ + if (*(buf + offset)) { + if (rfc1035NameSkip(buf, len, &offset)) + return 0; + if (offset + 10 > len) + return 0; + } else { + offset++; + if (offset + 10 > len) + return 0; + if (nptohs(buf + offset) == T_OPT) { + u_char version = *(buf + offset + 5); + us = nptohs(buf + offset + 8); // rd len + offset += 10; + if (offset + us > len) + return 0; + if (!version && us > 0) + process_edns0_options(buf + offset, us, cb); + offset += us; + return offset; + } + } + /* get rdlength */ + us = nptohs(buf + offset + 8); + offset += 10; + if (offset + us > len) + return 0; + offset += us; + return offset; +} + +static void parse_for_edns0_ecs(u_char* payload, size_t payloadlen, edns0_ecs_cb cb) +{ + off_t offset; + int qdcount, ancount, nscount, arcount; + + qdcount = nptohs(payload + 4); + ancount = nptohs(payload + 6); + nscount = nptohs(payload + 8); + arcount = nptohs(payload + 10); + + offset = DNS_MSG_HDR_SZ; + + while (qdcount > 0 && offset < payloadlen) { + if (!(offset = skip_question(payload, payloadlen, offset))) { + return; + } + qdcount--; + } + + while (ancount > 0 && offset < payloadlen) { + if (!(offset = skip_rr(payload, payloadlen, offset))) { + return; + } + ancount--; + } + + while (nscount > 0 && offset < payloadlen) { + if (!(offset = skip_rr(payload, payloadlen, offset))) { + return; + } + nscount--; + } + + while (arcount > 0 && offset < payloadlen) { + if (!(offset = grok_additional_for_opt_rr(payload, payloadlen, offset, cb))) { + return; + } + arcount--; + } +} \ No newline at end of file -- cgit v1.2.3