-
Notifications
You must be signed in to change notification settings - Fork 74
/
edns.c
133 lines (122 loc) · 4.51 KB
/
edns.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#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;
}