Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / minimal-examples / raw / minimal-raw-netcat / minimal-raw-netcat.c
1 /*
2  * lws-minimal-raw-netcat
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  * This demonstrates sending stdin to a remote socket and printing
10  * what is returned to stdout.
11  *
12  * All the logging is on stderr, so you can tune it out with 2>log
13  * or whatever.
14  */
15
16 #include <libwebsockets.h>
17 #include <string.h>
18 #include <signal.h>
19 #if !defined(WIN32)
20 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24 #include <arpa/inet.h>
25 #endif
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 static struct lws *raw_wsi, *stdin_wsi;
33 static uint8_t buf[LWS_PRE + 4096];
34 static int waiting, interrupted;
35 static struct lws_context *context;
36 static int us_wait_after_input_close = LWS_USEC_PER_SEC / 10;
37
38 static int
39 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
40                   void *user, void *in, size_t len)
41 {
42         const char *cp = (const char *)in;
43
44         switch (reason) {
45
46         /* callbacks related to file descriptor */
47
48         case LWS_CALLBACK_RAW_ADOPT_FILE:
49                 lwsl_user("LWS_CALLBACK_RAW_ADOPT_FILE\n");
50                 break;
51
52         case LWS_CALLBACK_RAW_CLOSE_FILE:
53                 lwsl_user("LWS_CALLBACK_RAW_CLOSE_FILE\n");
54                 /* stdin close, wait 1s then close the raw skt */
55                 stdin_wsi = NULL; /* invalid now we close */
56                 if (raw_wsi)
57                         lws_set_timer_usecs(raw_wsi, us_wait_after_input_close);
58                 else {
59                         interrupted = 1;
60                         lws_cancel_service(context);
61                 }
62                 break;
63
64         case LWS_CALLBACK_RAW_RX_FILE:
65                 lwsl_user("LWS_CALLBACK_RAW_RX_FILE\n");
66                 waiting = read(0, buf, sizeof(buf));
67                 lwsl_notice("raw file read %d\n", waiting);
68                 if (waiting < 0)
69                         return -1;
70
71                 if (raw_wsi)
72                         lws_callback_on_writable(raw_wsi);
73                 lws_rx_flow_control(wsi, 0);
74                 break;
75
76
77         /* callbacks related to raw socket descriptor */
78
79         case LWS_CALLBACK_RAW_ADOPT:
80                 lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
81                 lws_callback_on_writable(wsi);
82                 break;
83
84         case LWS_CALLBACK_RAW_CLOSE:
85                 lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
86                 /*
87                  * If the socket to the remote server closed, we must close
88                  * and drop any remaining stdin
89                  */
90                 interrupted = 1;
91                 lws_cancel_service(context);
92                 /* our pointer to this wsi is invalid now we close */
93                 raw_wsi = NULL;
94                 break;
95
96         case LWS_CALLBACK_RAW_RX:
97                 lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
98                 while (len--)
99                         putchar(*cp++);
100                 fflush(stdout);
101                 break;
102
103         case LWS_CALLBACK_RAW_WRITEABLE:
104                 lwsl_user("LWS_CALLBACK_RAW_WRITEABLE\n");
105                 // lwsl_hexdump_info(buf, waiting);
106                 if (stdin_wsi)
107                         lws_rx_flow_control(stdin_wsi, 1);
108                 if (lws_write(wsi, buf, waiting, LWS_WRITE_RAW) != waiting) {
109                         lwsl_notice("%s: raw skt write failed\n", __func__);
110
111                         return -1;
112                 }
113                 break;
114
115         case LWS_CALLBACK_TIMER:
116                 lwsl_user("LWS_CALLBACK_TIMER\n");
117                 interrupted = 1;
118                 lws_cancel_service(context);
119                 return -1;
120
121         default:
122                 break;
123         }
124
125         return 0;
126 }
127
128 static struct lws_protocols protocols[] = {
129         { "raw-test", callback_raw_test, 0, 0 },
130         { NULL, NULL, 0, 0 } /* terminator */
131 };
132
133 void sigint_handler(int sig)
134 {
135         interrupted = 1;
136 }
137
138 int main(int argc, const char **argv)
139 {
140         const char *server = "libwebsockets.org", *port = "80";
141         struct lws_context_creation_info info;
142         lws_sock_file_fd_type sock;
143         struct addrinfo h, *r, *rp;
144         struct lws_vhost *vhost;
145         const char *p;
146         int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
147
148         signal(SIGINT, sigint_handler);
149
150         if ((p = lws_cmdline_option(argc, argv, "-d")))
151                 logs = atoi(p);
152
153         lws_set_log_level(logs, NULL);
154         lwsl_user("LWS minimal raw netcat [--server ip] [--port port] [-w ms]\n");
155
156         memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
157         info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
158
159         context = lws_create_context(&info);
160         if (!context) {
161                 lwsl_err("lws init failed\n");
162                 return 1;
163         }
164
165         info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
166         info.protocols = protocols;
167
168         vhost = lws_create_vhost(context, &info);
169         if (!vhost) {
170                 lwsl_err("lws vhost creation failed\n");
171                 goto bail;
172         }
173
174         /*
175          * Connect our own "foreign" socket to libwebsockets.org:80
176          *
177          * Normally you would do this with lws_client_connect_via_info() inside
178          * the lws event loop, hiding all this detail.  But this example
179          * demonstrates how to integrate an externally-connected "foreign"
180          * socket, so we create one by hand.
181          */
182
183         memset(&h, 0, sizeof(h));
184         h.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
185         h.ai_socktype = SOCK_STREAM;
186         h.ai_protocol = IPPROTO_TCP;
187
188         if ((p = lws_cmdline_option(argc, argv, "--port")))
189                 port = p;
190
191         if ((p = lws_cmdline_option(argc, argv, "--server")))
192                 server = p;
193
194         if ((p = lws_cmdline_option(argc, argv, "-w")))
195                 us_wait_after_input_close = 1000 * atoi(p);
196
197         n = getaddrinfo(server, port, &h, &r);
198         if (n) {
199                 lwsl_err("%s: problem resolving %s: %s\n", __func__, 
200                          server, gai_strerror(n));
201                 return 1;
202         }
203
204         for (rp = r; rp; rp = rp->ai_next) {
205                 sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
206                                      rp->ai_protocol);
207                 if (sock.sockfd != LWS_SOCK_INVALID)
208                         break;
209         }
210         if (!rp) {
211                 lwsl_err("%s: unable to create INET socket\n", __func__);
212                 freeaddrinfo(r);
213
214                 return 1;
215         }
216
217         lwsl_user("Starting connect to %s:%s...\n", server, port);
218         if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) {
219                 lwsl_err("%s: unable to connect\n", __func__);
220                 freeaddrinfo(r);
221                 return 1;
222         }
223
224         freeaddrinfo(r);
225         signal(SIGINT, sigint_handler);
226         lwsl_user("Connected...\n");
227
228         /* our foreign socket is connected... adopt it into lws */
229
230         raw_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock,
231                                              protocols[0].name, NULL);
232         if (!raw_wsi) {
233                 lwsl_err("%s: foreign socket adoption failed\n", __func__);
234                 goto bail;
235         }
236
237         sock.filefd = 0;
238         stdin_wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_FILE_DESC,
239                                                sock, protocols[0].name, NULL);
240         if (!stdin_wsi) {
241                 lwsl_err("%s: stdin adoption failed\n", __func__);
242                 goto bail;
243         }
244
245         while (n >= 0 && !interrupted)
246                 n = lws_service(context, 0);
247
248 bail:
249
250         lwsl_user("%s: destroying context\n", __func__);
251
252         lws_context_destroy(context);
253
254         return 0;
255 }