Testsuite: add file-inclusion facility to "client" scripts
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 25 Feb 2017 18:32:11 +0000 (18:32 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 25 Feb 2017 19:40:29 +0000 (19:40 +0000)
test/README
test/src/client.c

index 8164cb2c8cd43dac26b49f497b23ad9a108f9e9e..c08b63b0d994d93b54d87a1b7e77f8c16ab4dc53 100644 (file)
@@ -1030,11 +1030,16 @@ Lines in client scripts are of two kinds:
     line is input to be sent to the server.  Backslash escaping is done as
     described below, but no trailing "\r\n" is sent.
 
-(4) Otherwise, the line is an input line line that is sent to the server. Any
+(4) If a line begin with three '<' characters and a space, the rest of the
+    line is a filename; the content of the file is inserted intto the script
+    at this point.
+
+(5) Otherwise, the line is an input line line that is sent to the server. Any
     occurrences of \r and \n in the line are turned into carriage return and
     linefeed, respectively. This is used for testing PIPELINING.
     Any sequences of \x followed by two hex digits are converted to the equvalent
     byte value.  Any other character following a \ is sent verbatim.
+    The line is sent with a trailing "\r\n".
 
 Here is a simple example:
 
index f34cf784c9a671c0f1d84d24a81da75f648680e3..a5e9e9da2c607deb39245ed5aa78f51ee195c122 100644 (file)
@@ -515,6 +515,313 @@ return len;
 
 
 /****************************************************************************/
+typedef struct {
+  int  sock;
+  int  tls_active;
+#ifdef HAVE_OPENSSL
+  SSL_CTX * ctx;
+  SSL * ssl;
+#endif
+  int  sent_starttls;
+} srv_ctx;
+
+static void
+do_file(srv_ctx * srv, FILE * f, int timeout,
+  unsigned char * inbuffer, unsigned bsiz, unsigned char * inptr)
+{
+unsigned char outbuffer[10240];
+
+while (fgets(CS outbuffer, sizeof(outbuffer), f) != NULL)
+  {
+  int n = (int)strlen(CS outbuffer);
+  int crlf = 1;
+  int rc;
+
+  /* Strip trailing newline */
+  if (outbuffer[n-1] == '\n') outbuffer[--n] = 0;
+
+  /* Expect incoming */
+
+  if (  strncmp(CS outbuffer, "???", 3) == 0
+     && (outbuffer[3] == ' ' || outbuffer[3] == '*')
+     )
+    {
+    unsigned char *lineptr;
+    unsigned exp_eof = outbuffer[3] == '*';
+
+    printf("%s\n", outbuffer);
+    n = unescape_buf(outbuffer, n);
+
+    if (*inptr == 0)   /* Refill input buffer */
+      {
+      if (srv->tls_active)
+        {
+        #ifdef HAVE_OPENSSL
+        rc = SSL_read (srv->ssl, inbuffer, bsiz - 1);
+        #endif
+        #ifdef HAVE_GNUTLS
+        rc = gnutls_record_recv(tls_session, CS inbuffer, bsiz - 1);
+        #endif
+        }
+      else
+        {
+        alarm(timeout);
+        rc = read(srv->sock, inbuffer, bsiz);
+        alarm(0);
+        }
+
+      if (rc < 0)
+       {
+        printf("Read error %s\n", strerror(errno));
+        exit(81);
+       }
+      else if (rc == 0)
+       if (exp_eof)
+         {
+          printf("Expected EOF read\n");
+         continue;
+         }
+       else
+         {
+         printf("Unexpected EOF read\n");
+         close(srv->sock);
+         exit(80);
+         }
+      else if (exp_eof)
+        {
+        printf("Expected EOF not read\n");
+        close(srv->sock);
+        exit(74);
+        }
+      else
+        {
+        inbuffer[rc] = 0;
+        inptr = inbuffer;
+        }
+      }
+
+    lineptr = inptr;
+    while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
+    if (*inptr != 0)
+      {
+      *inptr++ = 0;
+      if (*inptr == '\n') inptr++;
+      }
+
+    printf("<<< %s\n", lineptr);
+    if (strncmp(CS lineptr, CS outbuffer + 4, n - 4) != 0)
+      {
+      printf("\n******** Input mismatch ********\n");
+      exit(79);
+      }
+
+    #ifdef HAVE_TLS
+    if (srv->sent_starttls)
+      {
+      if (lineptr[0] == '2')
+        {
+int rc;
+       unsigned int verify;
+
+        printf("Attempting to start TLS\n");
+        fflush(stdout);
+
+        #ifdef HAVE_OPENSSL
+        srv->tls_active = tls_start(srv->sock, &srv->ssl, srv->ctx);
+        #endif
+
+        #ifdef HAVE_GNUTLS
+         {
+         int rc;
+         sigalrm_seen = FALSE;
+         alarm(timeout);
+         do {
+           rc = gnutls_handshake(tls_session);
+         } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
+         srv->tls_active = rc >= 0;
+         alarm(0);
+
+         if (!srv->tls_active) printf("%s\n", gnutls_strerror(rc));
+         }
+        #endif
+
+        if (!srv->tls_active)
+          {
+          printf("Failed to start TLS\n");
+          fflush(stdout);
+          }
+       #ifdef HAVE_GNUTLS
+       else if (ocsp_stapling)
+         {
+         if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
+           {
+           printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
+           fflush(stdout);
+           }
+         else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
+           {
+           printf("Bad certificate\n");
+           fflush(stdout);
+           }
+         #ifdef HAVE_OCSP
+         else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
+           {
+           printf("Failed to verify certificate status\n");
+             {
+             gnutls_datum_t stapling;
+             gnutls_ocsp_resp_t resp;
+             gnutls_datum_t printed;
+             if (  (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
+                && (rc= gnutls_ocsp_resp_init(&resp)) == 0
+                && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
+                && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
+                )
+               {
+               fprintf(stderr, "%.4096s", printed.data);
+               gnutls_free(printed.data);
+               }
+             else
+               (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
+             }
+           fflush(stdout);
+           }
+         #endif
+         }
+       #endif
+        else
+          printf("Succeeded in starting TLS\n");
+        }
+      else printf("Abandoning TLS start attempt\n");
+      }
+    srv->sent_starttls = 0;
+    #endif
+    }
+
+  /* Wait for a bit before proceeding */
+
+  else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
+    {
+    printf("%s\n", outbuffer);
+    sleep(atoi(CS outbuffer + 4));
+    }
+
+  /* Stack new input file */
+
+  else if (strncmp(CS outbuffer, "<<< ", 4) == 0)
+    {
+    FILE * new_f;
+    if (!(new_f = fopen(outbuffer+4 , "r")))
+      {
+      printf("Unable to open '%s': %s", inptr, sys_errlist[errno]);
+      exit(74);
+      }
+    do_file(srv, new_f, timeout, inbuffer, bsiz, inptr);
+    }
+
+
+  /* Send line outgoing, but barf if unconsumed incoming */
+
+  else
+    {
+    unsigned char * out = outbuffer;
+
+    if (strncmp(CS outbuffer, ">>> ", 4) == 0)
+      {
+      crlf = 0;
+      out += 4;
+      n -= 4;
+      }
+
+    if (*inptr != 0)
+      {
+      printf("Unconsumed input: %s", inptr);
+      printf("   About to send: %s\n", out);
+      exit(78);
+      }
+
+    #ifdef HAVE_TLS
+
+    /* Shutdown TLS */
+
+    if (strcmp(CS out, "stoptls") == 0 ||
+        strcmp(CS out, "STOPTLS") == 0)
+      {
+      if (!srv->tls_active)
+        {
+        printf("STOPTLS read when TLS not active\n");
+        exit(77);
+        }
+      printf("Shutting down TLS encryption\n");
+
+      #ifdef HAVE_OPENSSL
+      SSL_shutdown(srv->ssl);
+      SSL_free(srv->ssl);
+      #endif
+
+      #ifdef HAVE_GNUTLS
+      gnutls_bye(tls_session, GNUTLS_SHUT_WR);
+      gnutls_deinit(tls_session);
+      tls_session = NULL;
+      gnutls_global_deinit();
+      #endif
+
+      srv->tls_active = 0;
+      continue;
+      }
+
+    /* Remember that we sent STARTTLS */
+
+    srv->sent_starttls = (strcmp(CS out, "starttls") == 0 ||
+                     strcmp(CS out, "STARTTLS") == 0);
+
+    /* Fudge: if the command is "starttls_wait", we send the starttls bit,
+    but we haven't set the flag, so that there is no negotiation. This is for
+    testing the server's timeout. */
+
+    if (strcmp(CS out, "starttls_wait") == 0)
+      {
+      out[8] = 0;
+      n = 8;
+      }
+    #endif
+
+    printf(">>> %s\n", out);
+    if (crlf)
+      {
+      strcpy(CS out + n, "\r\n");
+      n += 2;
+      }
+
+    n = unescape_buf(out, n);
+
+    /* OK, do it */
+
+    alarm(timeout);
+    if (srv->tls_active)
+      {
+      #ifdef HAVE_OPENSSL
+        rc = SSL_write (srv->ssl, out, n);
+      #endif
+      #ifdef HAVE_GNUTLS
+        if ((rc = gnutls_record_send(tls_session, CS out, n)) < 0)
+          {
+          printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
+          exit(76);
+          }
+      #endif
+      }
+    else
+      rc = write(srv->sock, out, n);
+    alarm(0);
+
+    if (rc < 0)
+      {
+      printf("Write error: %s\n", strerror(errno));
+      exit(75);
+      }
+    }
+  }
+}
 
 
 
@@ -550,10 +857,8 @@ char *certfile = NULL;
 char *keyfile = NULL;
 char *end = NULL;
 int argi = 1;
-int host_af, port, s_len, rc, sock, save_errno;
+int host_af, port, s_len, rc, save_errno;
 int timeout = 5;
-int tls_active = 0;
-int sent_starttls = 0;
 int tls_on_connect = 0;
 long tmplong;
 
@@ -561,16 +866,14 @@ long tmplong;
 struct sockaddr_in6 s_in6;
 #endif
 
-#ifdef HAVE_OPENSSL
-SSL_CTX* ctx;
-SSL*     ssl;
-#endif
+srv_ctx srv;
 
-unsigned char outbuffer[10240];
 unsigned char inbuffer[10240];
 unsigned char *inptr = inbuffer;
 
 *inptr = 0;   /* Buffer empty */
+srv.tls_active = 0;
+srv.sent_starttls = 0;
 
 /* Options */
 
@@ -684,8 +987,8 @@ even on an IPv6 system. */
 
 printf("Connecting to %s port %d ... ", address, port);
 
-sock = socket(host_af, SOCK_STREAM, 0);
-if (sock < 0)
+srv.sock = socket(host_af, SOCK_STREAM, 0);
+if (srv.sock < 0)
   {
   printf("socket creation failed: %s\n", strerror(errno));
   exit(89);
@@ -730,7 +1033,7 @@ if (interface != NULL)
 
     /* Bind */
 
-    if (bind(sock, s_ptr, s_len) < 0)
+    if (bind(srv.sock, s_ptr, s_len) < 0)
       {
       printf("Unable to bind outgoing SMTP call to %s: %s",
         interface, strerror(errno));
@@ -769,7 +1072,7 @@ else
 
 signal(SIGALRM, sigalrm_handler_crash);
 alarm(timeout);
-rc = connect(sock, s_ptr, s_len);
+rc = connect(srv.sock, s_ptr, s_len);
 save_errno = errno;
 alarm(0);
 
@@ -778,7 +1081,7 @@ an externally applied timeout if the signal handler has been run. */
 
 if (rc < 0)
   {
-  close(sock);
+  close(srv.sock);
   printf("connect failed: %s\n", strerror(save_errno));
   exit(85);
   }
@@ -792,16 +1095,15 @@ printf("connected\n");
 SSL_library_init();
 SSL_load_error_strings();
 
-ctx = SSL_CTX_new(SSLv23_method());
-if (ctx == NULL)
+if (!(srv.ctx = SSL_CTX_new(SSLv23_method())))
   {
   printf ("SSL_CTX_new failed\n");
   exit(84);
   }
 
-if (certfile != NULL)
+if (certfile)
   {
-  if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
+  if (!SSL_CTX_use_certificate_file(srv.ctx, certfile, SSL_FILETYPE_PEM))
     {
     printf("SSL_CTX_use_certificate_file failed\n");
     exit(83);
@@ -809,9 +1111,9 @@ if (certfile != NULL)
   printf("Certificate file = %s\n", certfile);
   }
 
-if (keyfile != NULL)
+if (keyfile)
   {
-  if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
+  if (!SSL_CTX_use_PrivateKey_file(srv.ctx, keyfile, SSL_FILETYPE_PEM))
     {
     printf("SSL_CTX_use_PrivateKey_file failed\n");
     exit(82);
@@ -819,9 +1121,9 @@ if (keyfile != NULL)
   printf("Key file = %s\n", keyfile);
   }
 
-SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
-SSL_CTX_set_timeout(ctx, 200);
-SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+SSL_CTX_set_session_cache_mode(srv.ctx, SSL_SESS_CACHE_BOTH);
+SSL_CTX_set_timeout(srv.ctx, 200);
+SSL_CTX_set_info_callback(srv.ctx, (void (*)())info_callback);
 #endif
 
 
@@ -836,7 +1138,7 @@ tls_session = tls_session_init();
 if (ocsp_stapling)
   gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
 #endif
-gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)sock);
+gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)srv.sock);
 
 /* When the server asks for a certificate and the client does not have one,
 there is a SIGPIPE error in the gnutls_handshake() function for some reason
@@ -858,7 +1160,7 @@ if (tls_on_connect)
   printf("Attempting to start TLS\n");
 
 #ifdef HAVE_OPENSSL
-  tls_active = tls_start(sock, &ssl, ctx);
+  srv.tls_active = tls_start(srv.sock, &srv.ssl, srv.ctx);
 #endif
 
 #ifdef HAVE_GNUTLS
@@ -869,14 +1171,14 @@ if (tls_on_connect)
   do {
     rc = gnutls_handshake(tls_session);
   } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
-  tls_active = rc >= 0;
+  srv.tls_active = rc >= 0;
   alarm(0);
 
-  if (!tls_active) printf("%s\n", gnutls_strerror(rc));
+  if (!srv.tls_active) printf("%s\n", gnutls_strerror(rc));
   }
 #endif
 
-  if (!tls_active)
+  if (!srv.tls_active)
     printf("Failed to start TLS\n");
 #if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
   else if (  ocsp_stapling
@@ -888,286 +1190,12 @@ if (tls_on_connect)
   }
 #endif
 
-while (fgets(CS outbuffer, sizeof(outbuffer), stdin) != NULL)
-  {
-  int n = (int)strlen(CS outbuffer);
-  int crlf = 1;
-
-  /* Strip trailing newline */
-  if (outbuffer[n-1] == '\n') outbuffer[--n] = 0;
-
-  /* Expect incoming */
-
-  if (  strncmp(CS outbuffer, "???", 3) == 0
-     && (outbuffer[3] == ' ' || outbuffer[3] == '*')
-     )
-    {
-    unsigned char *lineptr;
-    unsigned exp_eof = outbuffer[3] == '*';
-
-    printf("%s\n", outbuffer);
-    n = unescape_buf(outbuffer, n);
-
-    if (*inptr == 0)   /* Refill input buffer */
-      {
-      if (tls_active)
-        {
-        #ifdef HAVE_OPENSSL
-        rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
-        #endif
-        #ifdef HAVE_GNUTLS
-        rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
-        #endif
-        }
-      else
-        {
-        alarm(timeout);
-        rc = read(sock, inbuffer, sizeof(inbuffer));
-        alarm(0);
-        }
-
-      if (rc < 0)
-       {
-        printf("Read error %s\n", strerror(errno));
-        exit(81);
-       }
-      else if (rc == 0)
-       if (exp_eof)
-         {
-          printf("Expected EOF read\n");
-         continue;
-         }
-       else
-         {
-         printf("Unexpected EOF read\n");
-         close(sock);
-         exit(80);
-         }
-      else if (exp_eof)
-        {
-        printf("Expected EOF not read\n");
-        close(sock);
-        exit(74);
-        }
-      else
-        {
-        inbuffer[rc] = 0;
-        inptr = inbuffer;
-        }
-      }
-
-    lineptr = inptr;
-    while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
-    if (*inptr != 0)
-      {
-      *inptr++ = 0;
-      if (*inptr == '\n') inptr++;
-      }
-
-    printf("<<< %s\n", lineptr);
-    if (strncmp(CS lineptr, CS outbuffer + 4, n - 4) != 0)
-      {
-      printf("\n******** Input mismatch ********\n");
-      exit(79);
-      }
-
-    #ifdef HAVE_TLS
-    if (sent_starttls)
-      {
-      if (lineptr[0] == '2')
-        {
-int rc;
-       unsigned int verify;
-
-        printf("Attempting to start TLS\n");
-        fflush(stdout);
-
-        #ifdef HAVE_OPENSSL
-        tls_active = tls_start(sock, &ssl, ctx);
-        #endif
-
-        #ifdef HAVE_GNUTLS
-         {
-         int rc;
-         sigalrm_seen = FALSE;
-         alarm(timeout);
-         do {
-           rc = gnutls_handshake(tls_session);
-         } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
-         tls_active = rc >= 0;
-         alarm(0);
-
-         if (!tls_active) printf("%s\n", gnutls_strerror(rc));
-         }
-        #endif
-
-        if (!tls_active)
-          {
-          printf("Failed to start TLS\n");
-          fflush(stdout);
-          }
-       #ifdef HAVE_GNUTLS
-       else if (ocsp_stapling)
-         {
-         if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
-           {
-           printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
-           fflush(stdout);
-           }
-         else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
-           {
-           printf("Bad certificate\n");
-           fflush(stdout);
-           }
-         #ifdef HAVE_OCSP
-         else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
-           {
-           printf("Failed to verify certificate status\n");
-             {
-             gnutls_datum_t stapling;
-             gnutls_ocsp_resp_t resp;
-             gnutls_datum_t printed;
-             if (  (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
-                && (rc= gnutls_ocsp_resp_init(&resp)) == 0
-                && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
-                && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
-                )
-               {
-               fprintf(stderr, "%.4096s", printed.data);
-               gnutls_free(printed.data);
-               }
-             else
-               (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
-             }
-           fflush(stdout);
-           }
-         #endif
-         }
-       #endif
-        else
-          printf("Succeeded in starting TLS\n");
-        }
-      else printf("Abandoning TLS start attempt\n");
-      }
-    sent_starttls = 0;
-    #endif
-    }
-
-  /* Wait for a bit before proceeding */
-
-  else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
-    {
-    printf("%s\n", outbuffer);
-    sleep(atoi(CS outbuffer + 4));
-    }
-
-  /* Send outgoing, but barf if unconsumed incoming */
-
-  else
-    {
-    unsigned char * out = outbuffer;
-
-    if (strncmp(CS outbuffer, ">>> ", 4) == 0)
-      {
-      crlf = 0;
-      out += 4;
-      n -= 4;
-      }
-
-    if (*inptr != 0)
-      {
-      printf("Unconsumed input: %s", inptr);
-      printf("   About to send: %s\n", out);
-      exit(78);
-      }
-
-    #ifdef HAVE_TLS
-
-    /* Shutdown TLS */
-
-    if (strcmp(CS out, "stoptls") == 0 ||
-        strcmp(CS out, "STOPTLS") == 0)
-      {
-      if (!tls_active)
-        {
-        printf("STOPTLS read when TLS not active\n");
-        exit(77);
-        }
-      printf("Shutting down TLS encryption\n");
-
-      #ifdef HAVE_OPENSSL
-      SSL_shutdown(ssl);
-      SSL_free(ssl);
-      #endif
-
-      #ifdef HAVE_GNUTLS
-      gnutls_bye(tls_session, GNUTLS_SHUT_WR);
-      gnutls_deinit(tls_session);
-      tls_session = NULL;
-      gnutls_global_deinit();
-      #endif
-
-      tls_active = 0;
-      continue;
-      }
-
-    /* Remember that we sent STARTTLS */
-
-    sent_starttls = (strcmp(CS out, "starttls") == 0 ||
-                     strcmp(CS out, "STARTTLS") == 0);
-
-    /* Fudge: if the command is "starttls_wait", we send the starttls bit,
-    but we haven't set the flag, so that there is no negotiation. This is for
-    testing the server's timeout. */
-
-    if (strcmp(CS out, "starttls_wait") == 0)
-      {
-      out[8] = 0;
-      n = 8;
-      }
-    #endif
-
-    printf(">>> %s\n", out);
-    if (crlf)
-      {
-      strcpy(CS out + n, "\r\n");
-      n += 2;
-      }
-
-    n = unescape_buf(out, n);
-
-    /* OK, do it */
-
-    alarm(timeout);
-    if (tls_active)
-      {
-      #ifdef HAVE_OPENSSL
-        rc = SSL_write (ssl, out, n);
-      #endif
-      #ifdef HAVE_GNUTLS
-        if ((rc = gnutls_record_send(tls_session, CS out, n)) < 0)
-          {
-          printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
-          exit(76);
-          }
-      #endif
-      }
-    else
-      rc = write(sock, out, n);
-    alarm(0);
-
-    if (rc < 0)
-      {
-      printf("Write error: %s\n", strerror(errno));
-      exit(75);
-      }
-    }
-  }
+do_file(&srv, stdin, timeout, inbuffer, sizeof(inbuffer), inptr);
 
 printf("End of script\n");
-shutdown(sock, SHUT_WR);
-while ((rc = read(sock, inbuffer, sizeof(inbuffer))) > 0) ;
-close(sock);
+shutdown(srv.sock, SHUT_WR);
+while (read(srv.sock, inbuffer, sizeof(inbuffer)) > 0) ;
+close(srv.sock);
 
 exit(0);
 }