2 * Copyright (C) 2012 Sean Buckheister
4 * This file is part of GnuTLS.
6 * GnuTLS is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuTLS is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GnuTLS; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 * DTLS stress test utility
24 * **** Available parameters ****
26 * -nb enable nonblocking operations on sessions
27 * -batch read test identifiers from stdin and run them
28 * -d increase debug level by one
29 * -d <n> set debug level to <n>
30 * -die don't start new tests after the first detected failure
31 * -timeout <n> set handshake timeout to <n> seconds. Tests that don't make progress
32 * within twice this time will be forcibly killed. (default: 120)
33 * -retransmit <n> set retransmit timeout to <n> milliseconds (default: 100)
34 * -j <n> run up to <n> tests in parallel
35 * -full use full handshake with mutual certificate authentication
36 * -shello <perm> run only one test, with the server hello flight permuted as <perm>
37 * -sfinished <perm> run only one test, with the server finished flight permuted as <perm>
38 * -cfinished <perm> run only one test, with the client finished flight permuted as <perm>
39 * <packet name> run only one test, drop <packet name> three times
40 * valid values for <packet name> are:
41 * SHello, SCertificate, SKeyExchange, SCertificateRequest, SHelloDone,
42 * CCertificate, CKeyExchange, CCertificateVerify, CChangeCipherSpec,
43 * CFinished, SChangeCipherSpec, SFinished
44 * using *Certificate* without -full will yield unexpected results
47 * **** Permutation handling ****
49 * Flight length for -sfinished is 2, for -shello and -cfinished they are 5 with -full, 3 otherwise.
50 * Permutations are given with base 0 and specify the order in which reordered packets are transmitted.
51 * For example, -full -shello 42130 will transmit server hello flight packets in the order
52 * SHelloDone, SKeyExchange, SCertificate, SCertificateRequest, SHello
55 * **** Output format ****
57 * Every line printed for any given test is prefixed by a unique id for that test. See run_test_by_id for
58 * exact composition. Errors encountered during execution are printed, with one status line after test
59 * completen. The format for this line is as follows:
61 * <id> <status> SHello(<shperm>), SFinished(<sfinperm>), CFinished(<cfinperm>) :- <drops>
63 * The format for error lines is <id> <role>| <text>, with <role> being the role of the child process
64 * that encountered the error, and <text> being obvious.
66 * <id> is the unique id for the test, it can be used as input to -batch.
67 * <status> can be ++ for a successful test, -- for a failure, TT for a deadlock timeout killed test,
68 * or !! for a test has died due to some unforeseen circumstances like syscall failures.
69 * <shperm>, <sfinperm>, <cfinperm> show the permutation for the respective flights used.
70 * They can be used as input to -shello, -sfinished, and -cfinished, respectively.
71 * <drops> is a comma separated list of <packet name>, one for every packet dropped thrice
74 * **** Exit status ****
76 * 0 all tests have passed
77 * 1 some tests have failed
78 * 4 the master processed has encountered unexpected errors
79 * 8 error parsing command line
83 #include <gnutls/gnutls.h>
84 #include <gnutls/openpgp.h>
85 #include <gnutls/dtls.h>
87 #include <sys/socket.h>
88 #include <sys/types.h>
89 #include <netinet/in.h>
99 #if _POSIX_TIMERS && (_POSIX_TIMERS - 200112L) >= 0
105 } filter_packet_state_t
;
108 gnutls_datum_t packets
[5];
111 } filter_permute_state_t
;
113 typedef void (*filter_fn
)(gnutls_transport_ptr_t
, const unsigned char*, size_t);
115 typedef int (*match_fn
)(const unsigned char*, size_t);
117 enum role
{ SERVER
, CLIENT
};
123 static int permutations2
[2][2]
124 = { { 0, 1 }, { 1, 0 } };
126 static const char* permutation_names2
[]
129 static int permutations3
[6][3]
130 = { { 0, 1, 2 }, { 0, 2, 1 }, { 1, 0, 2 }, { 1, 2, 0 }, { 2, 0, 1 }, { 2, 1, 0 } };
132 static const char* permutation_names3
[]
133 = { "012", "021", "102", "120", "201", "210", 0 };
135 static int permutations5
[120][5]
136 = { { 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 },
137 { 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 },
138 { 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 },
139 { 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 },
140 { 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 },
141 { 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 },
142 { 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 },
143 { 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 },
144 { 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 },
145 { 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 },
146 { 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 },
147 { 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 },
148 { 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 },
149 { 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 },
150 { 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 },
151 { 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 },
152 { 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 },
153 { 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 },
154 { 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 },
155 { 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 } };
157 static const char* permutation_names5
[]
158 = { "01234", "02134", "10234", "12034", "20134", "21034", "01324", "02314", "10324", "12304", "20314", "21304",
159 "03124", "03214", "13024", "13204", "23014", "23104", "30124", "30214", "31024", "31204", "32014", "32104",
160 "01243", "02143", "10243", "12043", "20143", "21043", "01342", "02341", "10342", "12340", "20341", "21340",
161 "03142", "03241", "13042", "13240", "23041", "23140", "30142", "30241", "31042", "31240", "32041", "32140",
162 "01423", "02413", "10423", "12403", "20413", "21403", "01432", "02431", "10432", "12430", "20431", "21430",
163 "03412", "03421", "13402", "13420", "23401", "23410", "30412", "30421", "31402", "31420", "32401", "32410",
164 "04123", "04213", "14023", "14203", "24013", "24103", "04132", "04231", "14032", "14230", "24031", "24130",
165 "04312", "04321", "14302", "14320", "24301", "24310", "34012", "34021", "34102", "34120", "34201", "34210",
166 "40123", "40213", "41023", "41203", "42013", "42103", "40132", "40231", "41032", "41230", "42031", "42130",
167 "40312", "40321", "41302", "41320", "42301", "42310", "43012", "43021", "43102", "43120", "43201", "43210", 0 };
169 static const char* filter_names
[8]
179 static const char* filter_names_full
[12]
183 "SCertificateRequest",
187 "CCertificateVerify",
193 static const unsigned char PUBKEY
[] =
194 "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
196 "mI0ETz0XRAEEAKXSU/tg2yGvoKf/r1pdzj7dnfPHeS+BRiT34763uUhibAbTgMkp\n"
197 "v44OlBPiAaZ54uuXVkz8e4pgvrBgQwIRtNp3xPaWF1CfC4F+V4LdZV8l8IG+AfES\n"
198 "K0GbfUS4q8vjnPJ0TyxnXE2KtbcRdzZzWBshJ8KChKwbH2vvrMrlmEeZABEBAAG0\n"
199 "CHRlc3Qga2V5iLgEEwECACIFAk89F0QCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B\n"
200 "AheAAAoJEMNjhmkfkLY9J/YD+wYZ2BD/0/c5gkkDP2NlVvrLGyFmEwQcR7DcaQYB\n"
201 "P3/Teq2gnscZ5Xm/z1qgGEpwmaVfVHY8mfEj8bYI8jAu0v1C1jCtJPUTmxf9tmkZ\n"
202 "QYFNR8T+F5Xae2XseOH70lSN/AEiW02BEBFlGBx0a3T30muFfqi/KawaE7KKn2e4\n"
203 "uNWvuI0ETz0XRAEEAKgZExsb7Lf9P3DmwJSvNVdkGVny7wr4/M1s0CDX20NkO7Y1\n"
204 "Ao9g+qFo5MlCOEuzjVaEYmM+rro7qyxmDKsaNIzZF1VN5UeYgPFyLcBK7C+QwUqw\n"
205 "1PUl/w4dFq8neQyqIPUVGRwQPlwpkkabRPNT3t/7KgDJvYzV9uu+cXCyfqErABEB\n"
206 "AAGInwQYAQIACQUCTz0XRAIbDAAKCRDDY4ZpH5C2PTBtBACVsR6l4HtuzQb5WFQt\n"
207 "sD/lQEk6BEY9aVfK957Oj+A4alGEGObToqVJFo/nq+P7aWExIXucJQRL8lYnC7u+\n"
208 "GjPVCun5TYzKMiryxHPkQr9NBx4hh8JjkDCc8nAgI3il49uPYkmsv70CgqJFFtT8\n"
209 "NfM+8fS537I+XA+hfjt20NUFIA==\n"
211 "-----END PGP PUBLIC KEY BLOCK-----\n";
213 static const unsigned char PRIVKEY
[] =
214 "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
216 "lQHYBE89F0QBBACl0lP7YNshr6Cn/69aXc4+3Z3zx3kvgUYk9+O+t7lIYmwG04DJ\n"
217 "Kb+ODpQT4gGmeeLrl1ZM/HuKYL6wYEMCEbTad8T2lhdQnwuBfleC3WVfJfCBvgHx\n"
218 "EitBm31EuKvL45zydE8sZ1xNirW3EXc2c1gbISfCgoSsGx9r76zK5ZhHmQARAQAB\n"
219 "AAP6A6VhRVi22MHE1YzQrTr8yvMSgwayynGcOjndHxdpEodferLx1Pp/BL+bT+ib\n"
220 "Qq7RZ363Xg/7I2rHJpenQYdkI5SI4KrXIV57p8G+isyTtsxU38SY84WoB5os8sfT\n"
221 "YhxG+edoTfDzXkRSWFB8EUjRaLa2b//nvLpxNRyqDSzzUxECAMtEnL5H/8gHbpZf\n"
222 "D98TSJVxdAl9rBAQaVMgrFgcU/IlmxCyVEh9eh/P261tefgOnyVcGFYHxdZvJ3td\n"
223 "miM+DNUCANDW1S9t7IiqflDpQIS2wGTZ/rLKPoE1F3285EaYAd0FQUq0O4/Nu31D\n"
224 "5pz/S7D+PfXn9oEZH3Dvl3EVIDyq4bUB+QEzFc3BsH2uueD3g42RoBfMGl6m3LI9\n"
225 "yWOnrUmIW+h9Fu8W9mcU6y82Q1G7OPIxA1me/Qtzo20lGQa8jAyzLhuit7QIdGVz\n"
226 "dCBrZXmIuAQTAQIAIgUCTz0XRAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA\n"
227 "CgkQw2OGaR+Qtj0n9gP7BhnYEP/T9zmCSQM/Y2VW+ssbIWYTBBxHsNxpBgE/f9N6\n"
228 "raCexxnleb/PWqAYSnCZpV9UdjyZ8SPxtgjyMC7S/ULWMK0k9RObF/22aRlBgU1H\n"
229 "xP4Xldp7Zex44fvSVI38ASJbTYEQEWUYHHRrdPfSa4V+qL8prBoTsoqfZ7i41a+d\n"
230 "AdgETz0XRAEEAKgZExsb7Lf9P3DmwJSvNVdkGVny7wr4/M1s0CDX20NkO7Y1Ao9g\n"
231 "+qFo5MlCOEuzjVaEYmM+rro7qyxmDKsaNIzZF1VN5UeYgPFyLcBK7C+QwUqw1PUl\n"
232 "/w4dFq8neQyqIPUVGRwQPlwpkkabRPNT3t/7KgDJvYzV9uu+cXCyfqErABEBAAEA\n"
233 "A/4wX+brqkGZQTv8lateHn3PRHM3O34nPjgiNeo/SV9EKZg1e1PdRx9ZTAJrGK9y\n"
234 "uZ03BKn7vZIy7fD4ufVzV/s/BaypVmvwjZud8fdMgsMQAJYtoMhozbOtUelCFpja\n"
235 "I1xAbDBx1PAAbS8Sh022/0jvOGnZhvkgZMG90z7AEANUYQIAwzywU087TcJk8Bzd\n"
236 "37JGWyE4f3iYFGA+r8BoIOrxvvgfUHKxdhG0gaT8SDeRAwNY6D43dCBZkG7Uel1F\n"
237 "x9MlLQIA3Goaz58hEN0fdm4TM7A8crtMB+f8/h87EneBgMl+Yj/3sklhyahR6Itm\n"
238 "lGuAAGTAOmD7i8OmS/a1ac5MtHAGtwH6A0B5GjaL8VnLQo4vFnuR7JuCQaLqGadV\n"
239 "mBmKxVHElduLf/VauBQPD5KZA+egpg+laJ4JLVXMmKIZGqRzopcIWZnKiJ8EGAEC\n"
240 "AAkFAk89F0QCGwwACgkQw2OGaR+Qtj0wbQQAlbEepeB7bs0G+VhULbA/5UBJOgRG\n"
241 "PWlXyveezo/gOGpRhBjm06KlSRaP56vj+2lhMSF7nCUES/JWJwu7vhoz1Qrp+U2M\n"
242 "yjIq8sRz5EK/TQceIYfCY5AwnPJwICN4pePbj2JJrL+9AoKiRRbU/DXzPvH0ud+y\n"
245 "-----END PGP PRIVATE KEY BLOCK-----\n";
249 // {{{ other global state
253 #define role_name (role == SERVER ? "server" : "client")
259 int retransmit_milliseconds
;
266 // {{{ logging and error handling
268 static void logfn(int level
, const char* s
)
271 fprintf(stdout
, "%i %s|<%i> %s", run_id
, role_name
, level
, s
);
275 static void auditfn(gnutls_session_t session
, const char* s
)
278 fprintf(stdout
, "%i %s| %s", run_id
, role_name
, s
);
282 static void drop(const char* packet
)
285 fprintf(stdout
, "%i %s| dropping %s\n", run_id
, role_name
, packet
);
289 static int _process_error(int loc
, int code
, int die
)
291 if (code
< 0 && (die
|| code
!= GNUTLS_E_AGAIN
)) {
292 fprintf(stdout
, "%i <%s tls> line %i: %s", run_id
, role_name
, loc
, gnutls_strerror(code
));
293 if (gnutls_error_is_fatal(code
) || die
) {
294 fprintf(stdout
, " (fatal)\n");
297 fprintf(stdout
, "\n");
303 #define die_on_error(code) _process_error(__LINE__, code, 1)
304 #define process_error(code) _process_error(__LINE__, code, 0)
306 static void _process_error_or_timeout(int loc
, int err
, time_t tdiff
)
309 if (err
!= GNUTLS_E_TIMEDOUT
|| tdiff
>= 60) {
310 _process_error(loc
, err
, 0);
312 fprintf(stdout
, "%i %s| line %i: {spurious timeout} (fatal)", run_id
, role_name
, loc
);
318 #define process_error_or_timeout(code, tdiff) _process_error_or_timeout(__LINE__, code, tdiff)
320 static void rperror(const char* name
)
322 fprintf(stdout
, "%i %s| %s: %s\n", run_id
, role_name
, name
, strerror(errno
));
327 // {{{ init, shared, and teardown code and data for packet stream filters
329 filter_packet_state_t state_packet_ServerHello
= { 0 };
330 filter_packet_state_t state_packet_ServerCertificate
= { 0 };
331 filter_packet_state_t state_packet_ServerKeyExchange
= { 0 };
332 filter_packet_state_t state_packet_ServerCertificateRequest
= { 0 };
333 filter_packet_state_t state_packet_ServerHelloDone
= { 0 };
334 filter_packet_state_t state_packet_ClientCertificate
= { 0 };
335 filter_packet_state_t state_packet_ClientKeyExchange
= { 0 };
336 filter_packet_state_t state_packet_ClientCertificateVerify
= { 0 };
337 filter_packet_state_t state_packet_ClientChangeCipherSpec
= { 0 };
338 filter_packet_state_t state_packet_ClientFinished
= { 0 };
339 filter_packet_state_t state_packet_ServerChangeCipherSpec
= { 0 };
340 filter_packet_state_t state_packet_ServerFinished
= { 0 };
342 filter_permute_state_t state_permute_ServerHello
= { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 };
343 filter_permute_state_t state_permute_ServerHelloFull
= { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 };
344 filter_permute_state_t state_permute_ServerFinished
= { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 };
345 filter_permute_state_t state_permute_ClientFinished
= { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 };
346 filter_permute_state_t state_permute_ClientFinishedFull
= { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 0, 0 };
348 filter_fn filter_chain
[32];
349 int filter_current_idx
;
351 static void filter_permute_state_free_buffer(filter_permute_state_t
* state
)
355 for (i
= 0; i
< sizeof(state
->packets
) / sizeof(state
->packets
[0]); i
++) {
356 free(state
->packets
[i
].data
);
357 state
->packets
[i
].data
= NULL
;
361 static void filter_clear_state(void)
363 filter_current_idx
= 0;
365 filter_permute_state_free_buffer(&state_permute_ServerHello
);
366 filter_permute_state_free_buffer(&state_permute_ServerHelloFull
);
367 filter_permute_state_free_buffer(&state_permute_ServerFinished
);
368 filter_permute_state_free_buffer(&state_permute_ClientFinished
);
369 filter_permute_state_free_buffer(&state_permute_ClientFinishedFull
);
371 memset(&state_packet_ServerHello
, 0, sizeof(state_packet_ServerHello
));
372 memset(&state_packet_ServerCertificate
, 0, sizeof(state_packet_ServerCertificate
));
373 memset(&state_packet_ServerKeyExchange
, 0, sizeof(state_packet_ServerKeyExchange
));
374 memset(&state_packet_ServerCertificateRequest
, 0, sizeof(state_packet_ServerCertificateRequest
));
375 memset(&state_packet_ServerHelloDone
, 0, sizeof(state_packet_ServerHelloDone
));
376 memset(&state_packet_ClientCertificate
, 0, sizeof(state_packet_ClientCertificate
));
377 memset(&state_packet_ClientKeyExchange
, 0, sizeof(state_packet_ClientKeyExchange
));
378 memset(&state_packet_ClientCertificateVerify
, 0, sizeof(state_packet_ClientCertificateVerify
));
379 memset(&state_packet_ClientChangeCipherSpec
, 0, sizeof(state_packet_ClientChangeCipherSpec
));
380 memset(&state_packet_ClientFinished
, 0, sizeof(state_packet_ClientFinished
));
381 memset(&state_packet_ServerChangeCipherSpec
, 0, sizeof(state_packet_ServerChangeCipherSpec
));
382 memset(&state_packet_ServerFinished
, 0, sizeof(state_packet_ServerFinished
));
383 memset(&state_permute_ServerHello
, 0, sizeof(state_permute_ServerHello
));
384 memset(&state_permute_ServerHelloFull
, 0, sizeof(state_permute_ServerHelloFull
));
385 memset(&state_permute_ServerFinished
, 0, sizeof(state_permute_ServerFinished
));
386 memset(&state_permute_ClientFinished
, 0, sizeof(state_permute_ClientFinished
));
387 memset(&state_permute_ClientFinishedFull
, 0, sizeof(state_permute_ClientFinishedFull
));
390 static void filter_run_next(gnutls_transport_ptr_t fd
,
391 const unsigned char* buffer
, size_t len
)
393 filter_fn fn
= filter_chain
[filter_current_idx
];
394 filter_current_idx
++;
398 send((intptr_t) fd
, buffer
, len
, 0);
400 filter_current_idx
--;
405 // {{{ packet match functions
407 static int match_ServerHello(const unsigned char* buffer
, size_t len
)
409 return role
== SERVER
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 2;
412 static int match_ServerCertificate(const unsigned char* buffer
, size_t len
)
414 return role
== SERVER
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 11;
417 static int match_ServerKeyExchange(const unsigned char* buffer
, size_t len
)
419 return role
== SERVER
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 12;
422 static int match_ServerCertificateRequest(const unsigned char* buffer
, size_t len
)
424 return role
== SERVER
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 13;
427 static int match_ServerHelloDone(const unsigned char* buffer
, size_t len
)
429 return role
== SERVER
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 14;
432 static int match_ClientCertificate(const unsigned char* buffer
, size_t len
)
434 return role
== CLIENT
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 11;
437 static int match_ClientKeyExchange(const unsigned char* buffer
, size_t len
)
439 return role
== CLIENT
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 16;
442 static int match_ClientCertificateVerify(const unsigned char* buffer
, size_t len
)
444 return role
== CLIENT
&& len
>= 13 + 1 && buffer
[0] == 22 && buffer
[13] == 15;
447 static int match_ClientChangeCipherSpec(const unsigned char* buffer
, size_t len
)
449 return role
== CLIENT
&& len
>= 13 && buffer
[0] == 20;
452 static int match_ClientFinished(const unsigned char* buffer
, size_t len
)
454 return role
== CLIENT
&& len
>= 13 && buffer
[0] == 22 && buffer
[4] == 1;
457 static int match_ServerChangeCipherSpec(const unsigned char* buffer
, size_t len
)
459 return role
== SERVER
&& len
>= 13 && buffer
[0] == 20;
462 static int match_ServerFinished(const unsigned char* buffer
, size_t len
)
464 return role
== SERVER
&& len
>= 13 && buffer
[0] == 22 && buffer
[4] == 1;
469 // {{{ packet drop filters
471 #define FILTER_DROP_COUNT 3
472 #define DECLARE_FILTER(packet) \
473 static void filter_packet_##packet(gnutls_transport_ptr_t fd, \
474 const unsigned char* buffer, size_t len) \
476 if (match_##packet(buffer, len) && (state_packet_##packet).count++ < FILTER_DROP_COUNT) { \
479 filter_run_next(fd, buffer, len); \
483 DECLARE_FILTER(ServerHello
)
484 DECLARE_FILTER(ServerCertificate
)
485 DECLARE_FILTER(ServerKeyExchange
)
486 DECLARE_FILTER(ServerCertificateRequest
)
487 DECLARE_FILTER(ServerHelloDone
)
488 DECLARE_FILTER(ClientCertificate
)
489 DECLARE_FILTER(ClientKeyExchange
)
490 DECLARE_FILTER(ClientCertificateVerify
)
491 DECLARE_FILTER(ClientChangeCipherSpec
)
492 DECLARE_FILTER(ClientFinished
)
493 DECLARE_FILTER(ServerChangeCipherSpec
)
494 DECLARE_FILTER(ServerFinished
)
498 // {{{ flight permutation filters
500 static void filter_permute_state_run(filter_permute_state_t
* state
, int packetCount
,
501 gnutls_transport_ptr_t fd
, const unsigned char* buffer
, size_t len
)
503 unsigned char* data
= malloc(len
);
504 int packet
= state
->order
[state
->count
];
506 memcpy(data
, buffer
, len
);
507 state
->packets
[packet
].data
= data
;
508 state
->packets
[packet
].size
= len
;
511 if (state
->count
== packetCount
) {
512 for (packet
= 0; packet
< packetCount
; packet
++) {
513 filter_run_next(fd
, state
->packets
[packet
].data
,
514 state
->packets
[packet
].size
);
516 filter_permute_state_free_buffer(state
);
521 #define DECLARE_PERMUTE(flight) \
522 static void filter_permute_##flight(gnutls_transport_ptr_t fd, \
523 const unsigned char* buffer, size_t len) \
525 int count = sizeof(permute_match_##flight) / sizeof(permute_match_##flight[0]); \
527 for (i = 0; i < count; i++) { \
528 if (permute_match_##flight[i](buffer, len)) { \
529 filter_permute_state_run(&state_permute_##flight, count, fd, buffer, len); \
533 filter_run_next(fd, buffer, len); \
536 static match_fn permute_match_ServerHello
[] = { match_ServerHello
, match_ServerKeyExchange
, match_ServerHelloDone
};
537 static match_fn permute_match_ServerHelloFull
[] = { match_ServerHello
, match_ServerCertificate
, match_ServerKeyExchange
,
538 match_ServerCertificateRequest
, match_ServerHelloDone
};
539 static match_fn permute_match_ServerFinished
[] = { match_ServerChangeCipherSpec
, match_ServerFinished
};
540 static match_fn permute_match_ClientFinished
[] = { match_ClientKeyExchange
, match_ClientChangeCipherSpec
, match_ClientFinished
};
541 static match_fn permute_match_ClientFinishedFull
[] = { match_ClientCertificate
, match_ClientKeyExchange
,
542 match_ClientCertificateVerify
, match_ClientChangeCipherSpec
, match_ClientFinished
};
544 DECLARE_PERMUTE(ServerHello
)
545 DECLARE_PERMUTE(ServerHelloFull
)
546 DECLARE_PERMUTE(ServerFinished
)
547 DECLARE_PERMUTE(ClientFinished
)
548 DECLARE_PERMUTE(ClientFinishedFull
)
552 // {{{ emergency deadlock resolution time bomb
554 timer_t killtimer_tid
= 0;
556 static void killtimer_set(void)
559 struct itimerspec tout
= { { 0, 0 }, { 2 * timeout_seconds
, 0 } };
561 if (killtimer_tid
!= 0) {
562 timer_delete(killtimer_tid
);
565 memset(&sig
, 0, sizeof(sig
));
566 sig
.sigev_notify
= SIGEV_SIGNAL
;
567 sig
.sigev_signo
= 15;
568 if (timer_create(CLOCK_MONOTONIC
, &sig
, &killtimer_tid
) < 0) {
569 rperror("timer_create");
573 timer_settime(killtimer_tid
, 0, &tout
, 0);
578 // {{{ actual gnutls operations
580 gnutls_certificate_credentials_t cred
;
581 gnutls_session_t session
;
583 static ssize_t
writefn(gnutls_transport_ptr_t fd
, const void* buffer
, size_t len
)
585 filter_run_next(fd
, (const unsigned char*) buffer
, len
);
589 static void await(int fd
, int timeout
)
592 struct pollfd p
= { fd
, POLLIN
, 0 };
593 if (poll(&p
, 1, timeout
) < 0 && errno
!= EAGAIN
&& errno
!= EINTR
) {
600 static void cred_init(void)
602 gnutls_datum_t key
= { (unsigned char*) PUBKEY
, sizeof(PUBKEY
) };
603 gnutls_datum_t sec
= { (unsigned char*) PRIVKEY
, sizeof(PRIVKEY
) };
605 gnutls_certificate_allocate_credentials(&cred
);
607 gnutls_certificate_set_openpgp_key_mem(cred
, &key
, &sec
, GNUTLS_OPENPGP_FMT_BASE64
);
610 static void session_init(int sock
, int server
)
612 gnutls_init(&session
, GNUTLS_DATAGRAM
| (server
? GNUTLS_SERVER
: GNUTLS_CLIENT
)
613 | GNUTLS_NONBLOCK
* nonblock
);
614 gnutls_priority_set_direct(session
, "+CTYPE-OPENPGP:+CIPHER-ALL:+MAC-ALL:+ECDHE-RSA:+ANON-ECDH", 0);
615 gnutls_transport_set_ptr(session
, (gnutls_transport_ptr_t
) (intptr_t) sock
);
618 gnutls_credentials_set(session
, GNUTLS_CRD_CERTIFICATE
, cred
);
620 gnutls_certificate_server_set_request(session
, GNUTLS_CERT_REQUIRE
);
623 gnutls_anon_server_credentials_t cred
;
624 gnutls_anon_allocate_server_credentials(&cred
);
625 gnutls_credentials_set(session
, GNUTLS_CRD_ANON
, cred
);
627 gnutls_anon_client_credentials_t cred
;
628 gnutls_anon_allocate_client_credentials(&cred
);
629 gnutls_credentials_set(session
, GNUTLS_CRD_ANON
, cred
);
632 gnutls_transport_set_push_function(session
, writefn
);
634 gnutls_dtls_set_mtu(session
, 1400);
635 gnutls_dtls_set_timeouts(session
, retransmit_milliseconds
, timeout_seconds
* 1000);
638 static void client(int sock
)
641 time_t started
= time(0);
642 const char* line
= "foobar!";
646 session_init(sock
, 0);
650 err
= process_error(gnutls_handshake(session
));
652 int t
= gnutls_dtls_get_timeout(session
);
653 await(sock
, t
? t
: 100);
656 process_error_or_timeout(err
, time(0) - started
);
659 die_on_error(gnutls_record_send(session
, line
, strlen(line
)));
663 len
= process_error(gnutls_record_recv(session
, buffer
, sizeof(buffer
)));
666 if (len
> 0 && strncmp(line
, buffer
, len
) == 0) {
673 static void server(int sock
)
676 time_t started
= time(0);
680 session_init(sock
, 1);
686 err
= process_error(gnutls_handshake(session
));
688 int t
= gnutls_dtls_get_timeout(session
);
689 await(sock
, t
? t
: 100);
692 process_error_or_timeout(err
, time(0) - started
);
697 len
= process_error(gnutls_record_recv(session
, buffer
, sizeof(buffer
)));
700 die_on_error(gnutls_record_send(session
, buffer
, len
));
706 // {{{ test running/handling itself
709 static void udp_sockpair(int* socks
)
711 struct sockaddr_in6 sa
= { AF_INET6
, htons(30000), 0, in6addr_loopback
, 0 };
712 struct sockaddr_in6 sb
= { AF_INET6
, htons(20000), 0, in6addr_loopback
, 0 };
714 socks
[0] = socket(AF_INET6
, SOCK_DGRAM
, 0);
715 socks
[1] = socket(AF_INET6
, SOCK_DGRAM
, 0);
717 bind(socks
[0], (struct sockaddr
*) &sa
, sizeof(sa
));
718 bind(socks
[1], (struct sockaddr
*) &sb
, sizeof(sb
));
720 connect(socks
[1], (struct sockaddr
*) &sa
, sizeof(sa
));
721 connect(socks
[0], (struct sockaddr
*) &sb
, sizeof(sb
));
725 static int run_test(void)
731 if (socketpair(AF_LOCAL
, SOCK_DGRAM
, 0, fds
) < 0) {
732 rperror("socketpair");
737 fcntl(fds
[0], F_SETFL
, (long) O_NONBLOCK
);
738 fcntl(fds
[1], F_SETFL
, (long) O_NONBLOCK
);
741 if (!(pid1
= fork())) {
743 server(fds
[1]); // noreturn
744 } else if (pid1
< 0) {
745 rperror("fork server");
748 if (!(pid2
= fork())) {
750 client(fds
[0]); // noreturn
751 } else if (pid2
< 0) {
752 rperror("fork client");
755 while (waitpid(pid2
, &status2
, 0) < 0 && errno
== EINTR
);
757 while (waitpid(pid1
, 0, 0) < 0 && errno
== EINTR
);
762 if (!WIFSIGNALED(status2
) && WEXITSTATUS(status2
) != 3) {
763 return !!WEXITSTATUS(status2
);
769 static filter_fn filters
[8]
770 = { filter_packet_ServerHello
,
771 filter_packet_ServerKeyExchange
,
772 filter_packet_ServerHelloDone
,
773 filter_packet_ClientKeyExchange
,
774 filter_packet_ClientChangeCipherSpec
,
775 filter_packet_ClientFinished
,
776 filter_packet_ServerChangeCipherSpec
,
777 filter_packet_ServerFinished
};
779 static filter_fn filters_full
[12]
780 = { filter_packet_ServerHello
,
781 filter_packet_ServerCertificate
,
782 filter_packet_ServerKeyExchange
,
783 filter_packet_ServerCertificateRequest
,
784 filter_packet_ServerHelloDone
,
785 filter_packet_ClientCertificate
,
786 filter_packet_ClientKeyExchange
,
787 filter_packet_ClientCertificateVerify
,
788 filter_packet_ClientChangeCipherSpec
,
789 filter_packet_ClientFinished
,
790 filter_packet_ServerChangeCipherSpec
,
791 filter_packet_ServerFinished
};
793 static int run_one_test(int dropMode
, int serverFinishedPermute
, int serverHelloPermute
, int clientFinishedPermute
)
797 filter_fn
* local_filters
= full
? filters_full
: filters
;
798 const char** local_filter_names
= full
? filter_names_full
: filter_names
;
799 const char** permutation_namesX
= full
? permutation_names5
: permutation_names3
;
800 int filter_count
= full
? 12 : 8;
801 run_id
= ((dropMode
* 2 + serverFinishedPermute
) * (full
? 120 : 6) + serverHelloPermute
) * (full
? 120 : 6) + clientFinishedPermute
;
803 filter_clear_state();
806 filter_chain
[fnIdx
++] = filter_permute_ServerHelloFull
;
807 state_permute_ServerHelloFull
.order
= permutations5
[serverHelloPermute
];
809 filter_chain
[fnIdx
++] = filter_permute_ClientFinishedFull
;
810 state_permute_ClientFinishedFull
.order
= permutations5
[clientFinishedPermute
];
812 filter_chain
[fnIdx
++] = filter_permute_ServerHello
;
813 state_permute_ServerHello
.order
= permutations3
[serverHelloPermute
];
815 filter_chain
[fnIdx
++] = filter_permute_ClientFinished
;
816 state_permute_ClientFinished
.order
= permutations3
[clientFinishedPermute
];
819 filter_chain
[fnIdx
++] = filter_permute_ServerFinished
;
820 state_permute_ServerFinished
.order
= permutations2
[serverFinishedPermute
];
823 for (filterIdx
= 0; filterIdx
< filter_count
; filterIdx
++) {
824 if (dropMode
& (1 << filterIdx
)) {
825 filter_chain
[fnIdx
++] = local_filters
[filterIdx
];
829 filter_chain
[fnIdx
++] = NULL
;
835 fprintf(stdout
, "%i ++ ", run_id
);
838 fprintf(stdout
, "%i -- ", run_id
);
841 fprintf(stdout
, "%i !! ", run_id
);
844 fprintf(stdout
, "%i TT ", run_id
);
848 fprintf(stdout
, "SHello(%s), ", permutation_namesX
[serverHelloPermute
]);
849 fprintf(stdout
, "SFinished(%s), ", permutation_names2
[serverFinishedPermute
]);
850 fprintf(stdout
, "CFinished(%s) :- ", permutation_namesX
[clientFinishedPermute
]);
852 for (filterIdx
= 0; filterIdx
< filter_count
; filterIdx
++) {
853 if (dropMode
& (1 << filterIdx
)) {
854 if (dropMode
& ((1 << filterIdx
) - 1)) {
855 fprintf(stdout
, ", ");
857 fprintf(stdout
, "%s", local_filter_names
[filterIdx
]);
861 fprintf(stdout
, "\n");
866 static int run_test_by_id(int id
)
868 int pscale
= full
? 120 : 6;
869 int dropMode
, serverFinishedPermute
, serverHelloPermute
, clientFinishedPermute
;
871 clientFinishedPermute
= id
% pscale
;
874 serverHelloPermute
= id
% pscale
;
877 serverFinishedPermute
= id
% 2;
882 return run_one_test(dropMode
, serverFinishedPermute
, serverHelloPermute
, clientFinishedPermute
);
889 static void register_child(int pid
)
894 for (idx
= 0; idx
< job_limit
; idx
++) {
895 if (job_pids
[idx
] == 0) {
902 static int wait_children(int child_limit
)
907 while (children
> child_limit
) {
910 int pid
= waitpid(0, &status
, 0);
911 if (pid
< 0 && errno
== ECHILD
) {
914 for (idx
= 0; idx
< job_limit
; idx
++) {
915 if (job_pids
[idx
] == pid
) {
917 if (WEXITSTATUS(status
)) {
919 if (!run_to_end
&& !fail
) {
920 fprintf(stderr
, "One test failed, waiting for remaining tests\n");
938 static int run_tests_from_id_list(int childcount
)
944 while ((ret
= fscanf(stdin
, "%i\n", &test_id
)) > 0) {
946 if (test_id
< 0 || test_id
> 2 * (full
? 120 * 120 * (1 << 12) : 6 * 6 * 256)) {
947 fprintf(stderr
, "Invalid test id %i\n", test_id
);
950 if (!(pid
= fork())) {
951 exit(run_test_by_id(test_id
));
952 } else if (pid
< 0) {
958 result
|= wait_children(childcount
);
962 if (ret
< 0 && ret
!= EOF
) {
963 fprintf(stderr
, "Error reading test id list\n");
966 result
|= wait_children(0);
971 static int run_all_tests(int childcount
)
973 int dropMode
, serverFinishedPermute
, serverHelloPermute
, clientFinishedPermute
;
976 for (dropMode
= 0; dropMode
!= 1 << (full
? 12 : 8); dropMode
++)
977 for (serverFinishedPermute
= 0; serverFinishedPermute
< 2; serverFinishedPermute
++)
978 for (serverHelloPermute
= 0; serverHelloPermute
< (full
? 120 : 6); serverHelloPermute
++)
979 for (clientFinishedPermute
= 0; clientFinishedPermute
< (full
? 120 : 6); clientFinishedPermute
++) {
981 if (!(pid
= fork())) {
982 exit(run_one_test(dropMode
, serverFinishedPermute
, serverHelloPermute
, clientFinishedPermute
));
983 } else if (pid
< 0) {
989 result
|= wait_children(childcount
);
993 result
|= wait_children(0);
1000 static int parse_permutation(const char* arg
, const char* permutations
[], int* val
)
1003 while (permutations
[*val
]) {
1004 if (strcmp(permutations
[*val
], arg
) == 0) {
1013 int main(int argc
, const char* argv
[])
1016 int serverFinishedPermute
= 0;
1017 int serverHelloPermute
= 0;
1018 int clientFinishedPermute
= 0;
1024 timeout_seconds
= 120;
1025 retransmit_milliseconds
= 100;
1030 #define NEXT_ARG(name) \
1032 if (++arg >= argc) { \
1033 fprintf(stderr, "No argument for -" #name "\n"); \
1037 #define FAIL_ARG(name) \
1039 fprintf(stderr, "Invalid argument for -" #name "\n"); \
1043 for (arg
= 1; arg
< argc
; arg
++) {
1044 if (strcmp("-die", argv
[arg
]) == 0) {
1046 } else if (strcmp("-batch", argv
[arg
]) == 0) {
1048 } else if (strcmp("-d", argv
[arg
]) == 0) {
1050 int level
= strtol(argv
[arg
+1], &end
, 10);
1057 } else if (strcmp("-nb", argv
[arg
]) == 0) {
1059 } else if (strcmp("-timeout", argv
[arg
]) == 0) {
1064 val
= strtol(argv
[arg
], &end
, 10);
1066 timeout_seconds
= val
;
1070 } else if (strcmp("-retransmit", argv
[arg
]) == 0) {
1074 NEXT_ARG(retransmit
);
1075 val
= strtol(argv
[arg
], &end
, 10);
1077 retransmit_milliseconds
= val
;
1079 FAIL_ARG(retransmit
);
1081 } else if (strcmp("-j", argv
[arg
]) == 0) {
1086 val
= strtol(argv
[arg
], &end
, 10);
1092 } else if (strcmp("-full", argv
[arg
]) == 0) {
1094 } else if (strcmp("-shello", argv
[arg
]) == 0) {
1096 if (!parse_permutation(argv
[arg
], full
? permutation_names5
: permutation_names3
, &serverHelloPermute
)) {
1099 } else if (strcmp("-sfinished", argv
[arg
]) == 0) {
1100 NEXT_ARG(sfinished
);
1101 if (!parse_permutation(argv
[arg
], permutation_names2
, &serverFinishedPermute
)) {
1102 FAIL_ARG(sfinished
);
1104 } else if (strcmp("-cfinished", argv
[arg
]) == 0) {
1105 NEXT_ARG(cfinished
);
1106 if (!parse_permutation(argv
[arg
], full
? permutation_names5
: permutation_names3
, &clientFinishedPermute
)) {
1107 FAIL_ARG(cfinished
);
1111 int filter_count
= full
? 12 : 8;
1112 const char** local_filter_names
= full
? filter_names_full
: filter_names
;
1113 for (drop
= 0; drop
< filter_count
; drop
++) {
1114 if (strcmp(local_filter_names
[drop
], argv
[arg
]) == 0) {
1115 dropMode
|= (1 << drop
);
1119 if (drop
== filter_count
) {
1120 fprintf(stderr
, "Unknown packet %s\n", argv
[arg
]);
1127 gnutls_global_init();
1129 gnutls_global_set_log_function(logfn
);
1130 gnutls_global_set_audit_log_function(auditfn
);
1131 gnutls_global_set_log_level(debug
);
1133 if (dropMode
|| serverFinishedPermute
|| serverHelloPermute
|| clientFinishedPermute
) {
1134 return run_one_test(dropMode
, serverFinishedPermute
, serverHelloPermute
, clientFinishedPermute
);
1136 job_pids
= calloc(sizeof(int), job_limit
);
1138 return run_tests_from_id_list(job_limit
);
1140 return run_all_tests(job_limit
);
1145 // vim: foldmethod=marker
1147 #else /* NO POSIX TIMERS */
1149 int main(int argc
, const char* argv
[])