summaryrefslogblamecommitdiffstats
path: root/prog/6/daemon.c
blob: 1807933b24d85b9fda60ef0e472cf8c36e853abc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                         




























                                                                                       



















                                                       
                                                         


                                        
                  



                                                  


                                                                                                           
                             
                                                                                                            

                                                                                                  


                           

                                      
                                                 



                                                                                         



                                         

                              




























































                                                                                                                                                          
#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
}