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