Skip to content

Commit c5b5e94

Browse files
committed
TCP support added.
1 parent 2963960 commit c5b5e94

File tree

7 files changed

+347
-76
lines changed

7 files changed

+347
-76
lines changed

src/dns_server.h

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/dns_server_tcp.c

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#include "dns_server_tcp.h"
2+
#include "logging.h"
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include <errno.h>
6+
#include <unistd.h>
7+
#include <netdb.h>
8+
#include <arpa/inet.h>
9+
10+
#define REQUEST_MAX 4096
11+
12+
typedef struct {
13+
int fd;
14+
ev_io io;
15+
ev_timer timer;
16+
dns_server_tcp_t *server;
17+
} tcp_client_t;
18+
19+
#define TCP_IDLE_TIMEOUT 90.0
20+
21+
static int get_listen_sock_tcp(const char *listen_addr, int listen_port) {
22+
struct addrinfo hints, *ai;
23+
memset(&hints, 0, sizeof(hints));
24+
hints.ai_family = AF_UNSPEC;
25+
hints.ai_socktype = SOCK_STREAM;
26+
hints.ai_flags = AI_PASSIVE;
27+
28+
char port_str[16];
29+
snprintf(port_str, sizeof(port_str), "%d", listen_port);
30+
int res = getaddrinfo(listen_addr, port_str, &hints, &ai);
31+
if (res != 0) {
32+
FLOG("getaddrinfo failed: %s", gai_strerror(res));
33+
return -1;
34+
}
35+
36+
int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
37+
if (sock < 0) {
38+
FLOG("socket failed: %s", strerror(errno));
39+
freeaddrinfo(ai);
40+
return -1;
41+
}
42+
43+
int optval = 1;
44+
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
45+
46+
if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
47+
FLOG("bind failed: %s", strerror(errno));
48+
close(sock);
49+
freeaddrinfo(ai);
50+
return -1;
51+
}
52+
53+
if (listen(sock, 128) < 0) {
54+
FLOG("listen failed: %s", strerror(errno));
55+
close(sock);
56+
freeaddrinfo(ai);
57+
return -1;
58+
}
59+
60+
freeaddrinfo(ai);
61+
ILOG("TCP Listening on %s:%d", listen_addr, listen_port);
62+
return sock;
63+
}
64+
65+
static void tcp_client_close(struct ev_loop *loop, tcp_client_t *client) {
66+
ev_io_stop(loop, &client->io);
67+
ev_timer_stop(loop, &client->timer);
68+
close(client->fd);
69+
free(client);
70+
}
71+
72+
static void tcp_client_timeout_cb(struct ev_loop *loop, ev_timer *w, int __attribute__((unused)) revents) {
73+
tcp_client_t *client = (tcp_client_t *)w->data;
74+
DLOG("TCP client fd %d timed out after %ds inactivity", client->fd, (int)TCP_IDLE_TIMEOUT);
75+
tcp_client_close(loop, client);
76+
}
77+
78+
static void tcp_client_cb(struct ev_loop *loop, ev_io *w, int __attribute__((unused)) revents) {
79+
tcp_client_t *client = (tcp_client_t *)w->data;
80+
int client_fd = client->fd;
81+
uint8_t lenbuf[2];
82+
ssize_t n = recv(client_fd, lenbuf, 2, MSG_PEEK);
83+
if (n < 2) {
84+
tcp_client_close(loop, client);
85+
return;
86+
}
87+
recv(client_fd, lenbuf, 2, 0);
88+
uint16_t msglen = (lenbuf[0] << 8) | lenbuf[1];
89+
if (msglen > REQUEST_MAX) {
90+
WLOG("TCP DNS request too large");
91+
tcp_client_close(loop, client);
92+
return;
93+
}
94+
char *buf = (char *)calloc(1, msglen + 1);
95+
if (!buf) {
96+
FLOG("Out of mem");
97+
tcp_client_close(loop, client);
98+
return;
99+
}
100+
ssize_t rlen = recv(client_fd, buf, msglen, 0);
101+
if (rlen < msglen) {
102+
WLOG("Short TCP DNS read");
103+
free(buf);
104+
tcp_client_close(loop, client);
105+
return;
106+
}
107+
uint16_t tx_id = ntohs(*((uint16_t*)buf));
108+
dns_server_tcp_t *d = client->server;
109+
d->cb(d, d->cb_data, client_fd, tx_id, buf, msglen);
110+
// Reset inactivity timer
111+
ev_timer_stop(loop, &client->timer);
112+
ev_timer_set(&client->timer, TCP_IDLE_TIMEOUT, 0.0);
113+
ev_timer_start(loop, &client->timer);
114+
}
115+
116+
static void tcp_accept_cb(struct ev_loop *loop, ev_io *w, int __attribute__((unused)) revents) {
117+
dns_server_tcp_t *d = (dns_server_tcp_t *)w->data;
118+
int client_fd = accept(w->fd, NULL, NULL);
119+
if (client_fd < 0) {
120+
ELOG("accept failed: %s", strerror(errno));
121+
return;
122+
}
123+
tcp_client_t *client = (tcp_client_t *)calloc(1, sizeof(tcp_client_t));
124+
if (!client) {
125+
FLOG("Out of mem");
126+
close(client_fd);
127+
return;
128+
}
129+
client->fd = client_fd;
130+
client->server = d;
131+
client->io.data = client;
132+
client->timer.data = client;
133+
ev_io_init(&client->io, tcp_client_cb, client_fd, EV_READ);
134+
ev_timer_init(&client->timer, tcp_client_timeout_cb, TCP_IDLE_TIMEOUT, 0.0);
135+
ev_io_start(loop, &client->io);
136+
ev_timer_start(loop, &client->timer);
137+
}
138+
139+
void dns_server_tcp_init(dns_server_tcp_t *d, struct ev_loop *loop,
140+
const char *listen_addr, int listen_port,
141+
dns_tcp_req_received_cb cb, void *data) {
142+
d->loop = loop;
143+
d->sock = get_listen_sock_tcp(listen_addr, listen_port);
144+
d->cb = cb;
145+
d->cb_data = data;
146+
ev_io_init(&d->watcher, tcp_accept_cb, d->sock, EV_READ);
147+
d->watcher.data = d;
148+
ev_io_start(d->loop, &d->watcher);
149+
}
150+
151+
void dns_server_tcp_respond(int client_fd, char *buf, size_t blen) {
152+
uint8_t lenbuf[2];
153+
lenbuf[0] = (blen >> 8) & 0xff;
154+
lenbuf[1] = blen & 0xff;
155+
send(client_fd, lenbuf, 2, 0);
156+
send(client_fd, buf, blen, 0);
157+
}
158+
159+
void dns_server_tcp_stop(dns_server_tcp_t *d) {
160+
ev_io_stop(d->loop, &d->watcher);
161+
}
162+
163+
void dns_server_tcp_cleanup(dns_server_tcp_t *d) {
164+
close(d->sock);
165+
}

