1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "extension-permessage-deflate.h"
27 #include <stdio.h>
28 #include <string.h>
29 #include <assert.h>
30
31 #define LWS_ZLIB_MEMLEVEL 8
32
33 const struct lws_ext_options lws_ext_pm_deflate_options[] = {
34 /* public RFC7692 settings */
35 { "server_no_context_takeover", EXTARG_NONE },
36 { "client_no_context_takeover", EXTARG_NONE },
37 { "server_max_window_bits", EXTARG_OPT_DEC },
38 { "client_max_window_bits", EXTARG_OPT_DEC },
39 /* ones only user code can set */
40 { "rx_buf_size", EXTARG_DEC },
41 { "tx_buf_size", EXTARG_DEC },
42 { "compression_level", EXTARG_DEC },
43 { "mem_level", EXTARG_DEC },
44 { NULL, 0 }, /* sentinel */
45 };
46
47 static void
lws_extension_pmdeflate_restrict_args(struct lws * wsi,struct lws_ext_pm_deflate_priv * priv)48 lws_extension_pmdeflate_restrict_args(struct lws *wsi,
49 struct lws_ext_pm_deflate_priv *priv)
50 {
51 int n, extra;
52
53 /* cap the RX buf at the nearest power of 2 to protocol rx buf */
54
55 n = (int)wsi->a.context->pt_serv_buf_size;
56 if (wsi->a.protocol->rx_buffer_size)
57 n = (int)wsi->a.protocol->rx_buffer_size;
58
59 extra = 7;
60 while (n >= 1 << (extra + 1))
61 extra++;
62
63 if (extra < priv->args[PMD_RX_BUF_PWR2]) {
64 priv->args[PMD_RX_BUF_PWR2] = (unsigned char)extra;
65 lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
66 }
67 }
68
69 static unsigned char trail[] = { 0, 0, 0xff, 0xff };
70
71 LWS_VISIBLE int
lws_extension_callback_pm_deflate(struct lws_context * context,const struct lws_extension * ext,struct lws * wsi,enum lws_extension_callback_reasons reason,void * user,void * in,size_t len)72 lws_extension_callback_pm_deflate(struct lws_context *context,
73 const struct lws_extension *ext,
74 struct lws *wsi,
75 enum lws_extension_callback_reasons reason,
76 void *user, void *in, size_t len)
77 {
78 struct lws_ext_pm_deflate_priv *priv =
79 (struct lws_ext_pm_deflate_priv *)user;
80 struct lws_ext_pm_deflate_rx_ebufs *pmdrx =
81 (struct lws_ext_pm_deflate_rx_ebufs *)in;
82 struct lws_ext_option_arg *oa;
83 int n, ret = 0, was_fin = 0, m;
84 unsigned int pen = 0;
85 int penbits = 0;
86
87 switch (reason) {
88 case LWS_EXT_CB_NAMED_OPTION_SET:
89 oa = in;
90 if (!oa->option_name)
91 break;
92 lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
93 for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
94 n++)
95 if (!strcmp(lws_ext_pm_deflate_options[n].name,
96 oa->option_name))
97 break;
98
99 if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
100 break;
101 oa->option_index = n;
102
103 /* fallthru */
104
105 case LWS_EXT_CB_OPTION_SET:
106 oa = in;
107 lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
108 oa->option_index, oa->start, oa->len);
109 if (oa->start)
110 priv->args[oa->option_index] = (unsigned char)atoi(oa->start);
111 else
112 priv->args[oa->option_index] = 1;
113
114 if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
115 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
116
117 lws_extension_pmdeflate_restrict_args(wsi, priv);
118 break;
119
120 case LWS_EXT_CB_OPTION_CONFIRM:
121 if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
122 priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
123 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
124 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
125 return -1;
126 break;
127
128 case LWS_EXT_CB_CLIENT_CONSTRUCT:
129 case LWS_EXT_CB_CONSTRUCT:
130
131 n = (int)context->pt_serv_buf_size;
132 if (wsi->a.protocol->rx_buffer_size)
133 n = (int)wsi->a.protocol->rx_buffer_size;
134
135 if (n < 128) {
136 lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
137 "(%s) to have an RX buffer >= 128",
138 wsi->a.protocol->name);
139 return -1;
140 }
141
142 /* fill in **user */
143 priv = lws_zalloc(sizeof(*priv), "pmd priv");
144 *((void **)user) = priv;
145 lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
146 memset(priv, 0, sizeof(*priv));
147
148 /* fill in pointer to options list */
149 if (in)
150 *((const struct lws_ext_options **)in) =
151 lws_ext_pm_deflate_options;
152
153 /* fallthru */
154
155 case LWS_EXT_CB_OPTION_DEFAULT:
156
157 /* set the public, RFC7692 defaults... */
158
159 priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
160 priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
161 priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
162 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
163
164 /* ...and the ones the user code can override */
165
166 priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
167 priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
168 priv->args[PMD_COMP_LEVEL] = 1;
169 priv->args[PMD_MEM_LEVEL] = 8;
170
171 lws_extension_pmdeflate_restrict_args(wsi, priv);
172 break;
173
174 case LWS_EXT_CB_DESTROY:
175 lwsl_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
176 lws_free(priv->buf_rx_inflated);
177 lws_free(priv->buf_tx_deflated);
178 if (priv->rx_init)
179 (void)inflateEnd(&priv->rx);
180 if (priv->tx_init)
181 (void)deflateEnd(&priv->tx);
182 lws_free(priv);
183
184 return ret;
185
186
187 case LWS_EXT_CB_PAYLOAD_RX:
188 /*
189 * ie, we are INFLATING
190 */
191 lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
192 pmdrx->eb_in.len, priv->rx.avail_in);
193
194 /*
195 * If this frame is not marked as compressed,
196 * there is nothing we should do with it
197 */
198
199 if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
200 /*
201 * This is a bit different than DID_NOTHING... we have
202 * identified using ext-private bits in the packet, or
203 * by it being a control fragment that we SHOULD not do
204 * anything to it, parent should continue as if we
205 * processed it
206 */
207 return PMDR_NOTHING_WE_SHOULD_DO;
208
209 /*
210 * we shouldn't come back in here if we already applied the
211 * trailer for this compressed packet
212 */
213 if (!wsi->ws->pmd_trailer_application)
214 return PMDR_DID_NOTHING;
215
216 pmdrx->eb_out.len = 0;
217
218 lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
219 "existing avail in %d, pkt fin: %d",
220 pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
221
222 /* if needed, initialize the inflator */
223
224 if (!priv->rx_init) {
225 if (inflateInit2(&priv->rx,
226 -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
227 lwsl_wsi_err(wsi, "iniflateInit failed");
228 return PMDR_FAILED;
229 }
230 priv->rx_init = 1;
231 if (!priv->buf_rx_inflated)
232 priv->buf_rx_inflated = lws_malloc(
233 (unsigned int)(LWS_PRE + 7 + 5 +
234 (1 << priv->args[PMD_RX_BUF_PWR2])),
235 "pmd rx inflate buf");
236 if (!priv->buf_rx_inflated) {
237 lwsl_wsi_err(wsi, "OOM");
238 return PMDR_FAILED;
239 }
240 }
241
242 #if 0
243 /*
244 * don't give us new input while we still work through
245 * the last input
246 */
247
248 if (priv->rx.avail_in && pmdrx->eb_in.token &&
249 pmdrx->eb_in.len) {
250 lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
251 priv->rx.avail_in);
252 // assert(0);
253 }
254 #endif
255 if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
256 priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
257 priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
258 }
259
260 priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
261 pmdrx->eb_out.token = priv->rx.next_out;
262 priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
263
264 /* so... if...
265 *
266 * - he has no remaining input content for this message, and
267 *
268 * - and this is the final fragment, and
269 *
270 * - we used everything that could be drained on the input side
271 *
272 * ...then put back the 00 00 FF FF the sender stripped as our
273 * input to zlib
274 */
275 if (!priv->rx.avail_in &&
276 wsi->ws->final &&
277 !wsi->ws->rx_packet_length &&
278 wsi->ws->pmd_trailer_application) {
279 lwsl_wsi_ext(wsi, "trailer apply 1");
280 was_fin = 1;
281 wsi->ws->pmd_trailer_application = 0;
282 priv->rx.next_in = trail;
283 priv->rx.avail_in = sizeof(trail);
284 }
285
286 /*
287 * if after all that there's nothing pending and nothing to give
288 * him right now, bail without having done anything
289 */
290
291 if (!priv->rx.avail_in)
292 return PMDR_DID_NOTHING;
293
294 n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
295 lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
296 priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
297 switch (n) {
298 case Z_NEED_DICT:
299 case Z_STREAM_ERROR:
300 case Z_DATA_ERROR:
301 case Z_MEM_ERROR:
302 lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
303 n, priv->rx.msg);
304 return PMDR_FAILED;
305 }
306
307 /*
308 * track how much input was used, and advance it
309 */
310
311 /* COV says we can overflow if "eb_in.len == 0 and rx->avail_in == 4" */
312 if ((unsigned int)priv->rx.avail_in > (unsigned int)pmdrx->eb_in.len) {
313 lwsl_wsi_err(wsi, "rx buffer underflow");
314 return PMDR_FAILED;
315 }
316
317 pmdrx->eb_in.token = pmdrx->eb_in.token +
318 ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
319 pmdrx->eb_in.len = (int)priv->rx.avail_in;
320
321 lwsl_wsi_debug(wsi, "%d %d %d %d %d",
322 priv->rx.avail_in,
323 wsi->ws->final,
324 (int)wsi->ws->rx_packet_length,
325 was_fin,
326 wsi->ws->pmd_trailer_application);
327
328 if (!priv->rx.avail_in &&
329 wsi->ws->final &&
330 !wsi->ws->rx_packet_length &&
331 !was_fin &&
332 wsi->ws->pmd_trailer_application) {
333 lwsl_wsi_ext(wsi, "RX trailer apply 2");
334
335 /* we overallocated just for this situation where
336 * we might issue something */
337 priv->rx.avail_out += 5;
338
339 was_fin = 1;
340 wsi->ws->pmd_trailer_application = 0;
341 priv->rx.next_in = trail;
342 priv->rx.avail_in = sizeof(trail);
343 n = inflate(&priv->rx, Z_SYNC_FLUSH);
344 lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
345 n, priv->rx.avail_in, priv->rx.avail_out);
346 switch (n) {
347 case Z_NEED_DICT:
348 case Z_STREAM_ERROR:
349 case Z_DATA_ERROR:
350 case Z_MEM_ERROR:
351 lwsl_wsi_info(wsi, "zlib error inflate %d: %s",
352 n, priv->rx.msg);
353 return -1;
354 }
355
356 assert(priv->rx.avail_out);
357 }
358
359 pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
360 pmdrx->eb_out.token);
361 priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
362
363 lwsl_wsi_ext(wsi, " RX leaving with new effbuff len %d, "
364 "rx.avail_in=%d, TOTAL RX since FIN %lu",
365 pmdrx->eb_out.len, priv->rx.avail_in,
366 (unsigned long)priv->count_rx_between_fin);
367
368 if (was_fin) {
369 lwsl_wsi_ext(wsi, "was_fin");
370 priv->count_rx_between_fin = 0;
371 if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
372 lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
373 (void)inflateEnd(&priv->rx);
374 priv->rx_init = 0;
375 }
376
377 return PMDR_EMPTY_FINAL;
378 }
379
380 if (priv->rx.avail_in)
381 return PMDR_HAS_PENDING;
382
383 return PMDR_EMPTY_NONFINAL;
384
385 case LWS_EXT_CB_PAYLOAD_TX:
386
387 /*
388 * ie, we are DEFLATING
389 *
390 * initialize us if needed
391 */
392
393 if (!priv->tx_init) {
394 n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
395 Z_DEFLATED,
396 -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
397 (wsi->a.vhost->listen_port <= 0)],
398 priv->args[PMD_MEM_LEVEL],
399 Z_DEFAULT_STRATEGY);
400 if (n != Z_OK) {
401 lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
402 return PMDR_FAILED;
403 }
404 priv->tx_init = 1;
405 }
406
407 if (!priv->buf_tx_deflated)
408 priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
409 (1 << priv->args[PMD_TX_BUF_PWR2])),
410 "pmd tx deflate buf");
411 if (!priv->buf_tx_deflated) {
412 lwsl_wsi_err(wsi, "OOM");
413 return PMDR_FAILED;
414 }
415
416 /* hook us up with any deflated input that the caller has */
417
418 if (pmdrx->eb_in.token) {
419
420 assert(!priv->tx.avail_in);
421
422 priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
423 lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
424 "TOTAL TX since FIN: %d",
425 pmdrx->eb_in.len,
426 (int)priv->count_tx_between_fin);
427 priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
428 priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
429 }
430
431 priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
432 pmdrx->eb_out.token = priv->tx.next_out;
433 priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
434
435 pen = 0;
436 penbits = 0;
437 deflatePending(&priv->tx, &pen, &penbits);
438 pen = pen | (unsigned int)penbits;
439
440 if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
441 lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
442
443 if (!pen)
444 return PMDR_DID_NOTHING;
445 }
446
447 m = Z_NO_FLUSH;
448 if (!(len & LWS_WRITE_NO_FIN)) {
449 lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
450 (int)wsi->ws->rx_packet_length);
451 m = Z_SYNC_FLUSH;
452 }
453
454 n = deflate(&priv->tx, m);
455 if (n == Z_STREAM_ERROR) {
456 lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
457 return PMDR_FAILED;
458 }
459
460 pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
461
462 lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
463 (unsigned int)len);
464
465 if ((len & 0xf) == LWS_WRITE_TEXT)
466 priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
467 if ((len & 0xf) == LWS_WRITE_BINARY)
468 priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
469
470 pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
471 pmdrx->eb_out.token);
472
473 if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
474 pmdrx->eb_out.len < 4) {
475 lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
476 (int)pmdrx->eb_out.len);
477 assert(0);
478 }
479
480 if (!(len & LWS_WRITE_NO_FIN) &&
481 m == Z_SYNC_FLUSH &&
482 !pen &&
483 pmdrx->eb_out.len >= 4) {
484 // lwsl_wsi_err(wsi, "Trimming 4 from end of write");
485 priv->tx.next_out -= 4;
486 priv->tx.avail_out += 4;
487 priv->count_tx_between_fin = 0;
488
489 assert(priv->tx.next_out[0] == 0x00 &&
490 priv->tx.next_out[1] == 0x00 &&
491 priv->tx.next_out[2] == 0xff &&
492 priv->tx.next_out[3] == 0xff);
493 }
494
495
496 /*
497 * track how much input was used and advance it
498 */
499
500 pmdrx->eb_in.token = pmdrx->eb_in.token +
501 ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
502 pmdrx->eb_in.len = (int)priv->tx.avail_in;
503
504 priv->compressed_out = 1;
505 pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
506 pmdrx->eb_out.token);
507
508 lwsl_wsi_ext(wsi, " TX rewritten with new eb_in len %d, "
509 "eb_out len %d, deflatePending %d",
510 pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
511
512 if (pmdrx->eb_in.len || pen)
513 return PMDR_HAS_PENDING;
514
515 if (!(len & LWS_WRITE_NO_FIN))
516 return PMDR_EMPTY_FINAL;
517
518 return PMDR_EMPTY_NONFINAL;
519
520 case LWS_EXT_CB_PACKET_TX_PRESEND:
521 if (!priv->compressed_out)
522 break;
523 priv->compressed_out = 0;
524
525 /*
526 * we may have not produced any output for the actual "first"
527 * write... in that case, we need to fix up the inappropriate
528 * use of CONTINUATION when the first real write does come.
529 */
530 if (priv->tx_first_frame_type & 0xf) {
531 *pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
532 ((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
533 /*
534 * We have now written the "first" fragment, only
535 * do that once
536 */
537 priv->tx_first_frame_type = 0;
538 }
539
540 n = *(pmdrx->eb_in.token) & 15;
541
542 /* set RSV1, but not on CONTINUATION */
543 if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
544 *pmdrx->eb_in.token |= 0x40;
545
546 lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
547 ((*pmdrx->eb_in.token) & 0xff),
548 pmdrx->eb_in.len);
549
550 if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
551 priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
552 lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
553 (void)deflateEnd(&priv->tx);
554 priv->tx_init = 0;
555 }
556
557 break;
558
559 default:
560 break;
561 }
562
563 return 0;
564 }
565
566