text: /* suites.c
   *
   * Copyright (C) 2006-2015 wolfSSL Inc.
   *
   * This file is part of wolfSSL. (formerly known as CyaSSL)
   *
   * wolfSSL is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * wolfSSL is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #ifdef HAVE_CONFIG_H
      #include <config.h>
  #endif
  
  #include <wolfssl/wolfcrypt/settings.h>
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #include <wolfssl/ssl.h>
  #include <tests/unit.h>
  
  
  #define MAX_ARGS 40
  #define MAX_COMMAND_SZ 240
  #define MAX_SUITE_SZ 80 
  #define NOT_BUILT_IN -123
  #ifdef NO_OLD_TLS
      #define VERSION_TOO_OLD -124
  #endif
  
  #include "examples/client/client.h"
  #include "examples/server/server.h"
  
  
  static WOLFSSL_CTX* cipherSuiteCtx = NULL;
  static char nonblockFlag[] = "-N";
  static char noVerifyFlag[] = "-d";
  static char portFlag[] = "-p";
  static char flagSep[] = " ";
  static char svrPort[] = "0";
  
  
  #ifdef NO_OLD_TLS
  /* if the protocol version is less than tls 1.2 return 1, else 0 */
  static int IsOldTlsVersion(const char* line)
  {
      const char* find = "-v ";
      char* begin = strstr(line, find);
  
      if (begin) {
          int version = -1;
  
          begin += 3;
  
          version = atoi(begin);
  
          if (version < 3)
              return 1;
      }
  
      return 0;
  } 
  #endif /* NO_OLD_TLS */
  
  
  /* if the cipher suite on line is valid store in suite and return 1, else 0 */
  static int IsValidCipherSuite(const char* line, char* suite)
  {
      int  found = 0;
      int  valid = 0;
  
      const char* find = "-l ";
      const char* begin = strstr(line, find);
      const char* end;
  
      suite[0] = '\0';
  
      if (begin) {
          begin += 3;
  
          end = strstr(begin, " ");
  
          if (end) {
              long len = end - begin;
              if (len > MAX_SUITE_SZ) {
                  printf("suite too long!\n");
                  return 0;
              }
              memcpy(suite, begin, len);
              suite[len] = '\0';
          }
          else
              strncpy(suite, begin, MAX_SUITE_SZ);
  
          suite[MAX_SUITE_SZ] = '\0';
          found = 1;
      }
  
      if (found) {
          if (wolfSSL_CTX_set_cipher_list(cipherSuiteCtx, suite) == SSL_SUCCESS)
              valid = 1;
      }
  
      return valid;
  }
  
  
  static int execute_test_case(int svr_argc, char** svr_argv,
                                int cli_argc, char** cli_argv,
                                int addNoVerify, int addNonBlocking)
  {
  #ifdef WOLFSSL_TIRTOS
      func_args cliArgs = {0};
      func_args svrArgs = {0};
      cliArgs.argc = cli_argc;
      cliArgs.argv = cli_argv;
      svrArgs.argc = svr_argc;
      svrArgs.argv = svr_argv;
  #else
      func_args cliArgs = {cli_argc, cli_argv, 0, NULL, NULL};
      func_args svrArgs = {svr_argc, svr_argv, 0, NULL, NULL};
  #endif
  
      tcp_ready   ready;
      THREAD_TYPE serverThread;
      char        commandLine[MAX_COMMAND_SZ];
      char        cipherSuite[MAX_SUITE_SZ+1];
      int         i;
      size_t      added = 0;
      static      int tests = 1;
  
      commandLine[0] = '\0';
      for (i = 0; i < svr_argc; i++) {
          added += strlen(svr_argv[i]) + 2;
          if (added >= MAX_COMMAND_SZ) {
              printf("server command line too long\n"); 
              break;
          }
          strcat(commandLine, svr_argv[i]);
          strcat(commandLine, flagSep);
      }
  
      if (IsValidCipherSuite(commandLine, cipherSuite) == 0) {
          #ifdef DEBUG_SUITE_TESTS
              printf("cipher suite %s not supported in build\n", cipherSuite);
          #endif
          return NOT_BUILT_IN;
      }
  
  #ifdef NO_OLD_TLS
      if (IsOldTlsVersion(commandLine) == 1) {
          #ifdef DEBUG_SUITE_TESTS
              printf("protocol version on line %s is too old\n", commandLine);
          #endif
          return VERSION_TOO_OLD;
      }
  #endif
  
      if (addNoVerify) {
          printf("repeating test with client cert request off\n"); 
          added += 4;   /* -d plus space plus terminator */
          if (added >= MAX_COMMAND_SZ || svr_argc >= MAX_ARGS)
              printf("server command line too long\n");
          else {
              svr_argv[svr_argc++] = noVerifyFlag;
              svrArgs.argc = svr_argc;
              strcat(commandLine, noVerifyFlag);
              strcat(commandLine, flagSep);
          }
      }
      if (addNonBlocking) {
          printf("repeating test with non blocking on\n"); 
          added += 4;   /* -N plus terminator */
          if (added >= MAX_COMMAND_SZ || svr_argc >= MAX_ARGS)
              printf("server command line too long\n");
          else {
              svr_argv[svr_argc++] = nonblockFlag;
              svrArgs.argc = svr_argc;
              strcat(commandLine, nonblockFlag);
              strcat(commandLine, flagSep);
          }
      }
      #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_TIRTOS)
          /* add port 0 */
          if (svr_argc + 2 > MAX_ARGS)
              printf("cannot add the magic port number flag to server\n");
          else
          {
              svr_argv[svr_argc++] = portFlag;
              svr_argv[svr_argc++] = svrPort;
              svrArgs.argc = svr_argc;
          }
      #endif
      printf("trying server command line[%d]: %s\n", tests, commandLine);
  
      commandLine[0] = '\0';
      added = 0;
      for (i = 0; i < cli_argc; i++) {
          added += strlen(cli_argv[i]) + 2;
          if (added >= MAX_COMMAND_SZ) {
              printf("client command line too long\n"); 
              break;
          }
          strcat(commandLine, cli_argv[i]);
          strcat(commandLine, flagSep);
      }
      if (addNonBlocking) {
          added += 4;   /* -N plus space plus terminator  */
          if (added >= MAX_COMMAND_SZ)
              printf("client command line too long\n");
          else  {
              cli_argv[cli_argc++] = nonblockFlag;
              strcat(commandLine, nonblockFlag);
              strcat(commandLine, flagSep);
              cliArgs.argc = cli_argc;
          }
      }
      printf("trying client command line[%d]: %s\n", tests++, commandLine);
  
      InitTcpReady(&ready);
  
  #ifdef WOLFSSL_TIRTOS
      fdOpenSession(Task_self());
  #endif
  
      /* start server */
      svrArgs.signal = &ready;
      start_thread(server_test, &svrArgs, &serverThread);
      wait_tcp_ready(&svrArgs);
      #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_TIRTOS)
          if (ready.port != 0)
          {
              if (cli_argc + 2 > MAX_ARGS)
                  printf("cannot add the magic port number flag to client\n");
              else {
                  char portNumber[8];
                  snprintf(portNumber, sizeof(portNumber), "%d", ready.port);
                  cli_argv[cli_argc++] = portFlag;
                  cli_argv[cli_argc++] = portNumber;
                  cliArgs.argc = cli_argc;
              }
          }
      #endif
      /* start client */
      client_test(&cliArgs);
  
      /* verify results */ 
      if (cliArgs.return_code != 0) {
          printf("client_test failed\n");
          exit(EXIT_FAILURE);
      }
  
      join_thread(serverThread);
      if (svrArgs.return_code != 0) { 
          printf("server_test failed\n");
          exit(EXIT_FAILURE);
      }
  
  #ifdef WOLFSSL_TIRTOS
      fdCloseSession(Task_self());
  #endif
      FreeTcpReady(&ready);
      
      return 0;
  }
  
  static void test_harness(void* vargs)
  {
      func_args* args = (func_args*)vargs;
      char* script;
      long  sz, len;
      int   cliMode = 0;   /* server or client command flag, server first */
      int   ret;
      FILE* file;
      char* svrArgs[MAX_ARGS];
      int   svrArgsSz;
      char* cliArgs[MAX_ARGS];
      int   cliArgsSz;
      char* cursor;
      char* comment;
      const char* fname = "tests/test.conf";
  
      if (args->argc == 1) {
          printf("notice: using default file %s\n", fname);
      }
      else if(args->argc != 2) {
          printf("usage: harness [FILE]\n");
          args->return_code = 1;
          return;
      }
      else {
          fname = args->argv[1];
      }
  
      file = fopen(fname, "r");
      if (file == NULL) {
          fprintf(stderr, "unable to open %s\n", fname);
          args->return_code = 1;
          return;
      }
      fseek(file, 0, SEEK_END);
      sz = ftell(file);
      rewind(file);
      if (sz <= 0) {
          fprintf(stderr, "%s is empty\n", fname);
          fclose(file);
          args->return_code = 1;
          return;
      }
  
      script = (char*)malloc(sz+1);
      if (script == 0) {
          fprintf(stderr, "unable to allocte script buffer\n");
          fclose(file);
          args->return_code = 1;
          return;
      }
  
      len = fread(script, 1, sz, file);
      if (len != sz) {
          fprintf(stderr, "read error\n");
          fclose(file);
          free(script);
          args->return_code = 1;
          return;
      }
      
      fclose(file);
      script[sz] = 0;
  
      cursor = script;
      svrArgsSz = 1;
      svrArgs[0] = args->argv[0];
      cliArgsSz = 1;
      cliArgs[0] = args->argv[0];
  
      while (*cursor != 0) {
          int do_it = 0;
  
          switch (*cursor) {
              case '\n':
                  /* A blank line triggers test case execution or switches
                     to client mode if we don't have the client command yet */
                  if (cliMode == 0)
                      cliMode = 1;  /* switch to client mode processing */
                  else
                      do_it = 1;    /* Do It, we have server and client */
                  cursor++;
                  break;
              case '#':
                  /* Ignore lines that start with a #. */
                  comment = strsep(&cursor, "\n");
  #ifdef DEBUG_SUITE_TESTS
                  printf("%s\n", comment);
  #else
                  (void)comment;
  #endif
                  break;
              case '-':
                  /* Parameters start with a -. They end in either a newline
                   * or a space. Capture until either, save in Args list. */
                  if (cliMode)
                      cliArgs[cliArgsSz++] = strsep(&cursor, " \n");
                  else
                      svrArgs[svrArgsSz++] = strsep(&cursor, " \n");
                  break;
              default:
                  /* Anything from cursor until end of line that isn't the above
                   * is data for a paramter. Just up until the next newline in
                   * the Args list. */
                  if (cliMode)
                      cliArgs[cliArgsSz++] = strsep(&cursor, "\n");
                  else
                      svrArgs[svrArgsSz++] = strsep(&cursor, "\n");
                  if (*cursor == 0)  /* eof */
                      do_it = 1; 
          }
  
          if (svrArgsSz == MAX_ARGS || cliArgsSz == MAX_ARGS) {
              fprintf(stderr, "too many arguments, forcing test run\n");
              do_it = 1;
          }
  
          if (do_it) {
              ret = execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs,0,0);
              /* don't repeat if not supported in build */
              if (ret == 0) {
                  execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 0, 1);
                  execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 1, 0);
                  execute_test_case(svrArgsSz, svrArgs, cliArgsSz, cliArgs, 1, 1);
              }
              svrArgsSz = 1;
              cliArgsSz = 1;
              cliMode   = 0;
          }
      }
  
      free(script);
      args->return_code = 0;
  }
  
  
  int SuiteTest(void)
  {
      func_args args;
      char argv0[2][80];
      char* myArgv[2];
  
      printf(" Begin Cipher Suite Tests\n");
  
      /* setup */
      myArgv[0] = argv0[0];
      myArgv[1] = argv0[1];
      args.argv = myArgv;
      strcpy(argv0[0], "SuiteTest");
  
      (void)test_harness;
  
      cipherSuiteCtx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
      if (cipherSuiteCtx == NULL) {
          printf("can't get cipher suite ctx\n");
          exit(EXIT_FAILURE);  
      }
  
      /* default case */
      args.argc = 1;
      printf("starting default cipher suite tests\n");
      test_harness(&args);
      if (args.return_code != 0) {
          printf("error from script %d\n", args.return_code);
          exit(EXIT_FAILURE);  
      }
  
      /* any extra cases will need another argument */
      args.argc = 2;
  
  #ifdef WOLFSSL_DTLS 
      /* add dtls extra suites */
      strcpy(argv0[1], "tests/test-dtls.conf");
      printf("starting dtls extra cipher suite tests\n");
      test_harness(&args);
      if (args.return_code != 0) {
          printf("error from script %d\n", args.return_code);
          exit(EXIT_FAILURE);  
      }
  #endif
  
      printf(" End Cipher Suite Tests\n");
  
      wolfSSL_CTX_free(cipherSuiteCtx);
      wolfSSL_Cleanup();
  
      return args.return_code;
  }
  
  
  