• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ws protocol handler plugin for testing raw file and raw socket
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  * These test plugins 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  * This plugin test both raw file descriptors and raw socket descriptors.  It
21  * can test both or just one depending on how you configure it.  libwebsockets-
22  * test-server-v2.0 is set up to test both.
23  *
24  * RAW File Descriptor Testing
25  * ===========================
26  *
27  * Enable on a vhost like this
28  *
29  *        "protocol-lws-raw-test": {
30  *                 "status": "ok",
31  *                 "fifo-path": "/tmp/lws-test-raw"
32  *        },
33  *
34  * Then you can feed it data through the FIFO like this
35  *
36  *  $ sudo sh -c "echo hello > /tmp/lws-test-raw"
37  *
38  * This plugin simply prints the data.  But it does it through the lws event
39  * loop / service poll.
40  *
41  *
42  * RAW Socket Descriptor Testing
43  * =============================
44  *
45  * 1) You must give the vhost the option flag
46  * 	LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG
47  *
48  * 2) Enable on a vhost like this
49  *
50  *        "protocol-lws-raw-test": {
51  *                 "status": "ok",
52  *                 "raw": "1"
53  *        },
54  *
55  *    The "raw" pvo marks this protocol as being used for RAW connections.
56  *
57  * 3) Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
58  *
59  *    telnet 127.0.0.1 7681
60  *
61  *    type something that isn't a valid HTTP method and enter, before the
62  *    connection times out.  The connection will switch to RAW mode using this
63  *    protocol, and pass the unused rx as a raw RX callback.
64  *
65  *    The test protocol echos back what was typed on telnet to telnet.
66  */
67 
68 #if !defined (LWS_PLUGIN_STATIC)
69 #if !defined(LWS_DLL)
70 #define LWS_DLL
71 #endif
72 #if !defined(LWS_INTERNAL)
73 #define LWS_INTERNAL
74 #endif
75 #include <libwebsockets.h>
76 #endif
77 
78 #include <string.h>
79 #include <fcntl.h>
80 
81 #include <sys/stat.h>
82 
83 struct per_vhost_data__raw_test {
84 	struct lws_context *context;
85 	struct lws_vhost *vhost;
86 	const struct lws_protocols *protocol;
87 	char fifo_path[100];
88 	int fifo;
89 	char zero_length_read;
90 };
91 
92 struct per_session_data__raw_test {
93 	int number;
94 	unsigned char buf[128];
95 	int len;
96 };
97 
98 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)99 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, void *user,
100 		  void *in, size_t len)
101 {
102 	struct per_session_data__raw_test *pss =
103 			(struct per_session_data__raw_test *)user;
104 	struct per_vhost_data__raw_test *vhd =
105 			(struct per_vhost_data__raw_test *)
106 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
107 					lws_get_protocol(wsi));
108 	lws_sock_file_fd_type u;
109 
110 	(void)pss;
111 
112 	switch (reason) {
113 	case LWS_CALLBACK_PROTOCOL_INIT:
114 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
115 				lws_get_protocol(wsi),
116 				sizeof(struct per_vhost_data__raw_test));
117 		if (!vhd)
118 			return 0;
119 		vhd->context = lws_get_context(wsi);
120 		vhd->protocol = lws_get_protocol(wsi);
121 		vhd->vhost = lws_get_vhost(wsi);
122 		{
123 			const struct lws_protocol_vhost_options *pvo =
124 				(const struct lws_protocol_vhost_options *)in;
125 			while (pvo) {
126 				if (!strcmp(pvo->name, "fifo-path"))
127 					lws_strncpy(vhd->fifo_path, pvo->value,
128 							sizeof(vhd->fifo_path));
129 				pvo = pvo->next;
130 			}
131 			if (vhd->fifo_path[0] == '\0') {
132 				lwsl_warn("%s: Missing pvo \"fifo-path\", "
133 					 "raw file fd testing disabled\n",
134 					 __func__);
135 				break;
136 			}
137 		}
138 		unlink(vhd->fifo_path);
139 		if (mkfifo(vhd->fifo_path, 0666)) {
140 			lwsl_err("mkfifo failed\n");
141 			return 1;
142 		}
143 		vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
144 		if (vhd->fifo == -1) {
145 			lwsl_err("opening fifo failed\n");
146 			unlink(vhd->fifo_path);
147 			return 1;
148 		}
149 		lwsl_notice("FIFO %s created\n", vhd->fifo_path);
150 		u.filefd = vhd->fifo;
151 		if (!lws_adopt_descriptor_vhost(vhd->vhost,
152 						LWS_ADOPT_RAW_FILE_DESC, u,
153 						"protocol-lws-raw-test",
154 						NULL)) {
155 			lwsl_err("Failed to adopt fifo descriptor\n");
156 			close(vhd->fifo);
157 			unlink(vhd->fifo_path);
158 			return 1;
159 		}
160 		break;
161 
162 	case LWS_CALLBACK_PROTOCOL_DESTROY:
163 		if (!vhd)
164 			break;
165 		if (vhd->fifo >= 0) {
166 			close(vhd->fifo);
167 			unlink(vhd->fifo_path);
168 		}
169 		break;
170 
171 
172 	/*
173 	 * Callbacks related to Raw file descriptor testing
174 	 */
175 
176 	case LWS_CALLBACK_RAW_ADOPT_FILE:
177 		lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
178 		break;
179 
180 
181 	case LWS_CALLBACK_RAW_RX_FILE:
182 		lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n");
183 		{
184 			char buf[256];
185 			int n;
186 
187 			n = (int)read(vhd->fifo, buf, sizeof(buf) - 1);
188 			if (n < 0) {
189 				lwsl_err("FIFO read failed\n");
190 				return 1;
191 			}
192 			/*
193 			 * When nobody opened the other side of the FIFO, the
194 			 * FIFO fd acts well and only signals POLLIN when
195 			 * somebody opened and wrote to it.
196 			 *
197 			 * But if the other side of the FIFO closed it, we will
198 			 * see an endless POLLIN and 0 available to read.
199 			 *
200 			 * The only way to handle it is to reopen the FIFO our
201 			 * side and wait for a new peer.  This is a quirk of
202 			 * FIFOs not of LWS.
203 			 */
204 			if (n == 0) { /* peer closed - reopen in close processing */
205 				vhd->zero_length_read = 1;
206 				return 1;
207 			}
208 			buf[n] = '\0';
209 			lwsl_info("read %d\n", n);
210 			puts(buf);
211 		}
212 		break;
213 
214 	case LWS_CALLBACK_RAW_CLOSE_FILE:
215 		lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n");
216 		if (vhd->zero_length_read) {
217 			vhd->zero_length_read = 0;
218 			close(vhd->fifo);
219 			/* the wsi that adopted the fifo file is closing...
220 			 * reopen the fifo and readopt
221 			 */
222 			vhd->fifo = lws_open(vhd->fifo_path,
223 					     O_NONBLOCK | O_RDONLY);
224 			if (vhd->fifo == -1) {
225 				lwsl_err("opening fifo failed\n");
226 				return 1;
227 			}
228 			lwsl_notice("FIFO %s reopened\n", vhd->fifo_path);
229 			u.filefd = vhd->fifo;
230 			if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u,
231 					"protocol-lws-raw-test", NULL)) {
232 				lwsl_err("Failed to adopt fifo descriptor\n");
233 				close(vhd->fifo);
234 				return 1;
235 			}
236 		}
237 		break;
238 
239 	case LWS_CALLBACK_RAW_WRITEABLE_FILE:
240 		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
241 		break;
242 
243 	/*
244 	 * Callbacks related to Raw socket descriptor testing
245 	 */
246 
247 	case LWS_CALLBACK_RAW_ADOPT:
248 		lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
249 		break;
250 
251 	case LWS_CALLBACK_RAW_RX:
252 		lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
253 		if (len > sizeof(pss->buf))
254 			len = sizeof(pss->buf);
255 		memcpy(pss->buf, in, len);
256 		pss->len = (int)len;
257 		lws_callback_on_writable(wsi);
258 		break;
259 
260 	case LWS_CALLBACK_RAW_CLOSE:
261 		lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
262 		break;
263 
264 	case LWS_CALLBACK_RAW_WRITEABLE:
265 		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
266 		lws_write(wsi, pss->buf, (size_t)pss->len, LWS_WRITE_HTTP);
267 		break;
268 
269 	default:
270 		break;
271 	}
272 
273 	return 0;
274 }
275 
276 #define LWS_PLUGIN_PROTOCOL_RAW_TEST \
277 	{ \
278 		"protocol-lws-raw-test", \
279 		callback_raw_test, \
280 		sizeof(struct per_session_data__raw_test), \
281 		1024, /* rx buf size must be >= permessage-deflate rx size */ 0, NULL, 0\
282 	}
283 
284 #if !defined (LWS_PLUGIN_STATIC)
285 
286 LWS_VISIBLE const struct lws_protocols lws_raw_test_protocols[] = {
287 	LWS_PLUGIN_PROTOCOL_RAW_TEST
288 };
289 
290 LWS_VISIBLE const lws_plugin_protocol_t lws_raw_test = {
291 	.hdr = {
292 		"lws raw test",
293 		"lws_protocol_plugin",
294 		LWS_BUILD_HASH,
295 		LWS_PLUGIN_API_MAGIC
296 	},
297 
298 	.protocols = lws_raw_test_protocols,
299 	.count_protocols = LWS_ARRAY_SIZE(lws_raw_test_protocols),
300 	.extensions = NULL,
301 	.count_extensions = 0,
302 };
303 
304 #endif
305