src/dns_server_tcp.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef _DNS_SERVER_TCP_H_
2+
#define _DNS_SERVER_TCP_H_
3+
4+
#include <stdint.h>
5+
#include <ev.h>
6+
7+
struct dns_server_tcp_s;
8+
9+
typedef void (*dns_tcp_req_received_cb)(struct dns_server_tcp_s *dns_server, void *data,
10+
int client_fd, uint16_t tx_id,
11+
char *dns_req, size_t dns_req_len);
12+
13+
typedef struct dns_server_tcp_s {
14+
struct ev_loop *loop;
15+
void *cb_data;
16+
dns_tcp_req_received_cb cb;
17+
int sock;
18+
ev_io watcher;
19+
} dns_server_tcp_t;
20+
21+
void dns_server_tcp_init(dns_server_tcp_t *d, struct ev_loop *loop,
22+
const char *listen_addr, int listen_port,
23+
dns_tcp_req_received_cb cb, void *data);
24+
25+
void dns_server_tcp_respond(int client_fd, char *buf, size_t blen);
26+
27+
void dns_server_tcp_stop(dns_server_tcp_t *d);
28+
29+
void dns_server_tcp_cleanup(dns_server_tcp_t *d);
30+
31+
#endif // _DNS_SERVER_TCP_H_
Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <sys/socket.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
88
#include <unistd.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
99

