xref: /dragonfly/crypto/libressl/apps/openssl/s_time.c (revision 961e30ea7dc61d1112b778ea4981eac68129fb86)
1 /* $OpenBSD: s_time.c,v 1.35 2022/08/31 12:29:08 tb Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 
59 /*-----------------------------------------
60    s_time - SSL client connection timer program
61    Written and donated by Larry Streepy <streepy@healthcare.com>
62   -----------------------------------------*/
63 
64 #include <sys/types.h>
65 #include <sys/socket.h>
66 
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <limits.h>
70 #include <string.h>
71 #include <unistd.h>
72 #include <poll.h>
73 
74 #include "apps.h"
75 
76 #include <openssl/err.h>
77 #include <openssl/pem.h>
78 #include <openssl/ssl.h>
79 #include <openssl/x509.h>
80 
81 #include "s_apps.h"
82 
83 #define SSL_CONNECT_NAME      "localhost:4433"
84 
85 #define BUFSIZZ 1024*10
86 
87 #define MYBUFSIZ 1024*8
88 
89 #define SECONDS     30
90 extern int verify_depth;
91 
92 static void s_time_usage(void);
93 static int run_test(SSL *);
94 static int benchmark(int);
95 static void print_tally_mark(SSL *);
96 
97 static SSL_CTX *tm_ctx = NULL;
98 static const SSL_METHOD *s_time_meth = NULL;
99 static long bytes_read = 0;
100 
101 struct {
102           int bugs;
103           char *CAfile;
104           char *CApath;
105           char *certfile;
106           char *cipher;
107           char *host;
108           char *keyfile;
109           time_t maxtime;
110           int nbio;
111           int no_shutdown;
112           int perform;
113           int verify;
114           int verify_depth;
115           char *www_path;
116 } s_time_config;
117 
118 static const struct option s_time_options[] = {
119           {
120                     .name = "bugs",
121                     .desc = "Enable workarounds for known SSL/TLS bugs",
122                     .type = OPTION_FLAG,
123                     .opt.flag = &s_time_config.bugs,
124           },
125           {
126                     .name = "CAfile",
127                     .argname = "file",
128                     .desc = "File containing trusted certificates in PEM format",
129                     .type = OPTION_ARG,
130                     .opt.arg = &s_time_config.CAfile,
131           },
132           {
133                     .name = "CApath",
134                     .argname = "path",
135                     .desc = "Directory containing trusted certificates",
136                     .type = OPTION_ARG,
137                     .opt.arg = &s_time_config.CApath,
138           },
139           {
140                     .name = "cert",
141                     .argname = "file",
142                     .desc = "Client certificate to use, if one is requested",
143                     .type = OPTION_ARG,
144                     .opt.arg = &s_time_config.certfile,
145           },
146           {
147                     .name = "cipher",
148                     .argname = "list",
149                     .desc = "List of cipher suites to send to the server",
150                     .type = OPTION_ARG,
151                     .opt.arg = &s_time_config.cipher,
152           },
153           {
154                     .name = "connect",
155                     .argname = "host:port",
156                     .desc = "Host and port to connect to (default "
157                         SSL_CONNECT_NAME ")",
158                     .type = OPTION_ARG,
159                     .opt.arg = &s_time_config.host,
160           },
161           {
162                     .name = "key",
163                     .argname = "file",
164                     .desc = "Client private key to use, if one is required",
165                     .type = OPTION_ARG,
166                     .opt.arg = &s_time_config.keyfile,
167           },
168           {
169                     .name = "nbio",
170                     .desc = "Use non-blocking I/O",
171                     .type = OPTION_FLAG,
172                     .opt.flag = &s_time_config.nbio,
173           },
174           {
175                     .name = "new",
176                     .desc = "Use a new session ID for each connection",
177                     .type = OPTION_VALUE,
178                     .opt.value = &s_time_config.perform,
179                     .value = 1,
180           },
181           {
182                     .name = "no_shutdown",
183                     .desc = "Shut down the connection without notifying the server",
184                     .type = OPTION_FLAG,
185                     .opt.flag = &s_time_config.no_shutdown,
186           },
187           {
188                     .name = "reuse",
189                     .desc = "Reuse the same session ID for each connection",
190                     .type = OPTION_VALUE,
191                     .opt.value = &s_time_config.perform,
192                     .value = 2,
193           },
194           {
195                     .name = "time",
196                     .argname = "seconds",
197                     .desc = "Duration to perform timing tests for (default 30)",
198                     .type = OPTION_ARG_TIME,
199                     .opt.tvalue = &s_time_config.maxtime,
200           },
201           {
202                     .name = "verify",
203                     .argname = "depth",
204                     .desc = "Enable peer certificate verification with given depth",
205                     .type = OPTION_ARG_INT,
206                     .opt.value = &s_time_config.verify_depth,
207           },
208           {
209                     .name = "www",
210                     .argname = "page",
211                     .desc = "Page to GET from the server (default none)",
212                     .type = OPTION_ARG,
213                     .opt.arg = &s_time_config.www_path,
214           },
215           { NULL },
216 };
217 
218 static void
s_time_usage(void)219 s_time_usage(void)
220 {
221           fprintf(stderr,
222               "usage: s_time "
223               "[-bugs] [-CAfile file] [-CApath directory] [-cert file]\n"
224               "    [-cipher cipherlist] [-connect host:port] [-key keyfile]\n"
225               "    [-nbio] [-new] [-no_shutdown] [-reuse] [-time seconds]\n"
226               "    [-verify depth] [-www page]\n\n");
227           options_usage(s_time_options);
228 }
229 
230 /***********************************************************************
231  * MAIN - main processing area for client
232  *                            real name depends on MONOLITH
233  */
234 int
s_time_main(int argc,char ** argv)235 s_time_main(int argc, char **argv)
236 {
237           int ret = 1;
238 
239           if (single_execution) {
240                     if (pledge("stdio rpath inet dns", NULL) == -1) {
241                               perror("pledge");
242                               exit(1);
243                     }
244           }
245 
246           s_time_meth = TLS_client_method();
247 
248           verify_depth = 0;
249 
250           memset(&s_time_config, 0, sizeof(s_time_config));
251 
252           s_time_config.host = SSL_CONNECT_NAME;
253           s_time_config.maxtime = SECONDS;
254           s_time_config.perform = 3;
255           s_time_config.verify = SSL_VERIFY_NONE;
256           s_time_config.verify_depth = -1;
257 
258           if (options_parse(argc, argv, s_time_options, NULL, NULL) != 0) {
259                     s_time_usage();
260                     goto end;
261           }
262 
263           if (s_time_config.verify_depth >= 0) {
264                     s_time_config.verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
265                     verify_depth = s_time_config.verify_depth;
266                     BIO_printf(bio_err, "verify depth is %d\n", verify_depth);
267           }
268 
269           if (s_time_config.www_path != NULL &&
270               strlen(s_time_config.www_path) > MYBUFSIZ - 100) {
271                     BIO_printf(bio_err, "-www option too long\n");
272                     goto end;
273           }
274 
275           if ((tm_ctx = SSL_CTX_new(s_time_meth)) == NULL)
276                     return (1);
277 
278           SSL_CTX_set_quiet_shutdown(tm_ctx, 1);
279 
280           if (s_time_config.bugs)
281                     SSL_CTX_set_options(tm_ctx, SSL_OP_ALL);
282 
283           if (s_time_config.cipher != NULL) {
284                     if (!SSL_CTX_set_cipher_list(tm_ctx, s_time_config.cipher)) {
285                               BIO_printf(bio_err, "error setting cipher list\n");
286                               ERR_print_errors(bio_err);
287                               goto end;
288                     }
289           }
290 
291           SSL_CTX_set_verify(tm_ctx, s_time_config.verify, NULL);
292 
293           if (!set_cert_stuff(tm_ctx, s_time_config.certfile,
294               s_time_config.keyfile))
295                     goto end;
296 
297           if ((!SSL_CTX_load_verify_locations(tm_ctx, s_time_config.CAfile,
298               s_time_config.CApath)) ||
299               (!SSL_CTX_set_default_verify_paths(tm_ctx))) {
300                     /*
301                      * BIO_printf(bio_err,"error setting default verify
302                      * locations\n");
303                      */
304                     ERR_print_errors(bio_err);
305                     /* goto end; */
306           }
307 
308           /* Loop and time how long it takes to make connections */
309           if (s_time_config.perform & 1) {
310                     printf("Collecting connection statistics for %lld seconds\n",
311                         (long long)s_time_config.maxtime);
312                     if (benchmark(0))
313                               goto end;
314           }
315           /*
316            * Now loop and time connections using the same session id over and
317            * over
318            */
319           if (s_time_config.perform & 2) {
320                     printf("\n\nNow timing with session id reuse.\n");
321                     if (benchmark(1))
322                               goto end;
323           }
324           ret = 0;
325  end:
326           if (tm_ctx != NULL) {
327                     SSL_CTX_free(tm_ctx);
328                     tm_ctx = NULL;
329           }
330 
331           return (ret);
332 }
333 
334 /***********************************************************************
335  * run_test - make a connection, get a file, and shut down the connection
336  *
337  * Args:
338  *                  scon      = SSL connection
339  * Returns:
340  *                  1 on success, 0 on error
341  */
342 static int
run_test(SSL * scon)343 run_test(SSL *scon)
344 {
345           char buf[1024 * 8];
346           struct pollfd pfd[1];
347           BIO *conn;
348           long verify_error;
349           int i, retval;
350 
351           if ((conn = BIO_new(BIO_s_connect())) == NULL)
352                     return 0;
353           BIO_set_conn_hostname(conn, s_time_config.host);
354           SSL_set_connect_state(scon);
355           SSL_set_bio(scon, conn, conn);
356           for (;;) {
357                     i = SSL_connect(scon);
358                     if (BIO_sock_should_retry(i)) {
359                               BIO_printf(bio_err, "DELAY\n");
360                               pfd[0].fd = SSL_get_fd(scon);
361                               pfd[0].events = POLLIN;
362                               poll(pfd, 1, -1);
363                               continue;
364                     }
365                     break;
366           }
367           if (i <= 0) {
368                     BIO_printf(bio_err, "ERROR\n");
369                     verify_error = SSL_get_verify_result(scon);
370                     if (verify_error != X509_V_OK)
371                               BIO_printf(bio_err, "verify error:%s\n",
372                                   X509_verify_cert_error_string(verify_error));
373                     else
374                               ERR_print_errors(bio_err);
375                     return 0;
376           }
377           if (s_time_config.www_path != NULL) {
378                     retval = snprintf(buf, sizeof buf,
379                         "GET %s HTTP/1.0\r\n\r\n", s_time_config.www_path);
380                     if (retval < 0 || retval >= sizeof buf) {
381                               fprintf(stderr, "URL too long\n");
382                               return 0;
383                     }
384                     if (SSL_write(scon, buf, retval) != retval)
385                               return 0;
386                     while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
387                               bytes_read += i;
388           }
389           if (s_time_config.no_shutdown)
390                     SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN |
391                         SSL_RECEIVED_SHUTDOWN);
392           else
393                     SSL_shutdown(scon);
394           return 1;
395 }
396 
397 static void
print_tally_mark(SSL * scon)398 print_tally_mark(SSL *scon)
399 {
400           int ver;
401 
402           if (SSL_session_reused(scon))
403                     ver = 'r';
404           else {
405                     ver = SSL_version(scon);
406                     if (ver == TLS1_VERSION)
407                               ver = 't';
408                     else
409                               ver = '*';
410           }
411           fputc(ver, stdout);
412           fflush(stdout);
413 }
414 
415 static int
benchmark(int reuse_session)416 benchmark(int reuse_session)
417 {
418           double elapsed, totalTime;
419           int nConn = 0;
420           SSL *scon = NULL;
421           int ret = 1;
422 
423           if (reuse_session) {
424                     /* Get an SSL object so we can reuse the session id */
425                     if ((scon = SSL_new(tm_ctx)) == NULL)
426                               goto end;
427                     if (!run_test(scon)) {
428                               fprintf(stderr, "Unable to get connection\n");
429                               goto end;
430                     }
431                     printf("starting\n");
432           }
433 
434           nConn = 0;
435           bytes_read = 0;
436 
437           app_timer_real(TM_RESET);
438           app_timer_user(TM_RESET);
439           for (;;) {
440                     elapsed = app_timer_real(TM_GET);
441                     if (elapsed > s_time_config.maxtime)
442                               break;
443                     if (scon == NULL) {
444                               if ((scon = SSL_new(tm_ctx)) == NULL)
445                                         goto end;
446                     }
447                     if (!run_test(scon))
448                               goto end;
449                     nConn += 1;
450                     print_tally_mark(scon);
451                     if (!reuse_session) {
452                               SSL_free(scon);
453                               scon = NULL;
454                     }
455           }
456           totalTime = app_timer_user(TM_GET);
457 
458           printf("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n",
459               nConn, totalTime, ((double) nConn / totalTime), bytes_read);
460           printf("%d connections in %.0f real seconds, %ld bytes read per connection\n",
461               nConn, elapsed, nConn > 0 ? bytes_read / nConn : 0);
462 
463           ret = 0;
464  end:
465           SSL_free(scon);
466           return ret;
467 }
468