Docs: ESMTP extensions indexing
[users/heiko/exim.git] / test / src / fakens.c
index a03f94a07414615bacdde0e5e7c85b75aa80a1e5..2c82c5a82858504ea1c486a604d2a52dd87c7865 100644 (file)
@@ -25,7 +25,7 @@ The arguments to the program are:
   the DNS record type that is being sought
 
 The output from the program is written to stdout. It is supposed to be in
-exactly the same format as a traditional namserver response (see RFC 1035) so
+exactly the same format as a traditional nameserver response (see RFC 1035) so
 that Exim can process it as normal. At present, no compression is used.
 Error messages are written to stderr.
 
@@ -53,14 +53,21 @@ HOST_NOT_FOUND.
 Any DNS record line in a zone file can be prefixed with "DELAY=" and
 a number of milliseconds (followed by one space).
 
-Any DNS record line in a zone file can be prefixed with "DNSSEC ";
+Any DNS record line can be prefixed with "DNSSEC ";
 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 "AA "
+Any DNS record line can be prefixed with "NXDOMAIN ";
+The record will be ignored (but the prefix set still applied);
+This lets us return a DNSSEC NXDOMAIN (=> HOST_NOT_FOUND).
+
+Any DNS record line can be prefixed with "AA "
 if all the records found by a lookup are marked
 as such then the response will have the "AA" bit set.
 
+Any DNS record line in a zone file can be prefixed with "TTL=" and
+a number of seconds (followed by one space).
+
 */
 
 #include <ctype.h>
@@ -72,9 +79,14 @@ as such then the response will have the "AA" bit set.
 #include <errno.h>
 #include <signal.h>
 #include <arpa/nameser.h>
+#include <arpa/inet.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <dirent.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
 
 #define FALSE         0
 #define TRUE          1
@@ -94,6 +106,7 @@ typedef unsigned char uschar;
 #define Ustrlen(s)         (int)strlen(CCS(s))
 #define Ustrncmp(s,t,n)    strncmp(CCS(s),CCS(t),n)
 #define Ustrncpy(s,t,n)    strncpy(CS(s),CCS(t),n)
+#define Ustrtok(s,t)       (uschar*)strtok(CS(s),CCS(t))
 
 typedef struct zoneitem {
   uschar *zone;
@@ -105,6 +118,8 @@ typedef struct tlist {
   int value;
 } tlist;
 
+#define DEFAULT_TTL 3600U
+
 /* On some (older?) operating systems, the standard ns_t_xxx definitions are
 not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
 not defined, assume we are in this state. A really old system might not even
@@ -298,8 +313,8 @@ Arguments:
   qtypelen    the length of qtype
   pkptr       points to the output buffer pointer; this is updated
   countptr    points to the record count; this is updated
-  dnssec      points to the AD flag indicator; this is updated
-  aa          points to the AA flag indicator; this is updated
+  dnssec_p    points to the AD flag indicator; this is updated
+  aa_p        points to the AA flag indicator; this is updated
 
 Returns:      0 on success, else HOST_NOT_FOUND or NO_DATA or NO_RECOVERY or
               PASS_ON - the latter if a "PASS ON NOT FOUND" line is seen
@@ -307,7 +322,7 @@ Returns:      0 on success, else HOST_NOT_FOUND or NO_DATA or NO_RECOVERY or
 
 static int
 find_records(FILE *f, uschar *zone, uschar *domain, uschar *qtype,
-  int qtypelen, uschar **pkptr, int *countptr, BOOL * dnssec, BOOL * aa)
+  int qtypelen, uschar **pkptr, int *countptr, BOOL * dnssec_p, BOOL * aa_p)
 {
 int yield = HOST_NOT_FOUND;
 int domainlen = Ustrlen(domain);
@@ -331,9 +346,6 @@ if (typeptr->name == NULL)
 rrdomain[0] = 0;                 /* No previous domain */
 (void)fseek(f, 0, SEEK_SET);     /* Start again at the beginning */
 
-if (dnssec) *dnssec = TRUE;     /* cancelled by first nonsecure rec found */
-if (aa) *aa = TRUE;             /* cancelled by first non-aa rec found */
-
 /* Scan for RRs */
 
 while (fgets(CS buffer, sizeof(buffer), f) != NULL)
@@ -346,7 +358,9 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   int qtlen = qtypelen;
   BOOL rr_sec = FALSE;
   BOOL rr_aa = FALSE;
+  BOOL rr_ignore = FALSE;
   int delay = 0;
+  uint ttl = DEFAULT_TTL;
 
   p = buffer;
   while (isspace(*p)) p++;
@@ -370,7 +384,12 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
       rr_sec = TRUE;
       p += 7;
       }
-    else if (Ustrncmp(p, US"AA ", 3) == 0)      /* tagged as authoritive */
+    if (Ustrncmp(p, US"NXDOMAIN ", 9) == 0)     /* ignore record content */
+      {
+      rr_ignore = TRUE;
+      p += 9;
+      }
+    else if (Ustrncmp(p, US"AA ", 3) == 0)      /* tagged as authoritative */
       {
       rr_aa = TRUE;
       p += 3;
@@ -380,6 +399,12 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
       for (p += 6; *p >= '0' && *p <= '9'; p++) delay = delay*10 + *p - '0';
       if (isspace(*p)) p++;
       }
+    else if (Ustrncmp(p, US"TTL=", 4) == 0)     /* TTL for record */
+      {
+      ttl = 0;
+      for (p += 4; *p >= '0' && *p <= '9'; p++) ttl = ttl*10 + *p - '0';
+      if (isspace(*p)) p++;
+      }
     else
       break;
     }
@@ -420,7 +445,12 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
 
   /* The domain matches */
 
