-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDnsd.cpp
171 lines (156 loc) · 5.33 KB
/
Dnsd.cpp
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
* SPDX-FileCopyrightText: 2019 Mike Dunston (atanisoft)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Dnsd.h"
#include "freertos_includes.h"
#include <iomanip>
#include <sys/socket.h>
#include <utils/format_utils.hxx>
#include <utils/logging.h>
#if CONFIG_IDF_TARGET_LINUX
#include <arpa/inet.h>
#endif
#ifndef CONFIG_HTTP_DNS_LOG_LEVEL
#define CONFIG_HTTP_DNS_LOG_LEVEL VERBOSE
#endif
namespace http
{
static void* dns_thread_start(void* arg)
{
Singleton<Dnsd>::instance()->dns_process_thread();
return nullptr;
}
Dnsd::Dnsd(uint32_t ip, string name, uint16_t port)
: local_ip_(ip), name_(name), port_(port),
dns_thread_(name_.c_str(), config_dnsd_priority(),
config_dnsd_stack_size(), dns_thread_start, nullptr),
buffer_(config_dnsd_buffer_size(), 0)
{
}
Dnsd::~Dnsd()
{
shutdown_ = true;
while(!shutdownComplete_)
{
vTaskDelay(pdMS_TO_TICKS(1));
}
}
void Dnsd::dns_process_thread()
{
struct sockaddr_in addr;
int fd;
ERRNOCHECK("socket", fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port_);
int val = 1;
ERRNOCHECK("setsockopt_reuseaddr",
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)));
ERRNOCHECK("bind",
::bind(fd, (struct sockaddr *) &addr, sizeof(addr)));
LOG(INFO, "[%s] Listening on port %d, fd %d, using %s for local IP",
name_.c_str(), port_, fd, ipv4_to_string(local_ip_).c_str());
while (!shutdown_)
{
sockaddr_in source;
socklen_t source_len = sizeof(sockaddr_in);
bzero(buffer_.data(), config_dnsd_buffer_size());
// This will block the thread until it receives a message on the socket.
int len = ::recvfrom(fd, buffer_.data(), config_dnsd_buffer_size(),
DNS_RECVFROM_FLAGS, (sockaddr *)&source, &source_len);
if (len >= sizeof(DNSHeader))
{
DNSHeader *header = (DNSHeader *)buffer_.data();
string parsed_domain_name;
// parse the request to extract the domain name being requested
if (ntohs(header->questions) == 1 && header->answers == 0 &&
header->authorties == 0 && header->resources == 0)
{
// extract the requested domain name, it is a broken into segments
// with the period replaced with a zero byte between segments instead
// of the period. Max length of the domain name segments is 255 bytes
// including the null markers.
uint16_t offset = sizeof(DNSHeader);
while (offset < 255 && offset < len)
{
uint8_t segment_len = buffer_[offset++];
if (segment_len)
{
if (parsed_domain_name.length())
{
parsed_domain_name += '.';
}
parsed_domain_name.append(
(const char *)(buffer_.data() + offset), segment_len);
offset += segment_len;
}
else
{
break;
}
}
}
if (parsed_domain_name.empty())
{
// no domain name to look up, discard the request and get another.
continue;
}
LOG(CONFIG_HTTP_DNS_LOG_LEVEL,
"[%s <- %s] id: %d, rd:%d, tc:%d, aa:%d, opc:%d, qr:%d, rc:%d, z:%d "
"ra:%d, q:%d, a:%d, au:%d, res:%d, len:%d, domain:%s",
name_.c_str(), inet_ntoa(source.sin_addr), header->id, header->rd,
header->tc, header->aa, header->opc, header->qr, header->rc,
header->z, header->ra, header->questions, header->answers,
header->authorties, header->resources, len,
parsed_domain_name.c_str());
// check if it is a request or response, qr = 0 and opc = 0 is request
if (!header->qr && !header->opc)
{
if ((len + sizeof(DNSResponse)) > config_dnsd_buffer_size())
{
LOG_ERROR("[%s] Buffer overrun (req:%d, resp:%zu, buf:%d), dropping "
"request!", name_.c_str(), len, len + sizeof(DNSResponse),
config_dnsd_buffer_size());
continue;
}
// convert the request to a response by modifying the request header.
header->qr = 1;
header->ra = 1;
header->answers = 1;
// response payload
DNSResponse response =
{
.id = htons(0xC00C),
.answer = htons(1),
.classes = htons(1),
.ttl = htonl(60),
.length = htons(sizeof(uint32_t)),
.address = htonl(local_ip_)
};
// Add the response payload to the buffer for sending
memcpy(buffer_.data() + len, &response, sizeof(DNSResponse));
LOG(CONFIG_HTTP_DNS_LOG_LEVEL, "[%s -> %s] %s -> %s", name_.c_str(),
inet_ntoa(source.sin_addr), parsed_domain_name.c_str(),
ipv4_to_string(local_ip_).c_str());
// send the response to the caller
ERRNOCHECK("sendto", sendto(fd, buffer_.data(),
len + sizeof(DNSResponse), DNS_SENDTO_FLAGS,
(const sockaddr*)&source,
sizeof(struct sockaddr_in)));
}
}
else
{
if (errno == EAGAIN || errno == ECONNRESET || errno == ENOTCONN ||
errno == ETIMEDOUT)
{
continue;
}
print_errno_and_exit("recvfrom");
}
}
shutdownComplete_ = true;
}
} // namespace http