diff options
Diffstat (limited to 'tests/dtls/dtls-stress.c')
-rw-r--r-- | tests/dtls/dtls-stress.c | 1225 |
1 files changed, 781 insertions, 444 deletions
diff --git a/tests/dtls/dtls-stress.c b/tests/dtls/dtls-stress.c index b7be2315e2..4daf934610 100644 --- a/tests/dtls/dtls-stress.c +++ b/tests/dtls/dtls-stress.c @@ -18,7 +18,70 @@ * 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 + * -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 + * -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 + * + * + * **** 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 <gnutls/gnutls.h> +#include <gnutls/openpgp.h> #include <gnutls/dtls.h> #include <unistd.h> #include <sys/socket.h> @@ -33,102 +96,293 @@ #include <time.h> #include <wait.h> -enum role { - SERVER, - CLIENT -} role; +// {{{ types + +typedef struct { + int count; +} filter_packet_state_t; + +typedef struct { + 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_full[12] + = { "SHello", + "SCertificate", + "SKeyExchange", + "SCertificateRequest", + "SHelloDone", + "CCertificate", + "CKeyExchange", + "CCertificateVerify", + "CChangeCipherSpec", + "CFinished", + "SChangeCipherSpec", + "SFinished" }; + +static const unsigned char PUBKEY[] = + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "mI0ETz0XRAEEAKXSU/tg2yGvoKf/r1pdzj7dnfPHeS+BRiT34763uUhibAbTgMkp\n" + "v44OlBPiAaZ54uuXVkz8e4pgvrBgQwIRtNp3xPaWF1CfC4F+V4LdZV8l8IG+AfES\n" + "K0GbfUS4q8vjnPJ0TyxnXE2KtbcRdzZzWBshJ8KChKwbH2vvrMrlmEeZABEBAAG0\n" + "CHRlc3Qga2V5iLgEEwECACIFAk89F0QCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B\n" + "AheAAAoJEMNjhmkfkLY9J/YD+wYZ2BD/0/c5gkkDP2NlVvrLGyFmEwQcR7DcaQYB\n" + "P3/Teq2gnscZ5Xm/z1qgGEpwmaVfVHY8mfEj8bYI8jAu0v1C1jCtJPUTmxf9tmkZ\n" + "QYFNR8T+F5Xae2XseOH70lSN/AEiW02BEBFlGBx0a3T30muFfqi/KawaE7KKn2e4\n" + "uNWvuI0ETz0XRAEEAKgZExsb7Lf9P3DmwJSvNVdkGVny7wr4/M1s0CDX20NkO7Y1\n" + "Ao9g+qFo5MlCOEuzjVaEYmM+rro7qyxmDKsaNIzZF1VN5UeYgPFyLcBK7C+QwUqw\n" + "1PUl/w4dFq8neQyqIPUVGRwQPlwpkkabRPNT3t/7KgDJvYzV9uu+cXCyfqErABEB\n" + "AAGInwQYAQIACQUCTz0XRAIbDAAKCRDDY4ZpH5C2PTBtBACVsR6l4HtuzQb5WFQt\n" + "sD/lQEk6BEY9aVfK957Oj+A4alGEGObToqVJFo/nq+P7aWExIXucJQRL8lYnC7u+\n" + "GjPVCun5TYzKMiryxHPkQr9NBx4hh8JjkDCc8nAgI3il49uPYkmsv70CgqJFFtT8\n" + "NfM+8fS537I+XA+hfjt20NUFIA==\n" + "=oD3a\n" + "-----END PGP PUBLIC KEY BLOCK-----\n"; + +static const unsigned char PRIVKEY[] = + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + "\n" + "lQHYBE89F0QBBACl0lP7YNshr6Cn/69aXc4+3Z3zx3kvgUYk9+O+t7lIYmwG04DJ\n" + "Kb+ODpQT4gGmeeLrl1ZM/HuKYL6wYEMCEbTad8T2lhdQnwuBfleC3WVfJfCBvgHx\n" + "EitBm31EuKvL45zydE8sZ1xNirW3EXc2c1gbISfCgoSsGx9r76zK5ZhHmQARAQAB\n" + "AAP6A6VhRVi22MHE1YzQrTr8yvMSgwayynGcOjndHxdpEodferLx1Pp/BL+bT+ib\n" + "Qq7RZ363Xg/7I2rHJpenQYdkI5SI4KrXIV57p8G+isyTtsxU38SY84WoB5os8sfT\n" + "YhxG+edoTfDzXkRSWFB8EUjRaLa2b//nvLpxNRyqDSzzUxECAMtEnL5H/8gHbpZf\n" + "D98TSJVxdAl9rBAQaVMgrFgcU/IlmxCyVEh9eh/P261tefgOnyVcGFYHxdZvJ3td\n" + "miM+DNUCANDW1S9t7IiqflDpQIS2wGTZ/rLKPoE1F3285EaYAd0FQUq0O4/Nu31D\n" + "5pz/S7D+PfXn9oEZH3Dvl3EVIDyq4bUB+QEzFc3BsH2uueD3g42RoBfMGl6m3LI9\n" + "yWOnrUmIW+h9Fu8W9mcU6y82Q1G7OPIxA1me/Qtzo20lGQa8jAyzLhuit7QIdGVz\n" + "dCBrZXmIuAQTAQIAIgUCTz0XRAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA\n" + "CgkQw2OGaR+Qtj0n9gP7BhnYEP/T9zmCSQM/Y2VW+ssbIWYTBBxHsNxpBgE/f9N6\n" + "raCexxnleb/PWqAYSnCZpV9UdjyZ8SPxtgjyMC7S/ULWMK0k9RObF/22aRlBgU1H\n" + "xP4Xldp7Zex44fvSVI38ASJbTYEQEWUYHHRrdPfSa4V+qL8prBoTsoqfZ7i41a+d\n" + "AdgETz0XRAEEAKgZExsb7Lf9P3DmwJSvNVdkGVny7wr4/M1s0CDX20NkO7Y1Ao9g\n" + "+qFo5MlCOEuzjVaEYmM+rro7qyxmDKsaNIzZF1VN5UeYgPFyLcBK7C+QwUqw1PUl\n" + "/w4dFq8neQyqIPUVGRwQPlwpkkabRPNT3t/7KgDJvYzV9uu+cXCyfqErABEBAAEA\n" + "A/4wX+brqkGZQTv8lateHn3PRHM3O34nPjgiNeo/SV9EKZg1e1PdRx9ZTAJrGK9y\n" + "uZ03BKn7vZIy7fD4ufVzV/s/BaypVmvwjZud8fdMgsMQAJYtoMhozbOtUelCFpja\n" + "I1xAbDBx1PAAbS8Sh022/0jvOGnZhvkgZMG90z7AEANUYQIAwzywU087TcJk8Bzd\n" + "37JGWyE4f3iYFGA+r8BoIOrxvvgfUHKxdhG0gaT8SDeRAwNY6D43dCBZkG7Uel1F\n" + "x9MlLQIA3Goaz58hEN0fdm4TM7A8crtMB+f8/h87EneBgMl+Yj/3sklhyahR6Itm\n" + "lGuAAGTAOmD7i8OmS/a1ac5MtHAGtwH6A0B5GjaL8VnLQo4vFnuR7JuCQaLqGadV\n" + "mBmKxVHElduLf/VauBQPD5KZA+egpg+laJ4JLVXMmKIZGqRzopcIWZnKiJ8EGAEC\n" + "AAkFAk89F0QCGwwACgkQw2OGaR+Qtj0wbQQAlbEepeB7bs0G+VhULbA/5UBJOgRG\n" + "PWlXyveezo/gOGpRhBjm06KlSRaP56vj+2lhMSF7nCUES/JWJwu7vhoz1Qrp+U2M\n" + "yjIq8sRz5EK/TQceIYfCY5AwnPJwICN4pePbj2JJrL+9AoKiRRbU/DXzPvH0ud+y\n" + "PlwPoX47dtDVBSA=\n" + "=EVlv\n" + "-----END PGP PRIVATE KEY BLOCK-----\n"; + +// }}} + +// {{{ other global state + +enum role role; + +#define role_name (role == SERVER ? "server" : "client") int debug; int nonblock; +int full; +int timeout_seconds; +int retransmit_milliseconds; +int run_to_end; int run_id; -static const char* role_to_name(enum role role) -{ - if (role == SERVER) { - return "server"; - } else { - return "client"; - } -} +// }}} + +// {{{ logging and error handling static void logfn(int level, const char* s) { if (debug) { - fprintf(stdout, "%i %s|<%i> %s", run_id, role_to_name(role), level, s); + 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_to_name(role), s); + fprintf(stdout, "%i %s| %s", run_id, role_name, s); } } static void drop(const char* packet) { if (debug) { - fprintf(stdout, "%i %s| dropping %s\n", run_id, role_to_name(role), packet); + fprintf(stdout, "%i %s| dropping %s\n", run_id, role_name, 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; +} -typedef struct { - int count; -} filter_packet_state_t; +#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 { + fprintf(stdout, "%i %s| line %i: {spurious timeout} (fatal)", run_id, role_name, 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: %s\n", run_id, role_name, name, strerror(errno)); +} + +// }}} + +// {{{ 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_ServerChangeCipherSpec = { 0 }; filter_packet_state_t state_packet_ServerFinished = { 0 }; -typedef struct { - gnutls_datum_t packets[3]; - int* order; - int count; -} filter_permute_state_t; - -filter_permute_state_t state_permute_ServerHello = { { { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; -filter_permute_state_t state_permute_ServerFinished = { { { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; -filter_permute_state_t state_permute_ClientFinished = { { { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; - -typedef void (*filter_fn)(gnutls_transport_ptr_t, const unsigned char*, size_t); +filter_permute_state_t state_permute_ServerHello = { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; +filter_permute_state_t state_permute_ServerHelloFull = { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; +filter_permute_state_t state_permute_ServerFinished = { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; +filter_permute_state_t state_permute_ClientFinished = { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 }; +filter_permute_state_t state_permute_ClientFinishedFull = { { { 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) { -int i; + 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_ClientFinished); + 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_ServerChangeCipherSpec, 0, sizeof(state_packet_ServerChangeCipherSpec)); memset(&state_packet_ServerFinished, 0, sizeof(state_packet_ServerFinished)); - - for (i = 0; i < 3; i++) { - if (state_permute_ServerHello.packets[i].data) { - free(state_permute_ServerHello.packets[i].data); - } - if (state_permute_ServerFinished.packets[i].data) { - free(state_permute_ServerFinished.packets[i].data); - } - if (state_permute_ClientFinished.packets[i].data) { - free(state_permute_ClientFinished.packets[i].data); - } - } - 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_ClientFinishedFull, 0, sizeof(state_permute_ClientFinishedFull)); } static void filter_run_next(gnutls_transport_ptr_t fd, @@ -139,26 +393,23 @@ static void filter_run_next(gnutls_transport_ptr_t fd, if (fn) { fn(fd, buffer, len); } else { - send((long int) fd, buffer, len, 0); + send((intptr_t) fd, buffer, len, 0); } filter_current_idx--; } +// }}} +// {{{ 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 void filter_packet_ServerHello(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) +static int match_ServerCertificate(const unsigned char* buffer, size_t len) { - if (match_ServerHello(buffer, len) && state_packet_ServerHello.count++ < 3) { - drop("Server Hello"); - } else { - filter_run_next(fd, buffer, len); - } + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 11; } static int match_ServerKeyExchange(const unsigned char* buffer, size_t len) @@ -166,14 +417,9 @@ static int match_ServerKeyExchange(const unsigned char* buffer, size_t len) return role == SERVER && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 12; } -static void filter_packet_ServerKeyExchange(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) +static int match_ServerCertificateRequest(const unsigned char* buffer, size_t len) { - if (match_ServerKeyExchange(buffer, len) && state_packet_ServerKeyExchange.count++ < 3) { - drop("Server Key Exchange"); - } else { - filter_run_next(fd, buffer, len); - } + return role == SERVER && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 13; } static int match_ServerHelloDone(const unsigned char* buffer, size_t len) @@ -181,116 +427,75 @@ static int match_ServerHelloDone(const unsigned char* buffer, size_t len) return role == SERVER && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 14; } -static -void filter_packet_ServerHelloDone(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) +static int match_ClientCertificate(const unsigned char* buffer, size_t len) { - if (match_ServerHelloDone(buffer, len) && state_packet_ServerHelloDone.count++ < 3) { - drop("Server Hello Done"); - } else { - filter_run_next(fd, buffer, len); - } + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 11; } -static -int match_ClientKeyExchange(const unsigned char* buffer, size_t len) +static int match_ClientKeyExchange(const unsigned char* buffer, size_t len) { return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 16; } -static -void filter_packet_ClientKeyExchange(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) +static int match_ClientCertificateVerify(const unsigned char* buffer, size_t len) { - if (match_ClientKeyExchange(buffer, len) && state_packet_ClientKeyExchange.count++ < 3) { - drop("Client Key Exchange"); - } else { - filter_run_next(fd, buffer, len); - } + return role == CLIENT && len >= 13 + 1 && buffer[0] == 22 && buffer[13] == 15; } -static -int match_ClientChangeCipherSpec(const unsigned char* buffer, size_t len) +static int match_ClientChangeCipherSpec(const unsigned char* buffer, size_t len) { return role == CLIENT && len >= 13 && buffer[0] == 20; } -static -void filter_packet_ClientChangeCipherSpec(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ClientChangeCipherSpec(buffer, len) && state_packet_ClientChangeCipherSpec.count++ < 3) { - drop("Client Change Cipher Spec"); - } else { - filter_run_next(fd, buffer, len); - } -} - -static -int match_ClientFinished(const unsigned char* buffer, size_t len) +static int match_ClientFinished(const unsigned char* buffer, size_t len) { return role == CLIENT && len >= 13 && buffer[0] == 22 && buffer[4] == 1; } -static -void filter_packet_ClientFinished(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ClientFinished(buffer, len) && state_packet_ClientFinished.count++ < 3) { - drop("Client Finished"); - } else { - filter_run_next(fd, buffer, len); - } -} - -static -int match_ServerChangeCipherSpec(const unsigned char* buffer, size_t len) +static int match_ServerChangeCipherSpec(const unsigned char* buffer, size_t len) { return role == SERVER && len >= 13 && buffer[0] == 20; } -static -void filter_packet_ServerChangeCipherSpec(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ServerChangeCipherSpec(buffer, len) && state_packet_ServerChangeCipherSpec.count++ < 3) { - drop("Server Change Cipher Spec"); - } else { - filter_run_next(fd, buffer, len); - } -} - -static -int match_ServerFinished(const unsigned char* buffer, size_t len) +static int match_ServerFinished(const unsigned char* buffer, size_t len) { return role == SERVER && len >= 13 && buffer[0] == 22 && buffer[4] == 1; } -static -void filter_packet_ServerFinished(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ServerFinished(buffer, len) && state_packet_ServerFinished.count++ < 3) { - drop("Server Finished"); - } else { - filter_run_next(fd, buffer, len); - } -} +// }}} -static -void filter_permutete_state_free_buffer(filter_permute_state_t* state) -{ - int i; +// {{{ packet drop filters - for (i = 0; i < 3; i++) { - if (state->packets[i].data) { - free(state->packets[i].data); - } +#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); \ + } \ } -} -static -void filter_permute_state_run(filter_permute_state_t* state, int packetCount, +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 = malloc(len); @@ -306,233 +511,200 @@ void filter_permute_state_run(filter_permute_state_t* state, int packetCount, filter_run_next(fd, state->packets[packet].data, state->packets[packet].size); } - filter_permutete_state_free_buffer(state); + filter_permute_state_free_buffer(state); state->count = 0; } } -static -void filter_permute_ServerHello(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ServerHello(buffer, len) - || match_ServerKeyExchange(buffer, len) - || match_ServerHelloDone(buffer, len)) { - filter_permute_state_run(&state_permute_ServerHello, 3, fd, buffer, len); - } else { - filter_run_next(fd, buffer, len); +#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 -void filter_permute_ServerFinished(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t 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_ClientFinished[] = { match_ClientKeyExchange, 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(ServerFinished) +DECLARE_PERMUTE(ClientFinished) +DECLARE_PERMUTE(ClientFinishedFull) + +// }}} + +// {{{ emergency deadlock resolution time bomb + +timer_t killtimer_tid = 0; + +static void killtimer_set(void) { - if (match_ServerChangeCipherSpec(buffer, len) - || match_ServerFinished(buffer, len)) { - filter_permute_state_run(&state_permute_ServerFinished, 2, fd, buffer, len); - } else { - filter_run_next(fd, buffer, len); + struct sigevent sig; + struct itimerspec tout = { { 0, 0 }, { 2 * timeout_seconds, 0 } }; + + if (killtimer_tid != 0) { + timer_delete(killtimer_tid); } -} -static -void filter_permute_ClientFinished(gnutls_transport_ptr_t fd, - const unsigned char* buffer, size_t len) -{ - if (match_ClientKeyExchange(buffer, len) - || match_ClientChangeCipherSpec(buffer, len) - || match_ClientFinished(buffer, len)) { - filter_permute_state_run(&state_permute_ClientFinished, 3, fd, buffer, len); - } else { - filter_run_next(fd, buffer, len); + 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); } +// }}} -static -ssize_t writefn(gnutls_transport_ptr_t fd, const void* buffer, size_t len) +// {{{ 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_current_idx = 0; filter_run_next(fd, (const unsigned char*) buffer, len); return len; } -static -void await(int fd) +static void await(int fd, int timeout) { if (nonblock) { struct pollfd p = { fd, POLLIN, 0 }; - poll(&p, 1, 100); + if (poll(&p, 1, timeout) < 0 && errno != EAGAIN && errno != EINTR) { + rperror("poll"); + exit(3); + } } } +static void cred_init(void) +{ + gnutls_datum_t key = { (unsigned char*) PUBKEY, sizeof(PUBKEY) }; + gnutls_datum_t sec = { (unsigned char*) PRIVKEY, sizeof(PRIVKEY) }; + gnutls_certificate_allocate_credentials(&cred); + gnutls_certificate_set_openpgp_key_mem(cred, &key, &sec, GNUTLS_OPENPGP_FMT_BASE64); +} -static -gnutls_session_t session(int sock, int server) +static void session_init(int sock, int server) { - gnutls_session_t r; - - gnutls_init(&r, GNUTLS_DATAGRAM | (server ? GNUTLS_SERVER : GNUTLS_CLIENT) + gnutls_init(&session, GNUTLS_DATAGRAM | (server ? GNUTLS_SERVER : GNUTLS_CLIENT) | GNUTLS_NONBLOCK * nonblock); - gnutls_priority_set_direct(r, "NORMAL:+ANON-ECDH", 0); - gnutls_transport_set_ptr(r, (gnutls_transport_ptr_t) sock); + gnutls_priority_set_direct(session, "+CTYPE-OPENPGP:+CIPHER-ALL:+MAC-ALL:+ECDHE-RSA:+ANON-ECDH", 0); + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (intptr_t) sock); - if (server) { + 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 cred; gnutls_anon_allocate_server_credentials(&cred); - gnutls_credentials_set(r, GNUTLS_CRD_ANON, cred); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, cred); } else { gnutls_anon_client_credentials_t cred; gnutls_anon_allocate_client_credentials(&cred); - gnutls_credentials_set(r, GNUTLS_CRD_ANON, cred); + gnutls_credentials_set(session, GNUTLS_CRD_ANON, cred); } - gnutls_transport_set_push_function(r, writefn); + gnutls_transport_set_push_function(session, writefn); - gnutls_dtls_set_mtu(r, 1400); - - /* The cases tested here might exceed the normal DTLS - * timers */ - gnutls_dtls_set_timeouts(r, 1000, 120000); - - return r; + gnutls_dtls_set_mtu(session, 1400); + gnutls_dtls_set_timeouts(session, retransmit_milliseconds, timeout_seconds * 1000); } -static -int log_error(int code) +static void client(int sock) { - if (code < 0 && code != GNUTLS_E_AGAIN) { - fprintf(stdout, "%i <%s tls> %s", run_id, role_to_name(role), gnutls_strerror(code)); - if (gnutls_error_is_fatal(code)) { - fprintf(stdout, " (fatal)\n"); - exit(1); - } else { - fprintf(stdout, "\n"); - } - } - return code; -} - -timer_t killtimer_tid; - -static -void reset_killtimer(void) -{ -struct itimerspec tout = { { 0, 0 }, { 120, 0 } }; - - if (nonblock) { - return; - } - timer_settime(killtimer_tid, 0, &tout, 0); -} - -static -void setup_killtimer(void) -{ - struct sigevent sig; - struct itimerspec tout = { { 0, 0 }, { 240, 0 } }; - - memset(&sig, 0, sizeof(sig)); - sig.sigev_notify = SIGEV_SIGNAL; - sig.sigev_signo = 15; - timer_create(CLOCK_MONOTONIC, &sig, &killtimer_tid); - - timer_settime(killtimer_tid, 0, &tout, 0); -} - -static -void log_error_with_time(int err, time_t started) -{ - if (err < 0) { - if (err != GNUTLS_E_TIMEDOUT || (time(0) - started) >= 60) { - log_error(err); - } else { - fprintf(stdout, "{spurious}"); - log_error(err); - } - if (gnutls_error_is_fatal(err)) { - exit(1); - } - } -} - -static -void client(int sock) -{ - gnutls_session_t s = session(sock, 0); int err = 0; time_t started = time(0); const char* line = "foobar!"; char buffer[8192]; int len; - setup_killtimer(); + session_init(sock, 0); + killtimer_set(); do { - await(sock); - err = log_error(gnutls_handshake(s)); - reset_killtimer(); - } while (err != 0 && !gnutls_error_is_fatal(err)); - log_error_with_time(err, started); + 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); - started = time(0); - do { - err = gnutls_record_send(s, line, strlen(line)); - reset_killtimer(); - } while (err < 0 && !gnutls_error_is_fatal(err)); - log_error_with_time(err, started); + killtimer_set(); + die_on_error(gnutls_record_send(session, line, strlen(line))); do { - await(sock); - len = gnutls_record_recv(s, buffer, sizeof(buffer)); - } while (len < 0 && !gnutls_error_is_fatal(len)); + await(sock, -1); + len = process_error(gnutls_record_recv(session, buffer, sizeof(buffer))); + } while (len < 0); + if (len > 0 && strcmp(line, buffer) == 0) { exit(0); } else { - log_error(len); exit(1); } } -static -void server(int sock) +static void server(int sock) { - gnutls_session_t s = session(sock, 1); int err; time_t started = time(0); + char buffer[8192]; + int len; - write(sock, &sock, 1); + session_init(sock, 1); - setup_killtimer(); + await(sock, -1); + killtimer_set(); do { - await(sock); - err = log_error(gnutls_handshake(s)); - reset_killtimer(); - } while (err != 0 && !gnutls_error_is_fatal(err)); - log_error_with_time(err, started); - - for (;;) { - char buffer[8192]; - int len; - do { - await(sock); - len = gnutls_record_recv(s, buffer, sizeof(buffer)); - reset_killtimer(); - } while (len < 0 && !gnutls_error_is_fatal(len)); - log_error_with_time(len, started); - - gnutls_record_send(s, buffer, len); - exit(0); - } + 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); + + killtimer_set(); + do { + await(sock, -1); + len = process_error(gnutls_record_recv(session, buffer, sizeof(buffer))); + } while (len < 0); + + die_on_error(gnutls_record_send(session, buffer, len)); + exit(0); } +// }}} + +// {{{ test running/handling itself + #if 0 -static -void udp_sockpair(int* socks) +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 }; @@ -548,14 +720,16 @@ void udp_sockpair(int* socks) } #endif -static -int run_test(void) +static int run_test(void) { int fds[2]; int pid1, pid2; int status2; - socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds); + if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, fds) < 0) { + rperror("socketpair"); + exit(2); + } if (nonblock) { fcntl(fds[0], F_SETFL, (long) O_NONBLOCK); @@ -563,40 +737,33 @@ int run_test(void) } if (!(pid1 = fork())) { - setpgrp(); role = SERVER; - server(fds[1]); + server(fds[1]); // noreturn + } else if (pid1 < 0) { + rperror("fork server"); + exit(2); } - read(fds[0], &status2, sizeof(status2)); if (!(pid2 = fork())) { - setpgrp(); role = CLIENT; - client(fds[0]); + client(fds[0]); // noreturn + } else if (pid2 < 0) { + rperror("fork client"); + exit(2); } - waitpid(pid2, &status2, 0); + while (waitpid(pid2, &status2, 0) < 0 && errno == EINTR); kill(pid1, 15); - waitpid(pid1, 0, 0); + while (waitpid(pid1, 0, 0) < 0 && errno == EINTR); close(fds[0]); close(fds[1]); - if (WIFEXITED(status2)) { + if (!WIFSIGNALED(status2) && WEXITSTATUS(status2) != 3) { return !!WEXITSTATUS(status2); } else { - return 2; + return 3; } } -static int permutations2[2][2] - = { { 0, 1 }, { 1, 0 } }; -static const char* permutations2names[2] - = { "01", "10" }; -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* permutations3names[6] - = { "012", "021", "102", "120", "201", "210" }; static filter_fn filters[8] = { filter_packet_ServerHello, filter_packet_ServerKeyExchange, @@ -606,38 +773,54 @@ static filter_fn filters[8] filter_packet_ClientFinished, filter_packet_ServerChangeCipherSpec, filter_packet_ServerFinished }; -static const char* filter_names[8] - = { "SHello", - "SKeyExchange", - "SHelloDone", - "CKeyExchange", - "CChangeCipherSpec", - "CFinished", - "SChangeCipherSpec", - "SFinished" }; -static -int run_one_test(int dropMode, int serverFinishedPermute, int serverHelloPermute, int clientFinishedPermute) +static filter_fn filters_full[12] + = { 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 filterIdx, res; - run_id = ((dropMode * 2 + serverFinishedPermute) * 6 + serverHelloPermute) * 6 + clientFinishedPermute; + int res, filterIdx; + filter_fn* local_filters = full ? filters_full : filters; + const char** local_filter_names = full ? filter_names_full : filter_names; + const char** permutation_namesX = full ? permutation_names5 : permutation_names3; + int filter_count = full ? 12 : 8; + run_id = ((dropMode * 2 + serverFinishedPermute) * (full ? 120 : 6) + serverHelloPermute) * (full ? 120 : 6) + clientFinishedPermute; filter_clear_state(); - filter_chain[fnIdx++] = filter_permute_ServerHello; - state_permute_ServerHello.order = permutations3[serverHelloPermute]; + 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]; + } 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]; - - filter_chain[fnIdx++] = filter_permute_ClientFinished; - state_permute_ClientFinished.order = permutations3[clientFinishedPermute]; if (dropMode) { - for (filterIdx = 0; filterIdx < 8; filterIdx++) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { if (dropMode & (1 << filterIdx)) { - filter_chain[fnIdx++] = filters[filterIdx]; + filter_chain[fnIdx++] = local_filters[filterIdx]; } } } @@ -655,152 +838,306 @@ int run_one_test(int dropMode, int serverFinishedPermute, int serverHelloPermute case 2: fprintf(stdout, "%i !! ", run_id); break; + case 3: + fprintf(stdout, "%i TT ", run_id); + break; } - fprintf(stdout, "SHello(%s), ", permutations3names[serverHelloPermute]); - fprintf(stdout, "SFinished(%s), ", permutations2names[serverFinishedPermute]); - fprintf(stdout, "CFinished(%s) :- ", permutations3names[clientFinishedPermute]); + fprintf(stdout, "SHello(%s), ", permutation_namesX[serverHelloPermute]); + fprintf(stdout, "SFinished(%s), ", permutation_names2[serverFinishedPermute]); + fprintf(stdout, "CFinished(%s) :- ", permutation_namesX[clientFinishedPermute]); if (dropMode) { - for (filterIdx = 0; filterIdx < 8; filterIdx++) { + for (filterIdx = 0; filterIdx < filter_count; filterIdx++) { if (dropMode & (1 << filterIdx)) { if (dropMode & ((1 << filterIdx) - 1)) { fprintf(stdout, ", "); } - fprintf(stdout, "%s", filter_names[filterIdx]); + fprintf(stdout, "%s", local_filter_names[filterIdx]); } } } fprintf(stdout, "\n"); - if (res && debug) { - return 1; - } else { - return 0; + 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 -void run_tests(int childcount) +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 children = 0; int dropMode, serverFinishedPermute, serverHelloPermute, clientFinishedPermute; + int result = 0; - for (dropMode = 0; dropMode != 1 << 8; dropMode++) + for (dropMode = 0; dropMode != 1 << (full ? 12 : 8); dropMode++) for (serverFinishedPermute = 0; serverFinishedPermute < 2; serverFinishedPermute++) - for (serverHelloPermute = 0; serverHelloPermute < 6; serverHelloPermute++) - for (clientFinishedPermute = 0; clientFinishedPermute < 6; clientFinishedPermute++) { - if (!fork()) { + 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 { - children++; - while (children >= childcount) { - wait(0); - children--; - } + register_child(pid); + result |= wait_children(childcount); } } - while (children > 0) { - wait(0); - children--; - } + 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 arg; - - setlinebuf(stdout); - gnutls_global_init(); - gnutls_global_set_log_function(logfn); - gnutls_global_set_audit_log_function(auditfn); + int dropMode = 0; + int serverFinishedPermute = 0; + int serverHelloPermute = 0; + int clientFinishedPermute = 0; + int batch = 0; + int arg; nonblock = 0; debug = 0; - - if (argc == 1) { - run_tests(100); - } else { - int dropMode = 0; - int serverFinishedPermute = 0; - int serverHelloPermute = 0; - int clientFinishedPermute = 0; - - for (arg = 1; arg < argc; arg++) { - if (strcmp("-shello", argv[arg]) == 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 = strtol(argv[arg+1], &end, 10); + if (*end == '\0') { + debug = level; arg++; - if (arg >= argc) { - fprintf(stderr, "No arg to -shello\n"); - exit(1); - } - while (serverHelloPermute < 6) { - if (strcmp(permutations3names[serverHelloPermute], argv[arg]) == 0) { - break; - } - serverHelloPermute++; - } - if (serverHelloPermute == 6) { - fprintf(stderr, "Unknown permutation %s\n", argv[arg]); - exit(1); - } - } else if (strcmp("-d", argv[arg]) == 0) { + } else { debug++; - } else if (strcmp("-nb", argv[arg]) == 0) { - nonblock = 1; - } else if (strcmp("-sfinished", argv[arg]) == 0) { - arg++; - if (arg >= argc) { - fprintf(stderr, "No arg to -sfinished\n"); - exit(1); - } - while (serverFinishedPermute < 2) { - if (strcmp(permutations2names[serverFinishedPermute], argv[arg]) == 0) { - break; - } - serverFinishedPermute++; - } - if (serverFinishedPermute == 2) { - fprintf(stderr, "Unknown permutation %s\n", argv[arg]); - exit(1); - } - } else if (strcmp("-cfinished", argv[arg]) == 0) { - arg++; - if (arg >= argc) { - fprintf(stderr, "No arg to -cfinished\n"); - exit(1); - } - while (clientFinishedPermute < 6) { - if (strcmp(permutations3names[clientFinishedPermute], argv[arg]) == 0) { - break; - } - clientFinishedPermute++; - } - if (clientFinishedPermute == 6) { - fprintf(stderr, "Unknown permutation %s\n", argv[arg]); - exit(1); - } + } + } else if (strcmp("-nb", argv[arg]) == 0) { + nonblock = 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 { - int drop; - for (drop = 0; drop < 8; drop++) { - if (strcmp(filter_names[drop], argv[arg]) == 0) { - dropMode |= (1 << drop); - break; - } - } - if (drop == 8) { - fprintf(stderr, "Unknown packet %s\n", argv[arg]); - exit(8); + 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) { + full = 1; + } else if (strcmp("-shello", argv[arg]) == 0) { + NEXT_ARG(shello); + if (!parse_permutation(argv[arg], full ? permutation_names5 : permutation_names3, &serverHelloPermute)) { + FAIL_ARG(shell); + } + } else if (strcmp("-sfinished", argv[arg]) == 0) { + NEXT_ARG(sfinished); + if (!parse_permutation(argv[arg], permutation_names2, &serverFinishedPermute)) { + FAIL_ARG(sfinished); + } + } else if (strcmp("-cfinished", argv[arg]) == 0) { + NEXT_ARG(cfinished); + if (!parse_permutation(argv[arg], full ? permutation_names5 : permutation_names3, &clientFinishedPermute)) { + FAIL_ARG(cfinished); + } + } else { + int drop; + int filter_count = full ? 12 : 8; + const char** local_filter_names = full ? filter_names_full : filter_names; + 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); + } } - - if (debug) - gnutls_global_set_log_level(99); - run_one_test(dropMode, serverFinishedPermute, serverHelloPermute, clientFinishedPermute); } - gnutls_global_deinit(); + setlinebuf(stdout); + gnutls_global_init(); + cred_init(); + gnutls_global_set_log_function(logfn); + gnutls_global_set_audit_log_function(auditfn); + gnutls_global_set_log_level(debug); + + if (dropMode || serverFinishedPermute || serverHelloPermute || clientFinishedPermute) { + return run_one_test(dropMode, serverFinishedPermute, serverHelloPermute, clientFinishedPermute); + } else { + 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 |