#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <poll.h>
#include <arpa/nameser.h>
#include <sys/uio.h>
#include <netdb.h>
#include <sys/types.h>
#include <resolv.h>
#include <errno.h>
#include <string.h>
#include "functions.c"
#include "conf.c"
int write_ptr6c (FILE * stream, const struct ptr ptr6c) {
if (!fwrite(ptr6c.addr.s6_addr, sizeof ptr6c.addr.s6_addr, 1, stream))
return 1;
if (!fwrite(&ptr6c.ttl, sizeof ptr6c.ttl, 1, stream))
return 2;
if (fputs(ptr6c.hostname, stream) == EOF)
return 3;
fputc('\0', stream);
return 0;
}
int prune (struct config * config, const char * filename) {
FILE * file = fopen(filename, "w");
if (!filename) {
fprintf(stderr, "fopen(\"%s\", \"w\"): %s", filename, strerror(errno));
return 1;
}
int ret = 0;
for (struct trie * trie = config->trie; trie; trie = next(trie))
if (trie->type == ptr6c) {
if (((struct ptr *) trie->data)->ttl+60*48 >= timestamp())
ret += write_ptr6c(file, *((struct ptr *) trie->data));
else
free_trie_ptr(trie);
}
return ret;
}
int handle (unsigned char * packet, int bytes) {
HEADER * header = (HEADER *) packet;
ns_msg handle;
if (ns_initparse(packet, bytes, &handle) == -1)
return -1;
if (header->qr) // response
return -1;
header->qr = 1;
header->tc = 0;
header->aa = 0;
header->ra = 0;
if (header->opcode) {
header->rcode = NOTIMP;
return bytes;
}
if (header->qdcount != 1) {
header->rcode = FORMERR;
return bytes;
}
ns_rr rr;
if (ns_parserr(&handle, ns_s_qd, 0, &rr) == -1) {
header->rcode = FORMERR;
return bytes;
}
return -1;
}
int main (int argc, char ** argv) {
if (argc != 3) {
fprintf(stderr, "%s port config\n"
" port: 53 (TCP+UDP listening port) (configurable to allow many daemons)\n"
" TCP is only for inter-6d zone transfers, queries will not work over TCP\n"
" config: file name of the configuration file (check the example config for documentation)\n"
"more information:\n"
" - SOA serial will be the number of UTC/UNIX (not real) minutes since 2023-08-08 00:00 UTC\n"
" - refresh, retry and expire in SOA will have values conforming to standard, but\n"
" they are irrelevant, as potential 6d slaves are not real DNS slaves\n"
, argv[0]);
return 1;
}
struct config conf;
memset(&conf, 0, sizeof conf);
int ret = config(&conf, argv[2], stderr);
if (ret) {
fprintf(stderr, "error %d while parsing the configuration file!\n", ret);
return 9+ret;
}
FILE * conf_output = stderr;
if (argv[1][0] == 'd')
conf_output = stdout;
print_config(&conf, conf_output);
if (argv[1][0] == 'd')
return 0;
int sock = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock == -1) {
perror("socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)");
return 2;
}
struct sockaddr_in6 listen = {
.sin6_family = AF_INET6,
.sin6_port = htons(53),
.sin6_addr = IN6ADDR_ANY_INIT
};
if (bind(sock, (struct sockaddr *) &listen, sizeof listen) == -1) {
perror("bind(sock, &listen, sizeof listen)");
return 3;
}
struct pollfd pfd = {
.fd = sock,
.events = POLLIN
};
while (poll(&pfd, 1, -1) != -1) {
if (pfd.revents & POLLERR) {
fprintf(stderr, "POLLERR\n");
return 5;
}
if (pfd.revents & POLLHUP) {
fprintf(stderr, "POLLHUP\n");
return 6;
}
if (pfd.revents & POLLNVAL) {
fprintf(stderr, "POLLNVAL\n");
return 7;
}
struct sockaddr_in6 sender;
unsigned char packet[512];
struct iovec parts[] = {
{
.iov_base = packet,
.iov_len = sizeof packet
}
};
struct msghdr msg = {
.msg_name = &sender,
.msg_namelen = sizeof sender,
.msg_iov = parts,
.msg_iovlen = sizeof parts/sizeof parts[0]
};
int bytes = recvmsg(sock, &msg, MSG_DONTWAIT | MSG_TRUNC);
if (bytes == -1) {
perror("recvmsg");
return 8;
}
int len = handle(packet, bytes);
if (len >= 0) {
if (sendto(sock, packet, len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) &sender, sizeof sender) == -1 && errno != EACCES) {
perror("sendto");
return 9;
}
}
}
perror("poll");
return 4; // there really is no successful exit code, this program should run indefinitely
}