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,
519 20,
520 },
521 {
522 "lws-mirror-protocol",
523 callback_lws_mirror,
524 0,
525 4096,
526 }, {
527 "lws-test-raw-client",
528 callback_test_raw_client,
529 0,
530 128
531 },
532 { NULL, NULL, 0, 0 } /* end */
533 };
534
535 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
536 static const struct lws_extension exts[] = {
537 {
538 "permessage-deflate",
539 lws_extension_callback_pm_deflate,
540 "permessage-deflate; client_no_context_takeover"
541 },
542 {
543 "deflate-frame",
544 lws_extension_callback_pm_deflate,
545 "deflate_frame"
546 },
547 { NULL, NULL, NULL /* terminator */ }
548 };
549 #endif
550
551
sighandler(int sig)552 void sighandler(int sig)
553 {
554 force_exit = 1;
555 }
556
557 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
558 static struct option options[] = {
559 { "help", no_argument, NULL, 'h' },
560 { "debug", required_argument, NULL, 'd' },
561 { "port", required_argument, NULL, 'p' },
562 { "ssl", no_argument, NULL, 's' },
563 { "strict-ssl", no_argument, NULL, 'S' },
564 { "version", required_argument, NULL, 'v' },
565 { "undeflated", no_argument, NULL, 'u' },
566 { "echo", no_argument, NULL, 'e' },
567 { "multi-test", no_argument, NULL, 'm' },
568 { "nomirror", no_argument, NULL, 'n' },
569 { "justmirror", no_argument, NULL, 'j' },
570 { "longlived", no_argument, NULL, 'l' },
571 { "post", no_argument, NULL, 'o' },
572 { "once", no_argument, NULL, 'O' },
573 { "ssl-cert", required_argument, NULL, 'C' },
574 { "ssl-key", required_argument, NULL, 'K' },
575 { "ssl-ca", required_argument, NULL, 'A' },
576 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
577 { "ssl-crl", required_argument, NULL, 'R' },
578 #endif
579 { NULL, 0, 0, 0 }
580 };
581 #endif
582
ratelimit_connects(unsigned int * last,unsigned int secs)583 static int ratelimit_connects(unsigned int *last, unsigned int secs)
584 {
585 struct timeval tv;
586
587 gettimeofday(&tv, NULL);
588
589 if ((unsigned long)tv.tv_sec - (unsigned long)(*last) < (unsigned long)secs)
590 return 0;
591
592 *last = (unsigned int)tv.tv_sec;
593
594 return 1;
595 }
596
main(int argc,char ** argv)597 int main(int argc, char **argv)
598 {
599 int n = 0, m, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
600 unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1, do_multi = 0;
601 struct lws_context_creation_info info;
602 struct lws_client_connect_info i;
603 struct lws_context *context;
604 const char *prot, *p;
605 char path[300];
606 char cert_path[1024] = "";
607 char key_path[1024] = "";
608 char ca_path[1024] = "";
609 unsigned long last = lws_now_secs();
610
611 memset(&info, 0, sizeof info);
612
613 lwsl_notice("libwebsockets test client - license MIT\n");
614 lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
615
616 if (argc < 2)
617 goto usage;
618
619 while (n >= 0) {
620 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
621 n = getopt_long(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO", options, NULL);
622 #else
623 n = getopt(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO");
624 #endif
625 if (n < 0)
626 continue;
627 switch (n) {
628 case 'd':
629 lws_set_log_level(atoi(optarg), NULL);
630 break;
631 case 's': /* lax SSL, allow selfsigned, skip checking hostname */
632 use_ssl = LCCSCF_USE_SSL |
633 LCCSCF_ALLOW_SELFSIGNED |
634 LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
635 break;
636 case 'S': /* Strict SSL, no selfsigned, check server hostname */
637 use_ssl = LCCSCF_USE_SSL;
638 break;
639 case 'p':
640 port = atoi(optarg);
641 break;
642 case 'e':
643 flag_echo = 1;
644 break;
645 case 'j':
646 justmirror = 1;
647 break;
648 case 'l':
649 longlived = 1;
650 break;
651 case 'v':
652 ietf_version = atoi(optarg);
653 break;
654 case 'u':
655 deny_deflate = 1;
656 break;
657 case 'm':
658 do_multi = 1;
659 break;
660 case 'o':
661 test_post = 1;
662 break;
663 case 'O':
664 once = 1;
665 break;
666 case 'n':
667 flag_no_mirror_traffic = 1;
668 lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
669 break;
670 case 'C':
671 lws_strncpy(cert_path, optarg, sizeof(cert_path));
672 break;
673 case 'K':
674 lws_strncpy(key_path, optarg, sizeof(key_path));
675 break;
676 case 'A':
677 lws_strncpy(ca_path, optarg, sizeof(ca_path));
678 break;
679
680 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
681 case 'R':
682 lws_strncpy(crl_path, optarg, sizeof(crl_path));
683 break;
684 #endif
685 case 'h':
686 goto usage;
687 }
688 }
689
690 if (optind >= argc)
691 goto usage;
692
693 signal(SIGINT, sighandler);
694
695 memset(&i, 0, sizeof(i));
696
697 i.port = port;
698 if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
699 goto usage;
700
701 /* add back the leading / on path */
702 if (p[0] != '/') {
703 path[0] = '/';
704 lws_strncpy(path + 1, p, sizeof(path) - 1);
705 i.path = path;
706 } else
707 i.path = p;
708
709 if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
710 use_ssl = 0;
711 if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
712 if (!use_ssl)
713 use_ssl = LCCSCF_USE_SSL;
714
715 lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
716
717 /*
718 * create the websockets context. This tracks open connections and
719 * knows how to route any traffic and which protocol version to use,
720 * and if each connection is client or server side.
721 *
722 * For this client-only demo, we tell it to not listen on any port.
723 */
724
725 info.port = CONTEXT_PORT_NO_LISTEN;
726 info.protocols = protocols;
727 info.gid = (gid_t)-1;
728 info.uid = (uid_t)-1;
729 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
730 info.extensions = exts;
731 #endif
732
733 /*
734 * since we know this lws context is only ever going to be used with
735 * a few client wsis / fds / sockets at a time, let lws know it doesn't
736 * have to use the default allocations for fd tables up to ulimit -n.
737 * It will just allocate for 2 internal and 4 that we might use.
738 */
739 info.fd_limit_per_thread = 2 + 4;
740
741 #if defined(LWS_WITH_TLS)
742 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
743 #endif
744
745 info.options |= LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
746 #if defined(LWS_WITH_TLS)
747 if (use_ssl) {
748 /*
749 * If the server wants us to present a valid SSL client certificate
750 * then we can set it up here.
751 */
752
753 if (cert_path[0])
754 info.client_ssl_cert_filepath = cert_path;
755 if (key_path[0])
756 info.client_ssl_private_key_filepath = key_path;
757
758 /*
759 * A CA cert and CRL can be used to validate the cert send by the server
760 */
761 if (ca_path[0])
762 info.client_ssl_ca_filepath = ca_path;
763
764 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
765 else if (crl_path[0])
766 lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
767 #endif
768 }
769
770 if (use_ssl & LCCSCF_USE_SSL) {
771 lwsl_notice(" Using SSL\n");
772 #if defined(LWS_WITH_MBEDTLS)
773 lwsl_notice(" (NOTE: mbedtls needs to be given the remote\n");
774 lwsl_notice(" CA cert to trust (with -A) to validate it)\n");
775 #endif
776 }
777 else
778 lwsl_notice(" SSL disabled\n");
779 if (use_ssl & LCCSCF_ALLOW_SELFSIGNED)
780 lwsl_notice(" Selfsigned certs allowed\n");
781 else
782 lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
783 if (use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)
784 lwsl_notice(" Skipping peer cert hostname check\n");
785 else
786 lwsl_notice(" Requiring peer cert hostname matches\n");
787 #endif
788 context = lws_create_context(&info);
789 if (context == NULL) {
790 fprintf(stderr, "Creating libwebsocket context failed\n");
791 return 1;
792 }
793
794 i.context = context;
795 i.ssl_connection = use_ssl;
796 i.host = i.address;
797 i.origin = i.address;
798 i.ietf_version_or_minus_one = ietf_version;
799
800 if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
801 lwsl_notice("using %s mode (non-ws)\n", prot);
802 if (test_post) {
803 i.method = "POST";
804 lwsl_notice("POST mode\n");
805 }
806 else
807 i.method = "GET";
808 do_ws = 0;
809 } else
810 if (!strcmp(prot, "raw")) {
811 i.method = "RAW";
812 i.protocol = "lws-test-raw-client";
813 lwsl_notice("using RAW mode connection\n");
814 do_ws = 0;
815 } else
816 lwsl_notice("using %s mode (ws)\n", prot);
817
818 /*
819 * sit there servicing the websocket context to handle incoming
820 * packets, and drawing random circles on the mirror protocol websocket
821 *
822 * nothing happens until the client websocket connection is
823 * asynchronously established... calling lws_client_connect() only
824 * instantiates the connection logically, lws_service() progresses it
825 * asynchronously.
826 */
827
828 m = 0;
829 while (!force_exit) {
830
831 if (do_multi) {
832 for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++) {
833 if (!wsi_multi[n] && ratelimit_connects(&rl_multi[n], 2u)) {
834 lwsl_notice("dumb %d: connecting\n", n);
835 i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
836 i.pwsi = &wsi_multi[n];
837 lws_client_connect_via_info(&i);
838 }
839 }
840 } else {
841
842 if (do_ws) {
843 if (!flag_echo && !justmirror && !wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
844 lwsl_notice("dumb: connecting\n");
845 i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
846 i.pwsi = &wsi_dumb;
847 lws_client_connect_via_info(&i);
848 }
849
850 if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
851 lwsl_notice("mirror: connecting\n");
852 i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
853 i.pwsi = &wsi_mirror;
854 wsi_mirror = lws_client_connect_via_info(&i);
855 }
856 } else
857 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
858 lwsl_notice("http: connecting\n");
859 i.pwsi = &wsi_dumb;
860 lws_client_connect_via_info(&i);
861 }
862 }
863
864 lws_service(context, 500);
865
866 if (do_multi) {
867 m++;
868 if (m == 10) {
869 m = 0;
870 lwsl_notice("doing lws_callback_on_writable_all_protocol\n");
871 lws_callback_on_writable_all_protocol(context,
872 &protocols[PROTOCOL_DUMB_INCREMENT]);
873 }
874 }
875
876 if (flag_echo && lws_now_secs() != last) {
877 lwsl_notice("rxb %d, rx_count %d\n", rxb, rx_count);
878 last = lws_now_secs();
879 }
880 }
881
882 lwsl_err("Exiting\n");
883 lws_context_destroy(context);
884
885 return ret;
886
887 usage:
888 fprintf(stderr, "Usage: libwebsockets-test-client "
889 "<server address> [--port=<p>] "
890 "[--ssl] [-k] [-v <ver>] "
891 "[-d <log bitfield>] [-l]\n");
892 return 1;
893 }
894