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