Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 0 additions & 35 deletions src/dns_server.h

This file was deleted.

165 changes: 165 additions & 0 deletions src/dns_server_tcp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "dns_server_tcp.h"
#include "logging.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>

#define REQUEST_MAX 4096

typedef struct {
int fd;
ev_io io;
ev_timer timer;
dns_server_tcp_t *server;
} tcp_client_t;

#define TCP_IDLE_TIMEOUT 90.0

static int get_listen_sock_tcp(const char *listen_addr, int listen_port) {
struct addrinfo hints, *ai;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

char port_str[16];
snprintf(port_str, sizeof(port_str), "%d", listen_port);
int res = getaddrinfo(listen_addr, port_str, &hints, &ai);
if (res != 0) {
FLOG("getaddrinfo failed: %s", gai_strerror(res));
return -1;
}

int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
FLOG("socket failed: %s", strerror(errno));
freeaddrinfo(ai);
return -1;
}

int optval = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
FLOG("bind failed: %s", strerror(errno));
close(sock);
freeaddrinfo(ai);
return -1;
}

if (listen(sock, 128) < 0) {
FLOG("listen failed: %s", strerror(errno));
close(sock);
freeaddrinfo(ai);
return -1;
}

freeaddrinfo(ai);
ILOG("TCP Listening on %s:%d", listen_addr, listen_port);
return sock;
}

static void tcp_client_close(struct ev_loop *loop, tcp_client_t *client) {
ev_io_stop(loop, &client->io);
ev_timer_stop(loop, &client->timer);
close(client->fd);
free(client);
}

static void tcp_client_timeout_cb(struct ev_loop *loop, ev_timer *w, int __attribute__((unused)) revents) {
tcp_client_t *client = (tcp_client_t *)w->data;
DLOG("TCP client fd %d timed out after %ds inactivity", client->fd, (int)TCP_IDLE_TIMEOUT);
tcp_client_close(loop, client);
}

static void tcp_client_cb(struct ev_loop *loop, ev_io *w, int __attribute__((unused)) revents) {
tcp_client_t *client = (tcp_client_t *)w->data;
int client_fd = client->fd;
uint8_t lenbuf[2];
ssize_t n = recv(client_fd, lenbuf, 2, MSG_PEEK);
if (n < 2) {
tcp_client_close(loop, client);
return;
}
recv(client_fd, lenbuf, 2, 0);
uint16_t msglen = (lenbuf[0] << 8) | lenbuf[1];
if (msglen > REQUEST_MAX) {
WLOG("TCP DNS request too large");
tcp_client_close(loop, client);
return;
}
char *buf = (char *)calloc(1, msglen + 1);
if (!buf) {
FLOG("Out of mem");
tcp_client_close(loop, client);
return;
}
ssize_t rlen = recv(client_fd, buf, msglen, 0);
if (rlen < msglen) {
WLOG("Short TCP DNS read");
free(buf);
tcp_client_close(loop, client);
return;
}
uint16_t tx_id = ntohs(*((uint16_t*)buf));
dns_server_tcp_t *d = client->server;
d->cb(d, d->cb_data, client_fd, tx_id, buf, msglen);
// Reset inactivity timer
ev_timer_stop(loop, &client->timer);
ev_timer_set(&client->timer, TCP_IDLE_TIMEOUT, 0.0);
ev_timer_start(loop, &client->timer);
}

static void tcp_accept_cb(struct ev_loop *loop, ev_io *w, int __attribute__((unused)) revents) {
dns_server_tcp_t *d = (dns_server_tcp_t *)w->data;
int client_fd = accept(w->fd, NULL, NULL);
if (client_fd < 0) {
ELOG("accept failed: %s", strerror(errno));
return;
}
tcp_client_t *client = (tcp_client_t *)calloc(1, sizeof(tcp_client_t));
if (!client) {
FLOG("Out of mem");
close(client_fd);
return;
}
client->fd = client_fd;
client->server = d;
client->io.data = client;
client->timer.data = client;
ev_io_init(&client->io, tcp_client_cb, client_fd, EV_READ);
ev_timer_init(&client->timer, tcp_client_timeout_cb, TCP_IDLE_TIMEOUT, 0.0);
ev_io_start(loop, &client->io);
ev_timer_start(loop, &client->timer);
}

void dns_server_tcp_init(dns_server_tcp_t *d, struct ev_loop *loop,
const char *listen_addr, int listen_port,
dns_tcp_req_received_cb cb, void *data) {
d->loop = loop;
d->sock = get_listen_sock_tcp(listen_addr, listen_port);
d->cb = cb;
d->cb_data = data;
ev_io_init(&d->watcher, tcp_accept_cb, d->sock, EV_READ);
d->watcher.data = d;
ev_io_start(d->loop, &d->watcher);
}

