Testsuite: locate fakens relative to the config_main_directory
[exim.git] / test / src / fakens.c
index ff0f1d4e9bba886cdbede412e18ab4c1934fd8aa..33bfe4f3e91aa07c216fac7eb66ba2b799e58f3e 100644 (file)
@@ -7,8 +7,8 @@ avoids the need to install special zones in a real nameserver. When Exim is
 running in its (new) test harness, DNS lookups are first passed to this program
 instead of to the real resolver. (With a few exceptions - see the discussion in
 the test suite's README file.) The program is also passed the name of the Exim
 running in its (new) test harness, DNS lookups are first passed to this program
 instead of to the real resolver. (With a few exceptions - see the discussion in
 the test suite's README file.) The program is also passed the name of the Exim
-spool directory; it expects to find its "zone files" in ../dnszones relative to
-that directory. Note that there is little checking in this program. The fake
+spool directory; it expects to find its "zone files" in dnszones relative to
+exim config_main_directory. Note that there is little checking in this program. The fake
 zone files are assumed to be syntactically valid.
 
 The zones that are handled are found by scanning the dnszones directory. A file
 zone files are assumed to be syntactically valid.
 
 The zones that are handled are found by scanning the dnszones directory. A file
@@ -50,6 +50,9 @@ line in the zone file contains exactly this:
 and the domain is not found. It converts the the result to PASS_ON instead of
 HOST_NOT_FOUND.
 
 and the domain is not found. It converts the the result to PASS_ON instead of
 HOST_NOT_FOUND.
 
+Any DNS record line in a zone file can be prefixed with "DELAY=" and
+a number of milliseconds (followed by whitespace).
+
 Any DNS record line in a zone file can be prefixed with "DNSSEC" and
 at least one space; if all the records found by a lookup are marked
 as such then the response will have the "AD" bit set. */
 Any DNS record line in a zone file can be prefixed with "DNSSEC" and
 at least one space; if all the records found by a lookup are marked
 as such then the response will have the "AD" bit set. */
@@ -61,8 +64,10 @@ as such then the response will have the "AD" bit set. */
 #include <string.h>
 #include <netdb.h>
 #include <errno.h>
 #include <string.h>
 #include <netdb.h>
 #include <errno.h>
+#include <signal.h>
 #include <arpa/nameser.h>
 #include <sys/types.h>
 #include <arpa/nameser.h>
 #include <sys/types.h>
+#include <sys/time.h>
 #include <dirent.h>
 
 #define FALSE         0
 #include <dirent.h>
 
 #define FALSE         0
@@ -125,7 +130,7 @@ static tlist type_list[] = {
   { US"A",       ns_t_a },
   { US"NS",      ns_t_ns },
   { US"CNAME",   ns_t_cname },
   { US"A",       ns_t_a },
   { US"NS",      ns_t_ns },
   { US"CNAME",   ns_t_cname },
-/*  { US"SOA",     ns_t_soa },  Not currently in use */
+  { US"SOA",     ns_t_soa },
   { US"PTR",     ns_t_ptr },
   { US"MX",      ns_t_mx },
   { US"TXT",     ns_t_txt },
   { US"PTR",     ns_t_ptr },
   { US"MX",      ns_t_mx },
   { US"TXT",     ns_t_txt },
@@ -222,6 +227,54 @@ while (isspace(*p)) p++;
 return pk;
 }
 
 return pk;
 }
 
+uschar *
+longfield(uschar ** pp, uschar * pk)
+{
+unsigned long value = 0;
+uschar * p = *pp;
+
+while (isdigit(*p)) value = value*10 + *p++ - '0';
+while (isspace(*p)) p++;
+*pp = p;
+*pk++ = (value >> 24) & 255;
+*pk++ = (value >> 16) & 255;
+*pk++ = (value >> 8) & 255;
+*pk++ = value & 255;
+return pk;
+}
+
+
+
+/*************************************************/
+
+static void
+milliwait(struct itimerval *itval)
+{
+sigset_t sigmask;
+sigset_t old_sigmask;
+
+if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
+  return;
+(void)sigemptyset(&sigmask);                           /* Empty mask */
+(void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
+(void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask);  /* Block SIGALRM */
+(void)setitimer(ITIMER_REAL, itval, NULL);             /* Start timer */
+(void)sigfillset(&sigmask);                            /* All signals */
+(void)sigdelset(&sigmask, SIGALRM);                    /* Remove SIGALRM */
+(void)sigsuspend(&sigmask);                            /* Until SIGALRM */
+(void)sigprocmask(SIG_SETMASK, &old_sigmask, NULL);    /* Restore mask */
+}
+
+static void
+millisleep(int msec)
+{
+struct itimerval itval;
+itval.it_interval.tv_sec = 0;
+itval.it_interval.tv_usec = 0;
+itval.it_value.tv_sec = msec/1000;
+itval.it_value.tv_usec = (msec % 1000) * 1000;
+milliwait(&itval);
+}
 
 
 /*************************************************
 
 
 /*************************************************
@@ -279,10 +332,11 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   uschar *rdlptr;
   uschar *p, *ep, *pp;
   BOOL found_cname = FALSE;
   uschar *rdlptr;
   uschar *p, *ep, *pp;
   BOOL found_cname = FALSE;
-  int i, plen, value;
+  int i, value;
   int tvalue = typeptr->value;
   int qtlen = qtypelen;
   BOOL rr_sec = FALSE;
   int tvalue = typeptr->value;
   int qtlen = qtypelen;
   BOOL rr_sec = FALSE;
+  int delay = 0;
 
   p = buffer;
   while (isspace(*p)) p++;
 
   p = buffer;
   while (isspace(*p)) p++;
@@ -299,11 +353,22 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   *ep = 0;
 
   p = buffer;
   *ep = 0;
 
   p = buffer;
-  if (Ustrncmp(p, US"DNSSEC ", 7) == 0)        /* tagged as secure */
-    {
-    rr_sec = TRUE;
-    p += 7;
-    }
+  for (;;)
+       {
+       if (Ustrncmp(p, US"DNSSEC ", 7) == 0)   /* tagged as secure */
+         {
+         rr_sec = TRUE;
+         p += 7;
+         }
+       else if (Ustrncmp(p, US"DELAY=", 6) == 0)       /* delay beforee response */
+         {
+         for (p += 6; *p >= '0' && *p <= '9'; p++)
+               delay = delay*10 + *p - '0';
+         while (isspace(*p)) p++;
+         }
+       else
+         break;
+       }
 
   if (!isspace(*p))
     {
 
   if (!isspace(*p))
     {
@@ -357,6 +422,9 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
 
   /* Found a relevant record */
 
 
   /* Found a relevant record */
 
+  if (delay)
+    millisleep(delay);
+
   if (!rr_sec)
     *dnssec = FALSE;                   /* cancel AD return */
 
   if (!rr_sec)
     *dnssec = FALSE;                   /* cancel AD return */
 
@@ -384,60 +452,72 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
 
   switch (tvalue)
     {
 
   switch (tvalue)
     {
-    case ns_t_soa:  /* Not currently used */
-    break;
+    case ns_t_soa:
+      p = strtok(p, " ");
+      ep = p + strlen(p);
+      if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
+      pk = packname(p, pk);                    /* primary ns */
+      p = strtok(NULL, " ");
+      pk = packname(p , pk);                   /* responsible mailbox */
+      *(p += strlen(p)) = ' ';
+      while (isspace(*p)) p++;
+      pk = longfield(&p, pk);                  /* serial */
+      pk = longfield(&p, pk);                  /* refresh */
+      pk = longfield(&p, pk);                  /* retry */
+      pk = longfield(&p, pk);                  /* expire */
+      pk = longfield(&p, pk);                  /* minimum */
+      break;
 
     case ns_t_a:
 
     case ns_t_a:
-    for (i = 0; i < 4; i++)
-      {
-      value = 0;
-      while (isdigit(*p)) value = value*10 + *p++ - '0';
-      *pk++ = value;
-      p++;
-      }
-    break;
+      for (i = 0; i < 4; i++)
+       {
+       value = 0;
+       while (isdigit(*p)) value = value*10 + *p++ - '0';
+       *pk++ = value;
+       p++;
+       }
+      break;
 
     /* The only occurrence of a double colon is for ::1 */
     case ns_t_aaaa:
 
     /* The only occurrence of a double colon is for ::1 */
     case ns_t_aaaa:
-    if (Ustrcmp(p, "::1") == 0)
-      {
-      memset(pk, 0, 15);
-      pk += 15;
-      *pk++ = 1;
-      }
-    else for (i = 0; i < 8; i++)
-      {
-      value = 0;
-      while (isxdigit(*p))
-        {
-        value = value * 16 + toupper(*p) - (isdigit(*p)? '0' : '7');
-        p++;
-        }
-      *pk++ = (value >> 8) & 255;
-      *pk++ = value & 255;
-      p++;
-      }
-    break;
+      if (Ustrcmp(p, "::1") == 0)
+       {
+       memset(pk, 0, 15);
+       pk += 15;
+       *pk++ = 1;
+       }
+      else for (i = 0; i < 8; i++)
+       {
+       value = 0;
+       while (isxdigit(*p))
+         {
+         value = value * 16 + toupper(*p) - (isdigit(*p)? '0' : '7');
+         p++;
+         }
+       *pk++ = (value >> 8) & 255;
+       *pk++ = value & 255;
+       p++;
+       }
+      break;
 
     case ns_t_mx:
 
     case ns_t_mx:
-    pk = shortfield(&p, pk);
-    if (ep[-1] != '.') sprintf(US ep, "%s.", zone);
-    pk = packname(p, pk);
-    plen = Ustrlen(p);
-    break;
+      pk = shortfield(&p, pk);
+      if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
+      pk = packname(p, pk);
+      break;
 
     case ns_t_txt:
 
     case ns_t_txt:
-    pp = pk++;
-    if (*p == '"') p++;   /* Should always be the case */
-    while (*p != 0 && *p != '"') *pk++ = *p++;
-    *pp = pk - pp - 1;
-    break;
+      pp = pk++;
+      if (*p == '"') p++;   /* Should always be the case */
+      while (*p != 0 && *p != '"') *pk++ = *p++;
+      *pp = pk - pp - 1;
+      break;
 
     case ns_t_tlsa:
 
     case ns_t_tlsa:
-    pk = bytefield(&p, pk);    /* usage */
-    pk = bytefield(&p, pk);    /* selector */
-    pk = bytefield(&p, pk);    /* match type */
-    while (isxdigit(*p))
+      pk = bytefield(&p, pk);  /* usage */
+      pk = bytefield(&p, pk);  /* selector */
+      pk = bytefield(&p, pk);  /* match type */
+      while (isxdigit(*p))
       {
       value = toupper(*p) - (isdigit(*p) ? '0' : '7') << 4;
       if (isxdigit(*++p))
       {
       value = toupper(*p) - (isdigit(*p) ? '0' : '7') << 4;
       if (isxdigit(*++p))
@@ -448,27 +528,26 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
       *pk++ = value & 255;
       }
 
       *pk++ = value & 255;
       }
 
-    break;
+      break;
 
     case ns_t_srv:
 
     case ns_t_srv:
-    for (i = 0; i < 3; i++)
-      {
-      value = 0;
-      while (isdigit(*p)) value = value*10 + *p++ - '0';
-      while (isspace(*p)) p++;
-      *pk++ = (value >> 8) & 255;
-      *pk++ = value & 255;
-      }
+      for (i = 0; i < 3; i++)
+       {
+       value = 0;
+       while (isdigit(*p)) value = value*10 + *p++ - '0';
+       while (isspace(*p)) p++;
+       *pk++ = (value >> 8) & 255;
+       *pk++ = value & 255;
+       }
 
     /* Fall through */
 
     case ns_t_cname:
     case ns_t_ns:
     case ns_t_ptr:
 
     /* Fall through */
 
     case ns_t_cname:
     case ns_t_ns:
     case ns_t_ptr:
-    if (ep[-1] != '.') sprintf(US ep, "%s.", zone);
-    pk = packname(p, pk);
-    plen = Ustrlen(p);
-    break;
+      if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
+      pk = packname(p, pk);
+      break;
     }
 
   /* Fill in the length, and we are done with this RR */
     }
 
   /* Fill in the length, and we are done with this RR */
@@ -482,6 +561,10 @@ return (yield == HOST_NOT_FOUND && pass_on_not_found)? PASS_ON : yield;
 }
 
 
 }
 
 
+static  void
+alarmfn(int sig)
+{
+}
 
 /*************************************************
 *           Entry point and main program         *
 
 /*************************************************
 *           Entry point and main program         *
@@ -508,6 +591,8 @@ uschar packet[512];
 uschar *pk = packet;
 BOOL dnssec;
 
 uschar *pk = packet;
 BOOL dnssec;
 
+signal(SIGALRM, alarmfn);
+
 if (argc != 4)
   {
   fprintf(stderr, "fakens: expected 3 arguments, received %d\n", argc-1);
 if (argc != 4)
   {
   fprintf(stderr, "fakens: expected 3 arguments, received %d\n", argc-1);
@@ -516,7 +601,7 @@ if (argc != 4)
 
 /* Find the zones */
 
 
 /* Find the zones */
 
-(void)sprintf(US buffer, "%s/../dnszones", argv[1]);
+(void)sprintf(CS buffer, "%s/dnszones", argv[1]);
 
 d = opendir(CCS buffer);
 if (d == NULL)
 
 d = opendir(CCS buffer);
 if (d == NULL)
@@ -587,7 +672,7 @@ if (zonefile == NULL)
   return PASS_ON;
   }
 
   return PASS_ON;
   }
 
-(void)sprintf(US buffer, "%s/../dnszones/%s", argv[1], zonefile);
+(void)sprintf(CS buffer, "%s/dnszones/%s", argv[1], zonefile);
 
 /* Initialize the start of the response packet. We don't have to fake up
 everything, because we know that Exim will look only at the answer and
 
 /* Initialize the start of the response packet. We don't have to fake up
 everything, because we know that Exim will look only at the answer and
@@ -598,7 +683,7 @@ pk += 12;
 
 /* Open the zone file. */
 
 
 /* Open the zone file. */
 
-f = fopen(US buffer, "r");
+f = fopen(CS buffer, "r");
 if (f == NULL)
   {
   fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));
 if (f == NULL)
   {
   fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));