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