-  if (yield == HOST_NOT_FOUND) yield = NO_DATA;
+  if (yield == HOST_NOT_FOUND)
+    {
+    yield = NO_DATA;
+    if (dnssec_p) *dnssec_p = TRUE;     /* cancelled by first nonsecure rec found */
+    if (aa_p) *aa_p = TRUE;             /* cancelled by first non-aa rec found */
+    }
 
   /* Compare RR types; a CNAME record is always returned */
 
@@ -438,11 +468,13 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   if (delay)
     millisleep(delay);
 
-  if (dnssec && !rr_sec)
-    *dnssec = FALSE;                    /* cancel AD return */
+  if (dnssec_p && !rr_sec)
+    *dnssec_p = FALSE;                    /* cancel AD return */
 
-  if (aa && !rr_aa)
-    *aa = FALSE;                        /* cancel AA return */
+  if (aa_p && !rr_aa)
+    *aa_p = FALSE;                        /* cancel AA return */
+
+  if (rr_ignore) continue;
 
   yield = 0;
   *countptr = *countptr + 1;
@@ -459,7 +491,10 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   *pk++ = 0;
   *pk++ = 1;     /* class = IN */
 
-  pk += 4;       /* TTL field; don't care */
+  *pk++ = (ttl >>24) & 255;
+  *pk++ = (ttl >>16) & 255;
+  *pk++ = (ttl >> 8) & 255;
+  *pk++ = ttl & 255;
 
   rdlptr = pk;   /* remember rdlength field */
   pk += 2;
@@ -469,13 +504,13 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
   switch (tvalue)
     {
     case ns_t_soa:
-      p = strtok(p, " ");
-      ep = p + strlen(p);
+      p = Ustrtok(p, " ");
+      ep = p + Ustrlen(p);
       if (ep[-1] != '.') sprintf(CS ep, "%s.", zone);
       pk = packname(p, pk);                     /* primary ns */
-      p = strtok(NULL, " ");
+      p = Ustrtok(NULL, " ");
       pk = packname(p , pk);                    /* responsible mailbox */
-      *(p += strlen(p)) = ' ';
+      *(p += Ustrlen(p)) = ' ';
       while (isspace(*p)) p++;
       pk = longfield(&p, pk);                   /* serial */
       pk = longfield(&p, pk);                   /* refresh */
@@ -485,35 +520,13 @@ while (fgets(CS buffer, sizeof(buffer), f) != NULL)
       break;
 
     case ns_t_a:
-      for (i = 0; i < 4; i++)
-        {
-        value = 0;
-        while (isdigit(*p)) value = value*10 + *p++ - '0';
-        *pk++ = value;
-        p++;
-        }
+      inet_pton(AF_INET, CCS p, pk);                /* FIXME: error checking */
+      pk += 4;
       break;
 
-    /* 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++;
-        }
+      inet_pton(AF_INET6, CCS p, pk);               /* FIXME: error checking */
+      pk += 16;
       break;
 
     case ns_t_mx:
@@ -655,8 +668,8 @@ uschar qtype[12];
 uschar packet[2048 * 32 + 32];
 HEADER *header = (HEADER *)packet;
 uschar *pk = packet;
-BOOL dnssec;
-BOOL aa;
+BOOL dnssec = FALSE;
+BOOL aa = FALSE;
 
 signal(SIGALRM, alarmfn);
 
@@ -670,15 +683,14 @@ if (argc != 4)
 
 (void)sprintf(CS buffer, "%s/dnszones", argv[1]);
 
-d = opendir(CCS buffer);
-if (d == NULL)
+if (!(d = opendir(CCS buffer)))
   {
   fprintf(stderr, "fakens: failed to opendir %s: %s\n", buffer,
     strerror(errno));
   return NO_RECOVERY;
   }
 
-while ((de = readdir(d)) != NULL)
+while ((de = readdir(d)))
   {
   uschar *name = US de->d_name;
   if (Ustrncmp(name, "qualify.", 8) == 0)
@@ -750,22 +762,23 @@ if (zonefile == NULL)
 
 (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
-additional section parts. */
+/* Initialize the start of the response packet. */
 
 memset(packet, 0, 12);
 pk += 12;
 
 /* Open the zone file. */
 
-f = fopen(CS buffer, "r");
-if (f == NULL)
+if (!(f = fopen(CS buffer, "r")))
   {
   fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));
   return NO_RECOVERY;
   }
 
+header->qr = 1;     /* query */
+header->opcode = QUERY; /* standard query */
+header->tc = 0;     /* no truncation */
+
 /* Find the records we want, and add them to the result. */
 
 count = 0;
@@ -774,14 +787,16 @@ if (yield == NO_RECOVERY) goto END_OFF;
 header->ancount = htons(count);
 
 /* If the AA bit should be set (as indicated by the AA prefix in the zone file),
-we are expected to return some records in the authortive section. Bind9: If
-there is data in the answer section, the authoritive section contains the NS
-records, otherwise it contains the SOA record.  Currently we mimic this
-behaviour for the first case (there is some answer record).
+we are expected to return some records in the authoritative section. Bind9: If
+there is data in the answer section, the authoritative section contains the NS
+records, otherwise it contains the SOA record.  Mimic that.
 */
 
-if (aa)
-  find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
+if (strcmp(qtype, "SOA") != 0 && strcmp(qtype, "NS") != 0)
+  if (count)
+    find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"NS", 2, &pk, &count, NULL, NULL);
+  else
+    find_records(f, zone, zone[0] == '.' ? zone+1 : zone, US"SOA", 3, &pk, &count, NULL, NULL);
 header->nscount = htons(count - ntohs(header->ancount));
 
 /* There is no need to return any additional records because Exim no longer