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