#include "dnscrypt.h" static int _skip_name(const uint8_t *const dns_packet, const size_t dns_packet_len, size_t * const offset_p) { size_t offset = *offset_p; uint8_t name_component_len; if (dns_packet_len < (size_t) 1U || offset >= dns_packet_len - (size_t) 1U) { return -1; } do { name_component_len = dns_packet[offset]; if ((name_component_len & 0xC0) == 0xC0) { name_component_len = 1U; } if (name_component_len >= dns_packet_len - offset - 1U) { return -1; } offset += name_component_len + 1U; } while (name_component_len != 0U); if (offset >= dns_packet_len) { return -1; } *offset_p = offset; return 0; } #define DNS_QTYPE_PLUS_QCLASS_LEN 4U static ssize_t edns_get_payload_size(const uint8_t *const dns_packet, const size_t dns_packet_len) { size_t offset; size_t payload_size; unsigned int arcount; assert(dns_packet_len >= DNS_HEADER_SIZE); arcount = (dns_packet[DNS_OFFSET_ARCOUNT] << 8) | dns_packet[DNS_OFFSET_ARCOUNT + 1U]; assert(arcount > 0U); assert(DNS_OFFSET_QUESTION <= DNS_HEADER_SIZE); if (dns_packet[DNS_OFFSET_QDCOUNT] != 0U || dns_packet[DNS_OFFSET_QDCOUNT + 1U] != 1U || (dns_packet[DNS_OFFSET_ANCOUNT] | dns_packet[DNS_OFFSET_ANCOUNT + 1U]) != 0U || (dns_packet[DNS_OFFSET_NSCOUNT] | dns_packet[DNS_OFFSET_NSCOUNT + 1U]) != 0U) { return (ssize_t) - 1; } offset = DNS_OFFSET_QUESTION; if (_skip_name(dns_packet, dns_packet_len, &offset) != 0) { return (ssize_t) - 1; } assert(dns_packet_len > (size_t) DNS_QTYPE_PLUS_QCLASS_LEN); if (offset >= dns_packet_len - (size_t) DNS_QTYPE_PLUS_QCLASS_LEN) { return (ssize_t) - 1; } offset += DNS_QTYPE_PLUS_QCLASS_LEN; assert(dns_packet_len >= DNS_OFFSET_EDNS_PAYLOAD_SIZE + 2U); if (_skip_name(dns_packet, dns_packet_len, &offset) != 0 || offset >= dns_packet_len - DNS_OFFSET_EDNS_PAYLOAD_SIZE - 2U) { return (ssize_t) - 1; } assert(DNS_OFFSET_EDNS_PAYLOAD_SIZE > DNS_OFFSET_EDNS_TYPE); if (dns_packet[offset + DNS_OFFSET_EDNS_TYPE] != 0U || dns_packet[offset + DNS_OFFSET_EDNS_TYPE + 1U] != DNS_TYPE_OPT) { return (ssize_t) - 1; } payload_size = (dns_packet[offset + DNS_OFFSET_EDNS_PAYLOAD_SIZE] << 8) | dns_packet[offset + DNS_OFFSET_EDNS_PAYLOAD_SIZE + 1U]; if (payload_size < DNS_MAX_PACKET_SIZE_UDP_SEND) { payload_size = DNS_MAX_PACKET_SIZE_UDP_SEND; } return (ssize_t) payload_size; } int edns_add_section(struct context *const c, uint8_t *const dns_packet, size_t * const dns_packet_len_p, size_t dns_packet_max_size, size_t * const request_edns_payload_size) { const size_t edns_payload_size = c->edns_payload_size; assert(edns_payload_size <= (size_t) 0xFFFF); assert(DNS_OFFSET_ARCOUNT + 2U <= DNS_HEADER_SIZE); if (edns_payload_size <= DNS_MAX_PACKET_SIZE_UDP_SEND || *dns_packet_len_p <= DNS_HEADER_SIZE) { *request_edns_payload_size = (size_t) 0U; return -1; } if ((dns_packet[DNS_OFFSET_ARCOUNT] | dns_packet[DNS_OFFSET_ARCOUNT + 1U]) != 0U) { const ssize_t edns_payload_ssize = edns_get_payload_size(dns_packet, *dns_packet_len_p); if (edns_payload_ssize <= (ssize_t) 0U) { *request_edns_payload_size = (size_t) 0U; return -1; } *request_edns_payload_size = (size_t) edns_payload_ssize; return 1; } assert(dns_packet_max_size >= *dns_packet_len_p); assert(DNS_OFFSET_EDNS_TYPE == 0U); assert(DNS_OFFSET_EDNS_PAYLOAD_SIZE == 2U); uint8_t opt_rr[] = { 0U, /* name */ 0U, DNS_TYPE_OPT, /* type */ (edns_payload_size >> 8) & 0xFF, edns_payload_size & 0xFF, 0U, 0U, 0U, 0U, /* rcode */ 0U, 0U /* rdlen */ }; if (dns_packet_max_size - *dns_packet_len_p < sizeof opt_rr) { *request_edns_payload_size = (size_t) 0U; return -1; } assert(dns_packet[DNS_OFFSET_ARCOUNT + 1U] == 0U); dns_packet[DNS_OFFSET_ARCOUNT + 1U] = 1U; memcpy(dns_packet + *dns_packet_len_p, opt_rr, sizeof opt_rr); *dns_packet_len_p += sizeof opt_rr; *request_edns_payload_size = edns_payload_size; assert(*dns_packet_len_p <= dns_packet_max_size); assert(*dns_packet_len_p <= 0xFFFF); return 0; }