1 /*
2 * libwebsockets-test-client - libwebsockets test implementation
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * The person who associated a work with this deed has dedicated
10 * the work to the public domain by waiving all of his or her rights
11 * to the work worldwide under copyright law, including all related
12 * and neighboring rights, to the extent allowed by law. You can copy,
13 * modify, distribute and perform the work, even for commercial purposes,
14 * all without asking permission.
15 *
16 * The test apps are intended to be adapted for use in your code, which
17 * may be proprietary. So unlike the library itself, they are licensed
18 * Public Domain.
19 */
20
21 #include "lws_config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
26 #include <getopt.h>
27 #endif
28 #include <string.h>
29 #include <signal.h>
30
31 #ifdef _WIN32
32 #define random rand
33 #include "gettimeofday.h"
34 #else
35 #include <syslog.h>
36 #include <sys/time.h>
37 #include <unistd.h>
38 #endif
39
40 #include <libwebsockets.h>
41
42 struct lws_poly_gen {
43 uint32_t cyc[2];
44 };
45
46 #define block_size (3 * 4096)
47
48 static int deny_deflate, longlived, mirror_lifetime, test_post, once;
49 static struct lws *wsi_dumb, *wsi_mirror;
50 static struct lws *wsi_multi[3];
51 static volatile int force_exit;
52 static unsigned int opts, rl_multi[3];
53 static int flag_no_mirror_traffic, justmirror, flag_echo;
54 static uint32_t count_blocks = 1024, txb, rxb, rx_count, errs;
55 static struct lws_poly_gen tx = { { 0xabcde, 0x23456789 } },
56 rx = { { 0xabcde, 0x23456789 } }
57 ;
58
59 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
60 char crl_path[1024] = "";
61 #endif
62
63 /*
64 * This demo shows how to connect multiple websockets simultaneously to a
65 * websocket server (there is no restriction on their having to be the same
66 * server just it simplifies the demo).
67 *
68 * dumb-increment-protocol: we connect to the server and print the number
69 * we are given
70 *
71 * lws-mirror-protocol: draws random circles, which are mirrored on to every
72 * client (see them being drawn in every browser
73 * session also using the test server)
74 */
75
76 enum demo_protocols {
77
78 PROTOCOL_DUMB_INCREMENT,
79 PROTOCOL_LWS_MIRROR,
80
81 /* always last */
82 DEMO_PROTOCOL_COUNT
83 };
84
85 static uint8_t
lws_poly_rand(struct lws_poly_gen * p)86 lws_poly_rand(struct lws_poly_gen *p)
87 {
88 p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
89 p->cyc[0] >> 1;
90 p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
91 p->cyc[0] >> 1;
92 p->cyc[1] = (p->cyc[1] & 1) ? (p->cyc[1] >> 1) ^ 0x7a5bc2e3 :
93 p->cyc[1] >> 1;
94
95 return (uint8_t)(p->cyc[0] ^ p->cyc[1]);
96 }
97
show_http_content(const char * p,size_t l)98 static void show_http_content(const char *p, size_t l)
99 {
100 if (lwsl_visible(LLL_INFO)) {
101 while (l--)
102 if (*p < 0x7f)
103 putchar(*p++);
104 else
105 putchar('.');
106 }
107 }
108
109
110 /*
111 * dumb_increment protocol
112 *
113 * since this also happens to be protocols[0], some callbacks that are not
114 * bound to a specific protocol also turn up here.
115 */
116
117 static int
callback_dumb_increment(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)118 callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
119 void *user, void *in, size_t len)
120 {
121 #if defined(LWS_WITH_TLS)
122 union lws_tls_cert_info_results ci;
123 #if defined(LWS_HAVE_CTIME_R) && !defined(LWS_WITH_NO_LOGS)
124 char date[32];
125 #endif
126 #endif
127 const char *which = "http";
128 char which_wsi[10], buf[50 + LWS_PRE];
129 int n;
130
131 switch (reason) {
132
133 case LWS_CALLBACK_CLIENT_ESTABLISHED:
134 lwsl_info("dumb: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
135 break;
136
137 case LWS_CALLBACK_CLOSED:
138 lwsl_notice("dumb: LWS_CALLBACK_CLOSED\n");
139 wsi_dumb = NULL;
140 break;
141
142 case LWS_CALLBACK_CLIENT_RECEIVE:
143 ((char *)in)[len] = '\0';
144 lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
145 break;
146
147 /* because we are protocols[0] ... */
148
149 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
150 if (wsi == wsi_dumb) {
151 which = "dumb";
152 wsi_dumb = NULL;
153 }
154 if (wsi == wsi_mirror) {
155 which = "mirror";
156 wsi_mirror = NULL;
157 }
158
159 for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++)
160 if (wsi == wsi_multi[n]) {
161 sprintf(which_wsi, "multi %d", n);
162 which = which_wsi;
163 wsi_multi[n] = NULL;
164 }
165
166 lwsl_err("CLIENT_CONNECTION_ERROR: %s: %s\n", which,
167 in ? (char *)in : "(null)");
168 break;
169
170 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
171 if ((strcmp((const char *)in, "deflate-stream") == 0) &&
172 deny_deflate) {
173 lwsl_notice("denied deflate-stream extension\n");
174 return 1;
175 }
176 if ((strcmp((const char *)in, "x-webkit-deflate-frame") == 0))
177 return 1;
178 if ((strcmp((const char *)in, "deflate-frame") == 0))
179 return 1;
180 break;
181
182 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
183 lwsl_notice("lws_http_client_http_response %d\n",
184 lws_http_client_http_response(wsi));
185 #if defined(LWS_WITH_TLS)
186 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME,
187 &ci, sizeof(ci.ns.name)))
188 lwsl_notice(" Peer Cert CN : %s\n", ci.ns.name);
189
190 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME,
191 &ci, sizeof(ci.ns.name)))
192 lwsl_notice(" Peer Cert issuer : %s\n", ci.ns.name);
193
194 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_FROM,
195 &ci, 0))
196 #if defined(LWS_HAVE_CTIME_R)
197 lwsl_notice(" Peer Cert Valid from: %s",
198 ctime_r(&ci.time, date));
199 #else
200 lwsl_notice(" Peer Cert Valid from: %s",
201 ctime(&ci.time));
202 #endif
203 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO,
204 &ci, 0))
205 #if defined(LWS_HAVE_CTIME_R)
206 lwsl_notice(" Peer Cert Valid to : %s",
207 ctime_r(&ci.time, date));
208 #else
209 lwsl_notice(" Peer Cert Valid to : %s",
210 ctime(&ci.time));
211 #endif
212 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_USAGE,
213 &ci, 0))
214 lwsl_notice(" Peer Cert usage bits: 0x%x\n", ci.usage);
215 #endif
216 break;
217
218 /* chunked content */
219 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
220 lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: %ld\n",
221 (long)len);
222 show_http_content(in, len);
223 break;
224
225 /* unchunked content */
226 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
227 {
228 char buffer[1024 + LWS_PRE];
229 char *px = buffer + LWS_PRE;
230 int lenx = sizeof(buffer) - LWS_PRE;
231
232 /*
233 * Often you need to flow control this by something
234 * else being writable. In that case call the api
235 * to get a callback when writable here, and do the
236 * pending client read in the writeable callback of
237 * the output.
238 *
239 * In the case of chunked content, this will call back
240 * LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ once per
241 * chunk or partial chunk in the buffer, and report
242 * zero length back here.
243 */
244 if (lws_http_client_read(wsi, &px, &lenx) < 0)
245 return -1;
246 }
247 break;
248
249 case LWS_CALLBACK_CLIENT_WRITEABLE:
250 lwsl_info("Client wsi %p writable\n", wsi);
251 break;
252
253 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
254 if (test_post) {
255 unsigned char **p = (unsigned char **)in, *end = (*p) + len;
256
257 if (lws_add_http_header_by_token(wsi,
258 WSI_TOKEN_HTTP_CONTENT_LENGTH,
259 (unsigned char *)"29", 2, p, end))
260 return -1;
261 if (lws_add_http_header_by_token(wsi,
262 WSI_TOKEN_HTTP_CONTENT_TYPE,
263 (unsigned char *)"application/x-www-form-urlencoded",
264 33, p, end))
265 return -1;
266
267 /* inform lws we have http body to send */
268 lws_client_http_body_pending(wsi, 1);
269 lws_callback_on_writable(wsi);
270 }
271 break;
272
273 case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
274 strcpy(buf + LWS_PRE, "text=hello&send=Send+the+form");
275 n = lws_write(wsi, (unsigned char *)&buf[LWS_PRE],
276 strlen(&buf[LWS_PRE]), LWS_WRITE_HTTP);
277 if (n < 0)
278 return -1;
279 /* we only had one thing to send, so inform lws we are done
280 * if we had more to send, call lws_callback_on_writable(wsi);
281 * and just return 0 from callback. On having sent the last
282 * part, call the below api instead.*/
283 lws_client_http_body_pending(wsi, 0);
284 break;
285
286 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
287 wsi_dumb = NULL;
288 force_exit = 1;
289 break;
290
291 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param) && \
292 !defined(LWS_WITH_MBEDTLS)
293 case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
294 if (crl_path[0]) {
295 /* Enable CRL checking of the server certificate */
296 X509_STORE *store;
297 X509_LOOKUP *lookup;
298 int n;
299 X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
300 X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
301 SSL_CTX_set1_param((SSL_CTX*)user, param);
302 store = SSL_CTX_get_cert_store((SSL_CTX*)user);
303 lookup = X509_STORE_add_lookup(store,
304 X509_LOOKUP_file());
305 n = X509_load_cert_crl_file(lookup, crl_path,
306 X509_FILETYPE_PEM);
307 X509_VERIFY_PARAM_free(param);
308 if (n != 1) {
309 char errbuf[256];
310 const char *es;
311
312 n = (int)ERR_get_error();
313 es = ERR_error_string(
314 #if defined(LWS_WITH_BORINGSSL)
315 (uint32_t)
316 #else
317 (unsigned long)
318 #endif
319 n, errbuf);
320 lwsl_err("EXTRA_CLIENT_VERIFY_CERTS: "
321 "SSL error: %s (%d)\n", es, n);
322 return 1;
323 }
324 }
325 break;
326 #endif
327
328 default:
329 break;
330 }
331
332 return 0;
333 }
334
335
336 /* lws-mirror_protocol */
337
338
339 static int
callback_lws_mirror(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)340 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
341 void *user, void *in, size_t len)
342 {
343 unsigned char buf[LWS_PRE + block_size], *p;
344 unsigned int rands[4];
345 int l = 0;
346 int n;
347
348 switch (reason) {
349 case LWS_CALLBACK_CLIENT_ESTABLISHED:
350
351 lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
352
353 if (flag_echo) {
354 rxb = txb = 0;
355 rx.cyc[0] = tx.cyc[0] = 0xabcde;
356 rx.cyc[1] = tx.cyc[1] = 0x23456789;
357
358 lws_callback_on_writable(wsi);
359
360 break;
361 }
362
363 lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
364 mirror_lifetime = (int)(16384 + (rands[0] & 65535));
365 /* useful to test single connection stability */
366 if (longlived)
367 mirror_lifetime += 500000;
368
369 lwsl_notice("opened mirror connection with "
370 "%d lifetime\n", mirror_lifetime);
371
372 /*
373 * mirror_lifetime is decremented each send, when it reaches
374 * zero the connection is closed in the send callback.
375 * When the close callback comes, wsi_mirror is set to NULL
376 * so a new connection will be opened
377 *
378 * start the ball rolling,
379 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
380 */
381 if (!flag_no_mirror_traffic)
382 lws_callback_on_writable(wsi);
383 break;
384
385 case LWS_CALLBACK_CLIENT_CLOSED:
386 lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d, "
387 "rxb %d, rx_count %d\n", mirror_lifetime, rxb,
388 rx_count);
389 wsi_mirror = NULL;
390 if (flag_echo || once)
391 force_exit = 1;
392 break;
393
394 case LWS_CALLBACK_CLIENT_WRITEABLE:
395 lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
396 if (flag_no_mirror_traffic)
397 return 0;
398
399 if (flag_echo) {
400 for (n = 0; n < (int)block_size; n++)
401 buf[LWS_PRE + n] = lws_poly_rand(&tx);
402
403 n = lws_write(wsi, &buf[LWS_PRE], block_size,
404 opts | LWS_WRITE_TEXT);
405 if (n < 0) {
406 lwsl_err("Error sending\n");
407 return -1;
408 }
409
410 txb++;
411 if (txb != count_blocks)
412 lws_callback_on_writable(wsi);
413 else {
414 lwsl_notice("send completed: %d x %d\n",
415 count_blocks, block_size);
416 }
417 break;
418 }
419
420 for (n = 0; n < 1; n++) {
421 lws_get_random(lws_get_context(wsi), rands,
422 sizeof(rands));
423 l += sprintf((char *)&buf[LWS_PRE + l],
424 "c #%06X %u %u %u;",
425 rands[0] & 0xffffff, /* colour */
426 rands[1] & 511, /* x */
427 rands[2] & 255, /* y */
428 (rands[3] & 31) + 1); /* radius */
429 }
430
431 n = (int)lws_write(wsi, &buf[LWS_PRE], (unsigned int)l,
432 opts | LWS_WRITE_TEXT);
433 if (n < 0)
434 return -1;
435 if (n < l) {
436 lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
437 return -1;
438 }
439 if (!justmirror)
440 mirror_lifetime--;
441 if (!mirror_lifetime) {
442 lwsl_notice("closing mirror session\n");
443 return -1;
444 }
445 /* get notified as soon as we can write again */
446 lws_callback_on_writable(wsi);
447
448 #if !defined(_WIN32) && !defined(WIN32)
449 usleep(50);
450 #endif
451 break;
452
453 case LWS_CALLBACK_CLIENT_RECEIVE:
454 if (flag_echo) {
455 p = (unsigned char *)in;
456 for (n = 0; n < (int)len; n++)
457 if (*p++ != lws_poly_rand(&rx)) {
458 lwsl_err("mismatch at rxb %d offset %d\n", (int)rxb + (n / block_size), n % block_size);
459 errs++;
460 force_exit = 1;
461 return -1;
462 }
463 rx_count += (unsigned int)(unsigned long long)len;
464 while (rx_count >= block_size) {
465 rx_count -= block_size;
466 rxb++;
467 }
468 if (rx_count == 0 && rxb == count_blocks) {
469 lwsl_notice("Everything received: errs %d\n",
470 errs);
471 force_exit = 1;
472 return -1;
473 }
474 }
475 break;
476 default:
477 break;
478 }
479
480 return 0;
481 }
482
483 static int
callback_test_raw_client(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)484 callback_test_raw_client(struct lws *wsi, enum lws_callback_reasons reason,
485 void *user, void *in, size_t len)
486 {
487 switch (reason) {
488 case LWS_CALLBACK_RAW_ADOPT:
489 lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
490 break;
491
492 case LWS_CALLBACK_RAW_RX:
493 lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
494 puts(in);
495 break;
496
497 case LWS_CALLBACK_RAW_CLOSE:
498 lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
499 break;
500
501 case LWS_CALLBACK_RAW_WRITEABLE:
502 lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
503 break;
504
505 default:
506 break;
507 }
508
509 return 0;
510 }
511
512 /* list of supported protocols and callbacks */
513
514 static const struct lws_protocols protocols[] = {
515 {
516 "dumb-increment-protocol",
517 callback_dumb_increment,
518 0, 20, 0, NULL, 0
519 },
520 {
521 "lws-mirror-protocol",
522 callback_lws_mirror,
523 0, 4096, 0, NULL, 0
524 }, {
525 "lws-test-raw-client",
526 callback_test_raw_client,
527 0, 128, 0, NULL, 0
528 },
529 LWS_PROTOCOL_LIST_TERM
530 };
531
532 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
533 static const struct lws_extension exts[] = {
534 {
535 "permessage-deflate",
536 lws_extension_callback_pm_deflate,
537 "permessage-deflate; client_no_context_takeover"
538 },
539 {
540 "deflate-frame",
541 lws_extension_callback_pm_deflate,
542 "deflate_frame"
543 },
544 { NULL, NULL, NULL /* terminator */ }
545 };
546 #endif
547
548
sighandler(int sig)549 void sighandler(int sig)
550 {
551 force_exit = 1;
552 }
553
554 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
555 static struct option options[] = {
556 { "help", no_argument, NULL, 'h' },
557 { "debug", required_argument, NULL, 'd' },
558 { "port", required_argument, NULL, 'p' },
559 { "ssl", no_argument, NULL, 's' },
560 { "strict-ssl", no_argument, NULL, 'S' },
561 { "version", required_argument, NULL, 'v' },
562 { "undeflated", no_argument, NULL, 'u' },
563 { "echo", no_argument, NULL, 'e' },
564 { "multi-test", no_argument, NULL, 'm' },
565 { "nomirror", no_argument, NULL, 'n' },
566 { "justmirror", no_argument, NULL, 'j' },
567 { "longlived", no_argument, NULL, 'l' },
568 { "post", no_argument, NULL, 'o' },
569 { "once", no_argument, NULL, 'O' },
570 { "ssl-cert", required_argument, NULL, 'C' },
571 { "ssl-key", required_argument, NULL, 'K' },
572 { "ssl-ca", required_argument, NULL, 'A' },
573 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
574 { "ssl-crl", required_argument, NULL, 'R' },
575 #endif
576 { NULL, 0, 0, 0 }
577 };
578 #endif
579
ratelimit_connects(unsigned int * last,unsigned int secs)580 static int ratelimit_connects(unsigned int *last, unsigned int secs)
581 {
582 struct timeval tv;
583
584 gettimeofday(&tv, NULL);
585
586 if ((unsigned long)tv.tv_sec - (unsigned long)(*last) < (unsigned long)secs)
587 return 0;
588
589 *last = (unsigned int)tv.tv_sec;
590
591 return 1;
592 }
593
main(int argc,char ** argv)594 int main(int argc, char **argv)
595 {
596 int n = 0, m, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
597 unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1, do_multi = 0;
598 struct lws_context_creation_info info;
599 struct lws_client_connect_info i;
600 struct lws_context *context;
601 const char *prot, *p;
602 char path[300];
603 char cert_path[1024] = "";
604 char key_path[1024] = "";
605 char ca_path[1024] = "";
606 unsigned long last = lws_now_secs();
607
608 memset(&info, 0, sizeof info);
609
610 lwsl_notice("libwebsockets test client - license MIT\n");
611 lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
612
613 if (argc < 2)
614 goto usage;
615
616 while (n >= 0) {
617 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
618 n = getopt_long(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO", options, NULL);
619 #else
620 n = getopt(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO");
621 #endif
622 if (n < 0)
623 continue;
624 switch (n) {
625 case 'd':
626 lws_set_log_level(atoi(optarg), NULL);
627 break;
628 case 's': /* lax SSL, allow selfsigned, skip checking hostname */
629 use_ssl = LCCSCF_USE_SSL |
630 LCCSCF_ALLOW_SELFSIGNED |
631 LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
632 break;
633 case 'S': /* Strict SSL, no selfsigned, check server hostname */
634 use_ssl = LCCSCF_USE_SSL;
635 break;
636 case 'p':
637 port = atoi(optarg);
638 break;
639 case 'e':
640 flag_echo = 1;
641 break;
642 case 'j':
643 justmirror = 1;
644 break;
645 case 'l':
646 longlived = 1;
647 break;
648 case 'v':
649 ietf_version = atoi(optarg);
650 break;
651 case 'u':
652 deny_deflate = 1;
653 break;
654 case 'm':
655 do_multi = 1;
656 break;
657 case 'o':
658 test_post = 1;
659 break;
660 case 'O':
661 once = 1;
662 break;
663 case 'n':
664 flag_no_mirror_traffic = 1;
665 lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
666 break;
667 case 'C':
668 lws_strncpy(cert_path, optarg, sizeof(cert_path));
669 break;
670 case 'K':
671 lws_strncpy(key_path, optarg, sizeof(key_path));
672 break;
673 case 'A':
674 lws_strncpy(ca_path, optarg, sizeof(ca_path));
675 break;
676
677 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
678 case 'R':
679 lws_strncpy(crl_path, optarg, sizeof(crl_path));
680 break;
681 #endif
682 case 'h':
683 goto usage;
684 }
685 }
686
687 if (optind >= argc)
688 goto usage;
689
690 signal(SIGINT, sighandler);
691
692 memset(&i, 0, sizeof(i));
693
694 i.port = port;
695 if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
696 goto usage;
697
698 /* add back the leading / on path */
699 if (p[0] != '/') {
700 path[0] = '/';
701 lws_strncpy(path + 1, p, sizeof(path) - 1);
702 i.path = path;
703 } else
704 i.path = p;
705
706 if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
707 use_ssl = 0;
708 if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
709 if (!use_ssl)
710 use_ssl = LCCSCF_USE_SSL;
711
712 lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
713
714 /*
715 * create the websockets context. This tracks open connections and
716 * knows how to route any traffic and which protocol version to use,
717 * and if each connection is client or server side.
718 *
719 * For this client-only demo, we tell it to not listen on any port.
720 */
721
722 info.port = CONTEXT_PORT_NO_LISTEN;
723 info.protocols = protocols;
724 info.gid = (gid_t)-1;
725 info.uid = (uid_t)-1;
726 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
727 info.extensions = exts;
728 #endif
729
730 /*
731 * since we know this lws context is only ever going to be used with
732 * a few client wsis / fds / sockets at a time, let lws know it doesn't
733 * have to use the default allocations for fd tables up to ulimit -n.
734 * It will just allocate for 2 internal and 4 that we might use.
735 */
736 info.fd_limit_per_thread = 2 + 4;
737
738 #if defined(LWS_WITH_TLS)
739 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
740 #endif
741
742 info.options |= LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
743 #if defined(LWS_WITH_TLS)
744 if (use_ssl) {
745 /*
746 * If the server wants us to present a valid SSL client certificate
747 * then we can set it up here.
748 */
749
750 if (cert_path[0])
751 info.client_ssl_cert_filepath = cert_path;
752 if (key_path[0])
753 info.client_ssl_private_key_filepath = key_path;
754
755 /*
756 * A CA cert and CRL can be used to validate the cert send by the server
757 */
758 if (ca_path[0])
759 info.client_ssl_ca_filepath = ca_path;
760
761 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
762 else if (crl_path[0])
763 lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
764 #endif
765 }
766
767 if (use_ssl & LCCSCF_USE_SSL) {
768 lwsl_notice(" Using SSL\n");
769 #if defined(LWS_WITH_MBEDTLS)
770 lwsl_notice(" (NOTE: mbedtls needs to be given the remote\n");
771 lwsl_notice(" CA cert to trust (with -A) to validate it)\n");
772 #endif
773 }
774 else
775 lwsl_notice(" SSL disabled\n");
776 if (use_ssl & LCCSCF_ALLOW_SELFSIGNED)
777 lwsl_notice(" Selfsigned certs allowed\n");
778 else
779 lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
780 if (use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)
781 lwsl_notice(" Skipping peer cert hostname check\n");
782 else
783 lwsl_notice(" Requiring peer cert hostname matches\n");
784 #endif
785 context = lws_create_context(&info);
786 if (context == NULL) {
787 fprintf(stderr, "Creating libwebsocket context failed\n");
788 return 1;
789 }
790
791 i.context = context;
792 i.ssl_connection = use_ssl;
793 i.host = i.address;
794 i.origin = i.address;
795 i.ietf_version_or_minus_one = ietf_version;
796
797 if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
798 lwsl_notice("using %s mode (non-ws)\n", prot);
799 if (test_post) {
800 i.method = "POST";
801 lwsl_notice("POST mode\n");
802 }
803 else
804 i.method = "GET";
805 do_ws = 0;
806 } else
807 if (!strcmp(prot, "raw")) {
808 i.method = "RAW";
809 i.protocol = "lws-test-raw-client";
810 lwsl_notice("using RAW mode connection\n");
811 do_ws = 0;
812 } else
813 lwsl_notice("using %s mode (ws)\n", prot);
814
815 /*
816 * sit there servicing the websocket context to handle incoming
817 * packets, and drawing random circles on the mirror protocol websocket
818 *
819 * nothing happens until the client websocket connection is
820 * asynchronously established... calling lws_client_connect() only
821 * instantiates the connection logically, lws_service() progresses it
822 * asynchronously.
823 */
824
825 m = 0;
826 while (!force_exit) {
827
828 if (do_multi) {
829 for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++) {
830 if (!wsi_multi[n] && ratelimit_connects(&rl_multi[n], 2u)) {
831 lwsl_notice("dumb %d: connecting\n", n);
832 i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
833 i.pwsi = &wsi_multi[n];
834 lws_client_connect_via_info(&i);
835 }
836 }
837 } else {
838
839 if (do_ws) {
840 if (!flag_echo && !justmirror && !wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
841 lwsl_notice("dumb: connecting\n");
842 i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
843 i.pwsi = &wsi_dumb;
844 lws_client_connect_via_info(&i);
845 }
846
847 if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
848 lwsl_notice("mirror: connecting\n");
849 i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
850 i.pwsi = &wsi_mirror;
851 wsi_mirror = lws_client_connect_via_info(&i);
852 }
853 } else
854 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
855 lwsl_notice("http: connecting\n");
856 i.pwsi = &wsi_dumb;
857 lws_client_connect_via_info(&i);
858 }
859 }
860
861 lws_service(context, 500);
862
863 if (do_multi) {
864 m++;
865 if (m == 10) {
866 m = 0;
867 lwsl_notice("doing lws_callback_on_writable_all_protocol\n");
868 lws_callback_on_writable_all_protocol(context,
869 &protocols[PROTOCOL_DUMB_INCREMENT]);
870 }
871 }
872
873 if (flag_echo && lws_now_secs() != last) {
874 lwsl_notice("rxb %d, rx_count %d\n", rxb, rx_count);
875 last = lws_now_secs();
876 }
877 }
878
879 lwsl_err("Exiting\n");
880 lws_context_destroy(context);
881
882 return ret;
883
884 usage:
885 fprintf(stderr, "Usage: libwebsockets-test-client "
886 "<server address> [--port=<p>] "
887 "[--ssl] [-k] [-v <ver>] "
888 "[-d <log bitfield>] [-l]\n");
889 return 1;
890 }
891