10-
#include "dns_server.h"
10+
#include "dns_server_udp.h"
1111
#include "logging.h"
1212

1313

@@ -60,7 +60,7 @@ static int get_listen_sock(const char *listen_addr, int listen_port,
6060

6161
static void watcher_cb(struct ev_loop __attribute__((unused)) *loop,
6262
ev_io *w, int __attribute__((unused)) revents) {
63-
dns_server_t *d = (dns_server_t *)w->data;
63+
dns_server_udp_t *d = (dns_server_udp_t *)w->data;
6464

6565
char *buf = (char *)calloc(1, REQUEST_MAX + 1);
6666
if (buf == NULL) {
@@ -85,32 +85,30 @@ static void watcher_cb(struct ev_loop __attribute__((unused)) *loop,
8585
d->cb(d, d->cb_data, (struct sockaddr*)&raddr, tx_id, buf, len);
8686
}
8787

88-
void dns_server_init(dns_server_t *d, struct ev_loop *loop,
89-
const char *listen_addr, int listen_port,
90-
dns_req_received_cb cb, void *data) {
88+
void dns_server_udp_init(dns_server_udp_t *d, struct ev_loop *loop,
89+
const char *listen_addr, int listen_port,
90+
dns_udp_req_received_cb cb, void *data) {
9191
d->loop = loop;
9292
d->sock = get_listen_sock(listen_addr, listen_port, &d->addrlen);
9393
d->cb = cb;
9494
d->cb_data = data;
95-
96-
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
9795
ev_io_init(&d->watcher, watcher_cb, d->sock, EV_READ);
9896
d->watcher.data = d;
9997
ev_io_start(d->loop, &d->watcher);
10098
}
10199

102-
void dns_server_respond(dns_server_t *d, struct sockaddr *raddr, char *buf,
103-
size_t blen) {
100+
void dns_server_udp_respond(dns_server_udp_t *d, struct sockaddr *raddr, char *buf,
101+
size_t blen) {
104102
ssize_t len = sendto(d->sock, buf, blen, 0, raddr, d->addrlen);
105103
if(len == -1) {
106104
DLOG("sendto failed: %s", strerror(errno));
107105
}
108106
}
109107

110-
void dns_server_stop(dns_server_t *d) {
108+
void dns_server_udp_stop(dns_server_udp_t *d) {
111109
ev_io_stop(d->loop, &d->watcher);
112110
}
113111

114-
void dns_server_cleanup(dns_server_t *d) {
112+
void dns_server_udp_cleanup(dns_server_udp_t *d) {
115113
close(d->sock);
116114
}

src/dns_server_udp.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef _DNS_SERVER_UDP_H_
2+
#define _DNS_SERVER_UDP_H_
3+
4+
#include <arpa/inet.h>
5+
#include <stdint.h>
6+
#include <ev.h>
7+
8+
struct dns_server_udp_s;
9+
10+
typedef void (*dns_udp_req_received_cb)(struct dns_server_udp_s *dns_server, void *data,
11+
struct sockaddr* addr, uint16_t tx_id,
12+
char *dns_req, size_t dns_req_len);
13+
14+
typedef struct dns_server_udp_s {
15+
struct ev_loop *loop;
16+
void *cb_data;
17+
dns_udp_req_received_cb cb;
18+
int sock;
19+
socklen_t addrlen;
20+
ev_io watcher;
21+
} dns_server_udp_t;
22+
23+
void dns_server_udp_init(dns_server_udp_t *d, struct ev_loop *loop,
24+
const char *listen_addr, int listen_port,
25+
dns_udp_req_received_cb cb, void *data);
26+
27+
// Sends a DNS response 'buf' of length 'blen' to 'raddr'.
28+
void dns_server_udp_respond(dns_server_udp_t *d, struct sockaddr *raddr, char *buf,
29+
size_t blen);
30+
31+
void dns_server_udp_stop(dns_server_udp_t *d);
32+
33+
void dns_server_udp_cleanup(dns_server_udp_t *d);
34+
35+
#endif // _DNS_SERVER_UDP_H_

0 commit comments

Comments
 (0)