Testsuite: fakens include unistd.h for sleep
[exim.git] / test / src / fakens.c
1 /*************************************************
2 * fakens - A Fake Nameserver Program *
3 *************************************************/
4
5 /* This program exists to support the testing of DNS handling code in Exim. It
6 avoids the need to install special zones in a real nameserver. When Exim is
7 running in its (new) test harness, DNS lookups are first passed to this program
8 instead of to the real resolver. (With a few exceptions - see the discussion in
9 the test suite's README file.) The program is also passed the name of the Exim
10 spool directory; it expects to find its "zone files" in dnszones relative to
11 exim config_main_directory. Note that there is little checking in this program. The fake
12 zone files are assumed to be syntactically valid.
13
14 The zones that are handled are found by scanning the dnszones directory. A file
15 whose name is of the form db.ip4.x is a zone file for .x.in-addr.arpa; a file
16 whose name is of the form db.ip6.x is a zone file for .x.ip6.arpa; a file of
17 the form db.anything.else is a zone file for .anything.else. A file of the form
18 qualify.x.y specifies the domain that is used to qualify single-component
19 names, except for the name "dontqualify".
20
21 The arguments to the program are:
22
23 the name of the Exim spool directory
24 the domain name that is being sought
25 the DNS record type that is being sought
26
27 The output from the program is written to stdout. It is supposed to be in
28 exactly the same format as a traditional namserver response (see RFC 1035) so
29 that Exim can process it as normal. At present, no compression is used.
30 Error messages are written to stderr.
31
32 The return codes from the program are zero for success, and otherwise the
33 values that are set in h_errno after a failing call to the normal resolver:
34
35 1 HOST_NOT_FOUND host not found (authoritative)
36 2 TRY_AGAIN server failure
37 3 NO_RECOVERY non-recoverable error
38 4 NO_DATA valid name, no data of requested type
39
40 In a real nameserver, TRY_AGAIN is also used for a non-authoritative not found,
41 but it is not used for that here. There is also one extra return code:
42
43 5 PASS_ON requests Exim to call res_search()
44
45 This is used for zones that fakens does not recognize. It is also used if a
46 line in the zone file contains exactly this:
47
48 PASS ON NOT FOUND
49
50 and the domain is not found. It converts the the result to PASS_ON instead of
51 HOST_NOT_FOUND.
52
53 Any DNS record line in a zone file can be prefixed with "DELAY=" and
54 a number of milliseconds (followed by one space).
55
56 Any DNS record line in a zone file can be prefixed with "DNSSEC ";
57 if all the records found by a lookup are marked
58 as such then the response will have the "AD" bit set.
59
60 Any DNS record line in a zone file can be prefixed with "AA "
61 if all the records found by a lookup are marked
62 as such then the response will have the "AA" bit set.
63
64 Any DNS record line in a zone file can be prefixed with "TTL=" and
65 a number of seconds (followed by one space).
66
67 */
68
69 #include <ctype.h>
70 #include <stdarg.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <netdb.h>
75 #include <errno.h>
76 #include <signal.h>
77 #include <arpa/nameser.h>
78 #include <sys/types.h>
79 #include <sys/time.h>
80 #include <dirent.h>
81 #include <unistd.h>
82
83 #define FALSE 0
84 #define TRUE 1
85 #define PASS_ON 5
86
87 typedef int BOOL;
88 typedef unsigned char uschar;
89
90 #define CS (char *)
91 #define CCS (const char *)
92 #define US (unsigned char *)
93
94 #define Ustrcat(s,t) strcat(CS(s),CCS(t))
95 #define Ustrchr(s,n) US strchr(CCS(s),n)
96 #define Ustrcmp(s,t) strcmp(CCS(s),CCS(t))
97 #define Ustrcpy(s,t) strcpy(CS(s),CCS(t))
98 #define Ustrlen(s) (int)strlen(CCS(s))
99 #define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n)
100 #define Ustrncpy(s,t,n) strncpy(CS(s),CCS(t),n)
101
102 typedef struct zoneitem {
103 uschar *zone;
104 uschar *zonefile;
105 } zoneitem;
106
107 typedef struct tlist {
108 uschar *name;
109 int value;
110 } tlist;
111
112 #define DEFAULT_TTL 3600U
113
114 /* On some (older?) operating systems, the standard ns_t_xxx definitions are
115 not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
116 not defined, assume we are in this state. A really old system might not even
117 know about AAAA and SRV at all. */
118
119 #ifndef ns_t_a
120 # define ns_t_a T_A
121 # define ns_t_ns T_NS
122 # define ns_t_cname T_CNAME
123 # define ns_t_soa T_SOA
124 # define ns_t_ptr T_PTR
125 # define ns_t_mx T_MX
126 # define ns_t_txt T_TXT
127 # define ns_t_aaaa T_AAAA
128 # define ns_t_srv T_SRV
129 # define ns_t_tlsa T_TLSA
130 # ifndef T_AAAA
131 # define T_AAAA 28
132 # endif
133 # ifndef T_SRV
134 # define T_SRV 33
135 # endif
136 # ifndef T_TLSA
137 # define T_TLSA 52
138 # endif
139 #endif
140
141 static tlist type_list[] = {
142 { US"A", ns_t_a },
143 { US"NS", ns_t_ns },
144 { US"CNAME", ns_t_cname },
145 { US"SOA", ns_t_soa },
146 { US"PTR", ns_t_ptr },
147 { US"MX", ns_t_mx },
148 { US"TXT", ns_t_txt },
149 { US"AAAA", ns_t_aaaa },
150 { US"SRV", ns_t_srv },
151 { US"TLSA", ns_t_tlsa },
152 { NULL, 0 }
153 };
154
155
156
157 /*************************************************
158 * Get memory and sprintf into it *
159 *************************************************/
160
161 /* This is used when building a table of zones and their files.
162
163 Arguments:
164 format a format string
165 ... arguments
166
167 Returns: pointer to formatted string
168 */
169
170 static uschar *
171 fcopystring(uschar *format, ...)
172 {
173 uschar *yield;
174 char buffer[256];
175 va_list ap;
176 va_start(ap, format);
177 vsprintf(buffer, CS format, ap);
178 va_end(ap);
179 yield = (uschar *)malloc(Ustrlen(buffer) + 1);
180 Ustrcpy(yield, buffer);
181 return yield;
182 }
183
184
185 /*************************************************
186 * Pack name into memory *
187 *************************************************/
188
189 /* This function packs a domain name into memory according to DNS rules. At
190 present, it doesn't do any compression.
191
192 Arguments:
193 name the name
194 pk where to put it
195
196 Returns: the updated value of pk
197 */
198
199 static uschar *
200 packname(uschar *name, uschar *pk)
201 {
202 while (*name != 0)
203 {
204 uschar *p = name;
205 while (*p != 0 && *p != '.') p++;
206 *pk++ = (p - name);
207 memmove(pk, name, p - name);
208 pk += p - name;
209 name = (*p == 0)? p : p + 1;
210 }
211 *pk++ = 0;
212 return pk;
213 }
214
215 uschar *
216 bytefield(uschar ** pp, uschar * pk)
217 {
218 unsigned value = 0;
219 uschar * p = *pp;
220
221 while (isdigit(*p)) value = value*10 + *p++ - '0';
222 while (isspace(*p)) p++;
223 *pp = p;
224 *pk++ = value & 255;
225 return pk;
226 }
227
228 uschar *
229 shortfield(uschar ** pp, uschar * pk)
230 {
231 unsigned value = 0;
232 uschar * p = *pp;
233
234 while (isdigit(*p)) value = value*10 + *p++ - '0';
235 while (isspace(*p)) p++;
236 *pp = p;
237 *pk++ = (value >> 8) & 255;
238 *pk++ = value & 255;
239 return pk;
240 }
241
242 uschar *
243 longfield(uschar ** pp, uschar * pk)
244 {
245 unsigned long value = 0;
246 uschar * p = *pp;
247
248 while (isdigit(*p)) value = value*10 + *p++ - '0';
249 while (isspace(*p)) p++;
250 *pp = p;
251 *pk++ = (value >> 24) & 255;
252 *pk++ = (value >> 16) & 255;
253 *pk++ = (value >> 8) & 255;
254 *pk++ = value & 255;
255 return pk;
256 }
257
258
259
260 /*************************************************/
261
262 static void
263 milliwait(struct itimerval *itval)
264 {
265 sigset_t sigmask;
266 sigset_t old_sigmask;
267
268 if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
269 return;
270 (void)sigemptyset(&sigmask); /* Empty mask */
271 (void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */
272 (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */
273 (void)setitimer(ITIMER_REAL, itval, NULL); /* Start timer */
274 (void)sigfillset(&sigmask); /* All signals */
275 (void)sigdelset(&sigmask, SIGALRM); /* Remove SIGALRM */
276 (void)sigsuspend(&sigmask); /* Until SIGALRM */
277 (void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL); /* Restore mask */
278 }
279
280 static void
281 millisleep(int msec)
282 {
283 struct itimerval itval;
284 itval.it_interval.tv_sec = 0;
285 itval.it_interval.tv_usec = 0;
286 itval.it_value.tv_sec = msec/1000;
287 itval.it_value.tv_usec = (msec % 1000) * 1000;
288 milliwait(&itval);
289 }
290
291
292 /*************************************************
293 * Scan file for RRs *
294 *************************************************/
295
296 /* This function scans an open "zone file" for appropriate records, and adds
297 any that are found to the output buffer.
298
299 Arguments:
300 f the input FILE
301 zone the current zone name
302 domain the domain we are looking for
303 qtype the type of RR we want
304 qtypelen the length of qtype
305 pkptr points to the output buffer pointer; this is updated
306 countptr points to the record count; this is updated
307 dnssec points to the AD flag indicator; this is updated
308 aa points to the AA flag indicator; this is updated
309
310 Returns: 0 on success, else HOST_NOT_FOUND or NO_DATA or NO_RECOVERY or
311 PASS_ON - the latter if a "PASS ON NOT FOUND" line is seen
312 */
313
314 static int
315 find_records(FILE *f, uschar *zone, uschar *domain, uschar *qtype,
316 int qtypelen, uschar **pkptr, int *countptr, BOOL * dnssec, BOOL * aa)
317 {
318 int yield = HOST_NOT_FOUND;
319 int domainlen = Ustrlen(domain);
320 BOOL pass_on_not_found = FALSE;
321 tlist *typeptr;
322 uschar *pk = *pkptr;
323 uschar buffer[256];
324 uschar rrdomain[256];
325 uschar RRdomain[256];
326
327
328 /* Decode the required type */
329 for (typeptr = type_list; typeptr->name != NULL; typeptr++)
330 { if (Ustrcmp(typeptr->name, qtype) == 0) break; }
331 if (typeptr->name == NULL)
332 {
333 fprintf(stderr, "fakens: unknown record type %s\n", qtype);
334 return NO_RECOVERY;
335 }
336
337 rrdomain[0] = 0; /* No previous domain */
338 (void)fseek(f, 0, SEEK_SET); /* Start again at the beginning */
339
340 if (dnssec) *dnssec = TRUE; /* cancelled by first nonsecure rec found */
341 if (aa) *aa = TRUE; /* cancelled by first non-aa rec found */
342
343 /* Scan for RRs */
344
345 while (fgets(CS buffer, sizeof(buffer), f) != NULL)
346 {
347 uschar *rdlptr;
348 uschar *p, *ep, *pp;
349 BOOL found_cname = FALSE;
350 int i, value;
351 int tvalue = typeptr->value;
352 int qtlen = qtypelen;
353 BOOL rr_sec = FALSE;
354 BOOL rr_aa = FALSE;
355 int delay = 0;
356 uint ttl = DEFAULT_TTL;
357
358 p = buffer;
359 while (isspace(*p)) p++;
360 if (*p == 0 || *p == ';') continue;
361
362 if (Ustrncmp(p, US"PASS ON NOT FOUND", 17) == 0)
363 {
364 pass_on_not_found = TRUE;
365 continue;
366 }
367
368 ep = buffer + Ustrlen(buffer);
369 while (isspace(ep[-1])) ep--;
370 *ep = 0;
371
372 p = buffer;
373 for (;;)
374 {
375 if (Ustrncmp(p, US"DNSSEC ", 7) == 0) /* tagged as secure */
376 {
377 rr_sec = TRUE;
378 p += 7;
379 }
380 else if (Ustrncmp(p, US"AA ", 3) == 0) /* tagged as authoritive */
381 {
382 rr_aa = TRUE;
383 p += 3;
384 }
385 else if (Ustrncmp(p, US"DELAY=", 6) == 0) /* delay before response */
386 {
387 for (p += 6; *p >= '0' && *p <= '9'; p++) delay = delay*10 + *p - '0';
388 if (isspace(*p)) p++;
389 }
390 else if (Ustrncmp(p, US"TTL=", 4) == 0) /* TTL for record */
391 {
392 ttl = 0;
393 for (p += 4; *p >= '0' && *p <= '9'; p++) ttl = ttl*10 + *p - '0';
394 if (isspace(*p)) p++;
395 }
396 else
397 break;
398 }
399
400 if (!isspace(*p)) /* new domain name */
401 {
402 uschar *pp = rrdomain;
403 uschar *PP = RRdomain;
404 while (!isspace(*p))
405 {
406 *pp++ = tolower(*p);
407 *PP++ = *p++;
408 }
409 if (pp[-1] != '.')
410 {
411 Ustrcpy(pp, zone);
412 Ustrcpy(PP, zone);
413 }
414 else
415 {
416 pp[-1] = 0;
417 PP[-1] = 0;
418 }
419 } /* else use previous line's domain name */
420
421 /* Compare domain names; first check for a wildcard */
422
423 if (rrdomain[0] == '*')
424 {
425 int restlen = Ustrlen(rrdomain) - 1;
426 if (domainlen > restlen &&
427 Ustrcmp(domain + domainlen - restlen, rrdomain + 1) != 0) continue;
428 }
429
430 /* Not a wildcard RR */
431
432 else if (Ustrcmp(domain, rrdomain) != 0) continue;
433
434 /* The domain matches */
435
436 if (yield == HOST_NOT_FOUND) yield = NO_DATA;
437
438 /* Compare RR types; a CNAME record is always returned */
439
440 while (isspace(*p)) p++;
441
442 if (Ustrncmp(p, "CNAME", 5) == 0)
443 {
444 tvalue = ns_t_cname;
445 qtlen = 5;
446 found_cname = TRUE;
447 }
448 else if (Ustrncmp(p, qtype, qtypelen) != 0 || !isspace(p[qtypelen])) continue;
449
450 /* Found a relevant record */
451 if (delay)
452 millisleep(delay);
453
454 if (dnssec && !rr_sec)
455 *dnssec = FALSE; /* cancel AD return */
456
457 if (aa && !rr_aa)
458 *aa = FALSE; /* cancel AA return */
459
460 yield = 0;
461 *countptr = *countptr + 1;
462
463 p += qtlen;
464 while (isspace(*p)) p++;
465
466 /* For a wildcard record, use the search name; otherwise use the record's
467 name in its original case because it might contain upper case letters. */
468
469 pk = packname((rrdomain[0] == '*')? domain : RRdomain, pk);
470 *pk++ = (tvalue >> 8) & 255;
471 *pk++ = (tvalue) & 255;
472 *pk++ = 0;
473 *pk++ = 1; /* class = IN */
474
475 *pk++ = (ttl >>24) & 255;
476 *pk++ = (ttl >>16) & 255;
477 *pk++ = (ttl >> 8) & 255;
478 *pk++ = ttl & 255;
479
480 rdlptr = pk; /* remember rdlength field */
481 pk += 2;
482
483 /* The rest of the data depends on the type */
484
485 switch (tvalue)
486 {
487 case ns_t_soa:
488 p = strtok(p, " ");
489 ep = p + strlen(p);
490 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
491 pk = packname(p, pk); /* primary ns */
492 p = strtok(NULL, " ");
493 pk = packname(p , pk); /* responsible mailbox */
494 *(p += strlen(p)) = ' ';
495 while (isspace(*p)) p++;
496 pk = longfield(&p, pk); /* serial */
497 pk = longfield(&p, pk); /* refresh */
498 pk = longfield(&p, pk); /* retry */
499 pk = longfield(&p, pk); /* expire */
500 pk = longfield(&p, pk); /* minimum */
501 break;
502
503 case ns_t_a:
504 for (i = 0; i < 4; i++)
505 {
506 value = 0;
507 while (isdigit(*p)) value = value*10 + *p++ - '0';
508 *pk++ = value;
509 p++;
510 }
511 break;
512
513 /* The only occurrence of a double colon is for ::1 */
514 case ns_t_aaaa:
515 if (Ustrcmp(p, "::1") == 0)
516 {
517 memset(pk, 0, 15);
518 pk += 15;
519 *pk++ = 1;
520 }
521 else for (i = 0; i < 8; i++)
522 {
523 value = 0;
524 while (isxdigit(*p))
525 {
526 value = value * 16 + toupper(*p) - (isdigit(*p)? '0' : '7');
527 p++;
528 }
529 *pk++ = (value >> 8) & 255;
530 *pk++ = value & 255;
531 p++;
532 }
533 break;
534
535 case ns_t_mx:
536 pk = shortfield(&p, pk);
537 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
538 pk = packname(p, pk);
539 break;
540
541 case ns_t_txt:
542 pp = pk++;
543 if (*p == '"') p++; /* Should always be the case */
544 while (*p != 0 && *p != '"') *pk++ = *p++;
545 *pp = pk - pp - 1;
546 break;
547
548 case ns_t_tlsa:
549 pk = bytefield(&p, pk); /* usage */
550 pk = bytefield(&p, pk); /* selector */
551 pk = bytefield(&p, pk); /* match type */
552 while (isxdigit(*p))
553 {
554 value = toupper(*p) - (isdigit(*p) ? '0' : '7') << 4;
555 if (isxdigit(*++p))
556 {
557 value |= toupper(*p) - (isdigit(*p) ? '0' : '7');
558 p++;
559 }
560 *pk++ = value & 255;
561 }
562
563 break;
564
565 case ns_t_srv:
566 for (i = 0; i < 3; i++)
567 {
568 value = 0;
569 while (isdigit(*p)) value = value*10 + *p++ - '0';
570 while (isspace(*p)) p++;
571 *pk++ = (value >> 8) & 255;
572 *pk++ = value & 255;
573 }
574
575 /* Fall through */
576
577 case ns_t_cname:
578 case ns_t_ns:
579 case ns_t_ptr:
580 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
581 pk = packname(p, pk);
582 break;
583 }
584
585 /* Fill in the length, and we are done with this RR */
586
587 rdlptr[0] = ((pk - rdlptr - 2) >> 8) & 255;
588 rdlptr[1] = (pk -rdlptr - 2) & 255;
589 }
590
591 *pkptr = pk;
592 return (yield == HOST_NOT_FOUND && pass_on_not_found)? PASS_ON : yield;
593 }
594
595
596 static void
597 alarmfn(int sig)
598 {
599 }
600
601
602 /*************************************************
603 * Special-purpose domains *
604 *************************************************/
605
606 static int
607 special_manyhome(uschar * packet, uschar * domain)
608 {
609 uschar *pk = packet + 12;
610 uschar *rdlptr;
611 int i, j;
612
613 memset(packet, 0, 12);
614
615 for (i = 104; i <= 111; i++) for (j = 0; j <= 255; j++)
616 {
617 pk = packname(domain, pk);
618 *pk++ = (ns_t_a >> 8) & 255;
619 *pk++ = (ns_t_a) & 255;
620 *pk++ = 0;
621 *pk++ = 1; /* class = IN */
622 pk += 4; /* TTL field; don't care */
623 rdlptr = pk; /* remember rdlength field */
624 pk += 2;
625
626 *pk++ = 10; *pk++ = 250; *pk++ = i; *pk++ = j;
627
628 rdlptr[0] = ((pk - rdlptr - 2) >> 8) & 255;
629 rdlptr[1] = (pk - rdlptr - 2) & 255;
630 }
631
632 packet[6] = (2048 >> 8) & 255;
633 packet[7] = 2048 & 255;
634 packet[10] = 0;
635 packet[11] = 0;
636
637 (void)fwrite(packet, 1, pk - packet, stdout);
638 return 0;
639 }
640
641 static int
642 special_again(uschar * packet, uschar * domain)
643 {
644 int delay = atoi(CCS domain); /* digits at the start of the name */
645 if (delay > 0) sleep(delay);
646 return TRY_AGAIN;
647 }
648
649
650 /*************************************************
651 * Entry point and main program *
652 *************************************************/
653
654 int
655 main(int argc, char **argv)
656 {
657 FILE *f;
658 DIR *d;
659 int domlen, qtypelen;
660 int yield, count;
661 int i;
662 int zonecount = 0;
663 struct dirent *de;
664 zoneitem zones[32];
665 uschar *qualify = NULL;
666 uschar *p, *zone;
667 uschar *zonefile = NULL;
668 uschar domain[256];
669 uschar buffer[256];
670 uschar qtype[12];
671 uschar packet[2048 * 32 + 32];
672 HEADER *header = (HEADER *)packet;
673 uschar *pk = packet;
674 BOOL dnssec;
675 BOOL aa;
676
677 signal(SIGALRM, alarmfn);
678
679 if (argc != 4)
680 {
681 fprintf(stderr, "fakens: expected 3 arguments, received %d\n", argc-1);
682 return NO_RECOVERY;
683 }
684
685 /* Find the zones */
686
687 (void)sprintf(CS buffer, "%s/dnszones", argv[1]);
688
689 d = opendir(CCS buffer);
690 if (d == NULL)
691 {
692 fprintf(stderr, "fakens: failed to opendir %s: %s\n", buffer,
693 strerror(errno));
694 return NO_RECOVERY;
695 }
696
697 while ((de = readdir(d)) != NULL)
698 {
699 uschar *name = US de->d_name;
700 if (Ustrncmp(name, "qualify.", 8) == 0)
701 {
702 qualify = fcopystring(US "%s", name + 7);
703 continue;
704 }
705 if (Ustrncmp(name, "db.", 3) != 0) continue;
706 if (Ustrncmp(name + 3, "ip4.", 4) == 0)
707 zones[zonecount].zone = fcopystring(US "%s.in-addr.arpa", name + 6);
708 else if (Ustrncmp(name + 3, "ip6.", 4) == 0)
709 zones[zonecount].zone = fcopystring(US "%s.ip6.arpa", name + 6);
710 else
711 zones[zonecount].zone = fcopystring(US "%s", name + 2);
712 zones[zonecount++].zonefile = fcopystring(US "%s", name);
713 }
714 (void)closedir(d);
715
716 /* Get the RR type and upper case it, and check that we recognize it. */
717
718 Ustrncpy(qtype, argv[3], sizeof(qtype));
719 qtypelen = Ustrlen(qtype);
720 for (p = qtype; *p != 0; p++) *p = toupper(*p);
721
722 /* Find the domain, lower case it, deal with any specials,
723 check that it is in a zone that we handle,
724 and set up the zone file name. The zone names in the table all start with a
725 dot. */
726
727 domlen = Ustrlen(argv[2]);
728 if (argv[2][domlen-1] == '.') domlen--;
729 Ustrncpy(domain, argv[2], domlen);
730 domain[domlen] = 0;
731 for (i = 0; i < domlen; i++) domain[i] = tolower(domain[i]);
732
733 if (Ustrcmp(domain, "manyhome.test.ex") == 0 && Ustrcmp(qtype, "A") == 0)
734 return special_manyhome(packet, domain);
735 else if (domlen >= 14 && Ustrcmp(domain + domlen - 14, "test.again.dns") == 0)
736 return special_again(packet, domain);
737 else if (domlen >= 13 && Ustrcmp(domain + domlen - 13, "test.fail.dns") == 0)
738 return NO_RECOVERY;
739
740
741 if (Ustrchr(domain, '.') == NULL && qualify != NULL &&
742 Ustrcmp(domain, "dontqualify") != 0)
743 {
744 Ustrcat(domain, qualify);
745 domlen += Ustrlen(qualify);
746 }
747
748 for (i = 0; i < zonecount; i++)
749 {
750 int zlen;
751 zone = zones[i].zone;
752 zlen = Ustrlen(zone);
753 if (Ustrcmp(domain, zone+1) == 0 || (domlen >= zlen &&
754 Ustrcmp(domain + domlen - zlen, zone) == 0))
755 {
756 zonefile = zones[i].zonefile;
757 break;
758 }
759 }
760
761 if (zonefile == NULL)
762 {
763 fprintf(stderr, "fakens: query not in faked zone: domain is: %s\n", domain);
764 return PASS_ON;
765 }
766
767 (void)sprintf(CS buffer, "%s/dnszones/%s", argv[1], zonefile);
768
769 /* Initialize the start of the response packet. We don't have to fake up
770 everything, because we know that Exim will look only at the answer and
771 additional section parts. */
772
773 memset(packet, 0, 12);
774 pk += 12;
775
776 /* Open the zone file. */
777
778 f = fopen(CS buffer, "r");
779 if (f == NULL)
780 {
781 fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));
782 return NO_RECOVERY;
783 }
784
785 /* Find the records we want, and add them to the result. */
786
787 count = 0;
788 yield = find_records(f, zone, domain, qtype, qtypelen, &pk, &count, &dnssec, &aa);
789 if (yield == NO_RECOVERY) goto END_OFF;
790 header->ancount = htons(count);
791
792 /* If the AA bit should be set (as indicated by the AA prefix in the zone file),
793 we are expected to return some records in the authortive section. Bind9: If
794 there is data in the answer section, the authoritive section contains the NS
795 records, otherwise it contains the SOA record. Currently we mimic this
796 behaviour for the first case (there is some answer record).
797 */
798
799 if (aa)
800 find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
801 header->nscount = htons(count - ntohs(header->ancount));
802
803 /* There is no need to return any additional records because Exim no longer
804 (from release 4.61) makes any use of them. */
805 header->arcount = 0;
806
807 if (dnssec)
808 header->ad = 1;
809
810 if (aa)
811 header->aa = 1;
812
813 /* Close the zone file, write the result, and return. */
814
815 END_OFF:
816 (void)fclose(f);
817 (void)fwrite(packet, 1, pk - packet, stdout);
818 return yield;
819 }
820
821 /* vi: aw ai sw=2 sts=2 ts=8 et
822 */
823 /* End of fakens.c */