gitlab-ci: Add Ubuntu runner
[resolv_wrapper.git] / tests / dns_srv.c
1 /*
2  * Copyright (C) Jakub Hrozek 2014 <jakub.hrozek@posteo.se>
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the author nor the names of its contributors
18  *      may be used to endorse or promote products derived from this software
19  *      without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "config.h"
35
36 #include <errno.h>
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41
42 #include <arpa/inet.h>
43 #include <arpa/nameser.h>
44 #include <netinet/in.h>
45 #include <netdb.h>
46 #include <resolv.h>
47
48 #include <fcntl.h>
49 #include <getopt.h>
50 #include <unistd.h>
51
52 #include <string.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <stdbool.h>
56
57 #include <getopt.h>
58
59 #ifndef PIDFILE
60 #define PIDFILE "dns_srv.pid"
61 #endif  /* PIDFILE */
62
63 #define DNS_PORT        53
64 #define DFL_TTL  30
65
66 #ifndef BUFSIZE
67 #define BUFSIZE 1024
68 #endif /* BUFSIZE */
69
70 #ifndef discard_const
71 #define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
72 #endif
73
74 #ifndef discard_const_p
75 #define discard_const_p(type, ptr) ((type *)discard_const(ptr))
76 #endif
77
78 #ifndef ZERO_STRUCT
79 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
80 #endif
81
82 /* The macros below are taken from c-ares */
83 #define DNS__16BIT(p)  ((unsigned short)((unsigned int) 0xffff & \
84                         (((unsigned int)((unsigned char)(p)[0]) << 8U) | \
85                         ((unsigned int)((unsigned char)(p)[1])))))
86
87 #define DNS__SET16BIT(p, v)  (((p)[0] = (unsigned char)(((v) >> 8) & 0xff)), \
88                               ((p)[1] = (unsigned char)((v) & 0xff)))
89
90 #define DNS__SET32BIT(p, v)  (((p)[0] = (unsigned char)(((v) >> 24) & 0xff)), \
91                               ((p)[1] = (unsigned char)(((v) >> 16) & 0xff)), \
92                               ((p)[2] = (unsigned char)(((v) >> 8) & 0xff)), \
93                               ((p)[3] = (unsigned char)((v) & 0xff)));
94
95 /* Macros for parsing a DNS header */
96 #define DNS_HEADER_QID(h)               DNS__16BIT(h)
97 #define DNS_HEADER_OPCODE(h)            (((h)[2] >> 3) & 0xf)
98 #define DNS_HEADER_TC(h)                (((h)[2] >> 1) & 0x1)
99 #define DNS_HEADER_QDCOUNT(h)           DNS__16BIT((h) + 4)
100
101 /* Macros for parsing the fixed part of a DNS question */
102 #define DNS_QUESTION_TYPE(q)            DNS__16BIT(q)
103 #define DNS_QUESTION_CLASS(q)           DNS__16BIT((q) + 2)
104
105 /* Macros for constructing a DNS header */
106 #define DNS_HEADER_SET_QID(h, v)        DNS__SET16BIT(h, v)
107 #define DNS_HEADER_SET_QR(h, v)         ((h)[2] |= (unsigned char)(((v) & 0x1) << 7))
108 #define DNS_HEADER_SET_RD(h, v)         ((h)[2] |= (unsigned char)((v) & 0x1))
109 #define DNS_HEADER_SET_RA(h, v)         ((h)[3] |= (unsigned char)(((v) & 0x1) << 7))
110 #define DNS_HEADER_SET_QDCOUNT(h, v)    DNS__SET16BIT((h) + 4, v)
111 #define DNS_HEADER_SET_ANCOUNT(h, v)    DNS__SET16BIT((h) + 6, v)
112
113 /* Macros for constructing the fixed part of a DNS question */
114 #define DNS_QUESTION_SET_TYPE(q, v)     DNS__SET16BIT(q, v)
115 #define DNS_QUESTION_SET_CLASS(q, v)    DNS__SET16BIT((q) + 2, v)
116
117 /* Macros for constructing the fixed part of a DNS resource record */
118 #define DNS_RR_SET_TYPE(r, v)           DNS__SET16BIT(r, v)
119 #define DNS_RR_SET_CLASS(r, v)          DNS__SET16BIT((r) + 2, v)
120 #define DNS_RR_SET_TTL(r, v)            DNS__SET32BIT((r) + 4, v)
121 #define DNS_RR_SET_LEN(r, v)            DNS__SET16BIT((r) + 8, v)
122
123 #define DEFAULT_A_REC   "127.0.10.10"
124
125 struct dns_srv_opts {
126         char *bind;
127         bool daemon;
128         int port;
129         const char *pidfile;
130 };
131
132 struct dns_query {
133         char *query;
134
135         uint16_t id;
136         uint16_t qtype;
137         uint16_t qclass;
138
139         unsigned char *reply;
140         size_t reply_len;
141 };
142
143 static void free_dns_query(struct dns_query *query)
144 {
145         free(query->query);
146         free(query->reply);
147         memset(query, 0, sizeof(struct dns_query));
148 }
149
150 static size_t encode_name(unsigned char *buffer, const char *name)
151 {
152         const char *p, *dot;
153         unsigned char *bp;
154         size_t len;
155
156         p = name;
157         bp = buffer;
158         len = 0;
159
160         while ((dot = strchr(p, '.')) != NULL) {
161                 *bp++ = dot - p;
162                 len++;
163
164                 while (p < dot) {
165                         *bp++ = *p++;
166                         len++;
167                 }
168                 p = dot + 1; /* move past the dot */
169         }
170
171         *bp = '\0';
172         len++;
173
174         return len;
175 }
176
177 static void fake_header(struct dns_query *query)
178 {
179         DNS_HEADER_SET_QID(query->reply, query->id);
180         DNS_HEADER_SET_QR(query->reply, 1);
181         DNS_HEADER_SET_RD(query->reply, 1);
182         DNS_HEADER_SET_RA(query->reply, 1);
183         DNS_HEADER_SET_QDCOUNT(query->reply, 1);
184         DNS_HEADER_SET_ANCOUNT(query->reply, 1);
185 }
186
187 static size_t fake_question(struct dns_query *query, unsigned char **pout)
188 {
189         unsigned char *p;
190         size_t len;
191
192         p = *pout;
193
194         len = encode_name(p, query->query);
195         p += len;
196         DNS_QUESTION_SET_TYPE(p, query->qtype);
197         len += sizeof(uint16_t);
198         DNS_QUESTION_SET_CLASS(p, query->qclass);
199         len += sizeof(uint16_t);
200
201         p += 2 * sizeof(uint16_t);
202
203         *pout = p;
204         return len;
205 }
206
207 static size_t fake_answer(struct dns_query *query, unsigned char **pout)
208 {
209         unsigned char *p;
210         size_t len;
211         size_t rlen;
212         char *val;
213         struct in_addr a_rec;
214
215         p = *pout;
216
217         len = encode_name(p, query->query);
218         p += len;
219
220         DNS_RR_SET_TYPE(p, query->qtype);
221         len += sizeof(uint16_t);
222
223         DNS_RR_SET_CLASS(p, query->qclass);
224         len += sizeof(uint16_t);
225
226         DNS_RR_SET_TTL(p, DFL_TTL);
227         len += sizeof(uint32_t);
228
229         switch (query->qtype) {
230                 case ns_t_a:
231                         val = getenv("RWRAP_TEST_A_REC");
232                         inet_pton(AF_INET,
233                                   val ? val : DEFAULT_A_REC,
234                                   &a_rec);
235                         rlen = sizeof(struct in_addr);
236                         break;
237                 default:
238                         /* Unhandled record */
239                         return -1;
240         }
241
242         DNS_RR_SET_LEN(p, rlen);
243         len += sizeof(uint16_t);
244
245         /* Move to the RDATA section */
246         p += sizeof(uint16_t) + /* type */
247                  sizeof(uint16_t) +     /* class */
248                  sizeof(uint32_t) +     /* ttl */
249                  sizeof(uint16_t);       /* rlen */
250
251         /* Copy RDATA */
252         memcpy(p, &a_rec, sizeof(struct in_addr));
253         len += rlen;
254
255         *pout = p;
256         return len;
257 }
258
259 static int fake_reply(struct dns_query *query)
260 {
261         unsigned char *p;
262
263         query->reply = malloc(BUFSIZE);
264         if (query->reply == NULL) {
265                 return ENOMEM;
266         }
267
268         memset(query->reply, 0, BUFSIZE);
269         p = query->reply;
270
271         fake_header(query);
272         query->reply_len = NS_HFIXEDSZ;
273         p += NS_HFIXEDSZ;
274
275         /* advances p internally */
276         query->reply_len += fake_question(query, &p);
277         query->reply_len += fake_answer(query, &p);
278
279         return 0;
280 }
281
282 static char *extract_name(char **buffer, size_t maxlen)
283 {
284         char *query, *qp, *bp;
285         unsigned int len;
286         unsigned int i;
287
288         query = malloc(maxlen);
289         if (query == NULL) return NULL;
290
291         i = 0;
292         qp = query;
293         bp = *buffer;
294         do {
295                 len = *bp;
296                 bp++;
297
298                 if (len > (maxlen - (qp - query))) {
299                         /* label is past the buffer */
300                         free(query);
301                         return NULL;
302                 }
303
304                 for (i = 0; i < len; i++) {
305                         *qp++ = *bp++;
306                 }
307
308                 if (len > 0) {
309                         *qp++ = '.';
310                 } else {
311                         *qp = '\0';
312                 }
313         } while (len > 0);
314
315         *buffer = bp;
316         return query;
317 }
318
319 static int parse_query(unsigned char *buffer,
320                        size_t len,
321                        struct dns_query *query)
322 {
323         unsigned char *p;
324
325         p = buffer;
326
327         if (len < NS_HFIXEDSZ) {
328                 /* Message too short */
329                 return EBADMSG;
330         }
331
332         if (DNS_HEADER_OPCODE(p) != 0) {
333                 /* Queries must have the opcode set to 0 */
334                 return EBADMSG;
335         }
336
337         if (DNS_HEADER_QDCOUNT(p) != 1) {
338                 /* We only support one query */
339                 return EBADMSG;
340         }
341
342         if (len < NS_HFIXEDSZ + 2 * sizeof(uint16_t)) {
343                 /* No room for class and type */
344                 return EBADMSG;
345         }
346
347         /* Need to remember the query to respond with the same */
348         query->id = DNS_HEADER_QID(p);
349
350         /* Done with the header, move past it */
351         p += NS_HFIXEDSZ;
352         query->query = extract_name((char **) &p, len - NS_HFIXEDSZ);
353         if (query->query == NULL) {
354                 return EIO;
355         }
356
357         query->qclass = DNS_QUESTION_CLASS(p);
358         if (query->qclass != ns_c_in) {
359                 /* We only support Internet queries */
360                 return EBADMSG;
361         }
362
363         query->qtype = DNS_QUESTION_TYPE(p);
364         return 0;
365 }
366
367 static void dns(int sock)
368 {
369         struct sockaddr_storage css;
370         socklen_t addrlen = sizeof(css);
371         ssize_t bret;
372         unsigned char buf[BUFSIZE];
373         struct dns_query query;
374         int rv;
375
376         ZERO_STRUCT(query);
377
378         while (1) {
379                 free_dns_query(&query);
380
381                 /* for advanced features, use recvmsg here */
382                 ZERO_STRUCT(buf);
383                 bret = recvfrom(sock, buf, BUFSIZE, 0,
384                                 (struct sockaddr *) &css, &addrlen);
385                 if (bret == -1) {
386                         perror("recvfrom");
387                         continue;
388                 }
389
390                 /* parse query */
391                 rv = parse_query(buf, bret, &query);
392                 if (rv != 0) {
393                         continue;
394                 }
395
396                 /* Construct the reply */
397                 rv = fake_reply(&query);
398                 if (rv != 0) {
399                         continue;
400                 }
401
402                 /* send reply back */
403                 bret = sendto(sock, query.reply, query.reply_len, 0,
404                                 (struct sockaddr *) &css, addrlen);
405                 if (bret == -1) {
406                         perror("sendto");
407                         continue;
408                 }
409         }
410 }
411
412 static int pidfile(const char *path)
413 {
414         int err;
415         int fd;
416         char pid_str[32] = { 0 };
417         ssize_t nwritten;
418         size_t len;
419
420         fd = open(path, O_RDONLY, 0644);
421         err = errno;
422         if (fd != -1) {
423                 close(fd);
424                 return EEXIST;
425         } else if (err != ENOENT) {
426                 return err;
427         }
428
429         fd = open(path, O_CREAT | O_WRONLY | O_EXCL, 0644);
430         err = errno;
431         if (fd == -1) {
432                 return err;
433         }
434
435         snprintf(pid_str, sizeof(pid_str) -1, "%u\n", (unsigned int) getpid());
436         len = strlen(pid_str);
437
438         nwritten = write(fd, pid_str, len);
439         close(fd);
440         if (nwritten != (ssize_t)len) {
441                 return EIO;
442         }
443
444         return 0;
445 }
446
447 static int become_daemon(void)
448 {
449         int ret;
450         pid_t child_pid;
451         int fd;
452         int i;
453
454         if (getppid() == 1) {
455                 return 0;
456         }
457
458         child_pid = fork();
459         if (child_pid == -1) {
460                 ret = errno;
461                 perror("fork");
462                 return ret;
463         } else if (child_pid > 0) {
464                 exit(0);
465         }
466
467         /* If a working directory was defined, go there */
468 #ifdef WORKING_DIR
469         chdir(WORKING_DIR);
470 #endif
471
472         ret = setsid();
473         if (ret == -1) {
474                 ret = errno;
475                 perror("setsid");
476                 return ret;
477         }
478
479         for (fd = getdtablesize(); fd >= 0; --fd) {
480                 close(fd);
481         }
482
483         for (i = 0; i < 3; i++) {
484                 fd = open("/dev/null", O_RDWR, 0);
485                 if (fd < 0) {
486                         fd = open("/dev/null", O_WRONLY, 0);
487                 }
488                 if (fd < 0) {
489                         ret = errno;
490                         perror("Can't open /dev/null");
491                         return ret;
492                 }
493                 if (fd != i) {
494                         perror("Didn't get correct fd");
495                         close(fd);
496                         return EINVAL;
497                 }
498         }
499
500         umask(0177);
501         return 0;
502 }
503
504 /*
505  * Returns 0 on success, errno on failure.
506  * If successful, sock is a ready to use socket.
507  */
508 static int setup_srv(struct dns_srv_opts *opts, int *_sock)
509 {
510         struct addrinfo hints;
511         struct addrinfo *res, *ri;
512         char svc[6];
513         int ret;
514         int sock;
515
516         memset(&hints, 0, sizeof(hints));
517         hints.ai_family = AF_UNSPEC;
518         hints.ai_socktype = SOCK_DGRAM;
519         hints.ai_flags = AI_PASSIVE;
520
521         snprintf(svc, sizeof(svc), "%d", opts->port);
522
523         ret = getaddrinfo(opts->bind, svc, &hints, &res);
524         if (ret != 0) {
525                 return errno;
526         }
527
528         for (ri = res; ri != NULL; ri = ri->ai_next) {
529                 sock = socket(ri->ai_family, ri->ai_socktype, ri->ai_protocol);
530                 if (sock == -1) {
531                         ret = errno;
532                         freeaddrinfo(res);
533                         perror("socket");
534                         return ret;
535                 }
536
537                 ret = bind(sock, ri->ai_addr, ri->ai_addrlen);
538                 if (ret == 0) {
539                         break;
540                 }
541
542                 close(sock);
543         }
544         freeaddrinfo(res);
545
546         if (ri == NULL) {
547                 fprintf(stderr, "Could not bind\n");
548                 return EFAULT;
549         }
550
551         *_sock = sock;
552         return 0;
553 }
554
555 int main(int argc, char **argv)
556 {
557         int ret;
558         int sock = -1;
559         struct dns_srv_opts opts;
560         int opt;
561         int optindex;
562         static struct option long_options[] = {
563                 { discard_const_p(char, "bind-addr"),   required_argument,      0,  'b' },
564                 { discard_const_p(char, "daemon"),      no_argument,            0,  'D' },
565                 { discard_const_p(char, "port"),        required_argument,      0,  'p' },
566                 { discard_const_p(char, "pid"),         required_argument,      0,  0 },
567                 { 0,                                    0,                      0,  0 }
568         };
569
570         opts.bind = NULL;
571         opts.pidfile = PIDFILE;
572         opts.daemon = false;
573         opts.port = DNS_PORT;
574
575         while ((opt = getopt_long(argc, argv, "Db:p:",
576                                   long_options, &optindex)) != -1)
577         {
578                 switch (opt) {
579                 case 0:
580                         if (optindex == 3) {
581                                 opts.pidfile = optarg;
582                         }
583                         break;
584                 case 'b':
585                         opts.bind = optarg;
586                         break;
587                 case 'D':
588                         opts.daemon = true;
589                         break;
590                 case 'p':
591                         opts.port = atoi(optarg);
592                         break;
593                 default: /* '?' */
594                         fprintf(stderr, "Usage: %s [-p port] [-b bind_addr] "
595                                         "[-D] [--pid pidfile]\n"
596                                         "-D tells the server to become a "
597                                         "deamon and write a PIDfile\n"
598                                         "The default PIDfile is '%s' "
599                                         "in the current directory\n",
600                                         PIDFILE, argv[0]);
601                         ret = 1;
602                         goto done;
603                 }
604         }
605
606         if (opts.daemon) {
607                 ret = become_daemon();
608                 if (ret != 0) {
609                         fprintf(stderr, "Cannot become daemon: %s\n",
610                                 strerror(ret));
611                         goto done;
612                 }
613         }
614
615         ret = setup_srv(&opts, &sock);
616         if (ret != 0) {
617                 fprintf(stderr, "Cannot setup server: %s\n", strerror(ret));
618                 goto done;
619         }
620
621         if (opts.daemon) {
622                 if (opts.pidfile == NULL) {
623                         fprintf(stderr, "Error: pidfile == NULL\n");
624                         ret = -1;
625                         goto done;
626                 }
627
628                 ret = pidfile(opts.pidfile);
629                 if (ret != 0) {
630                         fprintf(stderr, "Cannot create pidfile %s: %s\n",
631                                 opts.pidfile, strerror(ret));
632                         goto done;
633                 }
634         }
635
636         if (sock != -1) {
637                 dns(sock);
638                 close(sock);
639         }
640
641         if (opts.daemon) {
642                 unlink(opts.pidfile);
643         }
644
645         ret = 0;
646
647 done:
648         return ret;
649 }