Add cipher suite selection options to sptps_test.
[tinc] / src / sptps_test.c
index 7867b72..37b5a5a 100644 (file)
@@ -30,6 +30,7 @@
 #include "sptps.h"
 #include "utils.h"
 #include "names.h"
+#include "random.h"
 
 #ifndef HAVE_WINDOWS
 #define closesocket(s) close(s)
@@ -117,41 +118,68 @@ static bool receive_record(void *handle, uint8_t type, const void *data, uint16_
        return true;
 }
 
+typedef enum option_t {
+       OPT_BAD_OPTION      = '?',
+       OPT_LONG_OPTION     =  0,
+
+       // Short options
+       OPT_DATAGRAM        = 'd',
+       OPT_QUIT_ON_EOF     = 'q',
+       OPT_READONLY        = 'r',
+       OPT_WRITEONLY       = 'w',
+       OPT_PACKET_LOSS     = 'L',
+       OPT_REPLAY_WINDOW   = 'W',
+       OPT_SPECIAL_CHAR    = 's',
+       OPT_TUN             = 't',
+       OPT_VERBOSE         = 'v',
+        OPT_CIPHER_SUITES   = 'M',
+        OPT_PREFERRED_SUITE = 'P',
+       OPT_IPV4            = '4',
+       OPT_IPV6            = '6',
+
+       // Long options
+       OPT_HELP            = 255,
+} option_t;
+
 static struct option const long_options[] = {
-       {"datagram", no_argument, NULL, 'd'},
-       {"quit", no_argument, NULL, 'q'},
-       {"readonly", no_argument, NULL, 'r'},
-       {"writeonly", no_argument, NULL, 'w'},
-       {"packet-loss", required_argument, NULL, 'L'},
-       {"replay-window", required_argument, NULL, 'W'},
-       {"special", no_argument, NULL, 's'},
-       {"verbose", required_argument, NULL, 'v'},
-       {"help", no_argument, NULL, 1},
-       {NULL, 0, NULL, 0}
+       {"datagram",        no_argument,       NULL, OPT_DATAGRAM},
+       {"quit",            no_argument,       NULL, OPT_QUIT_ON_EOF},
+       {"readonly",        no_argument,       NULL, OPT_READONLY},
+       {"writeonly",       no_argument,       NULL, OPT_WRITEONLY},
+       {"packet-loss",     required_argument, NULL, OPT_PACKET_LOSS},
+       {"replay-window",   required_argument, NULL, OPT_REPLAY_WINDOW},
+       {"special",         no_argument,       NULL, OPT_SPECIAL_CHAR},
+       {"tun",             no_argument,       NULL, OPT_TUN},
+       {"verbose",         required_argument, NULL, OPT_VERBOSE},
+       {"cipher-suites",   required_argument, NULL, OPT_CIPHER_SUITES},
+       {"preferred-suite", required_argument, NULL, OPT_PREFERRED_SUITE},
+       {"help",            no_argument,       NULL, OPT_HELP},
+       {NULL,              0,                 NULL, 0}
 };
 
 static void usage(void) {
-       static const char *message =
+       fprintf(stderr,
                "Usage: %s [options] my_ed25519_key_file his_ed25519_key_file [host] port\n"
                "\n"
                "Valid options are:\n"
-               "  -d, --datagram          Enable datagram mode.\n"
-               "  -q, --quit              Quit when EOF occurs on stdin.\n"
-               "  -r, --readonly          Only send data from the socket to stdout.\n"
+               "  -d, --datagram            Enable datagram mode.\n"
+               "  -q, --quit                Quit when EOF occurs on stdin.\n"
+               "  -r, --readonly            Only send data from the socket to stdout.\n"
 #ifdef HAVE_LINUX
-               "  -t, --tun               Use a tun device instead of stdio.\n"
+               "  -t, --tun                 Use a tun device instead of stdio.\n"
 #endif
-               "  -w, --writeonly         Only send data from stdin to the socket.\n"
-               "  -L, --packet-loss RATE  Fake packet loss of RATE percent.\n"
-               "  -R, --replay-window N   Set replay window to N bytes.\n"
-               "  -s, --special           Enable special handling of lines starting with #, ^ and $.\n"
-               "  -v, --verbose           Display debug messages.\n"
-               "  -4                      Use IPv4.\n"
-               "  -6                      Use IPv6.\n"
+               "  -w, --writeonly           Only send data from stdin to the socket.\n"
+               "  -L, --packet-loss RATE    Fake packet loss of RATE percent.\n"
+               "  -R, --replay-window N     Set replay window to N bytes.\n"
+               "  -M, --cipher-suites MASK  Set the mask of allowed cipher suites.\n"
+               "  -P, --preferred-suite N   Set the preferred cipher suite.\n"
+               "  -s, --special             Enable special handling of lines starting with #, ^ and $.\n"
+               "  -v, --verbose             Display debug messages.\n"
+               "  -4                        Use IPv4.\n"
+               "  -6                        Use IPv6.\n"
                "\n"
-               "Report bugs to tinc@tinc-vpn.org.\n";
-
-       fprintf(stderr, message, program_name);
+               "Report bugs to tinc@tinc-vpn.org.\n",
+               program_name);
 }
 
 #ifdef HAVE_WINDOWS
