coverity 182068: 155650: unnecessary check against illegal NULL
[platform/upstream/libwebsockets.git] / test-server / test-echo.c
1 /*
2  * libwebsockets-test-echo
3  *
4  * Copyright (C) 2010-2016 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 <stdio.h>
22 #include <stdlib.h>
23 #include <getopt.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <signal.h>
27
28 #include "../lib/libwebsockets.h"
29
30 #ifndef _WIN32
31 #include <syslog.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34 #else
35 #include "gettimeofday.h"
36 #include <process.h>
37 #endif
38
39 static volatile int force_exit = 0;
40 static int versa, state;
41 static int times = -1;
42
43 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
44
45 #define MAX_ECHO_PAYLOAD 1024
46
47 struct per_session_data__echo {
48         size_t rx, tx;
49         unsigned char buf[LWS_PRE + MAX_ECHO_PAYLOAD];
50         unsigned int len;
51         unsigned int index;
52         int final;
53         int continuation;
54         int binary;
55 };
56
57 static int
58 callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user,
59               void *in, size_t len)
60 {
61         struct per_session_data__echo *pss =
62                         (struct per_session_data__echo *)user;
63         int n;
64
65         switch (reason) {
66
67 #ifndef LWS_NO_SERVER
68
69         case LWS_CALLBACK_ESTABLISHED:
70                 pss->index = 0;
71                 pss->len = -1;
72                 break;
73
74         case LWS_CALLBACK_SERVER_WRITEABLE:
75 do_tx:
76
77                 n = LWS_WRITE_CONTINUATION;
78                 if (!pss->continuation) {
79                         if (pss->binary)
80                                 n = LWS_WRITE_BINARY;
81                         else
82                                 n = LWS_WRITE_TEXT;
83                         pss->continuation = 1;
84                 }
85                 if (!pss->final)
86                         n |= LWS_WRITE_NO_FIN;
87                 lwsl_info("+++ test-echo: writing %d, with final %d\n",
88                           pss->len, pss->final);
89
90                 pss->tx += pss->len;
91                 n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, n);
92                 if (n < 0) {
93                         lwsl_err("ERROR %d writing to socket, hanging up\n", n);
94                         return 1;
95                 }
96                 if (n < (int)pss->len) {
97                         lwsl_err("Partial write\n");
98                         return -1;
99                 }
100                 pss->len = -1;
101                 if (pss->final)
102                         pss->continuation = 0;
103                 lws_rx_flow_control(wsi, 1);
104                 break;
105
106         case LWS_CALLBACK_RECEIVE:
107 do_rx:
108                 pss->final = lws_is_final_fragment(wsi);
109                 pss->binary = lws_frame_is_binary(wsi);
110                 lwsl_info("+++ test-echo: RX len %ld final %ld, pss->len=%ld\n",
111                           (long)len, (long)pss->final, (long)pss->len);
112
113                 memcpy(&pss->buf[LWS_PRE], in, len);
114                 assert((int)pss->len == -1);
115                 pss->len = (unsigned int)len;
116                 pss->rx += len;
117
118                 lws_rx_flow_control(wsi, 0);
119                 lws_callback_on_writable(wsi);
120                 break;
121 #endif
122
123 #ifndef LWS_NO_CLIENT
124         /* when the callback is used for client operations --> */
125
126         case LWS_CALLBACK_CLOSED:
127         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
128                 lwsl_debug("closed\n");
129                 state = 0;
130                 break;
131
132         case LWS_CALLBACK_CLIENT_ESTABLISHED:
133                 lwsl_debug("Client has connected\n");
134                 pss->index = 0;
135                 pss->len = -1;
136                 state = 2;
137                 break;
138
139         case LWS_CALLBACK_CLIENT_RECEIVE:
140 #ifndef LWS_NO_SERVER
141                 if (versa)
142                         goto do_rx;
143 #endif
144                 lwsl_notice("Client RX: %s", (char *)in);
145                 break;
146
147         case LWS_CALLBACK_CLIENT_WRITEABLE:
148 #ifndef LWS_NO_SERVER
149                 if (versa) {
150                         if (pss->len != (unsigned int)-1)
151                                 goto do_tx;
152                         break;
153                 }
154 #endif
155                 /* we will send our packet... */
156                 pss->len = sprintf((char *)&pss->buf[LWS_PRE],
157                                    "hello from libwebsockets-test-echo client pid %d index %d\n",
158                                    getpid(), pss->index++);
159                 lwsl_notice("Client TX: %s", &pss->buf[LWS_PRE]);
160                 n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, LWS_WRITE_TEXT);
161                 if (n < 0) {
162                         lwsl_err("ERROR %d writing to socket, hanging up\n", n);
163                         return -1;
164                 }
165                 if (n < (int)pss->len) {
166                         lwsl_err("Partial write\n");
167                         return -1;
168                 }
169                 break;
170 #endif
171
172         default:
173                 break;
174         }
175
176         return 0;
177 }
178
179
180
181 static struct lws_protocols protocols[] = {
182         /* first protocol must always be HTTP handler */
183
184         {
185                 "",             /* name - can be overridden with -e */
186                 callback_echo,
187                 sizeof(struct per_session_data__echo),  /* per_session_data_size */
188                 MAX_ECHO_PAYLOAD,
189         },
190         {
191                 NULL, NULL, 0           /* End of list */
192         }
193 };
194
195 static const struct lws_extension exts[] = {
196         {
197                 "permessage-deflate",
198                 lws_extension_callback_pm_deflate,
199                 "permessage-deflate; client_no_context_takeover; client_max_window_bits"
200         },
201         { NULL, NULL, NULL /* terminator */ }
202 };
203
204
205 void sighandler(int sig)
206 {
207         force_exit = 1;
208 }
209
210 static struct option options[] = {
211         { "help",       no_argument,            NULL, 'h' },
212         { "debug",      required_argument,      NULL, 'd' },
213         { "port",       required_argument,      NULL, 'p' },
214         { "ssl-cert",   required_argument,      NULL, 'C' },
215         { "ssl-key",    required_argument,      NULL, 'k' },
216 #ifndef LWS_NO_CLIENT
217         { "client",     required_argument,      NULL, 'c' },
218         { "ratems",     required_argument,      NULL, 'r' },
219 #endif
220         { "ssl",        no_argument,            NULL, 's' },
221         { "versa",      no_argument,            NULL, 'v' },
222         { "uri",        required_argument,      NULL, 'u' },
223         { "passphrase", required_argument,      NULL, 'P' },
224         { "interface",  required_argument,      NULL, 'i' },
225         { "times",      required_argument,      NULL, 'n' },
226         { "echogen",    no_argument,            NULL, 'e' },
227 #ifndef LWS_NO_DAEMONIZE
228         { "daemonize",  no_argument,            NULL, 'D' },
229 #endif
230         { NULL, 0, 0, 0 }
231 };
232
233 int main(int argc, char **argv)
234 {
235         int n = 0;
236         int port = 7681;
237         int use_ssl = 0;
238         struct lws_context *context;
239         int opts = 0;
240         char interface_name[128] = "";
241         const char *_interface = NULL;
242         char ssl_cert[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
243         char ssl_key[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
244 #ifndef _WIN32
245 /* LOG_PERROR is not POSIX standard, and may not be portable */
246 #ifdef __sun
247         int syslog_options = LOG_PID;
248 #else
249         int syslog_options = LOG_PID | LOG_PERROR;
250 #endif
251 #endif
252         int client = 0;
253         int listen_port = 80;
254         struct lws_context_creation_info info;
255         char passphrase[256];
256         char uri[256] = "/";
257 #ifndef LWS_NO_CLIENT
258         char address[256], ads_port[256 + 30];
259         int rate_us = 250000;
260         unsigned long long oldus;
261         struct lws *wsi;
262         int disallow_selfsigned = 0;
263         struct timeval tv;
264         const char *connect_protocol = NULL;
265         struct lws_client_connect_info i;
266 #endif
267
268         int debug_level = 7;
269 #ifndef LWS_NO_DAEMONIZE
270         int daemonize = 0;
271 #endif
272
273         memset(&info, 0, sizeof info);
274
275 #ifndef LWS_NO_CLIENT
276         lwsl_notice("Built to support client operations\n");
277 #endif
278 #ifndef LWS_NO_SERVER
279         lwsl_notice("Built to support server operations\n");
280 #endif
281
282         while (n >= 0) {
283                 n = getopt_long(argc, argv, "i:hsp:d:DC:k:P:vu:n:e"
284 #ifndef LWS_NO_CLIENT
285                         "c:r:"
286 #endif
287                                 , options, NULL);
288                 if (n < 0)
289                         continue;
290                 switch (n) {
291                 case 'P':
292                         strncpy(passphrase, optarg, sizeof(passphrase));
293                         passphrase[sizeof(passphrase) - 1] = '\0';
294                         info.ssl_private_key_password = passphrase;
295                         break;
296                 case 'C':
297                         strncpy(ssl_cert, optarg, sizeof(ssl_cert));
298                         ssl_cert[sizeof(ssl_cert) - 1] = '\0';
299                         disallow_selfsigned = 1;
300                         break;
301                 case 'k':
302                         strncpy(ssl_key, optarg, sizeof(ssl_key));
303                         ssl_key[sizeof(ssl_key) - 1] = '\0';
304                         break;
305                 case 'u':
306                         strncpy(uri, optarg, sizeof(uri));
307                         uri[sizeof(uri) - 1] = '\0';
308                         break;
309
310 #ifndef LWS_NO_DAEMONIZE
311                 case 'D':
312                         daemonize = 1;
313 #if !defined(_WIN32) && !defined(__sun)
314                         syslog_options &= ~LOG_PERROR;
315 #endif
316                         break;
317 #endif
318 #ifndef LWS_NO_CLIENT
319                 case 'c':
320                         client = 1;
321                         strncpy(address, optarg, sizeof(address) - 1);
322                         address[sizeof(address) - 1] = '\0';
323                         port = 80;
324                         break;
325                 case 'r':
326                         rate_us = atoi(optarg) * 1000;
327                         break;
328 #endif
329                 case 'd':
330                         debug_level = atoi(optarg);
331                         break;
332                 case 's':
333                         use_ssl = 1; /* 1 = take care about cert verification, 2 = allow anything */
334                         break;
335                 case 'p':
336                         port = atoi(optarg);
337                         break;
338                 case 'v':
339                         versa = 1;
340                         break;
341                 case 'e':
342                         protocols[0].name = "lws-echogen";
343                         connect_protocol = protocols[0].name;
344                         lwsl_err("using lws-echogen\n");
345                         break;
346                 case 'i':
347                         strncpy(interface_name, optarg, sizeof interface_name);
348                         interface_name[(sizeof interface_name) - 1] = '\0';
349                         _interface = interface_name;
350                         break;
351                 case 'n':
352                         times = atoi(optarg);
353                         break;
354                 case '?':
355                 case 'h':
356                         fprintf(stderr, "Usage: libwebsockets-test-echo\n"
357                                 "  --debug      / -d <debug bitfield>\n"
358                                 "  --port       / -p <port>\n"
359                                 "  --ssl-cert   / -C <cert path>\n"
360                                 "  --ssl-key    / -k <key path>\n"
361 #ifndef LWS_NO_CLIENT
362                                 "  --client     / -c <server IP>\n"
363                                 "  --ratems     / -r <rate in ms>\n"
364 #endif
365                                 "  --ssl        / -s\n"
366                                 "  --passphrase / -P <passphrase>\n"
367                                 "  --interface  / -i <interface>\n"
368                                 "  --uri        / -u <uri path>\n"
369                                 "  --times      / -n <-1 unlimited or times to echo>\n"
370 #ifndef LWS_NO_DAEMONIZE
371                                 "  --daemonize  / -D\n"
372 #endif
373                         );
374                         exit(1);
375                 }
376         }
377
378 #ifndef LWS_NO_DAEMONIZE
379         /*
380          * normally lock path would be /var/lock/lwsts or similar, to
381          * simplify getting started without having to take care about
382          * permissions or running as root, set to /tmp/.lwsts-lock
383          */
384 #if defined(WIN32) || defined(_WIN32)
385 #else
386         if (!client && daemonize && lws_daemonize("/tmp/.lwstecho-lock")) {
387                 fprintf(stderr, "Failed to daemonize\n");
388                 return 1;
389         }
390 #endif
391 #endif
392
393 #ifndef _WIN32
394         /* we will only try to log things according to our debug_level */
395         setlogmask(LOG_UPTO (LOG_DEBUG));
396         openlog("lwsts", syslog_options, LOG_DAEMON);
397 #endif
398
399         /* tell the library what debug level to emit and to send it to syslog */
400         lws_set_log_level(debug_level, lwsl_emit_syslog);
401
402         lwsl_notice("libwebsockets test server echo - license LGPL2.1+SLE\n");
403         lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
404
405 #ifndef LWS_NO_CLIENT
406         if (client) {
407                 lwsl_notice("Running in client mode\n");
408                 listen_port = CONTEXT_PORT_NO_LISTEN;
409                 if (use_ssl && !disallow_selfsigned) {
410                         lwsl_info("allowing selfsigned\n");
411                         use_ssl = 2;
412                 } else {
413                         lwsl_info("requiring server cert validation against %s\n",
414                                   ssl_cert);
415                         info.ssl_ca_filepath = ssl_cert;
416                 }
417         } else {
418 #endif
419 #ifndef LWS_NO_SERVER
420                 lwsl_notice("Running in server mode\n");
421                 listen_port = port;
422 #endif
423 #ifndef LWS_NO_CLIENT
424         }
425 #endif
426
427         info.port = listen_port;
428         info.iface = _interface;
429         info.protocols = protocols;
430         if (use_ssl && !client) {
431                 info.ssl_cert_filepath = ssl_cert;
432                 info.ssl_private_key_filepath = ssl_key;
433         } else
434                 if (use_ssl && client) {
435                         info.ssl_cert_filepath = NULL;
436                         info.ssl_private_key_filepath = NULL;
437                 }
438         info.gid = -1;
439         info.uid = -1;
440         info.extensions = exts;
441         info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
442
443         if (use_ssl)
444                 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
445 #ifndef LWS_NO_EXTENSIONS
446         info.extensions = exts;
447 #endif
448
449         context = lws_create_context(&info);
450         if (context == NULL) {
451                 lwsl_err("libwebsocket init failed\n");
452                 return -1;
453         }
454
455
456         signal(SIGINT, sighandler);
457
458 #ifndef LWS_NO_CLIENT
459         gettimeofday(&tv, NULL);
460         oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
461 #endif
462
463         n = 0;
464         while (n >= 0 && !force_exit) {
465 #ifndef LWS_NO_CLIENT
466                 if (client && !state && times) {
467                         state = 1;
468                         lwsl_notice("Client connecting to %s:%u....\n",
469                                     address, port);
470                         /* we are in client mode */
471
472                         address[sizeof(address) - 1] = '\0';
473                         sprintf(ads_port, "%s:%u", address, port & 65535);
474                         if (times > 0)
475                                 times--;
476
477                         memset(&i, 0, sizeof(i));
478
479                         i.context = context;
480                         i.address = address;
481                         i.port = port;
482                         i.ssl_connection = use_ssl;
483                         i.path = uri;
484                         i.host = ads_port;
485                         i.origin = ads_port;
486                         i.protocol = connect_protocol;
487
488                         wsi = lws_client_connect_via_info(&i);
489                         if (!wsi) {
490                                 lwsl_err("Client failed to connect to %s:%u\n",
491                                          address, port);
492                                 goto bail;
493                         }
494                 }
495
496                 if (client && !versa && times) {
497                         gettimeofday(&tv, NULL);
498
499                         if (((((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec) - oldus) > rate_us) {
500                                 lws_callback_on_writable_all_protocol(context,
501                                                 &protocols[0]);
502                                 oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
503                                 if (times > 0)
504                                         times--;
505                         }
506                 }
507
508                 if (client && !state && !times)
509                         break;
510 #endif
511                 n = lws_service(context, 10);
512         }
513 #ifndef LWS_NO_CLIENT
514 bail:
515 #endif
516         lws_context_destroy(context);
517
518         lwsl_notice("libwebsockets-test-echo exited cleanly\n");
519 #ifndef _WIN32
520         closelog();
521 #endif
522
523         return 0;
524 }