├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cache.c ├── cache.h ├── common.h ├── config.c ├── config.h ├── help.c ├── log.c ├── main.c ├── parse.c ├── parse.h ├── tinydns.conf └── tinydns.service /.gitignore: -------------------------------------------------------------------------------- 1 | tinydns 2 | *.o 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 CupIvan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | main: main.o cache.o config.o parse.o log.o help.o 2 | cc -o tinydns *.o 3 | clean: 4 | rm -f *.o tinydns 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyDNS 2 | 3 | This is a tiny DNS server with simple JSON config written in C. 4 | 5 | ## Features 6 | 7 | * filesize is just only 20Kb! 8 | * cache DNS queries for boost internet connection 9 | * resolve own domains from config 10 | * resolve multidomains like *.example.com 11 | * server can work on IPv6 address 12 | 13 | ## Compile and Install 14 | 15 | * if you use [Archlinux](https://archlinux.org), you may install [tinydns from AUR](https://aur.archlinux.org/packages/tinydns/) 16 | * for compile just run `make` 17 | * after install you need to write your IP address in `/etc/tinydns.conf` 18 | * you may also use `systemctl` for start and stop service 19 | -------------------------------------------------------------------------------- /cache.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | 5 | uint32_t getTimestamp() 6 | { 7 | return (uint32_t)time(0); 8 | } 9 | 10 | struct TCacheItem *cache = NULL; 11 | struct TCacheItem *cache_last = NULL; 12 | 13 | void* cache_question(void *buf, uint16_t n) 14 | { 15 | struct TCacheItem *ptr = (struct TCacheItem *)malloc(sizeof(TCacheItem) + n); 16 | if (!ptr) return NULL; 17 | 18 | memset(ptr, 0, sizeof(TCacheItem)); 19 | ptr-> que = (uint16_t*)(ptr + 1); 20 | ptr->n_que = n; 21 | ptr->timestamp = getTimestamp(); 22 | memcpy(ptr->que, buf, n); 23 | 24 | // search for star and calc part sizes 25 | char *star_ptr; 26 | star_ptr = (char*)ptr->que + sizeof(THeader); 27 | while (*star_ptr) 28 | { 29 | if (star_ptr[0] == 1 && star_ptr[1] == '*') 30 | { 31 | ptr->star_q_n1 = star_ptr - (char*)ptr->que; 32 | ptr->star_q_n2 = n - ptr->star_q_n1 - 2; 33 | break; 34 | } 35 | star_ptr++; 36 | } 37 | 38 | if (!cache) cache = cache_last = ptr; 39 | else { cache_last->next = ptr; cache_last = ptr; } 40 | 41 | return NULL; 42 | } 43 | 44 | int cache_answer(void *_buf, uint16_t n) 45 | { 46 | uint16_t *buf = (uint16_t*)_buf; 47 | struct TCacheItem *ptr = cache; 48 | char *star_ptr; 49 | 50 | while (ptr) 51 | { 52 | if (buf[0] == ptr->que[0]) 53 | { 54 | ptr-> ans = (uint16_t*)malloc(n); 55 | ptr->n_ans = n; 56 | if (!ptr->ans) return 0; 57 | memcpy(ptr->ans, buf, n); 58 | // search for star and calc part sizes 59 | star_ptr = (char*)ptr->ans + sizeof(THeader); 60 | while (*star_ptr) 61 | { 62 | if (star_ptr[0] == 1 && star_ptr[1] == '*') 63 | { 64 | ptr->star_a_n1 = star_ptr - (char*)ptr->ans; 65 | ptr->star_a_n2 = n - ptr->star_a_n1 - 2; 66 | if (ptr->ans_right = (uint16_t*)malloc(ptr->star_a_n2)) 67 | memcpy(ptr->ans_right, (char*)ptr->ans + ptr->star_a_n1 + 2, ptr->star_a_n2); 68 | break; 69 | } 70 | star_ptr++; 71 | } 72 | 73 | return 1; 74 | } 75 | ptr = ptr->next; 76 | } 77 | return 0; 78 | } 79 | 80 | void* cache_search(void *_buf, uint16_t *n) 81 | { 82 | uint16_t *buf = (uint16_t*)_buf; 83 | struct TCacheItem *ptr = cache; 84 | struct TCacheItem *ptr_prev = NULL; 85 | struct TCacheItem *ptr_tmp = NULL; 86 | uint32_t time = getTimestamp(); 87 | while (ptr) 88 | { 89 | if (config.cache_time) 90 | if (time - ptr->timestamp > config.cache_time) 91 | { 92 | ptr_tmp = ptr->next; 93 | if (ptr_prev == NULL) 94 | { 95 | cache = ptr->next; 96 | if (ptr->ans) free(ptr->ans); 97 | free(ptr); 98 | } 99 | else 100 | { 101 | ptr_prev->next = ptr->next; 102 | if (ptr->ans) free(ptr->ans); 103 | free(ptr); 104 | } 105 | ptr = ptr_tmp; 106 | continue; 107 | } 108 | 109 | if (ptr->ans) 110 | if (ptr->star_q_n1 > 0) 111 | { 112 | int16_t mid_sz = *n - ptr->star_q_n1 - ptr->star_q_n2; // size of middle 113 | if (mid_sz > 0) 114 | if (memcmp(&buf[1], &ptr->que[1], ptr->star_q_n1 - 2) == 0) // left part 115 | if (memcmp((char*)buf + *n - ptr->star_q_n2, 116 | (char*)ptr->que + ptr->n_que - ptr->star_q_n2, ptr->star_q_n2) == 0) // right part 117 | { 118 | *n = ptr->star_a_n1 + mid_sz + ptr->star_a_n2; 119 | char *p = (char*)ptr->ans; 120 | p += ptr->star_a_n1; // skip left part 121 | // fill subdomain 122 | memcpy(p, (char*)buf + ptr->star_q_n1, mid_sz); p += mid_sz; 123 | // fill right part 124 | memcpy(p, ptr->ans_right, ptr->star_a_n2); 125 | return ptr->ans; 126 | } 127 | } 128 | else 129 | if (memcmp(&buf[1], &ptr->que[1], *n - 2) == 0) 130 | { 131 | *n = ptr->n_ans; 132 | return ptr->ans; 133 | } 134 | ptr_prev = ptr; 135 | ptr = ptr->next; 136 | } 137 | return NULL; 138 | } 139 | -------------------------------------------------------------------------------- /cache.h: -------------------------------------------------------------------------------- 1 | typedef struct TCacheItem 2 | { 3 | struct TCacheItem *next; 4 | uint16_t *que; 5 | uint16_t *ans; 6 | uint16_t n_que; 7 | uint16_t n_ans; 8 | uint32_t timestamp; 9 | // num bytes before/after star 10 | uint16_t star_q_n1; 11 | uint16_t star_q_n2; 12 | uint16_t star_a_n1; 13 | uint16_t star_a_n2; 14 | uint16_t *ans_right; 15 | } TCacheItem; 16 | 17 | int cache_answer(void *_buf, uint16_t n); 18 | void* cache_question(void *buf, uint16_t n); 19 | void* cache_search(void *_buf, uint16_t *n); 20 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // for inet_aton 4 | #include // for usleep, fork 5 | #include // for exit 6 | 7 | #include "config.h" 8 | #include "parse.h" 9 | #include "cache.h" 10 | 11 | void error(char *msg); 12 | 13 | void log_s(char *msg); 14 | void log_b(char *prefix, void *ptr, int n); 15 | 16 | void help(); 17 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | TConfig config = {"127.0.0.1", "8.8.8.8", 6*3600}; 4 | 5 | char rr_buf[0xFFF] = {0}; 6 | 7 | char* config_param(char* s, void* res, uint type) 8 | { 9 | THeader *rr = (THeader*)rr_buf; rr->QRCOUNT = htons(1); 10 | char *rr_ptr, *rr_dot; 11 | int state = 1, x, rr_size, rr_uid = 1; 12 | if (type == CONFIG_TYPE_RR) state = 6; 13 | while (*s) 14 | { 15 | switch (state) 16 | { 17 | case 1: if (*s == ':') { state = (type==CONFIG_TYPE_INT)?5:2; } break; 18 | case 2: if (*s == '"') { state = 3; *(char**)res = s+1; } break; 19 | case 3: if (*s == '"') { state = 4; *s = 0; } break; 20 | case 4: return s; 21 | case 5: if (*s >= '0' && *s <= '9') { state = 4; sscanf(s, "%d", &x); *(uint16_t*)res = x; } break; 22 | // add query/answer from config 23 | case 6: if (*s == ']') state = 4; if (*s == '"') { state = 7; rr_ptr = rr_dot = s; x = 0; } break; 24 | case 7: 25 | if (*s == '.' || *s == '"') { *rr_dot = x; rr_dot = s; x = 0; } else x++; 26 | // construct question 27 | if (*s == '"') 28 | { 29 | state = 8; *s = 0; 30 | strcpy((char*)(rr + 1), rr_ptr); 31 | rr->uid = rr_uid++; 32 | rr->RD = 1; 33 | rr->QR = 0; 34 | rr->RA = 0; 35 | rr->ANCOUNT = 0; 36 | rr_size = sizeof(THeader) + s - rr_ptr + 1; 37 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x01; // TYPE 38 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x01; // CLASS 39 | log_b("A-->", rr_buf, rr_size); 40 | cache_question(rr_buf, rr_size); 41 | } 42 | break; 43 | case 8: if (*s == '"') { state = 9; rr_ptr = s + 1; } break; 44 | case 9: 45 | // construct answer 46 | if (*s == '"') 47 | { 48 | state = 6; *s = 0; 49 | rr->RD = 1; 50 | rr->QR = 1; 51 | rr->RA = 1; 52 | rr->ANCOUNT = htons(1); 53 | rr_buf[rr_size++] = 0xC0; 54 | rr_buf[rr_size++] = 0x0C; 55 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x01; // TYPE 56 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x01; // CLASS 57 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x00; // TTL 58 | rr_buf[rr_size++] = 0xAA; rr_buf[rr_size++] = 0xAA; // TTL 59 | rr_buf[rr_size++] = 0x00; rr_buf[rr_size++] = 0x04; // RDLENGTH 60 | inet_aton(rr_ptr, (struct in_addr *)&rr_buf[rr_size]); rr_size += 4; 61 | log_b("<--A", rr_buf, rr_size); 62 | cache_answer(rr_buf, rr_size); 63 | } 64 | break; 65 | } 66 | s++; 67 | } 68 | return s; 69 | } 70 | 71 | void config_parse(char* s) 72 | { 73 | char* ptr = s; 74 | while (*ptr) 75 | { 76 | if (memcmp(ptr, "server_ip", 9) == 0) ptr = config_param(ptr, &config.server_ip, CONFIG_TYPE_STRING); 77 | if (memcmp(ptr, "dns", 3) == 0) ptr = config_param(ptr, &config.dns, CONFIG_TYPE_STRING); 78 | if (memcmp(ptr, "cache_time", 10) == 0) ptr = config_param(ptr, &config.cache_time, CONFIG_TYPE_INT); 79 | if (memcmp(ptr, "debug_level",11) == 0) ptr = config_param(ptr, &config.debug_level,CONFIG_TYPE_INT); 80 | if (memcmp(ptr, "rr", 2) == 0) ptr = config_param(ptr, NULL, CONFIG_TYPE_RR); 81 | ptr++; 82 | } 83 | } 84 | 85 | void config_load() 86 | { 87 | FILE* f; 88 | f = fopen("tinydns.conf", "rb"); 89 | if (!f) 90 | f = fopen("/etc/tinydns.conf", "rb"); 91 | if (!f) return; 92 | 93 | fseek(f, 0, SEEK_END); 94 | int fsize = ftell(f); 95 | rewind(f); 96 | 97 | config.data = (char*)malloc(fsize); 98 | if (!config.data) error("Can't allocate memory for config!"); 99 | 100 | fread(config.data, 1, fsize, f); 101 | fclose(f); 102 | 103 | config_parse(config.data); 104 | } 105 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define CONFIG_TYPE_STRING 1 4 | #define CONFIG_TYPE_INT 2 5 | #define CONFIG_TYPE_RR 3 6 | 7 | typedef struct TConfig 8 | { 9 | char *server_ip; 10 | char *dns; 11 | uint32_t cache_time; 12 | uint8_t debug_level; 13 | char *data; 14 | } TConfig; 15 | 16 | extern TConfig config; 17 | 18 | void config_load(); 19 | -------------------------------------------------------------------------------- /help.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void help() 4 | { 5 | printf("tinydns - small proxy DNS server.\n"); 6 | printf(" -d Run as daemon.\n"); 7 | printf(" --version Show program version.\n"); 8 | printf(" --help Print this help.\n"); 9 | } 10 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void log_s(char *msg) 4 | { 5 | if (!config.debug_level) return; 6 | printf("%s\n", msg); 7 | } 8 | 9 | void log_b(char *prefix, void *ptr, int n) 10 | { 11 | int i; 12 | if (!config.debug_level) return; 13 | printf("%s %3d:", prefix, n); for (i=0; iARCOUNT > 0) 46 | { 47 | ptr->ARCOUNT = 0; 48 | i = sizeof(THeader); 49 | while (buf[i] && i < n) i += buf[i] + 1; 50 | n = i + 1 + 4; // COMMENT: don't forget end zero and last 2 words 51 | } 52 | // also clear Z: it's strange, but dig util set it in 0x02 53 | ptr->Z = 0; 54 | 55 | parse_buf((THeader*)buf); 56 | 57 | id = *((uint16_t*)buf); 58 | 59 | log_b("Q-->", buf, n); 60 | 61 | if (ans = (uint16_t *)cache_search(buf, &n)) 62 | { 63 | ans[0] = id; 64 | log_b("<--C", ans, n); 65 | } 66 | else 67 | cache_question(buf, n); 68 | 69 | // resend to parent 70 | if (!ans) 71 | { 72 | out_addr_len = sizeof(out_addr); 73 | n = sendto(out_socket, buf, n, 0, (struct sockaddr *) &out_addr, out_addr_len); 74 | if (n < 0) { log_s("ERROR in sendto"); } 75 | 76 | int ck = 0; 77 | uint32_t pow, i; 78 | while (++ck < 13) 79 | { 80 | pow = 1; for (i=0; i 106 | int hostname_to_ip(char *hostname, char *ip, int len) 107 | { 108 | int sockfd; 109 | struct addrinfo hints, *servinfo, *p; 110 | 111 | memset(&hints, 0, sizeof(hints)); 112 | hints.ai_family = AF_UNSPEC; 113 | if (getaddrinfo(hostname, NULL, &hints, &servinfo) != 0) return 0; 114 | 115 | for (p = servinfo; p != NULL; p = p->ai_next) 116 | { 117 | if (p->ai_family == AF_INET6) 118 | { 119 | struct sockaddr_in6 *serveraddr = (struct sockaddr_in6 *)p->ai_addr; 120 | inet_ntop(AF_INET6, (struct in_addr *)&serveraddr->sin6_addr, ip, len); 121 | break; 122 | } 123 | else 124 | if (p->ai_family == AF_INET) 125 | { 126 | struct sockaddr_in *serveraddr = (struct sockaddr_in *)p->ai_addr; 127 | inet_ntop(AF_INET, (struct in_addr *)&serveraddr->sin_addr, ip, len); 128 | } 129 | } 130 | freeaddrinfo(servinfo); 131 | return 1; 132 | } 133 | 134 | int server_init() 135 | { 136 | int sock; 137 | 138 | // convert domain to IP 139 | char buf[0xFF]; 140 | if (hostname_to_ip(config.server_ip, buf, sizeof(buf))) 141 | config.server_ip = buf; 142 | 143 | // is ipv6? 144 | int is_ipv6 = 0, i = 0; 145 | while (config.server_ip[i]) if (config.server_ip[i++] == ':') { is_ipv6 = 1; break; } 146 | 147 | // create socket 148 | sock = socket(is_ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); 149 | if (sock < 0) error("ERROR opening socket"); 150 | 151 | /* setsockopt: Handy debugging trick that lets 152 | * us rerun the server immediately after we kill it; 153 | * otherwise we have to wait about 20 secs. 154 | * Eliminates "ERROR on binding: Address already in use" error. 155 | */ 156 | int optval = 1; 157 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(optval)); 158 | 159 | // bind 160 | if (is_ipv6) 161 | { 162 | struct sockaddr_in6 serveraddr; /* server's addr */ 163 | memset((char *) &serveraddr, 0, sizeof(serveraddr)); 164 | serveraddr.sin6_family = AF_INET6; 165 | serveraddr.sin6_port = htons(DNS_PORT); 166 | inet_pton(AF_INET6, config.server_ip, (struct in_addr *)&serveraddr.sin6_addr.s6_addr); 167 | if (bind(sock, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) 168 | error("ERROR on binding ipv6"); 169 | } 170 | else 171 | { 172 | struct sockaddr_in serveraddr; /* server's addr */ 173 | memset((char *) &serveraddr, 0, sizeof(serveraddr)); 174 | serveraddr.sin_family = AF_INET; 175 | serveraddr.sin_port = htons(DNS_PORT); 176 | inet_aton(config.server_ip, (struct in_addr *)&serveraddr.sin_addr.s_addr); 177 | if (bind(sock, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) 178 | error("ERROR on binding ipv4"); 179 | } 180 | 181 | char s[0xFF]; 182 | sprintf(s, "bind on %s:%d", config.server_ip, DNS_PORT); 183 | log_s(s); 184 | 185 | return sock; 186 | } 187 | 188 | int main(int argc, char **argv) 189 | { 190 | if (argv[1] && 0 == strcmp(argv[1], "--version")) 191 | { 192 | printf("tinydns %s\nAuthor: CupIvan \nLicense: MIT\n", version); 193 | exit(0); 194 | } 195 | 196 | if (argv[1] && 0 == strcmp(argv[1], "--help")) 197 | { 198 | help(); 199 | exit(0); 200 | } 201 | 202 | if (argv[1] && 0 == strcmp(argv[1], "-d")) 203 | { 204 | pid_t pid = fork(); 205 | if (pid < 0) 206 | { 207 | if (pid < 0) error("Can't create daemon!"); 208 | exit(1); 209 | } 210 | if (pid > 0) exit(0); // exit from current process 211 | } 212 | else 213 | config.debug_level = 1; 214 | 215 | config_load(); 216 | 217 | int sockfd = server_init(); 218 | 219 | loop(sockfd); 220 | } 221 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | void parse_buf(THeader *buf) 4 | { 5 | char domain[255]; 6 | char *current = domain; 7 | 8 | strcpy(domain, (char*)(buf+1)); 9 | 10 | int n = current[0]; 11 | while (n > 0) 12 | { 13 | current[0] = '.'; 14 | current += n + 1; 15 | n = current[0]; 16 | } 17 | log_s(domain + 1); // COMMENT: skip first dot 18 | } 19 | -------------------------------------------------------------------------------- /parse.h: -------------------------------------------------------------------------------- 1 | #include // for uint16_t 2 | 3 | #define OPCODE_STANDART 0 4 | #define OPCODE_REVERSE 1 5 | #define OPCODE_SERVER_STATE 2 6 | 7 | #define RCODE_OK 0 8 | #define RCODE_UNKNOW_QFORM 1 9 | #define RCODE_NAMESERVER_ERROR 2 10 | #define RCODE_NONAME 3 11 | #define RCODE_UNKNOW_QTYPE 4 12 | #define RCODE_ACCESS_DENIED 5 13 | 14 | typedef struct 15 | { 16 | char* QNAME; 17 | uint16_t QTYPE; 18 | uint16_t QCLASS; 19 | } TQuestion; 20 | 21 | typedef struct __attribute__((__packed__)) 22 | { 23 | char* NAME; 24 | uint16_t TYPE; 25 | uint16_t CLASS; 26 | uint16_t TTL; 27 | uint16_t RDLENGTH; 28 | uint16_t RDATA; 29 | } TResource; 30 | 31 | /* 32 | 1 1 1 1 1 1 33 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 34 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 35 | | ID | 36 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 37 | |QR| Opcode |AA|TC|RD|RA| Z | RCODE | 38 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 39 | | QDCOUNT | 40 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 41 | | ANCOUNT | 42 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 43 | | NSCOUNT | 44 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 45 | | ARCOUNT | 46 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 47 | */ 48 | typedef struct __attribute__((__packed__)) 49 | { 50 | uint16_t uid; 51 | 52 | // COMMENT: this bit fields was rearrange becouse of big-endian 53 | uint16_t RD : 1; // is recursion 54 | uint16_t TC : 1; // is trunctated 55 | uint16_t AA : 1; // is authority answer 56 | uint16_t Opcode : 4; // query type 57 | uint16_t QR : 1; // QR=0 question, QR=1 answer 58 | 59 | uint16_t RCODE : 4; // request state 60 | uint16_t Z : 3; // reserve 61 | uint16_t RA : 1; // is recursion available 62 | 63 | uint16_t QRCOUNT; 64 | uint16_t ANCOUNT; 65 | uint16_t NSCOUNT; 66 | uint16_t ARCOUNT; 67 | } THeader; 68 | 69 | void parse_buf(THeader *buf); 70 | -------------------------------------------------------------------------------- /tinydns.conf: -------------------------------------------------------------------------------- 1 | { 2 | server_ip: "127.0.0.1", // address or domain of tinydns server 3 | dns: "8.8.8.8", // address of parent DNS server 4 | cache_time: 43200, // cache time in seconds 5 | debug_level: 0, // 0 = no messages, 1 = show messages 6 | rr: [ 7 | "domain.example.com": "127.0.0.1", 8 | "_.*.example.com": "127.0.0.2", 9 | "*.example.com": "127.0.0.3", 10 | ], 11 | } 12 | -------------------------------------------------------------------------------- /tinydns.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=tiny DNS cache service 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/bin/tinydns -d 7 | KillMode=mixed 8 | Restart=on-failure 9 | Type=forking 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | --------------------------------------------------------------------------------