summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/addr.c233
-rw-r--r--src/server/addr.h69
-rw-r--r--src/server/binding.c245
-rw-r--r--src/server/binding.h42
-rw-r--r--src/server/resolver.c166
-rw-r--r--src/server/resolver.h6
-rw-r--r--src/server/server.c100
-rw-r--r--src/server/server.h12
8 files changed, 873 insertions, 0 deletions
diff --git a/src/server/addr.c b/src/server/addr.c
new file mode 100644
index 0000000..982da13
--- /dev/null
+++ b/src/server/addr.c
@@ -0,0 +1,233 @@
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "addr.h"
+#include "../io/log.h"
+
+void create_ip_addr(char* domain, IpAddr* addr) {
+ addr->type = V4;
+ memcpy(&addr->data.v4.s_addr, domain, 4);
+}
+
+void create_ip_addr6(char* domain, IpAddr* addr) {
+ addr->type = V6;
+ memcpy(&addr->data.v6.__in6_u.__u6_addr8, domain, 16);
+}
+
+void ip_addr_any(IpAddr* addr) {
+ addr->type = V4;
+ addr->data.v4.s_addr = htonl(INADDR_ANY);
+}
+
+void ip_addr_any6(IpAddr* addr) {
+ addr->type = V6;
+ addr->data.v6 = in6addr_any;
+}
+
+static struct sockaddr_in create_socket_addr_v4(IpAddr addr, uint16_t port) {
+ struct sockaddr_in socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin_family = AF_INET;
+ socketaddr.sin_port = htons(port);
+ socketaddr.sin_addr = addr.data.v4;
+ return socketaddr;
+}
+
+static struct sockaddr_in6 create_socket_addr_v6(IpAddr addr, uint16_t port) {
+ struct sockaddr_in6 socketaddr;
+ memset(&socketaddr, 0, sizeof(socketaddr));
+ socketaddr.sin6_family = AF_INET6;
+ socketaddr.sin6_port = htons(port);
+ socketaddr.sin6_addr = addr.data.v6;
+ return socketaddr;
+}
+
+static size_t get_addr_len(AddrType type) {
+ if (type == V4) {
+ return sizeof(struct sockaddr_in);
+ } else if (type == V6) {
+ return sizeof(struct sockaddr_in6);
+ } else {
+ return 0;
+ }
+}
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket) {
+ socket->type = addr.type;
+ if (addr.type == V4) {
+ socket->data.v4 = create_socket_addr_v4(addr, port);
+ } else if(addr.type == V6) {
+ socket->data.v6 = create_socket_addr_v6(addr, port);
+ } else {
+ ERROR("Tried to create socketaddr with invalid protocol type");
+ exit(EXIT_FAILURE);
+ }
+ socket->len = get_addr_len(addr.type);
+}
+
+void print_socket_addr(SocketAddr* addr, char* buffer) {
+ INIT_LOG_BOUNDS
+ if(addr->type == V4) {
+ APPEND(buffer, "%hhu.%hhu.%hhu.%hhu:%hu",
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 24),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 16),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr >> 8),
+ (uint8_t) ((uint32_t)addr->data.v4.sin_addr.s_addr),
+ addr->data.v4.sin_port
+ );
+ } else {
+ for(int i = 0; i < 8; i++) {
+ APPEND(buffer, "%02hhx%02hhx:",
+ addr->data.v6.sin6_addr.__in6_u.__u6_addr8[i*2 + 0],
+ addr->data.v6.sin6_addr.__in6_u.__u6_addr8[i*2 + 1]
+ );
+ }
+ APPEND(buffer, ":[%hu]", addr->data.v6.sin6_port);
+ }
+}
+
+#define ADDR_DOMAIN(addr, var) \
+ struct sockaddr* var; \
+ if (addr->type == V4) { \
+ var = (struct sockaddr*) &addr->data.v4; \
+ } else if (addr->type == V6) { \
+ var = (struct sockaddr*) &addr->data.v6; \
+ } else { \
+ return -1; \
+ }
+
+#define ADDR_AFNET(type, var) \
+ int var; \
+ if (type == V4) { \
+ var = AF_INET; \
+ } else if (type == V6) { \
+ var = AF_INET6; \
+ } else { \
+ return -1; \
+ }
+
+int32_t create_udp_socket(AddrType type, UdpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_DGRAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ clientaddr->type = socket->type;
+ clientaddr->len = get_addr_len(socket->type);
+ ADDR_DOMAIN(clientaddr, __addr)
+ return recvfrom(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_WAITALL,
+ __addr,
+ (uint32_t*) &clientaddr->len
+ );
+}
+
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr) {
+ ADDR_DOMAIN(clientaddr, __addr)
+ return sendto(
+ socket->sockfd,
+ buffer,
+ (size_t) len,
+ MSG_CONFIRM,
+ __addr,
+ (uint32_t) clientaddr->len
+ );
+}
+
+int32_t close_udp_socket(UdpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* sock) {
+ ADDR_AFNET(type, __domain)
+ sock->type = type;
+ sock->sockfd = socket(__domain, SOCK_STREAM, 0);
+ return sock->sockfd;
+}
+
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* sock) {
+ if (addr->type == V6) {
+ int v6OnlyEnabled = 0;
+ int32_t res = setsockopt(
+ sock->sockfd,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ &v6OnlyEnabled,
+ sizeof(v6OnlyEnabled)
+ );
+ if (res < 0) return res;
+ }
+ ADDR_DOMAIN(addr, __addr)
+ return bind(sock->sockfd, __addr, addr->len);
+}
+
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max) {
+ return listen(socket->sockfd, max);
+}
+
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream) {
+ stream->clientaddr.type = socket->type;
+ memset(&stream->clientaddr, 0, sizeof(SocketAddr));
+ SocketAddr* addr = &stream->clientaddr;
+ ADDR_DOMAIN(addr, __addr)
+ stream->streamfd = accept(
+ socket->sockfd,
+ __addr,
+ (uint32_t*) &stream->clientaddr.len
+ );
+ return stream->streamfd;
+}
+
+int32_t close_tcp_socket(TcpSocket* socket) {
+ return close(socket->sockfd);
+}
+
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream) {
+ TcpSocket socket;
+ int32_t res = create_tcp_socket(servaddr->type, &socket);
+ if (res < 0) return res;
+ stream->clientaddr = *servaddr;
+ stream->streamfd = socket.sockfd;
+ ADDR_DOMAIN(servaddr, __addr)
+ return connect(
+ socket.sockfd,
+ __addr,
+ servaddr->len
+ );
+}
+
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ return recv(stream->streamfd, buffer, len, 0);
+}
+
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len) {
+ return send(stream->streamfd, buffer, len, MSG_NOSIGNAL);
+}
+
+int32_t close_tcp_stream(TcpStream* stream) {
+ return close(stream->streamfd);
+}
diff --git a/src/server/addr.h b/src/server/addr.h
new file mode 100644
index 0000000..173c7fd
--- /dev/null
+++ b/src/server/addr.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "../packet/record.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+typedef enum {
+ V4,
+ V6
+} AddrType;
+
+typedef struct {
+ AddrType type;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } data;
+} IpAddr;
+
+void create_ip_addr(char* domain, IpAddr* addr);
+void create_ip_addr6(char* domain, IpAddr* addr);
+void ip_addr_any(IpAddr* addr);
+void ip_addr_any6(IpAddr* addr);
+
+typedef struct {
+ AddrType type;
+ union {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ } data;
+ size_t len;
+} SocketAddr;
+
+void create_socket_addr(uint16_t port, IpAddr addr, SocketAddr* socket);
+void print_socket_addr(SocketAddr* addr, char* buffer);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} UdpSocket;
+
+int32_t create_udp_socket(AddrType type, UdpSocket* socket);
+int32_t bind_udp_socket(SocketAddr* addr, UdpSocket* socket);
+int32_t read_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t write_udp_socket(UdpSocket* socket, void* buffer, uint16_t len, SocketAddr* clientaddr);
+int32_t close_udp_socket(UdpSocket* socket);
+
+typedef struct {
+ AddrType type;
+ uint32_t sockfd;
+} TcpSocket;
+
+typedef struct {
+ SocketAddr clientaddr;
+ uint32_t streamfd;
+} TcpStream;
+
+int32_t create_tcp_socket(AddrType type, TcpSocket* socket);
+int32_t bind_tcp_socket(SocketAddr* addr, TcpSocket* socket);
+int32_t listen_tcp_socket(TcpSocket* socket, uint32_t max);
+int32_t accept_tcp_socket(TcpSocket* socket, TcpStream* stream);
+int32_t close_tcp_socket(TcpSocket* socket);
+int32_t connect_tcp_stream(SocketAddr* servaddr, TcpStream* stream);
+int32_t read_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t write_tcp_stream(TcpStream* stream, void* buffer, uint16_t len);
+int32_t close_tcp_stream(TcpStream* stream);
diff --git a/src/server/binding.c b/src/server/binding.c
new file mode 100644
index 0000000..47c62c6
--- /dev/null
+++ b/src/server/binding.c
@@ -0,0 +1,245 @@
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "addr.h"
+#include "binding.h"
+#include "../io/log.h"
+
+static void create_udp_binding(UdpSocket* socket, uint16_t port) {
+ if (create_udp_socket(V6, socket) < 0) {
+ ERROR("Failed to create UDP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ IpAddr addr;
+ ip_addr_any6(&addr);
+
+ SocketAddr socketaddr;
+ create_socket_addr(port, addr, &socketaddr);
+
+ if (bind_udp_socket(&socketaddr, socket) < 0) {
+ ERROR("Failed to bind UDP socket on port %hu: %s", port, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void create_tcp_binding(TcpSocket* socket, uint16_t port) {
+ if (create_tcp_socket(V6, socket) < 0) {
+ ERROR("Failed to create TCP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ IpAddr addr;
+ ip_addr_any6(&addr);
+
+ SocketAddr socketaddr;
+ create_socket_addr(port, addr, &socketaddr);
+
+ if (bind_tcp_socket(&socketaddr, socket) < 0) {
+ ERROR("Failed to bind TCP socket on port %hu: %s", port, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen_tcp_socket(socket, 5) < 0) {
+ ERROR("Failed to listen on TCP socket: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void create_binding(BindingType type, uint16_t port, Binding* binding) {
+ binding->type = type;
+ if (type == UDP) {
+ create_udp_binding(&binding->sock.udp, port);
+ } else if(type == TCP) {
+ create_tcp_binding(&binding->sock.tcp, port);
+ } else {
+ exit(EXIT_FAILURE);
+ }
+}
+
+void free_binding(Binding* binding) {
+ if (binding->type == UDP) {
+ close_udp_socket(&binding->sock.udp);
+ } else if(binding->type == TCP) {
+ close_tcp_socket(&binding->sock.tcp);
+ }
+}
+
+bool accept_connection(Binding* binding, Connection* connection) {
+ connection->type = binding->type;
+
+ if(binding->type == UDP) {
+ connection->sock.udp.udp = binding->sock.udp;
+ memset(&connection->sock.udp.clientaddr, 0, sizeof(SocketAddr));
+ return true;
+ }
+
+ if (accept_tcp_socket(&binding->sock.tcp, &connection->sock.tcp) < 0) {
+ ERROR("Failed to accept TCP connection: %s", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static void read_to_packet(uint8_t* buf, uint16_t len, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(len);
+ for (int i = 0; i < len; i++) {
+ buffer_write(pkbuffer, buf[i]);
+ }
+ buffer_seek(pkbuffer, 0);
+ read_packet(pkbuffer, packet);
+ buffer_free(pkbuffer);
+}
+
+static bool read_udp(Connection* connection, Packet* packet) {
+ uint8_t buffer[512];
+ int32_t n = read_udp_socket(
+ &connection->sock.udp.udp,
+ buffer,
+ 512,
+ &connection->sock.udp.clientaddr
+ );
+ if (n < 0) {
+ return false;
+ }
+ read_to_packet(buffer, n, packet);
+ return true;
+}
+
+static bool read_tcp(Connection* connection, Packet* packet) {
+ uint16_t len;
+ if ( read_tcp_stream(
+ &connection->sock.tcp,
+ &len,
+ sizeof(uint16_t)
+ ) < 0) {
+ return false;
+ }
+
+ uint8_t buffer[len];
+ if ( read_tcp_stream(
+ &connection->sock.tcp,
+ buffer,
+ len
+ ) < 0) {
+ return false;
+ }
+
+ read_to_packet(buffer, len, packet);
+ return true;
+}
+
+bool read_connection(Connection* connection, Packet* packet) {
+ if (connection->type == UDP) {
+ return read_udp(connection, packet);
+ } else if (connection->type == TCP) {
+ return read_tcp(connection, packet);
+ }
+ return false;
+}
+
+static bool write_udp(Connection* connection, uint8_t* buf, uint16_t len) {
+ //if (len > 512) {
+ buf[2] = buf[2] | 0x03;
+ // len = 512;
+ // }
+ return write_udp_socket(
+ &connection->sock.udp.udp,
+ buf,
+ len,
+ &connection->sock.udp.clientaddr
+ ) == len;
+}
+
+static bool write_tcp(Connection* connection, uint8_t* buf, uint16_t len) {
+ len = htons(len);
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ &len,
+ sizeof(uint16_t)
+ ) < 0) {
+ return false;
+ }
+
+ if (write_tcp_stream(
+ &connection->sock.tcp,
+ buf,
+ len
+ ) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool write_connection(Connection* connection, Packet* packet) {
+ PacketBuffer* pkbuffer = buffer_create(64);
+ write_packet(pkbuffer, packet);
+ uint16_t len = buffer_get_size(pkbuffer);
+ uint8_t* buffer = buffer_get_ptr(pkbuffer);
+ bool success = false;
+ if(connection->type == UDP) {
+ success = write_udp(connection, buffer, len);
+ } else if(connection->type == TCP) {
+ success = write_tcp(connection, buffer, len);
+ };
+ buffer_free(pkbuffer);
+ return success;
+}
+
+void free_connection(Connection* connection) {
+ if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
+
+static bool create_udp_request(SocketAddr* addr, Connection* request) {
+ if ( create_udp_socket(addr->type, &request->sock.udp.udp) < 0) {
+ ERROR("Failed to connect to UDP socket: %s", strerror(errno));
+ return false;
+ }
+ request->sock.udp.clientaddr = *addr;
+ return true;
+}
+
+static bool create_tcp_request(SocketAddr* addr, Connection* request) {
+ if( connect_tcp_stream(addr, &request->sock.tcp) < 0) {
+ ERROR("Failed to connect to TCP socket: %s", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request) {
+ request->type = type;
+ if (type == UDP) {
+ return create_udp_request(addr, request);
+ } else if (type == TCP) {
+ return create_tcp_request(addr, request);
+ } else {
+ return true;
+ }
+}
+
+bool request_packet(Connection* request, Packet* in, Packet* out) {
+ if (!write_connection(request, in)) {
+ return false;
+ }
+ if (!read_connection(request, out)) {
+ return false;
+ }
+ return true;
+}
+
+void free_request(Connection* connection) {
+ if (connection->type == UDP) {
+ close_udp_socket(&connection->sock.udp.udp);
+ } else if (connection->type == TCP) {
+ close_tcp_stream(&connection->sock.tcp);
+ }
+}
diff --git a/src/server/binding.h b/src/server/binding.h
new file mode 100644
index 0000000..e2e6160
--- /dev/null
+++ b/src/server/binding.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "../packet/packet.h"
+#include "addr.h"
+
+#include <netinet/in.h>
+
+typedef enum {
+ UDP,
+ TCP
+} BindingType;
+
+typedef struct {
+ BindingType type;
+ union {
+ UdpSocket udp;
+ TcpSocket tcp;
+ } sock;
+} Binding;
+
+void create_binding(BindingType type, uint16_t port, Binding* binding);
+void free_binding(Binding* binding);
+
+typedef struct {
+ BindingType type;
+ union {
+ struct {
+ UdpSocket udp;
+ SocketAddr clientaddr;
+ } udp;
+ TcpStream tcp;
+ } sock;
+} Connection;
+
+bool accept_connection(Binding* binding, Connection* connection);
+bool read_connection(Connection* connection, Packet* packet);
+bool write_connection(Connection* connection, Packet* packet);
+void free_connection(Connection* connection);
+
+bool create_request(BindingType type, SocketAddr* addr, Connection* request);
+bool request_packet(Connection* request, Packet* in, Packet* out);
+void free_request(Connection* connection);
diff --git a/src/server/resolver.c b/src/server/resolver.c
new file mode 100644
index 0000000..e05f365
--- /dev/null
+++ b/src/server/resolver.c
@@ -0,0 +1,166 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "resolver.h"
+#include "addr.h"
+#include "binding.h"
+#include "../io/log.h"
+
+static bool lookup(
+ Question* question,
+ Packet* response,
+ BindingType type,
+ SocketAddr addr
+) {
+ INIT_LOG_BUFFER(log)
+ LOGONLY(print_socket_addr(&addr, log);)
+ TRACE("Attempting lookup on fallback dns %s", log);
+
+ Connection request;
+ if (!create_request(type, &addr, &request)) {
+ return false;
+ }
+
+ Packet req;
+ memset(&req, 0, sizeof(Packet));
+ req.header.id = response->header.id;
+ req.header.opcode = response->header.opcode;
+ req.header.questions = 1;
+ req.header.recursion_desired = true;
+ req.questions = malloc(sizeof(Question));
+ req.questions[0] = *question;
+
+ if (!request_packet(&request, &req, response)) {
+ free_request(&request);
+ free(req.questions);
+ ERROR("Failed to request fallback dns: %s", strerror(errno));
+ return false;
+ }
+
+ free_request(&request);
+ free(req.questions);
+ return true;
+}
+
+static bool search(Question* question, Packet* result, BindingType type) {
+ IpAddr addr;
+ char ip[4] = {1, 1, 1, 1};
+ create_ip_addr(ip, &addr);
+
+ uint16_t port = 53;
+ SocketAddr saddr;
+ create_socket_addr(port, addr, &saddr);
+
+ while(1) {
+ if (!lookup(question, result, type, saddr)) {
+ return false;
+ }
+
+ if (result->header.answers > 0 && result->header.rescode == NOERROR) {
+ return true;
+ }
+
+ if (result->header.rescode == NXDOMAIN) {
+ return true;
+ }
+
+ if (get_resolved_ns(result, question->domain, &addr)) {
+ continue;
+ }
+
+ Question new_question;
+ if (!get_unresoled_ns(result, question->domain, &new_question)) {
+ return true;
+ }
+
+ Packet recurse;
+ if (!search(&new_question, &recurse, type)) {
+ return false;
+ }
+
+ free_question(&new_question);
+
+ IpAddr random;
+ if (!get_random_a(&recurse, &random)) {
+ free_packet(&recurse);
+ return true;
+ } else {
+ free_packet(&recurse);
+ addr = random;
+ }
+ }
+}
+
+static void push_records(Record* from, uint8_t from_len, Record** to, uint8_t to_len) {
+ if(from_len < 1) return;
+ *to = realloc(*to, sizeof(Record) * (from_len + to_len));
+ memcpy(*to + to_len, from, from_len * sizeof(Record));
+}
+
+static void push_questions(Question* from, uint8_t from_len, Question** to, uint8_t to_len) {
+ if(from_len < 1) return;
+ *to = realloc(*to, sizeof(Question) * (from_len + to_len));
+ memcpy(*to + to_len, from, from_len * sizeof(Question));
+}
+
+void handle_query(Packet* request, Packet* response, BindingType type) {
+ memset(response, 0, sizeof(Packet));
+ response->header.id = request->header.id;
+ response->header.opcode = request->header.opcode;
+ response->header.recursion_desired = true;
+ response->header.recursion_available = true;
+ response->header.response = true;
+
+ if (request->header.questions < 1) {
+ response->header.response = FORMERR;
+ return;
+ }
+
+ for (uint16_t i = 0; i < request->header.questions; i++) {
+ Packet result;
+ memset(&result, 0, sizeof(Packet));
+ result.header.id = response->header.id;
+ if (!search(&request->questions[i], &result, type)) {
+ response->header.response = SERVFAIL;
+ break;
+ }
+
+ push_questions(
+ result.questions,
+ result.header.questions,
+ &response->questions,
+ response->header.questions
+ );
+ response->header.questions += result.header.questions;
+
+ push_records(
+ result.answers,
+ result.header.answers,
+ &response->answers,
+ response->header.answers
+ );
+ response->header.answers += result.header.answers;
+
+ push_records(
+ result.authorities,
+ result.header.authoritative_entries,
+ &response->authorities,
+ response->header.authoritative_entries
+ );
+ response->header.authoritative_entries += result.header.authoritative_entries;
+
+ push_records(
+ result.resources,
+ result.header.resource_entries,
+ &response->resources,
+ response->header.resource_entries
+ );
+ response->header.resource_entries += result.header.resource_entries;
+
+ free(result.questions);
+ free(result.answers);
+ free(result.authorities);
+ free(result.resources);
+ }
+} \ No newline at end of file
diff --git a/src/server/resolver.h b/src/server/resolver.h
new file mode 100644
index 0000000..79b4825
--- /dev/null
+++ b/src/server/resolver.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "../packet/packet.h"
+#include "binding.h"
+
+void handle_query(Packet* request, Packet* response, BindingType type); \ No newline at end of file
diff --git a/src/server/server.c b/src/server/server.c
new file mode 100644
index 0000000..c8975ee
--- /dev/null
+++ b/src/server/server.c
@@ -0,0 +1,100 @@
+#define _POSIX_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "addr.h"
+#include "server.h"
+#include "resolver.h"
+#include "../io/log.h"
+
+static pid_t udp, tcp;
+
+void server_init(uint16_t port, Server* server) {
+ INFO("Server port set to %hu", port);
+ create_binding(UDP, port, &server->udp);
+ create_binding(TCP, port, &server->tcp);
+}
+
+static void server_listen(Binding* binding) {
+ while(1) {
+
+ Connection connection;
+ if (!accept_connection(binding, &connection)) {
+ ERROR("Failed to accept connection");
+ continue;
+ }
+
+ Packet request;
+ if (!read_connection(&connection, &request)) {
+ ERROR("Failed to read connection");
+ free_connection(&connection);
+ continue;
+ }
+
+ if(fork() != 0) {
+ free_packet(&request);
+ free_connection(&connection);
+ continue;
+ }
+
+ INFO("Recieved packet request ID %hu", request.header.id);
+
+ Packet response;
+ handle_query(&request, &response, connection.type);
+
+ if (!write_connection(&connection, &response)) {
+ ERROR("Failed to respond to connection ID %hu: %s", request.header.id, strerror(errno));
+ }
+
+ free_packet(&request);
+ free_packet(&response);
+ free_connection(&connection);
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void signal_handler() {
+ printf("\n");
+ kill(udp, SIGTERM);
+ kill(tcp, SIGTERM);
+}
+
+void server_run(Server* server) {
+ if ((udp = fork()) == 0) {
+ INFO("Listening for connections on UDP");
+ server_listen(&server->udp);
+ exit(EXIT_SUCCESS);
+ }
+
+ if ((tcp = fork()) == 0) {
+ INFO("Listening for connections on TCP");
+ server_listen(&server->tcp);
+ exit(EXIT_SUCCESS);
+ }
+
+ signal(SIGINT, signal_handler);
+
+ int status;
+ waitpid(udp, &status, 0);
+ if (status == 0) {
+ INFO("UDP process closed successfully");
+ } else {
+ ERROR("UDP process failed with error code %d", status);
+ }
+
+ waitpid(tcp, &status, 0);
+ if (status == 0) {
+ INFO("TCP process closed successfully");
+ } else {
+ ERROR("TCP process failed with error code %d", status);
+ }
+}
+
+void server_free(Server* server) {
+ free_binding(&server->udp);
+ free_binding(&server->tcp);
+}
diff --git a/src/server/server.h b/src/server/server.h
new file mode 100644
index 0000000..c9509f2
--- /dev/null
+++ b/src/server/server.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "binding.h"
+
+typedef struct {
+ Binding udp;
+ Binding tcp;
+} Server;
+
+void server_init(uint16_t port, Server* server);
+void server_run(Server* server);
+void server_free(Server* server); \ No newline at end of file