Testsuite: fakens include unistd.h for sleep
[exim.git] / test / src / fakens.c
CommitLineData
c55a77db
PH
1/*************************************************
2* fakens - A Fake Nameserver Program *
3*************************************************/
4
5/* This program exists to support the testing of DNS handling code in Exim. It
6avoids the need to install special zones in a real nameserver. When Exim is
7running in its (new) test harness, DNS lookups are first passed to this program
8instead of to the real resolver. (With a few exceptions - see the discussion in
9the test suite's README file.) The program is also passed the name of the Exim
5f3d0983
HSHR
10spool directory; it expects to find its "zone files" in dnszones relative to
11exim config_main_directory. Note that there is little checking in this program. The fake
c55a77db
PH
12zone files are assumed to be syntactically valid.
13
14The zones that are handled are found by scanning the dnszones directory. A file
15whose name is of the form db.ip4.x is a zone file for .x.in-addr.arpa; a file
16whose name is of the form db.ip6.x is a zone file for .x.ip6.arpa; a file of
17the form db.anything.else is a zone file for .anything.else. A file of the form
18qualify.x.y specifies the domain that is used to qualify single-component
19names, except for the name "dontqualify".
20
21The 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
27The output from the program is written to stdout. It is supposed to be in
28exactly the same format as a traditional namserver response (see RFC 1035) so
29that Exim can process it as normal. At present, no compression is used.
30Error messages are written to stderr.
31
32The return codes from the program are zero for success, and otherwise the
33values 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
40In a real nameserver, TRY_AGAIN is also used for a non-authoritative not found,
41but 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
45This is used for zones that fakens does not recognize. It is also used if a
46line in the zone file contains exactly this:
47
48 PASS ON NOT FOUND
49
50and the domain is not found. It converts the the result to PASS_ON instead of
4d4c2a9b
JH
51HOST_NOT_FOUND.
52
846430d9 53Any DNS record line in a zone file can be prefixed with "DELAY=" and
6aa849d3 54a number of milliseconds (followed by one space).
846430d9 55
6aa849d3
JH
56Any DNS record line in a zone file can be prefixed with "DNSSEC ";
57if all the records found by a lookup are marked
f6584c83 58as such then the response will have the "AD" bit set.
589b3179 59
6aa849d3
JH
60Any DNS record line in a zone file can be prefixed with "AA "
61if all the records found by a lookup are marked
589b3179
HSHR
62as such then the response will have the "AA" bit set.
63
14b3c5bc
JH
64Any DNS record line in a zone file can be prefixed with "TTL=" and
65a number of seconds (followed by one space).
66
589b3179 67*/
c55a77db
PH
68
69#include <ctype.h>
70#include <stdarg.h>
71#include <stdio.h>
e265af1f 72#include <stdlib.h>
c55a77db
PH
73#include <string.h>
74#include <netdb.h>
75#include <errno.h>
9d787f10 76#include <signal.h>
c55a77db
PH
77#include <arpa/nameser.h>
78#include <sys/types.h>
846430d9 79#include <sys/time.h>
c55a77db 80#include <dirent.h>
2c98a555 81#include <unistd.h>
c55a77db
PH
82
83#define FALSE 0
84#define TRUE 1
85#define PASS_ON 5
86
87typedef int BOOL;
88typedef 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
c55a77db
PH
102typedef struct zoneitem {
103 uschar *zone;
104 uschar *zonefile;
105} zoneitem;
106
107typedef struct tlist {
108 uschar *name;
109 int value;
110} tlist;
111
14b3c5bc
JH
112#define DEFAULT_TTL 3600U
113
c55a77db
PH
114/* On some (older?) operating systems, the standard ns_t_xxx definitions are
115not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
116not defined, assume we are in this state. A really old system might not even
117know about AAAA and SRV at all. */
118
119#ifndef ns_t_a
b4161d10
JH
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
c55a77db
PH
139#endif
140
141static tlist type_list[] = {
142 { US"A", ns_t_a },
143 { US"NS", ns_t_ns },
144 { US"CNAME", ns_t_cname },
d2a2c69b 145 { US"SOA", ns_t_soa },
c55a77db
PH
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 },
b4161d10 151 { US"TLSA", ns_t_tlsa },
c55a77db
PH
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
163Arguments:
164 format a format string
165 ... arguments
166
167Returns: pointer to formatted string
168*/
169
170static uschar *
171fcopystring(uschar *format, ...)
172{
173uschar *yield;
174char buffer[256];
175va_list ap;
176va_start(ap, format);
e265af1f 177vsprintf(buffer, CS format, ap);
c55a77db
PH
178va_end(ap);
179yield = (uschar *)malloc(Ustrlen(buffer) + 1);
180Ustrcpy(yield, buffer);
181return 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
190present, it doesn't do any compression.
191
192Arguments:
193 name the name
194 pk where to put it
195
196Returns: the updated value of pk
197*/
198
199static uschar *
200packname(uschar *name, uschar *pk)
201{
202while (*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;
212return pk;
213}
214
36b894a6
JH
215uschar *
216bytefield(uschar ** pp, uschar * pk)
217{
218unsigned value = 0;
219uschar * p = *pp;
220
221while (isdigit(*p)) value = value*10 + *p++ - '0';
222while (isspace(*p)) p++;
223*pp = p;
224*pk++ = value & 255;
225return pk;
226}
227
b4161d10
JH
228uschar *
229shortfield(uschar ** pp, uschar * pk)
230{
231unsigned value = 0;
232uschar * p = *pp;
233
234while (isdigit(*p)) value = value*10 + *p++ - '0';
235while (isspace(*p)) p++;
236*pp = p;
237*pk++ = (value >> 8) & 255;
238*pk++ = value & 255;
239return pk;
240}
241
d2a2c69b
JH
242uschar *
243longfield(uschar ** pp, uschar * pk)
244{
245unsigned long value = 0;
246uschar * p = *pp;
247
248while (isdigit(*p)) value = value*10 + *p++ - '0';
249while (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;
255return pk;
256}
257
c55a77db
PH
258
259
846430d9
JH
260/*************************************************/
261
262static void
263milliwait(struct itimerval *itval)
264{
265sigset_t sigmask;
266sigset_t old_sigmask;
267
268if (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
280static void
281millisleep(int msec)
282{
283struct itimerval itval;
284itval.it_interval.tv_sec = 0;
285itval.it_interval.tv_usec = 0;
286itval.it_value.tv_sec = msec/1000;
287itval.it_value.tv_usec = (msec % 1000) * 1000;
288milliwait(&itval);
289}
290
291
c55a77db
PH
292/*************************************************
293* Scan file for RRs *
294*************************************************/
295
296/* This function scans an open "zone file" for appropriate records, and adds
297any that are found to the output buffer.
298
299Arguments:
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
f6584c83
HSHR
307 dnssec points to the AD flag indicator; this is updated
308 aa points to the AA flag indicator; this is updated
c55a77db
PH
309
310Returns: 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
314static int
315find_records(FILE *f, uschar *zone, uschar *domain, uschar *qtype,
589b3179 316 int qtypelen, uschar **pkptr, int *countptr, BOOL * dnssec, BOOL * aa)
c55a77db
PH
317{
318int yield = HOST_NOT_FOUND;
c55a77db
PH
319int domainlen = Ustrlen(domain);
320BOOL pass_on_not_found = FALSE;
321tlist *typeptr;
322uschar *pk = *pkptr;
323uschar buffer[256];
324uschar rrdomain[256];
75e0e026 325uschar RRdomain[256];
c55a77db 326
c55a77db 327
f6584c83 328/* Decode the required type */
c55a77db
PH
329for (typeptr = type_list; typeptr->name != NULL; typeptr++)
330 { if (Ustrcmp(typeptr->name, qtype) == 0) break; }
331if (typeptr->name == NULL)
332 {
333 fprintf(stderr, "fakens: unknown record type %s\n", qtype);
334 return NO_RECOVERY;
335 }
336
337rrdomain[0] = 0; /* No previous domain */
338(void)fseek(f, 0, SEEK_SET); /* Start again at the beginning */
339
f6584c83
HSHR
340if (dnssec) *dnssec = TRUE; /* cancelled by first nonsecure rec found */
341if (aa) *aa = TRUE; /* cancelled by first non-aa rec found */
4d4c2a9b 342
c55a77db
PH
343/* Scan for RRs */
344
345while (fgets(CS buffer, sizeof(buffer), f) != NULL)
346 {
347 uschar *rdlptr;
348 uschar *p, *ep, *pp;
349 BOOL found_cname = FALSE;
d2a2c69b 350 int i, value;
c55a77db
PH
351 int tvalue = typeptr->value;
352 int qtlen = qtypelen;
4d4c2a9b 353 BOOL rr_sec = FALSE;
589b3179 354 BOOL rr_aa = FALSE;
846430d9 355 int delay = 0;
14b3c5bc 356 uint ttl = DEFAULT_TTL;
c55a77db
PH
357
358 p = buffer;
359 while (isspace(*p)) p++;
360 if (*p == 0 || *p == ';') continue;
361
4d4c2a9b 362 if (Ustrncmp(p, US"PASS ON NOT FOUND", 17) == 0)
c55a77db
PH
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;
846430d9 373 for (;;)
1fb7660f 374 {
f6584c83 375 if (Ustrncmp(p, US"DNSSEC ", 7) == 0) /* tagged as secure */
1fb7660f
JH
376 {
377 rr_sec = TRUE;
378 p += 7;
379 }
f6584c83 380 else if (Ustrncmp(p, US"AA ", 3) == 0) /* tagged as authoritive */
589b3179
HSHR
381 {
382 rr_aa = TRUE;
383 p += 3;
384 }
f6584c83 385 else if (Ustrncmp(p, US"DELAY=", 6) == 0) /* delay before response */
1fb7660f
JH
386 {
387 for (p += 6; *p >= '0' && *p <= '9'; p++) delay = delay*10 + *p - '0';
6aa849d3 388 if (isspace(*p)) p++;
1fb7660f 389 }
14b3c5bc
JH
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 }
1fb7660f
JH
396 else
397 break;
398 }
4d4c2a9b 399
6aa849d3 400 if (!isspace(*p)) /* new domain name */
c55a77db
PH
401 {
402 uschar *pp = rrdomain;
75e0e026
PH
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 }
6aa849d3 419 } /* else use previous line's domain name */
c55a77db
PH
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 */
846430d9
JH
451 if (delay)
452 millisleep(delay);
453
f6584c83
HSHR
454 if (dnssec && !rr_sec)
455 *dnssec = FALSE; /* cancel AD return */
4d4c2a9b 456
f6584c83
HSHR
457 if (aa && !rr_aa)
458 *aa = FALSE; /* cancel AA return */
589b3179 459
c55a77db
PH
460 yield = 0;
461 *countptr = *countptr + 1;
462
463 p += qtlen;
464 while (isspace(*p)) p++;
465
75e0e026
PH
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);
c55a77db
PH
470 *pk++ = (tvalue >> 8) & 255;
471 *pk++ = (tvalue) & 255;
472 *pk++ = 0;
473 *pk++ = 1; /* class = IN */
474
14b3c5bc
JH
475 *pk++ = (ttl >>24) & 255;
476 *pk++ = (ttl >>16) & 255;
477 *pk++ = (ttl >> 8) & 255;
478 *pk++ = ttl & 255;
c55a77db
PH
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 {
d2a2c69b
JH
487 case ns_t_soa:
488 p = strtok(p, " ");
489 ep = p + strlen(p);
490 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
f6584c83 491 pk = packname(p, pk); /* primary ns */
d2a2c69b 492 p = strtok(NULL, " ");
f6584c83 493 pk = packname(p , pk); /* responsible mailbox */
d2a2c69b
JH
494 *(p += strlen(p)) = ' ';
495 while (isspace(*p)) p++;
f6584c83
HSHR
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 */
d2a2c69b 501 break;
c55a77db
PH
502
503 case ns_t_a:
d2a2c69b 504 for (i = 0; i < 4; i++)
f6584c83
HSHR
505 {
506 value = 0;
507 while (isdigit(*p)) value = value*10 + *p++ - '0';
508 *pk++ = value;
509 p++;
510 }
d2a2c69b 511 break;
c55a77db
PH
512
513 /* The only occurrence of a double colon is for ::1 */
514 case ns_t_aaaa:
d2a2c69b 515 if (Ustrcmp(p, "::1") == 0)
f6584c83
HSHR
516 {
517 memset(pk, 0, 15);
518 pk += 15;
519 *pk++ = 1;
520 }
d2a2c69b 521 else for (i = 0; i < 8; i++)
f6584c83
HSHR
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 }
d2a2c69b 533 break;
c55a77db
PH
534
535 case ns_t_mx:
d2a2c69b
JH
536 pk = shortfield(&p, pk);
537 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
538 pk = packname(p, pk);
539 break;
c55a77db
PH
540
541 case ns_t_txt:
d2a2c69b
JH
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;
c55a77db 547
b4161d10 548 case ns_t_tlsa:
f6584c83
HSHR
549 pk = bytefield(&p, pk); /* usage */
550 pk = bytefield(&p, pk); /* selector */
551 pk = bytefield(&p, pk); /* match type */
d2a2c69b 552 while (isxdigit(*p))
b4161d10
JH
553 {
554 value = toupper(*p) - (isdigit(*p) ? '0' : '7') << 4;
555 if (isxdigit(*++p))
f6584c83
HSHR
556 {
557 value |= toupper(*p) - (isdigit(*p) ? '0' : '7');
558 p++;
559 }
b4161d10
JH
560 *pk++ = value & 255;
561 }
562
d2a2c69b 563 break;
b4161d10 564
c55a77db 565 case ns_t_srv:
d2a2c69b 566 for (i = 0; i < 3; i++)
f6584c83
HSHR
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 }
c55a77db
PH
574
575 /* Fall through */
576
577 case ns_t_cname:
578 case ns_t_ns:
579 case ns_t_ptr:
d2a2c69b
JH
580 if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
581 pk = packname(p, pk);
582 break;
c55a77db
PH
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;
c55a77db
PH
589 }
590
591*pkptr = pk;
592return (yield == HOST_NOT_FOUND && pass_on_not_found)? PASS_ON : yield;
593}
594
595
846430d9
JH
596static void
597alarmfn(int sig)
598{
599}
c55a77db 600
dd2a32ad
JH
601
602/*************************************************
603* Special-purpose domains *
604*************************************************/
605
606static int
607special_manyhome(uschar * packet, uschar * domain)
608{
609uschar *pk = packet + 12;
610uschar *rdlptr;
611int i, j;
612
613memset(packet, 0, 12);
614
615for (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
632packet[6] = (2048 >> 8) & 255;
633packet[7] = 2048 & 255;
634packet[10] = 0;
635packet[11] = 0;
636
637(void)fwrite(packet, 1, pk - packet, stdout);
638return 0;
639}
640
641static int
642special_again(uschar * packet, uschar * domain)
643{
644int delay = atoi(CCS domain); /* digits at the start of the name */
645if (delay > 0) sleep(delay);
646return TRY_AGAIN;
647}
648
649
c55a77db
PH
650/*************************************************
651* Entry point and main program *
652*************************************************/
653
654int
655main(int argc, char **argv)
656{
657FILE *f;
658DIR *d;
75e0e026 659int domlen, qtypelen;
c55a77db
PH
660int yield, count;
661int i;
662int zonecount = 0;
c55a77db 663struct dirent *de;
c55a77db
PH
664zoneitem zones[32];
665uschar *qualify = NULL;
666uschar *p, *zone;
667uschar *zonefile = NULL;
668uschar domain[256];
669uschar buffer[256];
670uschar qtype[12];
1fb7660f 671uschar packet[2048 * 32 + 32];
f6584c83 672HEADER *header = (HEADER *)packet;
c55a77db 673uschar *pk = packet;
4d4c2a9b 674BOOL dnssec;
589b3179 675BOOL aa;
c55a77db 676
846430d9
JH
677signal(SIGALRM, alarmfn);
678
c55a77db
PH
679if (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
5f3d0983 687(void)sprintf(CS buffer, "%s/dnszones", argv[1]);
c55a77db
PH
688
689d = opendir(CCS buffer);
690if (d == NULL)
691 {
692 fprintf(stderr, "fakens: failed to opendir %s: %s\n", buffer,
693 strerror(errno));
694 return NO_RECOVERY;
695 }
696
697while ((de = readdir(d)) != NULL)
698 {
e265af1f 699 uschar *name = US de->d_name;
c55a77db
PH
700 if (Ustrncmp(name, "qualify.", 8) == 0)
701 {
e265af1f 702 qualify = fcopystring(US "%s", name + 7);
c55a77db
PH
703 continue;
704 }
705 if (Ustrncmp(name, "db.", 3) != 0) continue;
706 if (Ustrncmp(name + 3, "ip4.", 4) == 0)
e265af1f 707 zones[zonecount].zone = fcopystring(US "%s.in-addr.arpa", name + 6);
c55a77db 708 else if (Ustrncmp(name + 3, "ip6.", 4) == 0)
e265af1f 709 zones[zonecount].zone = fcopystring(US "%s.ip6.arpa", name + 6);
c55a77db 710 else
e265af1f
JH
711 zones[zonecount].zone = fcopystring(US "%s", name + 2);
712 zones[zonecount++].zonefile = fcopystring(US "%s", name);
c55a77db
PH
713 }
714(void)closedir(d);
715
716/* Get the RR type and upper case it, and check that we recognize it. */
717
718Ustrncpy(qtype, argv[3], sizeof(qtype));
719qtypelen = Ustrlen(qtype);
720for (p = qtype; *p != 0; p++) *p = toupper(*p);
721
1fb7660f
JH
722/* Find the domain, lower case it, deal with any specials,
723check that it is in a zone that we handle,
c55a77db
PH
724and set up the zone file name. The zone names in the table all start with a
725dot. */
726
727domlen = Ustrlen(argv[2]);
728if (argv[2][domlen-1] == '.') domlen--;
729Ustrncpy(domain, argv[2], domlen);
730domain[domlen] = 0;
731for (i = 0; i < domlen; i++) domain[i] = tolower(domain[i]);
732
1fb7660f 733if (Ustrcmp(domain, "manyhome.test.ex") == 0 && Ustrcmp(qtype, "A") == 0)
dd2a32ad
JH
734 return special_manyhome(packet, domain);
735else if (domlen >= 14 && Ustrcmp(domain + domlen - 14, "test.again.dns") == 0)
736 return special_again(packet, domain);
737else if (domlen >= 13 && Ustrcmp(domain + domlen - 13, "test.fail.dns") == 0)
738 return NO_RECOVERY;
1fb7660f
JH
739
740
c55a77db
PH
741if (Ustrchr(domain, '.') == NULL && qualify != NULL &&
742 Ustrcmp(domain, "dontqualify") != 0)
743 {
744 Ustrcat(domain, qualify);
745 domlen += Ustrlen(qualify);
746 }
747
748for (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
761if (zonefile == NULL)
762 {
763 fprintf(stderr, "fakens: query not in faked zone: domain is: %s\n", domain);
764 return PASS_ON;
765 }
766
5f3d0983 767(void)sprintf(CS buffer, "%s/dnszones/%s", argv[1], zonefile);
c55a77db
PH
768
769/* Initialize the start of the response packet. We don't have to fake up
770everything, because we know that Exim will look only at the answer and
771additional section parts. */
772
773memset(packet, 0, 12);
774pk += 12;
775
776/* Open the zone file. */
777
42ec9880 778f = fopen(CS buffer, "r");
c55a77db
PH
779if (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
787count = 0;
589b3179 788yield = find_records(f, zone, domain, qtype, qtypelen, &pk, &count, &dnssec, &aa);
c55a77db 789if (yield == NO_RECOVERY) goto END_OFF;
f6584c83 790header->ancount = htons(count);
c55a77db 791
f6584c83
HSHR
792/* If the AA bit should be set (as indicated by the AA prefix in the zone file),
793we are expected to return some records in the authortive section. Bind9: If
794there is data in the answer section, the authoritive section contains the NS
795records, otherwise it contains the SOA record. Currently we mimic this
796behaviour for the first case (there is some answer record).
797*/
798
799if (aa)
800 find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
801header->nscount = htons(count - ntohs(header->ancount));
c55a77db 802
75e0e026
PH
803/* There is no need to return any additional records because Exim no longer
804(from release 4.61) makes any use of them. */
f6584c83 805header->arcount = 0;
c55a77db 806
4d4c2a9b 807if (dnssec)
f6584c83 808 header->ad = 1;
4d4c2a9b 809
589b3179 810if (aa)
f6584c83 811 header->aa = 1;
589b3179 812
c55a77db
PH
813/* Close the zone file, write the result, and return. */
814
815END_OFF:
816(void)fclose(f);
817(void)fwrite(packet, 1, pk - packet, stdout);
818return yield;
819}
820
589b3179 821/* vi: aw ai sw=2 sts=2 ts=8 et
4d4c2a9b 822*/
c55a77db 823/* End of fakens.c */