]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tests/dnsd: read config from file
authorDaniel Stenberg <daniel@haxx.se>
Thu, 5 Jun 2025 20:59:24 +0000 (22:59 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 23 Jun 2025 06:24:09 +0000 (08:24 +0200)
Make the <dns> tag in a test case control what is stored there. Also
documented. Make test 2102 and 2103 use the new tag.

Lets the test case config the A and AAAA contents the server replies
with. Initial work for the HTTPS RR exists, but does not yet work.

Closes #17543

docs/tests/FILEFORMAT.md
tests/data/Makefile.am
tests/data/test2102
tests/data/test2103 [new file with mode: 0644]
tests/globalconfig.pm
tests/runner.pm
tests/server/Makefile.inc
tests/server/dnsd.c

index 062329d2117cca537cdcc67a41a24e1908f77cb8..98b09ed3a0c6901892ec8475eb8d91901dadac70 100644 (file)
@@ -383,6 +383,14 @@ issue.
 `writedelay: [secs]` delay this amount between reply packets (each packet
   being 512 bytes payload)
 
+### `<dns>`
+
+Commands for the test DNS server.
+
+- `A: [dotted ipv4 address]` - set IPv4 address to return
+- `AAAA: [numerical IPv6 address]` - set IPv6 address to return, with or
+  without `[]`
+
 ## `<client>`
 
 ### `<server>`
index 8e502220f177008e9d1ebd19674cd8a8e0e8e4b5..f5f83f2b461978392b6de5cabbbdd71bbf7358a3 100644 (file)
@@ -253,7 +253,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \
 test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \
 test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \
 test2088 test2089 \
-test2100 test2101 test2102 \
+test2100 test2101 test2102 test2103 \
 \
 test2200 test2201 test2202 test2203 test2204 test2205 \
 \
index 32b29658dc10166c24e2c9899d8c4d20bd399590..5ee35bdf6e4e87516f65dfe0f57425199fbb3d13 100644 (file)
@@ -23,6 +23,10 @@ Funny-head: yesyes
 
 -foo-
 </data>
+<dns>
+A: %HOSTIP
+AAAA: %HOST6IP
+</dns>
 </reply>
 
 #
diff --git a/tests/data/test2103 b/tests/data/test2103
new file mode 100644 (file)
index 0000000..fd214e5
--- /dev/null
@@ -0,0 +1,45 @@
+<testcase>
+<info>
+<keywords>
+DNS fail
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<dns>
+A: none
+AAAA: none
+</dns>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+dns
+</server>
+<features>
+override-dns
+</features>
+<name>
+HTTP GET with bad host name
+</name>
+<setenv>
+CURL_DNS_SERVER=127.0.0.1:%DNSPORT
+</setenv>
+<command>
+http://examplehost.example/%TESTNUMBER
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+# curl: (6) Could not resolve host: examplehost.example
+<errorcode>
+6
+</errorcode>
+</verify>
+</testcase>
index 7aabd9d48def52546d5328ccf1978cb83fc50532..b120250f1b77df1f67f9614f6f5fb65122444a3a 100644 (file)
@@ -61,6 +61,7 @@ BEGIN {
         $run_duphandle
         $run_event_based
         $SERVERCMD
+        $DNSCMD
         $SERVERIN
         $srcdir
         $TESTDIR
@@ -134,6 +135,7 @@ our $SERVERIN="server.input";   # what curl sent the server
 our $PROXYIN="proxy.input";     # what curl sent the proxy
 our $MEMDUMP="memdump";         # file that the memory debugging creates
 our $SERVERCMD="server.cmd";    # copy server instructions here
+our $DNSCMD="dnsd.cmd";         # write DNS instructions here
 
 # other config variables
 our @protocols;   # array of lowercase supported protocol servers
index 324c49dcf7a2d10a40092da878e52db552aac717..36127c7c03884a15bb08f26d1fd4f483d6f1e8c1 100644 (file)
@@ -750,11 +750,16 @@ sub singletest_prepare {
     unlink("$LOGDIR/$SERVERIN");
     unlink("$LOGDIR/$PROXYIN");
 
-    # if this section exists, it might be FTP server instructions:
-    my @ftpservercmd = getpart("reply", "servercmd");
-    push @ftpservercmd, "Testnum $testnum\n";
+    # if this section exists, it might be server instructions:
+    my @servercmd = getpart("reply", "servercmd");
+    push @servercmd, "Testnum $testnum\n";
     # write the instructions to file
-    writearray("$LOGDIR/$SERVERCMD", \@ftpservercmd);
+    writearray("$LOGDIR/$SERVERCMD", \@servercmd);
+
+    # if this section exists, it might be DNS instructions:
+    my @dnscmd = getpart("reply", "dns");
+    # write the instructions to file
+    writearray("$LOGDIR/$DNSCMD", \@dnscmd);
 
     # provide an environment variable
     $ENV{'CURL_TESTNUM'} = $testnum;
index 84b4261dab45bdb0fdf311385a8d76c48e7dc22a..82916744b8b1e94133f59e553cdf150c4fc1f327 100644 (file)
@@ -34,6 +34,7 @@ UTILS = getpart.c util.c
 CURLX_CFILES = \
   ../../lib/curlx/base64.c \
   ../../lib/curlx/inet_pton.c \
+  ../../lib/curlx/inet_ntop.c \
   ../../lib/curlx/multibyte.c \
   ../../lib/curlx/nonblock.c \
   ../../lib/curlx/strparse.c \
index 069a93f33e0fd2a39c84a17e261f995c4409330e..305030376021427eefce493a593d27f47938f758 100644 (file)
@@ -62,6 +62,7 @@ static int qname(const unsigned char **pkt, size_t *size)
 
 #define QTYPE_A 1
 #define QTYPE_AAAA 28
+#define QTYPE_HTTPS 0x41
 
 /*
  * Handle initial connection protocol.
@@ -142,11 +143,13 @@ static int store_incoming(const unsigned char *data, size_t size,
     logmsg("Question for '%s' type %x", name, qd);
 
     qd = get16bit(&data, &size);
-    fprintf(server, "QCLASS: %04x\n", qd);
+    logmsg("QCLASS: %04x\n", qd);
 
     *qlen = qsize - size; /* total size of the query */
     memcpy(qbuf, qptr, *qlen);
   }
+  else
+    logmsg("Bad input qname");
 #if 0
   for(i = 0; i < size; i++) {
     fprintf(server, "%02d", (unsigned int)data[i]);
@@ -159,76 +162,6 @@ static int store_incoming(const unsigned char *data, size_t size,
   return 0;
 }
 
-#if 0
-static int send_response(curl_socket_t sock,
-                         struct sockaddr *addr,
-                         curl_socklen_t addrlen,
-                         unsigned short id)
-{
-  ssize_t rc;
-  unsigned char bytes[] = {
-    0x80, 0xea, /* ID, overwrite */
-    0x81, 0x80,
-    /*
-    Flags: 0x8180 Standard query response, No error
-        1... .... .... .... = Response: Message is a response
-        .000 0... .... .... = Opcode: Standard query (0)
-        .... .0.. .... .... = Authoritative: Server is not an authority for
-                              domain
-        .... ..0. .... .... = Truncated: Message is not truncated
-        .... ...1 .... .... = Recursion desired: Do query recursively
-        .... .... 1... .... = Recursion available: Server can do recursive
-                              queries
-        .... .... .0.. .... = Z: reserved (0)
-        .... .... ..0. .... = Answer authenticated: Answer/authority portion
-                              was not authenticated by the server
-        .... .... ...0 .... = Non-authenticated data: Unacceptable
-        .... .... .... 0000 = Reply code: No error (0)
-    */
-    0x0, 0x1, /* QDCOUNT */
-    0x0, 0x4, /* ANCOUNT */
-    0x0, 0x0, /* NSCOUNT */
-    0x0, 0x0, /* ARCOUNT */
-
-    /* here's the question */
-    0x4, 0x63, 0x75, 0x72, 0x6c, 0x2, 0x73, 0x65, 0x0, /* curl.se */
-    0x0, 0x1, /* QTYPE: A */
-    0x0, 0x1, /* QCLASS: IN */
-
-    /* 4 answers */
-    0xc0, 0xc, /* points to curl.se */
-    0x0, 0x1, /* QTYPE A */
-    0x0, 0x1, /* QCLASS IN */
-    0x0, 0x0, 0xa, 0x14, /* Time to live: 2580 (43 minutes) */
-    0x0, 0x4, /* data length */
-    0x97, 0x65, 0x41, 0x5b, /* Address: 151.101.65.91 */
-
-    0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0xa, 0x14,
-    0x0, 0x4, 0x97, 0x65, 0x81, 0x5b, /* Address: 151.101.129.91 */
-    0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0xa, 0x14,
-    0x0, 0x4, 0x97, 0x65, 0xc1, 0x5b, /* Address: 151.101.193.91 */
-    0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0xa, 0x14,
-    0x0, 0x4, 0x97, 0x65, 0x1, 0x5b,  /* Address: 151.101.1.91 */
-#if 0
-    /* 1 additional record (ARCOUNT) */
-
-    0x0, 0x0, 0x29, 0x4, 0xd0, 0x0, 0x0,
-    0x0, 0x0, 0x0, 0x0
-#endif
-  };
-  size_t len = sizeof(bytes);
-
-  bytes[0] = (unsigned char)(id >> 8);
-  bytes[1] = (unsigned char)(id & 0xff);
-
-  rc = sendto(sock, bytes, len, 0, addr, addrlen);
-  if(rc != (ssize_t)len) {
-    fprintf(stderr, "failed sending %d bytes\n", (int)len);
-  }
-  return 0;
-}
-#endif
-
 static void add_answer(unsigned char *bytes, size_t *w,
                        const unsigned char *a, size_t alen,
                        unsigned short qtype)
@@ -269,6 +202,17 @@ static void add_answer(unsigned char *bytes, size_t *w,
 #define SENDTO3 size_t
 #endif
 
+#define INSTRUCTIONS "dnsd.cmd"
+
+#define MAX_ALPN 5
+
+static unsigned char ipv4_pref[4];
+static unsigned char ipv6_pref[16];
+static unsigned char alpn_pref[MAX_ALPN];
+static int alpn_count;
+static unsigned char ancount_a;
+static unsigned char ancount_aaaa;
+
 /* this is an answer to a question */
 static int send_response(curl_socket_t sock,
                          const struct sockaddr *addr, curl_socklen_t addrlen,
@@ -278,7 +222,7 @@ static int send_response(curl_socket_t sock,
   ssize_t rc;
   size_t i;
   int a;
-  unsigned char ancount = 3;
+  char addrbuf[128]; /* IP address buffer */
   unsigned char bytes[256] = {
     0x80, 0xea, /* ID, overwrite */
     0x81, 0x80,
@@ -303,14 +247,9 @@ static int send_response(curl_socket_t sock,
     0x0, 0x0, /* NSCOUNT */
     0x0, 0x0  /* ARCOUNT */
   };
-  static const unsigned char ipv4_localhost[] = { 127, 0, 0, 1 };
-  static const unsigned char ipv6_localhost[] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
-  };
 
   bytes[0] = (unsigned char)(id >> 8);
   bytes[1] = (unsigned char)(id & 0xff);
-  bytes[7] = ancount;
 
   if(qlen > (sizeof(bytes) - 12))
     return -1;
@@ -320,16 +259,28 @@ static int send_response(curl_socket_t sock,
 
   i = 12 + qlen;
 
-  for(a = 0; a < ancount; a++) {
-    switch(qtype) {
-    case QTYPE_A:
-      add_answer(bytes, &i, ipv4_localhost, sizeof(ipv4_localhost), QTYPE_A);
-      break;
-    case QTYPE_AAAA:
-      add_answer(bytes, &i, ipv6_localhost, sizeof(ipv6_localhost),
-                 QTYPE_AAAA);
-      break;
+  switch(qtype) {
+  case QTYPE_A:
+    bytes[7] = ancount_a;
+    for(a = 0; a < ancount_a; a++) {
+      const unsigned char *store = ipv4_pref;
+      add_answer(bytes, &i, store, sizeof(ipv4_pref), QTYPE_A);
+      logmsg("Sending back A (%x) '%s'", QTYPE_A,
+             curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)));
+    }
+    break;
+  case QTYPE_AAAA:
+    bytes[7] = ancount_aaaa;
+    for(a = 0; a < ancount_aaaa; a++) {
+      const unsigned char *store = ipv6_pref;
+      add_answer(bytes, &i, store, sizeof(ipv6_pref), QTYPE_AAAA);
+      logmsg("Sending back AAAA (%x) '%s'", QTYPE_AAAA,
+             curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)));
     }
+    break;
+  case QTYPE_HTTPS:
+    bytes[7] = 1; /* one answer */
+    break;
   }
 
 #ifdef __AMIGA__
@@ -349,6 +300,68 @@ static int send_response(curl_socket_t sock,
   return 0;
 }
 
+
+static void read_instructions(void)
+{
+  char file[256];
+  FILE *f;
+  snprintf(file, sizeof(file), "%s/" INSTRUCTIONS, logdir);
+  f = fopen(file, FOPEN_READTEXT);
+  if(f) {
+    char buf[256];
+    ancount_aaaa = ancount_a = 0;
+    alpn_count = 0;
+    while(fgets(buf, sizeof(buf), f)) {
+      char *p = strchr(buf, '\n');
+      if(p) {
+        int rc;
+        *p = 0;
+        if(!strncmp("A: ", buf, 3)) {
+          rc = curlx_inet_pton(AF_INET, &buf[3], ipv4_pref);
+          ancount_a = (rc == 1);
+        }
+        else if(!strncmp("AAAA: ", buf, 6)) {
+          char *p6 = &buf[6];
+          if(*p6 == '[') {
+            char *pt = strchr(p6, ']');
+            if(pt)
+              *pt = 0;
+            p6++;
+          }
+          rc = curlx_inet_pton(AF_INET6, p6, ipv6_pref);
+          ancount_aaaa = (rc == 1);
+        }
+        else if(!strncmp("ALPN: ", buf, 6)) {
+          char *ap = &buf[6];
+          rc = 0;
+          while(*ap) {
+            if('h' == *ap) {
+              ap++;
+              if(*ap >= '1' && *ap <= '3') {
+                if(alpn_count < MAX_ALPN)
+                  alpn_pref[alpn_count++] = *ap;
+              }
+              else
+                break;
+            }
+            else
+              break;
+          }
+        }
+        else {
+          rc = 0;
+        }
+        if(rc != 1) {
+          logmsg("Bad line in %s: '%s'\n", file, buf);
+        }
+      }
+    }
+    fclose(f);
+  }
+  else
+    logmsg("Error opening file '%s'", file);
+}
+
 static int test_dnsd(int argc, char **argv)
 {
   srvr_sockaddr_union_t me;
@@ -586,6 +599,10 @@ static int test_dnsd(int argc, char **argv)
       break;
     }
 
+    /* read once per incoming query, which is probably more than one
+       per test case */
+    read_instructions();
+
     store_incoming(inbuffer, n, qbuf, &qlen, &qtype, &id);
 
     set_advisor_read_lock(loglockfile);