• 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 #define LWS_DLL
70 #define LWS_INTERNAL
71 #include <libwebsockets.h>
72 #endif
73 
74 #include <string.h>
75 
76 struct per_vhost_data__raw_test {
77 	struct lws_context *context;
78 	struct lws_vhost *vhost;
79 	const struct lws_protocols *protocol;
80 	char fifo_path[100];
81 	int fifo;
82 	char zero_length_read;
83 };
84 
85 struct per_session_data__raw_test {
86 	int number;
87 	unsigned char buf[128];
88 	int len;
89 };
90 
91 static int
callback_raw_test(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)92 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, void *user,
93 		  void *in, size_t len)
94 {
95 	struct per_session_data__raw_test *pss =
96 			(struct per_session_data__raw_test *)user;
97 	struct per_vhost_data__raw_test *vhd =
98 			(struct per_vhost_data__raw_test *)
99 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
100 					lws_get_protocol(wsi));
101 	lws_sock_file_fd_type u;
102 
103 	(void)pss;
104 
105 	switch (reason) {
106 	case LWS_CALLBACK_PROTOCOL_INIT:
107 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
108 				lws_get_protocol(wsi),
109 				sizeof(struct per_vhost_data__raw_test));
110 		vhd->context = lws_get_context(wsi);
111 		vhd->protocol = lws_get_protocol(wsi);
112 		vhd->vhost = lws_get_vhost(wsi);
113 		{
114 			const struct lws_protocol_vhost_options *pvo =
115 				(const struct lws_protocol_vhost_options *)in;
116 			while (pvo) {
117 				if (!strcmp(pvo->name, "fifo-path"))
118 					lws_strncpy(vhd->fifo_path, pvo->value,
119 							sizeof(vhd->fifo_path));
120 				pvo = pvo->next;
121 			}
122 			if (vhd->fifo_path[0] == '\0') {
123 				lwsl_err("%s: Missing pvo \"fifo-path\", "
124 					 "raw file fd testing disabled\n",
125 					 __func__);
126 				break;
127 			}
128 		}
129 		unlink(vhd->fifo_path);
130 		if (mkfifo(vhd->fifo_path, 0666)) {
131 			lwsl_err("mkfifo failed\n");
132 			return 1;
133 		}
134 		vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY);
135 		if (vhd->fifo == -1) {
136 			lwsl_err("opening fifo failed\n");
137 			unlink(vhd->fifo_path);
138 			return 1;
139 		}
140 		lwsl_notice("FIFO %s created\n", vhd->fifo_path);
141 		u.filefd = vhd->fifo;
142 		if (!lws_adopt_descriptor_vhost(vhd->vhost,
143 						LWS_ADOPT_RAW_FILE_DESC, u,
144 						"protocol-lws-raw-test",
145 						NULL)) {
146 			lwsl_err("Failed to adopt fifo descriptor\n");
147 			close(vhd->fifo);
148 			unlink(vhd->fifo_path);
149 			return 1;
150 		}
151 		break;
152 
153 	case LWS_CALLBACK_PROTOCOL_DESTROY:
154 		if (!vhd)
155 			break;
156 		if (vhd->fifo >= 0) {
157 			close(vhd->fifo);
158 			unlink(vhd->fifo_path);
159 		}
160 		break;
161 
162 
163 	/*
164 	 * Callbacks related to Raw file descriptor testing
165 	 */
166 
167 	case LWS_CALLBACK_RAW_ADOPT_FILE:
168 		lwsl_notice("LWS_CALLBACK_RAW_ADOPT_FILE\n");
169 		break;
170 
171 
172 	case LWS_CALLBACK_RAW_RX_FILE:
173 		lwsl_notice("LWS_CALLBACK_RAW_RX_FILE\n");
174 		{
175 			char buf[256];
176 			int n;
177 
178 			n = read(vhd->fifo, buf, sizeof(buf) - 1);
179 			if (n < 0) {
180 				lwsl_err("FIFO read failed\n");
181 				return 1;
182 			}
183 			/*
184 			 * When nobody opened the other side of the FIFO, the
185 			 * FIFO fd acts well and only signals POLLIN when
186 			 * somebody opened and wrote to it.
187 			 *
188 			 * But if the other side of the FIFO closed it, we will
189 			 * see an endless POLLIN and 0 available to read.
190 			 *
191 			 * The only way to handle it is to reopen the FIFO our
192 			 * side and wait for a new peer.  This is a quirk of
193 			 * FIFOs not of LWS.
194 			 */
195 			if (n == 0) { /* peer closed - reopen in close processing */
196 				vhd->zero_length_read = 1;
197 				return 1;
198 			}
199 			buf[n] = '\0';
200 			lwsl_info("read %d\n", n);
201 			puts(buf);
202 		}
203 		break;
204 
205 	case LWS_CALLBACK_RAW_CLOSE_FILE:
206 		lwsl_notice("LWS_CALLBACK_RAW_CLOSE_FILE\n");
207 		if (vhd->zero_length_read) {
208 			vhd->zero_length_read = 0;
209 			close(vhd->fifo);
210 			/* the wsi that adopted the fifo file is closing...
211 			 * reopen the fifo and readopt
212 			 */
213 			vhd->fifo = lws_open(vhd->fifo_path,
214 					     O_NONBLOCK | O_RDONLY);
215 			if (vhd->fifo == -1) {
216 				lwsl_err("opening fifo failed\n");
217 				return 1;
218 			}
219 			lwsl_notice("FIFO %s reopened\n", vhd->fifo_path);
220 			u.filefd = vhd->fifo;
221 			if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u,
222 					"protocol-lws-raw-test", NULL)) {
223 				lwsl_err("Failed to adopt fifo descriptor\n");
224 				close(vhd->fifo);
225 				return 1;
226 			}
227 		}
228 		break;
229 
230 	case LWS_CALLBACK_RAW_WRITEABLE_FILE:
231 		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE_FILE\n");
232 		break;
233 
234 	/*
235 	 * Callbacks related to Raw socket descriptor testing
236 	 */
237 
238 	case LWS_CALLBACK_RAW_ADOPT:
239 		lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
240 		break;
241 
242 	case LWS_CALLBACK_RAW_RX:
243 		lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
244 		if (len > sizeof(pss->buf))
245 			len = sizeof(pss->buf);
246 		memcpy(pss->buf, in, len);
247 		pss->len = len;
248 		lws_callback_on_writable(wsi);
249 		break;
250 
251 	case LWS_CALLBACK_RAW_CLOSE:
252 		lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
253 		break;
254 
255 	case LWS_CALLBACK_RAW_WRITEABLE:
256 		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
257 		lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP);
258 		break;
259 
260 	default:
261 		break;
262 	}
263 
264 	return 0;
265 }
266 
267 #define LWS_PLUGIN_PROTOCOL_RAW_TEST \
268 	{ \
269 		"protocol-lws-raw-test", \
270 		callback_raw_test, \
271 		sizeof(struct per_session_data__raw_test), \
272 		1024, /* rx buf size must be >= permessage-deflate rx size */ \
273 	}
274 
275 #if !defined (LWS_PLUGIN_STATIC)
276 
277 static const struct lws_protocols protocols[] = {
278 	LWS_PLUGIN_PROTOCOL_RAW_TEST
279 };
280 
281 LWS_VISIBLE int
init_protocol_lws_raw_test(struct lws_context * context,struct lws_plugin_capability * c)282 init_protocol_lws_raw_test(struct lws_context *context,
283 			     struct lws_plugin_capability *c)
284 {
285 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
286 		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
287 			 c->api_magic);
288 		return 1;
289 	}
290 
291 	c->protocols = protocols;
292 	c->count_protocols = LWS_ARRAY_SIZE(protocols);
293 	c->extensions = NULL;
294 	c->count_extensions = 0;
295 
296 	return 0;
297 }
298 
299 LWS_VISIBLE int
destroy_protocol_lws_raw_test(struct lws_context * context)300 destroy_protocol_lws_raw_test(struct lws_context *context)
301 {
302 	return 0;
303 }
304 
305 #endif
306