diff options
Diffstat (limited to 'tests/dtls/dtls-stress.c')
-rw-r--r-- | tests/dtls/dtls-stress.c | 1558 |
1 files changed, 1558 insertions, 0 deletions
diff --git a/tests/dtls/dtls-stress.c b/tests/dtls/dtls-stress.c new file mode 100644 index 0000000..826bd29 --- /dev/null +++ b/tests/dtls/dtls-stress.c @@ -0,0 +1,1558 @@ +/* + * Copyright (C) 2012-2016 Sean Buckheister + * Copyright (C) 2016 Nikos Mavrogiannopoulos + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of GnuTLS. + * + * GnuTLS 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 3 of the License, or + * (at your option) any later version. + * + * GnuTLS 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 GnuTLS; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * DTLS stress test utility + * + * **** Available parameters **** + * + * -nb enable nonblocking operations on sessions + * -batch read test identifiers from stdin and run them + * -d increase debug level by one + * -r replay messages (very crude replay mechanism) + * -d <n> set debug level to <n> + * -die don't start new tests after the first detected failure + * -timeout <n> set handshake timeout to <n> seconds. Tests that don't make progress + * within twice this time will be forcibly killed. (default: 120) + * -retransmit <n> set retransmit timeout to <n> milliseconds (default: 100) + * -j <n> run up to <n> tests in parallel + * -full use full handshake with mutual certificate authentication + * -resume use resumed handshake + * -shello <perm> run only one test, with the server hello flight permuted as <perm> + * -sfinished <perm> run only one test, with the server finished flight permuted as <perm> + * -cfinished <perm> run only one test, with the client finished flight permuted as <perm> + * <packet name> run only one test, drop <packet name> three times + * valid values for <packet name> are: + * SHello, SCertificate, SKeyExchange, SCertificateRequest, SHelloDone, + * CCertificate, CKeyExchange, CCertificateVerify, CChangeCipherSpec, + * CFinished, SChangeCipherSpec, SFinished + * using *Certificate* without -full will yield unexpected results + * + * + * **** Permutation handling **** + * + * Flight length for -sfinished is 2, for -shello and -cfinished they are 5 with -full, 3 otherwise. + * Permutations are given with base 0 and specify the order in which reordered packets are transmitted. + * For example, -full -shello 42130 will transmit server hello flight packets in the order + * SHelloDone, SKeyExchange, SCertificate, SCertificateRequest, SHello + * + * When -resume is specified the -sfinished flight length is 3 (same as shello), cfinished is 2. + * The -resume option has to be combined with sfinished or cfinished. + * + * **** Output format **** + * + * Every line printed for any given test is prefixed by a unique id for that test. See run_test_by_id for + * exact composition. Errors encountered during execution are printed, with one status line after test + * completen. The format for this line is as follows: + * + * <id> <status> SHello(<shperm>), SFinished(<sfinperm>), CFinished(<cfinperm>) :- <drops> + * + * The format for error lines is <id> <role>| <text>, with <role> being the role of the child process + * that encountered the error, and <text> being obvious. + * + * <id> is the unique id for the test, it can be used as input to -batch. + * <status> can be ++ for a successful test, -- for a failure, TT for a deadlock timeout killed test, + * or !! for a test has died due to some unforeseen circumstances like syscall failures. + * <shperm>, <sfinperm>, <cfinperm> show the permutation for the respective flights used. + * They can be used as input to -shello, -sfinished, and -cfinished, respectively. + * <drops> is a comma separated list of <packet name>, one for every packet dropped thrice + * + * + * **** Exit status **** + * + * 0 all tests have passed + * 1 some tests have failed + * 4 the master processed has encountered unexpected errors + * 8 error parsing command line + */ + +#include <config.h> +#include <gnutls/gnutls.h> +#include <gnutls/openpgp.h> +#include <gnutls/dtls.h> +#include <unistd.h> +#include "../utils.h" +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <poll.h> +#include <time.h> +#include <assert.h> +#include <sys/wait.h> + +#if _POSIX_TIMERS && (_POSIX_TIMERS - 200112L) >= 0 + +// {{{ types + +#define log(fmt, ...) \ + if (debug) fprintf(stdout, "%i %s| "fmt, run_id, role_name, ##__VA_ARGS__) + +typedef struct { + int count; +} filter_packet_state_t; + +typedef struct { + const char *name; + gnutls_datum_t packets[5]; + int *order; + int count; +} filter_permute_state_t; + +typedef void (*filter_fn) (gnutls_transport_ptr_t, const unsigned char *, + size_t); + +typedef int (*match_fn) (const unsigned char *, size_t); + +enum role { SERVER, CLIENT }; + +// }}} + +// {{{ static data + +static int permutations2[2][2] += { {0, 1}, {1, 0} }; + +static const char *permutation_names2[] += { "01", "10", 0 }; + +static int permutations3[6][3] += { {0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0} }; + +static const char *permutation_names3[] += { "012", "021", "102", "120", "201", "210", 0 }; + +static int permutations5[120][5] = { + {0, 1, 2, 3, 4}, {0, 2, 1, 3, 4}, {1, 0, 2, 3, 4}, {1, 2, 0, 3, 4}, + {2, 0, 1, 3, 4}, {2, 1, 0, 3, 4}, {0, 1, 3, 2, 4}, {0, 2, 3, 1, 4}, + {1, 0, 3, 2, 4}, {1, 2, 3, 0, 4}, {2, 0, 3, 1, 4}, {2, 1, 3, 0, 4}, + {0, 3, 1, 2, 4}, {0, 3, 2, 1, 4}, {1, 3, 0, 2, 4}, {1, 3, 2, 0, 4}, + {2, 3, 0, 1, 4}, {2, 3, 1, 0, 4}, {3, 0, 1, 2, 4}, {3, 0, 2, 1, 4}, + {3, 1, 0, 2, 4}, {3, 1, 2, 0, 4}, {3, 2, 0, 1, 4}, {3, 2, 1, 0, 4}, + {0, 1, 2, 4, 3}, {0, 2, 1, 4, 3}, {1, 0, 2, 4, 3}, {1, 2, 0, 4, 3}, + {2, 0, 1, 4, 3}, {2, 1, 0, 4, 3}, {0, 1, 3, 4, 2}, {0, 2, 3, 4, 1}, + {1, 0, 3, 4, 2}, {1, 2, 3, 4, 0}, {2, 0, 3, 4, 1}, {2, 1, 3, 4, 0}, + {0, 3, 1, 4, 2}, {0, 3, 2, 4, 1}, {1, 3, 0, 4, 2}, {1, 3, 2, 4, 0}, + {2, 3, 0, 4, 1}, {2, 3, 1, 4, 0}, {3, 0, 1, 4, 2}, {3, 0, 2, 4, 1}, + {3, 1, 0, 4, 2}, {3, 1, 2, 4, 0}, {3, 2, 0, 4, 1}, {3, 2, 1, 4, 0}, + {0, 1, 4, 2, 3}, {0, 2, 4, 1, 3}, {1, 0, 4, 2, 3}, {1, 2, 4, 0, 3}, + {2, 0, 4, 1, 3}, {2, 1, 4, 0, 3}, {0, 1, 4, 3, 2}, {0, 2, 4, 3, 1}, + {1, 0, 4, 3, 2}, {1, 2, 4, 3, 0}, {2, 0, 4, 3, 1}, {2, 1, 4, 3, 0}, + {0, 3, 4, 1, 2}, {0, 3, 4, 2, 1}, {1, 3, 4, 0, 2}, {1, 3, 4, 2, 0}, + {2, 3, 4, 0, 1}, {2, 3, 4, 1, 0}, {3, 0, 4, 1, 2}, {3, 0, 4, 2, 1}, + {3, 1, 4, 0, 2}, {3, 1, 4, 2, 0}, {3, 2, 4, 0, 1}, {3, 2, 4, 1, 0}, + {0, 4, 1, 2, 3}, {0, 4, 2, 1, 3}, {1, 4, 0, 2, 3}, {1, 4, 2, 0, 3}, + {2, 4, 0, 1, 3}, {2, 4, 1, 0, 3}, {0, 4, 1, 3, 2}, {0, 4, 2, 3, 1}, + {1, 4, 0, 3, 2}, {1, 4, 2, 3, 0}, {2, 4, 0, 3, 1}, {2, 4, 1, 3, 0}, + {0, 4, 3, 1, 2}, {0, 4, 3, 2, 1}, {1, 4, 3, 0, 2}, {1, 4, 3, 2, 0}, + {2, 4, 3, 0, 1}, {2, 4, 3, 1, 0}, {3, 4, 0, 1, 2}, {3, 4, 0, 2, 1}, + {3, 4, 1, 0, 2}, {3, 4, 1, 2, 0}, {3, 4, 2, 0, 1}, {3, 4, 2, 1, 0}, + {4, 0, 1, 2, 3}, {4, 0, 2, 1, 3}, {4, 1, 0, 2, 3}, {4, 1, 2, 0, 3}, + {4, 2, 0, 1, 3}, {4, 2, 1, 0, 3}, {4, 0, 1, 3, 2}, {4, 0, 2, 3, 1}, + {4, 1, 0, 3, 2}, {4, 1, 2, 3, 0}, {4, 2, 0, 3, 1}, {4, 2, 1, 3, 0}, + {4, 0, 3, 1, 2}, {4, 0, 3, 2, 1}, {4, 1, 3, 0, 2}, {4, 1, 3, 2, 0}, + {4, 2, 3, 0, 1}, {4, 2, 3, 1, 0}, {4, 3, 0, 1, 2}, {4, 3, 0, 2, 1}, + {4, 3, 1, 0, 2}, {4, 3, 1, 2, 0}, {4, 3, 2, 0, 1}, {4, 3, 2, 1, 0} +}; + +static const char *permutation_names5[] + = { "01234", "02134", "10234", "12034", "20134", "21034", "01324", + "02314", "10324", "12304", "20314", "21304", "03124", "03214", + "13024", "13204", "23014", "23104", "30124", "30214", "31024", + "31204", "32014", "32104", "01243", "02143", "10243", "12043", + "20143", "21043", "01342", "02341", "10342", "12340", "20341", + "21340", "03142", "03241", "13042", "13240", "23041", "23140", + "30142", "30241", "31042", "31240", "32041", "32140", "01423", + "02413", "10423", "12403", "20413", "21403", "01432", "02431", + "10432", "12430", "20431", "21430", "03412", "03421", "13402", + "13420", "23401", "23410", "30412", "30421", "31402", "31420", + "32401", "32410", "04123", "04213", "14023", "14203", "24013", + "24103", "04132", "04231", "14032", "14230", "24031", "24130", + "04312", "04321", "14302", "14320", "24301", "24310", "34012", + "34021", "34102", "34120", "34201", "34210", "40123", "40213", + "41023", "41203", "42013", "42103", "40132", "40231", "41032", + "41230", "42031", "42130", "40312", "40321", "41302", "41320", + "42301", "42310", "43012", "43021", "43102", "43120", "43201", + "43210", 0 +}; + +static const char *filter_names[8] + = { "SHello", + "SKeyExchange", + "SHelloDone", + "CKeyExchange", + "CChangeCipherSpec", + "CFinished", + "SChangeCipherSpec", + "SFinished" +}; + +static const char *filter_names_resume[] + = { "SHello", + "SChangeCipherSpec", + "SFinished", + "CChangeCipherSpec", + "CFinished" +}; + +static const char *filter_names_full[12] + = { "SHello", + "SCertificate", + "SKeyExchange", + "SCertificateRequest", + "SHelloDone", + "CCertificate", + "CKeyExchange", + "CCertificateVerify", + "CChangeCipherSpec", + "CFinished", + "SChangeCipherSpec", + "SFinished" +}; + +#include "cert-common.h" + +// }}} + +// {{{ other global state + +enum role role; + +#define role_name (role == SERVER ? "server" : "client") + +int debug; +int nonblock; +int replay; +int full; +int resume; +int timeout_seconds; +int retransmit_milliseconds; +int run_to_end; + +int run_id; + +// }}} + +// {{{ logging and error handling + +static void logfn(int level, const char *s) +{ + if (debug) { + fprintf(stdout, "%i %s|<%i> %s", run_id, role_name, level, s); + } +} + +static void auditfn(gnutls_session_t session, const char *s) +{ + if (debug) { + fprintf(stdout, "%i %s| %s", run_id, role_name, s); + } +} + +static void drop(const char *packet) +{ + if (debug) { + log("dropping %s\n", packet); + } +} + +static int _process_error(int loc, int code, int die) +{ + if (code < 0 && (die || code != GNUTLS_E_AGAIN)) { + fprintf(stdout, "%i <%s tls> line %i: %s", run_id, + role_name, loc, gnutls_strerror(code)); + if (gnutls_error_is_fatal(code) || die) { + fprintf(stdout, " (fatal)\n"); + exit(1); + } else { + fprintf(stdout, "\n"); + } + } + return code; +} + +#define die_on_error(code) _process_error(__LINE__, code, 1) +#define process_error(code) _process_error(__LINE__, code, 0) + +static void _process_error_or_timeout(int loc, int err, time_t tdiff) +{ + if (err < 0) { + if (err != GNUTLS_E_TIMEDOUT || tdiff >= 60) { + _process_error(loc, err, 0); + } else { + log("line %i: {spurious timeout} (fatal)", loc); + exit(1); + } + } +} + +#define process_error_or_timeout(code, tdiff) _process_error_or_timeout(__LINE__, code, tdiff) + +static void rperror(const char *name) +{ + fprintf(stdout, "%i %s| %s\n", run_id, role_name, name); +} + +// }}} + +// {{{ init, shared, and teardown code and data for packet stream filters + +filter_packet_state_t state_packet_ServerHello = { 0 }; +filter_packet_state_t state_packet_ServerCertificate = { 0 }; +filter_packet_state_t state_packet_ServerKeyExchange = { 0 }; +filter_packet_state_t state_packet_ServerCertificateRequest = { 0 }; +filter_packet_state_t state_packet_ServerHelloDone = { 0 }; +filter_packet_state_t state_packet_ClientCertificate = { 0 }; +filter_packet_state_t state_packet_ClientKeyExchange = { 0 }; +filter_packet_state_t state_packet_ClientCertificateVerify = { 0 }; +filter_packet_state_t state_packet_ClientChangeCipherSpec = { 0 }; +filter_packet_state_t state_packet_ClientFinished = { 0 }; +filter_packet_state_t state_packet_ClientFinishedResume = { 0 }; +filter_packet_state_t state_packet_ServerChangeCipherSpec = { 0 }; +filter_packet_state_t state_packet_ServerFinished = { 0 }; +filter_packet_state_t state_packet_ServerFinishedResume = { 0 }; + +static filter_permute_state_t state_permute_ServerHello = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerHelloFull = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerFinished = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ServerFinishedResume = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinished = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinishedResume = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; +static filter_permute_state_t state_permute_ClientFinishedFull = + { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 }; + +filter_fn filter_chain[32]; +int filter_current_idx; + +static void filter_permute_state_free_buffer(filter_permute_state_t * state) +{ + unsigned int i; + + for (i = 0; i < sizeof(state->packets) / sizeof(state->packets[0]); i++) { + free(state->packets[i].data); + state->packets[i].data = NULL; + } +} + +static void filter_clear_state(void) +{ + filter_current_idx = 0; + + filter_permute_state_free_buffer(&state_permute_ServerHello); + filter_permute_state_free_buffer(&state_permute_ServerHelloFull); + filter_permute_state_free_buffer(&state_permute_ServerFinished); + filter_permute_state_free_buffer(&state_permute_ServerFinishedResume); + filter_permute_state_free_buffer(&state_permute_ClientFinished); + filter_permute_state_free_buffer(&state_permute_ClientFinishedResume); + filter_permute_state_free_buffer(&state_permute_ClientFinishedFull); + + memset(&state_packet_ServerHello, 0, sizeof(state_packet_ServerHello)); + memset(&state_packet_ServerCertificate, 0, + sizeof(state_packet_ServerCertificate)); + memset(&state_packet_ServerKeyExchange, 0, + sizeof(state_packet_ServerKeyExchange)); + memset(&state_packet_ServerCertificateRequest, 0, + sizeof(state_packet_ServerCertificateRequest)); + memset(&state_packet_ServerHelloDone, 0, + sizeof(state_packet_ServerHelloDone)); + memset(&state_packet_ClientCertificate, 0, + sizeof(state_packet_ClientCertificate)); + memset(&state_packet_ClientKeyExchange, 0, + sizeof(state_packet_ClientKeyExchange)); + memset(&state_packet_ClientCertificateVerify, 0, + sizeof(state_packet_ClientCertificateVerify)); + memset(&state_packet_ClientChangeCipherSpec, 0, + sizeof(state_packet_ClientChangeCipherSpec)); + memset(&state_packet_ClientFinished, 0, + sizeof(state_packet_ClientFinished)); + memset(&state_packet_ClientFinishedResume, 0, + sizeof(state_packet_ClientFinishedResume)); + memset(&state_packet_ServerChangeCipherSpec, 0, + sizeof(state_packet_ServerChangeCipherSpec)); + memset(&state_packet_ServerFinished, 0, + sizeof(state_packet_ServerFinished)); + memset(&state_packet_ServerFinishedResume, 0, + sizeof(state_packet_ServerFinishedResume)); + memset(&state_permute_ServerHello, 0, + sizeof(state_permute_ServerHello)); + memset(&state_permute_ServerHelloFull, 0, + sizeof(state_permute_ServerHelloFull)); + memset(&state_permute_ServerFinished, 0, + sizeof(state_permute_ServerFinished)); + memset(&state_permute_ClientFinished, 0, + sizeof(state_permute_ClientFinished)); + memset(&state_permute_ClientFinishedResume, 0, + sizeof(state_permute_ClientFinishedResume)); + memset(&state_permute_ClientFinishedFull, 0, + sizeof(state_permute_ClientFinishedFull)); + + state_permute_ServerHello.name = "ServerHello"; + state_permute_ServerHelloFull.name = "ServerHelloFull"; + state_permute_ServerFinished.name = "ServerFinished"; + state_permute_ServerFinishedResume.name = "ServerFinishedResume"; + state_permute_ClientFinished.name = "ClientFinished"; + state_permute_ClientFinishedResume.name = "ClientFinishedResume"; + state_permute_ClientFinishedFull.name = "ClientFinishedFull"; +} + +/* replay buffer */ +static int rbuffer[5 * 1024]; +unsigned rbuffer_size = 0; + +static void filter_run_next(gnutls_transport_ptr_t fd, + const unsigned char *buffer, size_t len) +{ + int ret = 0; + filter_fn fn = filter_chain[filter_current_idx]; + filter_current_idx++; + if (fn) { + fn(fd, buffer, len); + } else { + ret = send((int)(intptr_t) fd, buffer, len, 0); + } + filter_current_idx--; + + if (ret > 0 && replay != 0) { + if (rbuffer_size == 0 && len < sizeof(rbuffer)) { + memcpy(rbuffer, buffer, len); + rbuffer_size = len; + } else if (rbuffer_size != 0) { + send((int)(intptr_t) fd, rbuffer, rbuffer_size, 0); + if (len < sizeof(rbuffer) && len > rbuffer_size) { + memcpy(rbuffer, buffer, len); + rbuffer_size = len; + } + } + } +} + +// }}} + +// {{{ packet match functions + +static int match_ServerHello(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 2; +} + +static int match_ServerCertificate(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 11; +} + +static int match_ServerKeyExchange(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 12; +} + +static int match_ServerCertificateRequest(const unsigned char *buffer, + size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 13; +} + +static int match_ServerHelloDone(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 14; +} + +static int match_ClientCertificate(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 11; +} + +static int match_ClientKeyExchange(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 16; +} + +static int match_ClientCertificateVerify(const unsigned char *buffer, + size_t len) +{ + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 + && buffer[13] == 15; +} + +static int match_ClientChangeCipherSpec(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 && buffer[0] == 20; +} + +static int match_ClientFinished(const unsigned char *buffer, size_t len) +{ + return role == CLIENT && len >= 13 && buffer[0] == 22 && buffer[4] == 1; +} + +static int match_ServerChangeCipherSpec(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 && buffer[0] == 20; +} + +static int match_ServerFinished(const unsigned char *buffer, size_t len) +{ + return role == SERVER && len >= 13 && buffer[0] == 22 && buffer[4] == 1; +} + +// }}} + +// {{{ packet drop filters + +#define FILTER_DROP_COUNT 3 +#define DECLARE_FILTER(packet) \ + static void filter_packet_##packet(gnutls_transport_ptr_t fd, \ + const unsigned char* buffer, size_t len) \ + { \ + if (match_##packet(buffer, len) && (state_packet_##packet).count++ < FILTER_DROP_COUNT) { \ + drop(#packet); \ + } else { \ + filter_run_next(fd, buffer, len); \ + } \ + } + +DECLARE_FILTER(ServerHello) + DECLARE_FILTER(ServerCertificate) + DECLARE_FILTER(ServerKeyExchange) + DECLARE_FILTER(ServerCertificateRequest) + DECLARE_FILTER(ServerHelloDone) + DECLARE_FILTER(ClientCertificate) + DECLARE_FILTER(ClientKeyExchange) + DECLARE_FILTER(ClientCertificateVerify) + DECLARE_FILTER(ClientChangeCipherSpec) + DECLARE_FILTER(ClientFinished) + DECLARE_FILTER(ServerChangeCipherSpec) + DECLARE_FILTER(ServerFinished) +// }}} +// {{{ flight permutation filters +static void filter_permute_state_run(filter_permute_state_t * state, + int packetCount, + gnutls_transport_ptr_t fd, + const unsigned char *buffer, size_t len) +{ + unsigned char *data; + int packet = state->order[state->count]; + + if (debug > 2) + log("running permutation for %s/%d/%d\n", state->name, packetCount, state->count); + + data = malloc(len); + assert(data); + memcpy(data, buffer, len); + state->packets[packet].data = data; + state->packets[packet].size = len; + state->count++; + + if (state->count == packetCount) { + for (packet = 0; packet < packetCount; packet++) { + filter_run_next(fd, state->packets[packet].data, + state->packets[packet].size); + } + filter_permute_state_free_buffer(state); + state->count = 0; + } +} + +#define DECLARE_PERMUTE(flight) \ + static void filter_permute_##flight(gnutls_transport_ptr_t fd, \ + const unsigned char* buffer, size_t len) \ + { \ + int count = sizeof(permute_match_##flight) / sizeof(permute_match_##flight[0]); \ + int i; \ + for (i = 0; i < count; i++) { \ + if (permute_match_##flight[i](buffer, len)) { \ + filter_permute_state_run(&state_permute_##flight, count, fd, buffer, len); \ + return; \ + } \ + } \ + filter_run_next(fd, buffer, len); \ + } + +static match_fn permute_match_ServerHello[] = + { match_ServerHello, match_ServerKeyExchange, match_ServerHelloDone }; + +static match_fn permute_match_ServerHelloFull[] = + { match_ServerHello, match_ServerCertificate, match_ServerKeyExchange, + match_ServerCertificateRequest, match_ServerHelloDone +}; + +static match_fn permute_match_ServerFinished[] = + { match_ServerChangeCipherSpec, match_ServerFinished }; + +static match_fn permute_match_ServerFinishedResume[] = + { match_ServerHello, match_ServerChangeCipherSpec, match_ServerFinished }; + +static match_fn permute_match_ClientFinished[] = + { match_ClientKeyExchange, match_ClientChangeCipherSpec, + match_ClientFinished +}; + +static match_fn permute_match_ClientFinishedResume[] = + { match_ClientChangeCipherSpec, match_ClientFinished +}; + +static match_fn permute_match_ClientFinishedFull[] = + { match_ClientCertificate, match_ClientKeyExchange, + match_ClientCertificateVerify, match_ClientChangeCipherSpec, + match_ClientFinished +}; + +DECLARE_PERMUTE(ServerHello) + DECLARE_PERMUTE(ServerHelloFull) + DECLARE_PERMUTE(ServerFinishedResume) + DECLARE_PERMUTE(ServerFinished) + DECLARE_PERMUTE(ClientFinished) + DECLARE_PERMUTE(ClientFinishedResume) + DECLARE_PERMUTE(ClientFinishedFull) +// }}} +// {{{ emergency deadlock resolution time bomb +timer_t killtimer_tid = 0; + +static void killtimer_set(void) +{ + struct sigevent sig; + struct itimerspec tout = { {0, 0}, {2 * timeout_seconds, 0} }; + + if (killtimer_tid != 0) { + timer_delete(killtimer_tid); + } + + memset(&sig, 0, sizeof(sig)); + sig.sigev_notify = SIGEV_SIGNAL; + sig.sigev_signo = 15; + if (timer_create(CLOCK_MONOTONIC, &sig, &killtimer_tid) < 0) { + rperror("timer_create"); + exit(3); + } + + timer_settime(killtimer_tid, 0, &tout, 0); +} + +// }}} + +// {{{ actual gnutls operations + +gnutls_certificate_credentials_t cred; +gnutls_session_t session; + +static ssize_t writefn(gnutls_transport_ptr_t fd, const void *buffer, + size_t len) +{ + filter_run_next(fd, (const unsigned char *)buffer, len); + return len; +} + +static void await(int fd, int timeout) +{ + if (nonblock) { + struct pollfd p = { fd, POLLIN, 0 }; + if (poll(&p, 1, timeout) < 0 && errno != EAGAIN + && errno != EINTR) { + rperror("poll"); + exit(3); + } + } +} + +static void cred_init(void) +{ + assert(gnutls_certificate_allocate_credentials(&cred)>=0); + + gnutls_certificate_set_x509_key_mem(cred, &cli_ca3_cert, &cli_ca3_key, + GNUTLS_X509_FMT_PEM); +} + +static void session_init(int sock, int server) +{ + gnutls_init(&session, + GNUTLS_DATAGRAM | (server ? GNUTLS_SERVER : GNUTLS_CLIENT) + | GNUTLS_NONBLOCK * nonblock); + gnutls_priority_set_direct(session, + "NORMAL:+ECDHE-RSA:+ANON-ECDH", + 0); + gnutls_transport_set_int(session, sock); + + if (full) { + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); + if (server) { + gnutls_certificate_server_set_request(session, + GNUTLS_CERT_REQUIRE); + } + } else if (server) { + gnutls_anon_server_credentials_t acred; + assert(gnutls_anon_allocate_server_credentials(&acred)>=0); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred); + } else { + gnutls_anon_client_credentials_t acred; + assert(gnutls_anon_allocate_client_credentials(&acred)>=0); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred); + } + + gnutls_dtls_set_mtu(session, 1400); + gnutls_dtls_set_timeouts(session, retransmit_milliseconds, + timeout_seconds * 1000); +} + +static void client(int sock) +{ + int err = 0; + time_t started = time(0); + const char *line = "foobar!"; + char buffer[8192]; + int len, ret; + gnutls_datum_t data = {NULL, 0}; + + session_init(sock, 0); + + killtimer_set(); + + if (resume) { + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + ret = gnutls_session_get_data2(session, &data); + if (ret < 0) { + exit(1); + } + gnutls_deinit(session); + + session_init(sock, 0); + gnutls_session_set_data(session, data.data, data.size); + gnutls_free(data.data); + data.data = NULL; + + if (debug) { + fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name); + } + } + + gnutls_transport_set_push_function(session, writefn); + + killtimer_set(); + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + if (debug) { + fprintf(stdout, "%i %s| handshake complete\n", run_id, role_name); + } + + if (resume) { + killtimer_set(); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + die_on_error(gnutls_record_send(session, buffer, len)); + + log("sent data\n"); + exit(0); + + } else { + killtimer_set(); + die_on_error(gnutls_record_send(session, line, strlen(line))); + + log("sent data\n"); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + if (len > 0 && strncmp(line, buffer, len) == 0) { + exit(0); + } else { + exit(1); + } + } + +} + +static gnutls_datum_t saved_data = {NULL, 0}; + +static gnutls_datum_t db_fetch(void *dbf, gnutls_datum_t key) +{ + gnutls_datum_t t = {NULL, 0}; + t.data = malloc(saved_data.size); + if (t.data == NULL) + return t; + memcpy(t.data, saved_data.data, saved_data.size); + t.size = saved_data.size; + + return t; +} + +static int db_delete(void *dbf, gnutls_datum_t key) +{ + return 0; +} + +static int db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data) +{ + saved_data.data = malloc(data.size); + if (saved_data.data == NULL) + return -1; + memcpy(saved_data.data, data.data, data.size); + saved_data.size = data.size; + return 0; +} + +static void server(int sock) +{ + int err; + const char *line = "server foobar!"; + time_t started = time(0); + char buffer[8192]; + int len; + + session_init(sock, 1); + + await(sock, -1); + + killtimer_set(); + if (resume) { + gnutls_db_set_retrieve_function(session, db_fetch); + gnutls_db_set_store_function(session, db_store); + gnutls_db_set_remove_function(session, db_delete); + gnutls_db_set_ptr(session, NULL); + + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + gnutls_deinit(session); + + session_init(sock, 1); + gnutls_db_set_retrieve_function(session, db_fetch); + gnutls_db_set_store_function(session, db_store); + gnutls_db_set_remove_function(session, db_delete); + gnutls_db_set_ptr(session, NULL); + + if (debug) { + fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name); + } + } + + gnutls_transport_set_push_function(session, writefn); + + await(sock, -1); + + killtimer_set(); + do { + err = process_error(gnutls_handshake(session)); + if (err != 0) { + int t = gnutls_dtls_get_timeout(session); + await(sock, t ? t : 100); + } + } while (err != 0); + process_error_or_timeout(err, time(0) - started); + + log("handshake complete\n"); + + if (resume) { + free(saved_data.data); + saved_data.data = NULL; + } + + if (resume) { + killtimer_set(); + die_on_error(gnutls_record_send(session, line, strlen(line))); + + log("sent data\n"); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + if (len > 0 && strncmp(line, buffer, len) == 0) { + exit(0); + } else { + exit(1); + } + } else { + killtimer_set(); + + do { + await(sock, -1); + len = + process_error(gnutls_record_recv + (session, buffer, sizeof(buffer))); + } while (len < 0); + + log("received data\n"); + + die_on_error(gnutls_record_send(session, buffer, len)); + + log("sent data\n"); + } + + exit(0); +} + +// }}} + +// {{{ test running/handling itself + +#if 0 +static void udp_sockpair(int *socks) +{ + struct sockaddr_in6 sa = + { AF_INET6, htons(30000), 0, in6addr_loopback, 0 }; + struct sockaddr_in6 sb = + { AF_INET6, htons(20000), 0, in6addr_loopback, 0 }; + + socks[0] = socket(AF_INET6, SOCK_DGRAM, 0); + socks[1] = socket(AF_INET6, SOCK_DGRAM, 0); + + bind(socks[0], (struct sockaddr *)&sa, sizeof(sa)); + bind(socks[1], (struct sockaddr *)&sb, sizeof(sb)); + + connect(socks[1], (struct sockaddr *)&sa, sizeof(sa)); + connect(socks[0], (struct sockaddr *)&sb, sizeof(sb)); +} +#endif + +static int run_test(void) +{ + int fds[2]; + int pid1, pid2; + int status2; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + rperror("socketpair"); + exit(2); + } + + if (nonblock) { + fcntl(fds[0], F_SETFL, (long)O_NONBLOCK); + fcntl(fds[1], F_SETFL, (long)O_NONBLOCK); + } + + if (!(pid1 = fork())) { + role = SERVER; + server(fds[1]); // noreturn + } else if (pid1 < 0) { + rperror("fork server"); + exit(2); + } + if (!(pid2 = fork())) { + role = CLIENT; + client(fds[0]); // noreturn + } else if (pid2 < 0) { + rperror("fork client"); + exit(2); + } + while (waitpid(pid2, &status2, 0) < 0 && errno == EINTR) ; + kill(pid1, 15); + while (waitpid(pid1, 0, 0) < 0 && errno == EINTR) ; + + close(fds[0]); + close(fds[1]); + + if (!WIFSIGNALED(status2) && WEXITSTATUS(status2) != 3) { + return ! !WEXITSTATUS(status2); + } else { + return 3; + } +} + +static filter_fn filters[] + = { filter_packet_ServerHello, + filter_packet_ServerKeyExchange, + filter_packet_ServerHelloDone, + filter_packet_ClientKeyExchange, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished +}; + +static filter_fn filters_resume[] + = { filter_packet_ServerHello, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished +}; + +static filter_fn filters_full[] + = { filter_packet_ServerHello, + filter_packet_ServerCertificate, + filter_packet_ServerKeyExchange, + filter_packet_ServerCertificateRequest, + filter_packet_ServerHelloDone, + filter_packet_ClientCertificate, + filter_packet_ClientKeyExchange, + filter_packet_ClientCertificateVerify, + filter_packet_ClientChangeCipherSpec, + filter_packet_ClientFinished, + filter_packet_ServerChangeCipherSpec, + filter_packet_ServerFinished +}; + +static int run_one_test(int dropMode, int serverFinishedPermute, + int serverHelloPermute, int clientFinishedPermute) +{ + int fnIdx = 0; + int res, filterIdx; + filter_fn *local_filters; + const char **local_filter_names; + const char **client_finished_permutation_names; + const char **server_finished_permutation_names; + const char **server_hello_permutation_names; + int filter_count; + + if (full) { + local_filters = filters_full; + local_filter_names = filter_names_full; + filter_count = sizeof(filters_full)/sizeof(filters_full[0]); + client_finished_permutation_names = permutation_names5; + server_finished_permutation_names = permutation_names2; + server_hello_permutation_names = permutation_names5; + } else if (resume) { + local_filters = filters_resume; + local_filter_names = filter_names_resume; + filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]); + client_finished_permutation_names = permutation_names2; + server_finished_permutation_names = permutation_names3; + server_hello_permutation_names = NULL; + } else { + local_filters = filters; + local_filter_names = filter_names; + filter_count = sizeof(filters)/sizeof(filters[0]); + client_finished_permutation_names = permutation_names3; + server_finished_permutation_names = permutation_names2; + server_hello_permutation_names = permutation_names3; + } + + run_id = + ((dropMode * 2 + serverFinishedPermute) * (full ? 120 : 6) + + serverHelloPermute) * (full ? 120 : 6) + clientFinishedPermute; + + filter_clear_state(); + + if (full) { + filter_chain[fnIdx++] = filter_permute_ServerHelloFull; + state_permute_ServerHelloFull.order = + permutations5[serverHelloPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinishedFull; + state_permute_ClientFinishedFull.order = + permutations5[clientFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ServerFinished; + state_permute_ServerFinished.order = + permutations2[serverFinishedPermute]; + } else if (resume) { + filter_chain[fnIdx++] = filter_permute_ServerFinishedResume; + state_permute_ServerFinishedResume.order = + permutations3[serverFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinishedResume; + state_permute_ClientFinishedResume.order = + permutations2[clientFinishedPermute]; + } else { + filter_chain[fnIdx++] = filter_permute_ServerHello; + state_permute_ServerHello.order = + permutations3[serverHelloPermute]; + + filter_chain[fnIdx++] = filter_permute_ClientFinished; + state_permute_ClientFinished.order = + permutations3[clientFinishedPermute]; + + filter_chain[fnIdx++] = filter_permute_ServerFinished; + state_permute_ServerFinished.order = + permutations2[serverFinishedPermute]; + } + + if (dropMode) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { + if (dropMode & (1 << filterIdx)) { + filter_chain[fnIdx++] = + local_filters[filterIdx]; + } + } + } + filter_chain[fnIdx++] = NULL; + + res = run_test(); + + switch (res) { + case 0: + fprintf(stdout, "%i ++ ", run_id); + break; + case 1: + fprintf(stdout, "%i -- ", run_id); + break; + case 2: + fprintf(stdout, "%i !! ", run_id); + break; + case 3: + fprintf(stdout, "%i TT ", run_id); + break; + } + + if (!resume) + fprintf(stdout, "SHello(%s), ", server_hello_permutation_names[serverHelloPermute]); + fprintf(stdout, "SFinished(%s), ", + server_finished_permutation_names[serverFinishedPermute]); + fprintf(stdout, "CFinished(%s) :- ", + client_finished_permutation_names[clientFinishedPermute]); + if (dropMode) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { + if (dropMode & (1 << filterIdx)) { + if (dropMode & ((1 << filterIdx) - 1)) { + fprintf(stdout, ", "); + } + fprintf(stdout, "%s", + local_filter_names[filterIdx]); + } + } + } + fprintf(stdout, "\n"); + + return res; +} + +static int run_test_by_id(int id) +{ + int pscale = full ? 120 : 6; + int dropMode, serverFinishedPermute, serverHelloPermute, + clientFinishedPermute; + + clientFinishedPermute = id % pscale; + id /= pscale; + + serverHelloPermute = id % pscale; + id /= pscale; + + serverFinishedPermute = id % 2; + id /= 2; + + dropMode = id; + + return run_one_test(dropMode, serverFinishedPermute, + serverHelloPermute, clientFinishedPermute); +} + +int *job_pids; +int job_limit; +int children = 0; + +static void register_child(int pid) +{ + int idx; + + children++; + for (idx = 0; idx < job_limit; idx++) { + if (job_pids[idx] == 0) { + job_pids[idx] = pid; + return; + } + } +} + +static int wait_children(int child_limit) +{ + int fail = 0; + int result = 1; + + while (children > child_limit) { + int status; + int idx; + int pid = waitpid(0, &status, 0); + if (pid < 0 && errno == ECHILD) { + break; + } + for (idx = 0; idx < job_limit; idx++) { + if (job_pids[idx] == pid) { + children--; + if (WEXITSTATUS(status)) { + result = 1; + if (!run_to_end && !fail) { + fprintf(stderr, + "One test failed, waiting for remaining tests\n"); + fail = 1; + child_limit = 0; + } + } + job_pids[idx] = 0; + break; + } + } + } + + if (fail) { + exit(1); + } + + return result; +} + +static int run_tests_from_id_list(int childcount) +{ + int test_id; + int ret; + int result = 0; + + while ((ret = fscanf(stdin, "%i\n", &test_id)) > 0) { + int pid; + if (test_id < 0 + || test_id > + 2 * (full ? 120 * 120 * (1 << 12) : 6 * 6 * 256)) { + fprintf(stderr, "Invalid test id %i\n", test_id); + break; + } + if (!(pid = fork())) { + exit(run_test_by_id(test_id)); + } else if (pid < 0) { + rperror("fork"); + result = 4; + break; + } else { + register_child(pid); + result |= wait_children(childcount); + } + } + + if (ret < 0 && ret != EOF) { + fprintf(stderr, "Error reading test id list\n"); + } + + result |= wait_children(0); + + return result; +} + +static int run_all_tests(int childcount) +{ + int dropMode, serverFinishedPermute, serverHelloPermute, + clientFinishedPermute; + int result = 0; + + for (dropMode = 0; dropMode != 1 << (full ? 12 : 8); dropMode++) + for (serverFinishedPermute = 0; serverFinishedPermute < 2; + serverFinishedPermute++) + for (serverHelloPermute = 0; + serverHelloPermute < (full ? 120 : 6); + serverHelloPermute++) + for (clientFinishedPermute = 0; + clientFinishedPermute < + (full ? 120 : 6); + clientFinishedPermute++) { + int pid; + if (!(pid = fork())) { + exit(run_one_test + (dropMode, + serverFinishedPermute, + serverHelloPermute, + clientFinishedPermute)); + } else if (pid < 0) { + rperror("fork"); + result = 4; + break; + } else { + register_child(pid); + result |= + wait_children(childcount); + } + } + + result |= wait_children(0); + + return result; +} + +// }}} + +static int parse_permutation(const char *arg, const char *permutations[], + int *val) +{ + *val = 0; + while (permutations[*val]) { + if (strcmp(permutations[*val], arg) == 0) { + return 1; + } else { + *val += 1; + } + } + return 0; +} + +int main(int argc, const char *argv[]) +{ + int dropMode = 0; + int serverFinishedPermute = 0; + int serverHelloPermute = 0; + int clientFinishedPermute = 0; + int batch = 0; + unsigned single = 0; + int arg; + + nonblock = 0; + replay = 0; + debug = 0; + timeout_seconds = 120; + retransmit_milliseconds = 100; + full = 0; + run_to_end = 1; + job_limit = 1; + +#define NEXT_ARG(name) \ + do { \ + if (++arg >= argc) { \ + fprintf(stderr, "No argument for -" #name "\n"); \ + exit(8); \ + } \ + } while (0); +#define FAIL_ARG(name) \ + do { \ + fprintf(stderr, "Invalid argument for -" #name "\n"); \ + exit(8); \ + } while (0); + + for (arg = 1; arg < argc; arg++) { + if (strcmp("-die", argv[arg]) == 0) { + run_to_end = 0; + } else if (strcmp("-batch", argv[arg]) == 0) { + batch = 1; + } else if (strcmp("-d", argv[arg]) == 0) { + char *end; + int level; + + if (arg+1 < argc) { + level = strtol(argv[arg + 1], &end, 10); + if (*end == '\0') { + debug = level; + arg++; + } else + debug++; + } else { + debug++; + } + } else if (strcmp("-nb", argv[arg]) == 0) { + nonblock = 1; + } else if (strcmp("-r", argv[arg]) == 0) { + replay = 1; + } else if (strcmp("-timeout", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(timeout); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + timeout_seconds = val; + } else { + FAIL_ARG(timeout); + } + } else if (strcmp("-retransmit", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(retransmit); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + retransmit_milliseconds = val; + } else { + FAIL_ARG(retransmit); + } + } else if (strcmp("-j", argv[arg]) == 0) { + char *end; + int val; + + NEXT_ARG(timeout); + val = strtol(argv[arg], &end, 10); + if (*end == '\0') { + job_limit = val; + } else { + FAIL_ARG(j); + } + } else if (strcmp("-full", argv[arg]) == 0) { + if (resume) { + fprintf(stderr, "You cannot combine full with resume\n"); + exit(1); + } + + full = 1; + } else if (strcmp("-resume", argv[arg]) == 0) { + if (full) { + fprintf(stderr, "You cannot combine full with resume\n"); + exit(1); + } + + resume = 1; + } else if (strcmp("-shello", argv[arg]) == 0) { + if (resume) { + fprintf(stderr, "Please use -sfinished instead of -shello\n"); + exit(1); + } + + NEXT_ARG(shello); + if (!parse_permutation + (argv[arg], + full ? permutation_names5 : + permutation_names3, &serverHelloPermute)) { + FAIL_ARG(shell); + } + single++; + } else if (strcmp("-sfinished", argv[arg]) == 0) { + const char **pname; + NEXT_ARG(cfinished); + if (resume) pname = permutation_names3; + else pname = permutation_names2; + if (!parse_permutation + (argv[arg], pname, + &serverFinishedPermute)) { + FAIL_ARG(cfinished); + } + single++; + } else if (strcmp("-cfinished", argv[arg]) == 0) { + const char **pname; + NEXT_ARG(cfinished); + if (full) pname = permutation_names5; + else if (resume) pname = permutation_names2; + else pname = permutation_names3; + if (!parse_permutation + (argv[arg], pname, + &clientFinishedPermute)) { + FAIL_ARG(cfinished); + } + single++; + } else { + int drop; + int filter_count; + const char **local_filter_names; + + if (full) { + local_filter_names = filter_names_full; + filter_count = sizeof(filters_full)/sizeof(filters_full[0]); + } else if (resume) { + local_filter_names = filter_names_resume; + filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]); + } else { + local_filter_names = filter_names; + filter_count = sizeof(filters)/sizeof(filters[0]); + } + + for (drop = 0; drop < filter_count; drop++) { + if (strcmp + (local_filter_names[drop], + argv[arg]) == 0) { + dropMode |= (1 << drop); + break; + } + } + if (drop == filter_count) { + fprintf(stderr, "Unknown packet %s\n", + argv[arg]); + exit(8); + } + single++; + } + } + + setlinebuf(stdout); + global_init(); + cred_init(); + gnutls_global_set_log_function(logfn); + gnutls_global_set_audit_log_function(auditfn); + gnutls_global_set_log_level(debug); + + if (single) { + if (debug) + fprintf(stderr, "single test mode\n"); + return run_one_test(dropMode, serverFinishedPermute, + serverHelloPermute, clientFinishedPermute); + } else { + if (debug) + fprintf(stderr, "multi test mode\n"); + + if (resume) { + fprintf(stderr, "full run not implemented yet for resumed runs\n"); + exit(5); + } + + job_pids = calloc(sizeof(int), job_limit); + if (batch) { + return run_tests_from_id_list(job_limit); + } else { + return run_all_tests(job_limit); + } + } +} + +// vim: foldmethod=marker + +#else /* NO POSIX TIMERS */ + +int main(int argc, const char *argv[]) +{ + exit(77); +} + +#endif |