upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/dns_server.c
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-15 13:06:25 +0530
committerYour Name <you@example.com>2026-05-15 13:06:25 +0530
commit8a2307a5ced6da94cc674602219d5a68a1246264 (patch)
treefe622a9960434fc3a42d3d1aa2ba748d804118fa /main/dns_server.c
initiall commit
Diffstat (limited to 'main/dns_server.c')
-rw-r--r--main/dns_server.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/main/dns_server.c b/main/dns_server.c
new file mode 100644
index 0000000..f7977c6
--- /dev/null
+++ b/main/dns_server.c
@@ -0,0 +1,270 @@
1#include "dns_server.h"
2#include "esp_log.h"
3#include "freertos/FreeRTOS.h"
4#include "freertos/task.h"
5#include "lwip/sockets.h"
6#include "lwip/netdb.h"
7#include <string.h>
8#include <sys/param.h>
9
10#define MAX_AUTH_IPS 10
11#define MAX_PENDING 50
12#define DNS_BUF_SIZE 512
13#define DNS_PORT 53
14#define DNS_TASK_STACK 4096
15#define DNS_TASK_PRIO 5
16#define DNS_FORWARD_TIMEOUT_MS 2000
17#define NXDOMAIN_TTL 30
18
19static const char *TAG = "dns_server";
20
21#pragma pack(push, 1)
22typedef struct {
23 uint16_t id;
24 uint16_t flags;
25 uint16_t qdcount;
26 uint16_t ancount;
27 uint16_t nscount;
28 uint16_t arcount;
29} dns_header_t;
30#pragma pack(pop)
31
32#pragma pack(push, 1)
33typedef struct {
34 uint16_t name;
35 uint16_t type;
36 uint16_t class;
37 uint32_t ttl;
38 uint16_t len;
39 uint32_t addr;
40} dns_answer_t;
41#pragma pack(pop)
42
43typedef struct {
44 uint32_t ip;
45} auth_entry_t;
46
47static auth_entry_t s_auth_list[MAX_AUTH_IPS];
48static int s_auth_count = 0;
49static TaskHandle_t s_dns_task = NULL;
50static volatile bool s_dns_running = false;
51static esp_ip4_addr_t s_ap_ip;
52static esp_ip4_addr_t s_upstream_dns;
53
54static bool is_authenticated(uint32_t ip)
55{
56 for (int i = 0; i < s_auth_count; i++) {
57 if (s_auth_list[i].ip == ip) return true;
58 }
59 return false;
60}
61
62static void parse_dns_name(const uint8_t *buf, int buf_len, int offset, char *out, int out_len)
63{
64 int pos = offset;
65 int out_pos = 0;
66 int jumped = 0;
67 int jump_pos = 0;
68 while (pos < buf_len && out_pos < out_len - 1) {
69 uint8_t len = buf[pos];
70 if (len == 0) break;
71 if ((len & 0xC0) == 0xC0) {
72 if (!jumped) jump_pos = pos + 2;
73 pos = ((len & 0x3F) << 8) | buf[pos + 1];
74 jumped = 1;
75 continue;
76 }
77 if (out_pos > 0 && out_pos < out_len - 1) out[out_pos++] = '.';
78 pos++;
79 for (int i = 0; i < len && pos < buf_len && out_pos < out_len - 1; i++) {
80 out[out_pos++] = buf[pos++];
81 }
82 }
83 out[out_pos] = '\0';
84}
85
86static int build_nxdomain(uint8_t *response, int req_len)
87{
88 memcpy(response, response, req_len);
89 dns_header_t *hdr = (dns_header_t *)response;
90 hdr->flags = htons(0x8403);
91 hdr->ancount = 0;
92 hdr->nscount = 0;
93 hdr->arcount = 0;
94 return req_len;
95}
96
97static int build_redirect_response(uint8_t *response, int req_len)
98{
99 memcpy(response, response, req_len);
100 dns_header_t *hdr = (dns_header_t *)response;
101 hdr->flags = htons(0x8180);
102 hdr->ancount = htons(1);
103 hdr->nscount = 0;
104 hdr->arcount = 0;
105 int resp_len = req_len;
106 dns_answer_t ans;
107 ans.name = htons(0xC00C);
108 ans.type = htons(1);
109 ans.class = htons(1);
110 ans.ttl = htonl(NXDOMAIN_TTL);
111 ans.len = htons(4);
112 ans.addr = s_ap_ip.addr;
113 memcpy(response + resp_len, &ans, sizeof(ans));
114 resp_len += sizeof(ans);
115 return resp_len;
116}
117
118static int forward_dns(const uint8_t *req, int req_len, uint8_t *resp, int resp_buf_len,
119 const struct sockaddr_in *client_addr, uint16_t txn_id)
120{
121 int upstream_sock = socket(AF_INET, SOCK_DGRAM, 0);
122 if (upstream_sock < 0) return -1;
123
124 struct timeval tv = { .tv_sec = DNS_FORWARD_TIMEOUT_MS / 1000, .tv_usec = (DNS_FORWARD_TIMEOUT_MS % 1000) * 1000 };
125 setsockopt(upstream_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
126
127 struct sockaddr_in upstream_addr = {
128 .sin_family = AF_INET,
129 .sin_port = htons(DNS_PORT),
130 .sin_addr.s_addr = s_upstream_dns.addr,
131 };
132
133 sendto(upstream_sock, req, req_len, 0, (struct sockaddr *)&upstream_addr, sizeof(upstream_addr));
134
135 int n = recvfrom(upstream_sock, resp, resp_buf_len, 0, NULL, NULL);
136 close(upstream_sock);
137
138 if (n > 0) {
139 if (n >= sizeof(dns_header_t)) {
140 dns_header_t *hdr = (dns_header_t *)resp;
141 hdr->id = htons(txn_id);
142 }
143 }
144 return n;
145}
146
147static void dns_server_task(void *arg)
148{
149 int sock = socket(AF_INET, SOCK_DGRAM, 0);
150 if (sock < 0) {
151 ESP_LOGE(TAG, "Failed to create DNS socket");
152 s_dns_running = false;
153 vTaskDelete(NULL);
154 return;
155 }
156
157 struct sockaddr_in bind_addr = {
158 .sin_family = AF_INET,
159 .sin_port = htons(DNS_PORT),
160 .sin_addr.s_addr = INADDR_ANY,
161 };
162 if (bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) {
163 ESP_LOGE(TAG, "Failed to bind DNS socket");
164 close(sock);
165 s_dns_running = false;
166 vTaskDelete(NULL);
167 return;
168 }
169
170 ESP_LOGI(TAG, "DNS server started on port %d, AP IP=" IPSTR ", upstream DNS=" IPSTR,
171 DNS_PORT, IP2STR(&s_ap_ip), IP2STR(&s_upstream_dns));
172
173 uint8_t rx_buf[DNS_BUF_SIZE];
174 uint8_t tx_buf[DNS_BUF_SIZE + sizeof(dns_answer_t)];
175
176 while (s_dns_running) {
177 struct sockaddr_in client_addr;
178 socklen_t client_len = sizeof(client_addr);
179 int n = recvfrom(sock, rx_buf, sizeof(rx_buf), 0,
180 (struct sockaddr *)&client_addr, &client_len);
181 if (n < (int)sizeof(dns_header_t)) continue;
182
183 uint32_t client_ip = client_addr.sin_addr.s_addr;
184 dns_header_t *hdr = (dns_header_t *)rx_buf;
185 uint16_t txn_id = ntohs(hdr->id);
186 bool is_query = (ntohs(hdr->flags) & 0x8000) == 0;
187 uint16_t qdcount = ntohs(hdr->qdcount);
188
189 if (!is_query || qdcount == 0) continue;
190
191 int q_offset = sizeof(dns_header_t);
192 while (q_offset < n && rx_buf[q_offset] != 0) {
193 q_offset += rx_buf[q_offset] + 1;
194 }
195 if (q_offset + 5 > n) continue;
196 uint16_t qtype = (rx_buf[q_offset + 1] << 8) | rx_buf[q_offset + 2];
197 int req_len = q_offset + 5;
198
199 if (is_authenticated(client_ip)) {
200 int resp_len = forward_dns(rx_buf, req_len, tx_buf, sizeof(tx_buf), &client_addr, txn_id);
201 if (resp_len > 0) {
202 sendto(sock, tx_buf, resp_len, 0, (struct sockaddr *)&client_addr, client_len);
203 }
204 } else {
205 if (qtype == 1) {
206 int resp_len = build_redirect_response(rx_buf, req_len);
207 memcpy(tx_buf, rx_buf, resp_len);
208 dns_header_t *resp_hdr = (dns_header_t *)tx_buf;
209 resp_hdr->id = htons(txn_id);
210 sendto(sock, tx_buf, resp_len, 0, (struct sockaddr *)&client_addr, client_len);
211 } else if (qtype == 28) {
212 int resp_len = build_nxdomain(rx_buf, req_len);
213 memcpy(tx_buf, rx_buf, resp_len);
214 dns_header_t *resp_hdr = (dns_header_t *)tx_buf;
215 resp_hdr->id = htons(txn_id);
216 sendto(sock, tx_buf, resp_len, 0, (struct sockaddr *)&client_addr, client_len);
217 } else {
218 int resp_len = forward_dns(rx_buf, req_len, tx_buf, sizeof(tx_buf), &client_addr, txn_id);
219 if (resp_len > 0) {
220 sendto(sock, tx_buf, resp_len, 0, (struct sockaddr *)&client_addr, client_len);
221 }
222 }
223 }
224 }
225
226 close(sock);
227 ESP_LOGI(TAG, "DNS server stopped");
228 vTaskDelete(NULL);
229}
230
231esp_err_t dns_server_start(esp_ip4_addr_t ap_ip, esp_ip4_addr_t upstream_dns)
232{
233 if (s_dns_running) return ESP_OK;
234 s_ap_ip = ap_ip;
235 s_upstream_dns = upstream_dns;
236 s_dns_running = true;
237 xTaskCreate(dns_server_task, "dns_server", DNS_TASK_STACK, NULL, DNS_TASK_PRIO, &s_dns_task);
238 return ESP_OK;
239}
240
241void dns_server_stop(void)
242{
243 s_dns_running = false;
244 vTaskDelay(pdMS_TO_TICKS(200));
245 s_dns_task = NULL;
246}
247
248void dns_server_set_client_authenticated(uint32_t client_ip, bool authenticated)
249{
250 if (authenticated) {
251 if (is_authenticated(client_ip)) return;
252 if (s_auth_count < MAX_AUTH_IPS) {
253 s_auth_list[s_auth_count].ip = client_ip;
254 s_auth_count++;
255 }
256 } else {
257 for (int i = 0; i < s_auth_count; i++) {
258 if (s_auth_list[i].ip == client_ip) {
259 s_auth_list[i] = s_auth_list[s_auth_count - 1];
260 s_auth_count--;
261 return;
262 }
263 }
264 }
265}
266
267bool dns_server_is_running(void)
268{
269 return s_dns_running;
270}