@@ -301,7 +329,20 @@ server_err:
 
 #endif // HAVE_WINDOWS
 
-int main(int argc, char *argv[]) {
+static void print_listening_msg(int sock) {
+       sockaddr_t sa = {0};
+       socklen_t salen = sizeof(sa);
+       int port = 0;
+
+       if(!getsockname(sock, &sa.sa, &salen)) {
+               port = ntohs(sa.in.sin_port);
+       }
+
+       fprintf(stderr, "Listening on %d...\n", port);
+       fflush(stderr);
+}
+
+static int run_test(int argc, char *argv[]) {
        program_name = argv[0];
        bool initiator = false;
        bool datagram = false;
@@ -312,25 +353,31 @@ int main(int argc, char *argv[]) {
        int r;
        int option_index = 0;
        bool quit = false;
+       unsigned long cipher_suites = SPTPS_ALL_CIPHER_SUITES;
+       unsigned long preferred_suite = 0;
 
        while((r = getopt_long(argc, argv, "dqrstwL:W:v46", long_options, &option_index)) != EOF) {
-               switch(r) {
-               case 0:   /* long option */
+               switch((option_t) r) {
+               case OPT_LONG_OPTION:
                        break;
 
-               case 'd': /* datagram mode */
+               case OPT_BAD_OPTION:
+                       usage();
+                       return 1;
+
+               case OPT_DATAGRAM:
                        datagram = true;
                        break;
 
-               case 'q': /* close connection on EOF from stdin */
+               case OPT_QUIT_ON_EOF:
                        quit = true;
                        break;
 
-               case 'r': /* read only */
+               case OPT_READONLY:
                        readonly = true;
                        break;
 
-               case 't': /* read only */
+               case OPT_TUN:
 #ifdef HAVE_LINUX
                        tun = true;
 #else
@@ -340,39 +387,43 @@ int main(int argc, char *argv[]) {
 #endif
                        break;
 
-               case 'w': /* write only */
+               case OPT_WRITEONLY:
                        writeonly = true;
                        break;
 
-               case 'L': /* packet loss rate */
+               case OPT_PACKET_LOSS:
                        packetloss = atoi(optarg);
                        break;
 
-               case 'W': /* replay window size */
+               case OPT_REPLAY_WINDOW:
                        sptps_replaywin = atoi(optarg);
                        break;
 
-               case 'v': /* be verbose */
+               case OPT_CIPHER_SUITES:
+                       cipher_suites = strtoul(optarg, NULL, 0);
+                       break;
+
+               case OPT_PREFERRED_SUITE:
+                       preferred_suite = strtoul(optarg, NULL, 0);
+                       break;
+
+               case OPT_VERBOSE:
                        verbose = true;
                        break;
 
-               case 's': /* special character handling */
+               case OPT_SPECIAL_CHAR:
                        special = true;
                        break;
 
-               case '?': /* wrong options */
-                       usage();
-                       return 1;
-
-               case '4': /* IPv4 */
+               case OPT_IPV4:
                        addressfamily = AF_INET;
                        break;
 
-               case '6': /* IPv6 */
+               case OPT_IPV6:
                        addressfamily = AF_INET6;
                        break;
 
-               case 1: /* help */
+               case OPT_HELP:
                        usage();
                        return 0;
 
@@ -481,7 +532,7 @@ int main(int argc, char *argv[]) {
                                return 1;
                        }
 
-                       fprintf(stderr, "Listening...\n");
+                       print_listening_msg(sock);
 
                        sock = accept(sock, NULL, NULL);
 
@@ -490,7 +541,7 @@ int main(int argc, char *argv[]) {
                                return 1;
                        }
                } else {
-                       fprintf(stderr, "Listening...\n");
+                       print_listening_msg(sock);
 
                        char buf[65536];
                        struct sockaddr addr;
@@ -510,9 +561,6 @@ int main(int argc, char *argv[]) {
                fprintf(stderr, "Connected\n");
        }
 
-       crypto_init();
-       prng_init();
-
        FILE *fp = fopen(argv[1], "r");
 
        if(!fp) {
@@ -532,14 +580,14 @@ int main(int argc, char *argv[]) {
 
        if(!fp) {
                fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
-               free(mykey);
+               ecdsa_free(mykey);
                return 1;
        }
 
        ecdsa_t *hiskey = NULL;
 
        if(!(hiskey = ecdsa_read_pem_public_key(fp))) {
-               free(mykey);
+               ecdsa_free(mykey);
                return 1;
        }
 
@@ -551,9 +599,22 @@ int main(int argc, char *argv[]) {
 
        sptps_t s;
 
-       if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record)) {
-               free(mykey);
-               free(hiskey);
+       sptps_params_t params = {
+               .handle = &sock,
+               .initiator = initiator,
+               .datagram = datagram,
+               .mykey = mykey,
+               .hiskey = hiskey,
+               .label = "sptps_test",
+               .send_data = send_data,
+               .receive_record = receive_record,
+               .cipher_suites = cipher_suites,
+               .preferred_suite = preferred_suite,
+       };
+
+       if(!sptps_start(&s, &params)) {
+               ecdsa_free(mykey);
+               ecdsa_free(hiskey);
                return 1;
        }
 
@@ -564,8 +625,8 @@ int main(int argc, char *argv[]) {
 
                if(in < 0) {
                        fprintf(stderr, "Could not init stdin reader thread\n");
-                       free(mykey);
-                       free(hiskey);
+                       ecdsa_free(mykey);
+                       ecdsa_free(hiskey);
                        return 1;
                }
        }
@@ -592,8 +653,8 @@ int main(int argc, char *argv[]) {
                FD_SET(sock, &fds);
 
                if(select(max_fd + 1, &fds, NULL, NULL, NULL) <= 0) {
-                       free(mykey);
-                       free(hiskey);
+                       ecdsa_free(mykey);
+                       ecdsa_free(hiskey);
                        return 1;
                }
 
@@ -606,8 +667,8 @@ int main(int argc, char *argv[]) {
 
                        if(len < 0) {
                                fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
-                               free(mykey);
-                               free(hiskey);
+                               ecdsa_free(mykey);
+                               ecdsa_free(hiskey);
                                return 1;
                        }
 
@@ -638,8 +699,8 @@ int main(int argc, char *argv[]) {
                                        sptps_send_record(&s, 0, buf, len);
                                }
                        } else if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && buf[0] == '\n') ? 0 : buf[0] == '*' ? sizeof(buf) : (size_t)len)) {
-                               free(mykey);
-                               free(hiskey);
+                               ecdsa_free(mykey);
+                               ecdsa_free(hiskey);
                                return 1;
                        }
                }
@@ -649,8 +710,8 @@ int main(int argc, char *argv[]) {
 
                        if(len < 0) {
                                fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
-                               free(mykey);
-                               free(hiskey);
+                               ecdsa_free(mykey);
+                               ecdsa_free(hiskey);
                                return 1;
                        }
 
@@ -680,8 +741,8 @@ int main(int argc, char *argv[]) {
 
                                if(!done) {
                                        if(!datagram) {
-                                               free(mykey);
-                                               free(hiskey);
+                                               ecdsa_free(mykey);
+                                               ecdsa_free(hiskey);
                                                return 1;
                                        }
                                }
@@ -694,14 +755,21 @@ int main(int argc, char *argv[]) {
 
        bool stopped = sptps_stop(&s);
 
-       free(mykey);
-       free(hiskey);
+       ecdsa_free(mykey);
+       ecdsa_free(hiskey);
+       closesocket(sock);
 
-       if(!stopped) {
-               return 1;
-       }
+       return !stopped;
+}
 
-       closesocket(sock);
+int main(int argc, char *argv[]) {
+       random_init();
+       crypto_init();
+       prng_init();
 
-       return 0;
+       int result = run_test(argc, argv);
+
+       random_exit();
+
+       return result;
 }