void dns_server_tcp_respond(int client_fd, char *buf, size_t blen) {
uint8_t lenbuf[2];
lenbuf[0] = (blen >> 8) & 0xff;
lenbuf[1] = blen & 0xff;
send(client_fd, lenbuf, 2, 0);
send(client_fd, buf, blen, 0);
}

void dns_server_tcp_stop(dns_server_tcp_t *d) {
ev_io_stop(d->loop, &d->watcher);
}

void dns_server_tcp_cleanup(dns_server_tcp_t *d) {
close(d->sock);
}
31 changes: 31 additions & 0 deletions src/dns_server_tcp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef _DNS_SERVER_TCP_H_
#define _DNS_SERVER_TCP_H_

#include <stdint.h>
#include <ev.h>

struct dns_server_tcp_s;

typedef void (*dns_tcp_req_received_cb)(struct dns_server_tcp_s *dns_server, void *data,
int client_fd, uint16_t tx_id,
char *dns_req, size_t dns_req_len);

typedef struct dns_server_tcp_s {
struct ev_loop *loop;
void *cb_data;
dns_tcp_req_received_cb cb;
int sock;
ev_io watcher;
} dns_server_tcp_t;

void dns_server_tcp_init(dns_server_tcp_t *d, struct ev_loop *loop,
const char *listen_addr, int listen_port,
dns_tcp_req_received_cb cb, void *data);

void dns_server_tcp_respond(int client_fd, char *buf, size_t blen);

void dns_server_tcp_stop(dns_server_tcp_t *d);

void dns_server_tcp_cleanup(dns_server_tcp_t *d);

#endif // _DNS_SERVER_TCP_H_
20 changes: 9 additions & 11 deletions src/dns_server.c → src/dns_server_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include <sys/socket.h> // NOLINT(llvmlibc-restrict-system-libc-headers)
#include <unistd.h> // NOLINT(llvmlibc-restrict-system-libc-headers)

#include "dns_server.h"
#include "dns_server_udp.h"
#include "logging.h"


Expand Down Expand Up @@ -60,7 +60,7 @@ static int get_listen_sock(const char *listen_addr, int listen_port,

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

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

void dns_server_init(dns_server_t *d, struct ev_loop *loop,
const char *listen_addr, int listen_port,
dns_req_received_cb cb, void *data) {
void dns_server_udp_init(dns_server_udp_t *d, struct ev_loop *loop,
const char *listen_addr, int listen_port,
dns_udp_req_received_cb cb, void *data) {
d->loop = loop;
d->sock = get_listen_sock(listen_addr, listen_port, &d->addrlen);
d->cb = cb;
d->cb_data = data;

// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
ev_io_init(&d->watcher, watcher_cb, d->sock, EV_READ);
d->watcher.data = d;
ev_io_start(d->loop, &d->watcher);
}

void dns_server_respond(dns_server_t *d, struct sockaddr *raddr, char *buf,
size_t blen) {
void dns_server_udp_respond(dns_server_udp_t *d, struct sockaddr *raddr, char *buf,
size_t blen) {
ssize_t len = sendto(d->sock, buf, blen, 0, raddr, d->addrlen);
if(len == -1) {
DLOG("sendto failed: %s", strerror(errno));
}
}

void dns_server_stop(dns_server_t *d) {
void dns_server_udp_stop(dns_server_udp_t *d) {
ev_io_stop(d->loop, &d->watcher);
}

void dns_server_cleanup(dns_server_t *d) {
void dns_server_udp_cleanup(dns_server_udp_t *d) {
close(d->sock);
}
35 changes: 35 additions & 0 deletions src/dns_server_udp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef _DNS_SERVER_UDP_H_
#define _DNS_SERVER_UDP_H_

#include <arpa/inet.h>
#include <stdint.h>
#include <ev.h>

struct dns_server_udp_s;

typedef void (*dns_udp_req_received_cb)(struct dns_server_udp_s *dns_server, void *data,
struct sockaddr* addr, uint16_t tx_id,
char *dns_req, size_t dns_req_len);

typedef struct dns_server_udp_s {
struct ev_loop *loop;
void *cb_data;
dns_udp_req_received_cb cb;
int sock;
socklen_t addrlen;
ev_io watcher;
} dns_server_udp_t;

void dns_server_udp_init(dns_server_udp_t *d, struct ev_loop *loop,
const char *listen_addr, int listen_port,
dns_udp_req_received_cb cb, void *data);

// Sends a DNS response 'buf' of length 'blen' to 'raddr'.
void dns_server_udp_respond(dns_server_udp_t *d, struct sockaddr *raddr, char *buf,
size_t blen);

void dns_server_udp_stop(dns_server_udp_t *d);

void dns_server_udp_cleanup(dns_server_udp_t *d);

#endif // _DNS_SERVER_UDP_H_
Loading
Loading