1 /*
2  * lws-minimal-raw-adopt-tcp
3  *
4  * Written in 2010-2019 by Andy Green <[email protected]>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * This demonstrates integrating somebody else's connected tcp
10  * socket into the lws event loop as a RAW wsi.  It's interesting in
11  * the kind of situation where you already have a connected socket
12  * in your application, and you need to hand it over to lws to deal with.
13  *
14  * Lws supports "adopting" these foreign sockets.
15  *
16  * If you simply want a connected client raw socket using lws alone, you
17  * can just use lws_client_connect_via_info() with info.method = "RAW".
18  *
19  */
20 
21 #include <libwebsockets.h>
22 #include <string.h>
23 #include <signal.h>
24 #if !defined(WIN32)
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28 #include <arpa/inet.h>
29 #endif
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #if !defined(WIN32)
35 #include <unistd.h>
36 #endif
37 #include <errno.h>
38 
39 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)40 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
41 			void *user, void *in, size_t len)
42 {
43 
44 	switch (reason) {
45 
46 	/* callbacks related to raw socket descriptor */
47 
48         case LWS_CALLBACK_RAW_ADOPT:
49 		lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
50 		lws_callback_on_writable(wsi);
51                 break;
52 
53 	case LWS_CALLBACK_RAW_CLOSE:
54 		lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
55 		break;
56 
57 	case LWS_CALLBACK_RAW_RX:
58 		lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
59 		lwsl_hexdump_level(LLL_NOTICE, in, len);
60 		break;
61 
62 	case LWS_CALLBACK_RAW_WRITEABLE:
63 		if (lws_write(wsi,
64 			      (uint8_t *)"GET / HTTP/1.1\xd\xa\xd\xa", 18,
65 			      LWS_WRITE_RAW) != 18) {
66 			lwsl_notice("%s: raw write failed\n", __func__);
67 			return 1;
68 		}
69 		break;
70 
71 	default:
72 		break;
73 	}
74 
75 	return 0;
76 }
77 
78 static struct lws_protocols protocols[] = {
79 	{ "raw-test", callback_raw_test, 0, 0, 0, NULL, 0 },
80 	LWS_PROTOCOL_LIST_TERM
81 };
82 
83 static int interrupted;
84 
sigint_handler(int sig)85 void sigint_handler(int sig)
86 {
87 	interrupted = 1;
88 }
89 
main(int argc,const char ** argv)90 int main(int argc, const char **argv)
91 {
92 	struct lws_context_creation_info info;
93 	struct lws_context *context;
94 	lws_sock_file_fd_type sock;
95 	struct addrinfo h, *r, *rp;
96 	struct lws_vhost *vhost;
97 	const char *p;
98 	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
99 			/* for LLL_ verbosity above NOTICE to be built into lws,
100 			 * lws must have been configured and built with
101 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
102 			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
103 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
104 			/* | LLL_DEBUG */;
105 
106 	signal(SIGINT, sigint_handler);
107 
108 	if ((p = lws_cmdline_option(argc, argv, "-d")))
109 		logs = atoi(p);
110 
111 	lws_set_log_level(logs, NULL);
112 	lwsl_user("LWS minimal raw adopt tcp\n");
113 
114 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
115 	info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
116 
117 	context = lws_create_context(&info);
118 	if (!context) {
119 		lwsl_err("lws init failed\n");
120 		return 1;
121 	}
122 
123 	info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
124 	info.protocols = protocols;
125 
126 	vhost = lws_create_vhost(context, &info);
127 	if (!vhost) {
128 		lwsl_err("lws vhost creation failed\n");
129 		goto bail;
130 	}
131 
132 	/*
133 	 * Connect our own "foreign" socket to libwebsockets.org:80
134 	 *
135 	 * Normally you would do this with lws_client_connect_via_info() inside
136 	 * the lws event loop, hiding all this detail.  But this example
137 	 * demonstrates how to integrate an externally-connected "foreign"
138 	 * socket, so we create one by hand.
139 	 */
140 
141 	memset(&h, 0, sizeof(h));
142 	h.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
143 	h.ai_socktype = SOCK_STREAM;
144 	h.ai_protocol = IPPROTO_TCP;
145 
146 	n = getaddrinfo("libwebsockets.org", "80", &h, &r);
147 	if (n) {
148 		lwsl_err("%s: problem resolving libwebsockets.org: %s\n", __func__, gai_strerror(n));
149 		return 1;
150 	}
151 
152 	for (rp = r; rp; rp = rp->ai_next) {
153 		sock.sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
154 		if (sock.sockfd != LWS_SOCK_INVALID)
155 			break;
156 	}
157 	if (!rp) {
158 		lwsl_err("%s: unable to create INET socket\n", __func__);
159 		freeaddrinfo(r);
160 
161 		return 1;
162 	}
163 
164 	lwsl_user("Starting connect...\n");
165 	if (connect(sock.sockfd, rp->ai_addr, sizeof(*rp->ai_addr)) < 0) {
166 		lwsl_err("%s: unable to connect to libwebsockets.org:80\n", __func__);
167 		freeaddrinfo(r);
168 		return 1;
169 	}
170 
171 	freeaddrinfo(r);
172 	signal(SIGINT, sigint_handler);
173 	lwsl_user("Connected...\n");
174 
175 	/* our foreign socket is connected... adopt it into lws */
176 
177 	if (!lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_SOCKET, sock,
178 				       protocols[0].name, NULL)) {
179 		lwsl_err("%s: foreign socket adoption failed\n", __func__);
180 		goto bail;
181 	}
182 
183 	while (n >= 0 && !interrupted)
184 		n = lws_service(context, 0);
185 
186 bail:
187 	lws_context_destroy(context);
188 
189 	return 0;
190 }
191