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_info(" Capping pmd rx to %d\n", 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_ext("%s: named option set: %s\n", __func__,
93 oa->option_name);
94 for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
95 n++)
96 if (!strcmp(lws_ext_pm_deflate_options[n].name,
97 oa->option_name))
98 break;
99
100 if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
101 break;
102 oa->option_index = n;
103
104 /* fallthru */
105
106 case LWS_EXT_CB_OPTION_SET:
107 oa = in;
108 lwsl_ext("%s: option set: idx %d, %s, len %d\n", __func__,
109 oa->option_index, oa->start, oa->len);
110 if (oa->start)
111 priv->args[oa->option_index] = (unsigned char)atoi(oa->start);
112 else
113 priv->args[oa->option_index] = 1;
114
115 if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
116 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
117
118 lws_extension_pmdeflate_restrict_args(wsi, priv);
119 break;
120
121 case LWS_EXT_CB_OPTION_CONFIRM:
122 if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
123 priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
124 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
125 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
126 return -1;
127 break;
128
129 case LWS_EXT_CB_CLIENT_CONSTRUCT:
130 case LWS_EXT_CB_CONSTRUCT:
131
132 n = (int)context->pt_serv_buf_size;
133 if (wsi->a.protocol->rx_buffer_size)
134 n = (int)wsi->a.protocol->rx_buffer_size;
135
136 if (n < 128) {
137 lwsl_info(" permessage-deflate requires the protocol "
138 "(%s) to have an RX buffer >= 128\n",
139 wsi->a.protocol->name);
140 return -1;
141 }
142
143 /* fill in **user */
144 priv = lws_zalloc(sizeof(*priv), "pmd priv");
145 *((void **)user) = priv;
146 lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
147 memset(priv, 0, sizeof(*priv));
148
149 /* fill in pointer to options list */
150 if (in)
151 *((const struct lws_ext_options **)in) =
152 lws_ext_pm_deflate_options;
153
154 /* fallthru */
155
156 case LWS_EXT_CB_OPTION_DEFAULT:
157
158 /* set the public, RFC7692 defaults... */
159
160 priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
161 priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
162 priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
163 priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
164
165 /* ...and the ones the user code can override */
166
167 priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
168 priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
169 priv->args[PMD_COMP_LEVEL] = 1;
170 priv->args[PMD_MEM_LEVEL] = 8;
171
172 lws_extension_pmdeflate_restrict_args(wsi, priv);
173 break;
174
175 case LWS_EXT_CB_DESTROY:
176 lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
177 lws_free(priv->buf_rx_inflated);
178 lws_free(priv->buf_tx_deflated);
179 if (priv->rx_init)
180 (void)inflateEnd(&priv->rx);
181 if (priv->tx_init)
182 (void)deflateEnd(&priv->tx);
183 lws_free(priv);
184
185 return ret;
186
187
188 case LWS_EXT_CB_PAYLOAD_RX:
189 /*
190 * ie, we are INFLATING
191 */
192 lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
193 __func__, pmdrx->eb_in.len, priv->rx.avail_in);
194
195 /*
196 * If this frame is not marked as compressed,
197 * there is nothing we should do with it
198 */
199
200 if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
201 /*
202 * This is a bit different than DID_NOTHING... we have
203 * identified using ext-private bits in the packet, or
204 * by it being a control fragment that we SHOULD not do
205 * anything to it, parent should continue as if we
206 * processed it
207 */
208 return PMDR_NOTHING_WE_SHOULD_DO;
209
210 /*
211 * we shouldn't come back in here if we already applied the
212 * trailer for this compressed packet
213 */
214 if (!wsi->ws->pmd_trailer_application)
215 return PMDR_DID_NOTHING;
216
217 pmdrx->eb_out.len = 0;
218
219 lwsl_ext("%s: LWS_EXT_CB_PAYLOAD_RX: in %d, "
220 "existing avail in %d, pkt fin: %d\n", __func__,
221 pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
222
223 /* if needed, initialize the inflator */
224
225 if (!priv->rx_init) {
226 if (inflateInit2(&priv->rx,
227 -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
228 lwsl_err("%s: iniflateInit failed\n", __func__);
229 return PMDR_FAILED;
230 }
231 priv->rx_init = 1;
232 if (!priv->buf_rx_inflated)
233 priv->buf_rx_inflated = lws_malloc(
234 (unsigned int)(LWS_PRE + 7 + 5 +
235 (1 << priv->args[PMD_RX_BUF_PWR2])),
236 "pmd rx inflate buf");
237 if (!priv->buf_rx_inflated) {
238 lwsl_err("%s: OOM\n", __func__);
239 return PMDR_FAILED;
240 }
241 }
242
243 #if 0
244 /*
245 * don't give us new input while we still work through
246 * the last input
247 */
248
249 if (priv->rx.avail_in && pmdrx->eb_in.token &&
250 pmdrx->eb_in.len) {
251 lwsl_warn("%s: priv->rx.avail_in %d while getting new in\n",
252 __func__, priv->rx.avail_in);
253 // assert(0);
254 }
255 #endif
256 if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
257 priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
258 priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
259 }
260
261 priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
262 pmdrx->eb_out.token = priv->rx.next_out;
263 priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
264
265 /* so... if...
266 *
267 * - he has no remaining input content for this message, and
268 *
269 * - and this is the final fragment, and
270 *
271 * - we used everything that could be drained on the input side
272 *
273 * ...then put back the 00 00 FF FF the sender stripped as our
274 * input to zlib
275 */
276 if (!priv->rx.avail_in &&
277 wsi->ws->final &&
278 !wsi->ws->rx_packet_length &&
279 wsi->ws->pmd_trailer_application) {
280 lwsl_ext("%s: trailer apply 1\n", __func__);
281 was_fin = 1;
282 wsi->ws->pmd_trailer_application = 0;
283 priv->rx.next_in = trail;
284 priv->rx.avail_in = sizeof(trail);
285 }
286
287 /*
288 * if after all that there's nothing pending and nothing to give
289 * him right now, bail without having done anything
290 */
291
292 if (!priv->rx.avail_in)
293 return PMDR_DID_NOTHING;
294
295 n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
296 lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
297 priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
298 switch (n) {
299 case Z_NEED_DICT:
300 case Z_STREAM_ERROR:
301 case Z_DATA_ERROR:
302 case Z_MEM_ERROR:
303 lwsl_err("%s: zlib error inflate %d: \"%s\"\n",
304 __func__, n, priv->rx.msg);
305 return PMDR_FAILED;
306 }
307
308 /*
309 * track how much input was used, and advance it
310 */
311
312 pmdrx->eb_in.token = pmdrx->eb_in.token +
313 ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
314 pmdrx->eb_in.len = (int)priv->rx.avail_in;
315
316 lwsl_debug("%s: %d %d %d %d %d\n", __func__,
317 priv->rx.avail_in,
318 wsi->ws->final,
319 (int)wsi->ws->rx_packet_length,
320 was_fin,
321 wsi->ws->pmd_trailer_application);
322
323 if (!priv->rx.avail_in &&
324 wsi->ws->final &&
325 !wsi->ws->rx_packet_length &&
326 !was_fin &&
327 wsi->ws->pmd_trailer_application) {
328 lwsl_ext("%s: RX trailer apply 2\n", __func__);
329
330 /* we overallocated just for this situation where
331 * we might issue something */
332 priv->rx.avail_out += 5;
333
334 was_fin = 1;
335 wsi->ws->pmd_trailer_application = 0;
336 priv->rx.next_in = trail;
337 priv->rx.avail_in = sizeof(trail);
338 n = inflate(&priv->rx, Z_SYNC_FLUSH);
339 lwsl_ext("RX trailer infl ret %d, avi %d, avo %d\n",
340 n, priv->rx.avail_in, priv->rx.avail_out);
341 switch (n) {
342 case Z_NEED_DICT:
343 case Z_STREAM_ERROR:
344 case Z_DATA_ERROR:
345 case Z_MEM_ERROR:
346 lwsl_info("zlib error inflate %d: %s\n",
347 n, priv->rx.msg);
348 return -1;
349 }
350
351 assert(priv->rx.avail_out);
352 }
353
354 pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
355 pmdrx->eb_out.token);
356 priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
357
358 lwsl_ext(" %s: RX leaving with new effbuff len %d, "
359 "rx.avail_in=%d, TOTAL RX since FIN %lu\n",
360 __func__, pmdrx->eb_out.len, priv->rx.avail_in,
361 (unsigned long)priv->count_rx_between_fin);
362
363 if (was_fin) {
364 lwsl_ext("%s: was_fin\n", __func__);
365 priv->count_rx_between_fin = 0;
366 if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
367 lwsl_ext("PMD_SERVER_NO_CONTEXT_TAKEOVER\n");
368 (void)inflateEnd(&priv->rx);
369 priv->rx_init = 0;
370 }
371
372 return PMDR_EMPTY_FINAL;
373 }
374
375 if (priv->rx.avail_in)
376 return PMDR_HAS_PENDING;
377
378 return PMDR_EMPTY_NONFINAL;
379
380 case LWS_EXT_CB_PAYLOAD_TX:
381
382 /*
383 * ie, we are DEFLATING
384 *
385 * initialize us if needed
386 */
387
388 if (!priv->tx_init) {
389 n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
390 Z_DEFLATED,
391 -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
392 (wsi->a.vhost->listen_port <= 0)],
393 priv->args[PMD_MEM_LEVEL],
394 Z_DEFAULT_STRATEGY);
395 if (n != Z_OK) {
396 lwsl_ext("inflateInit2 failed %d\n", n);
397 return PMDR_FAILED;
398 }
399 priv->tx_init = 1;
400 }
401
402 if (!priv->buf_tx_deflated)
403 priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
404 (1 << priv->args[PMD_TX_BUF_PWR2])),
405 "pmd tx deflate buf");
406 if (!priv->buf_tx_deflated) {
407 lwsl_err("%s: OOM\n", __func__);
408 return PMDR_FAILED;
409 }
410
411 /* hook us up with any deflated input that the caller has */
412
413 if (pmdrx->eb_in.token) {
414
415 assert(!priv->tx.avail_in);
416
417 priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
418 lwsl_ext("%s: TX: eb_in length %d, "
419 "TOTAL TX since FIN: %d\n", __func__,
420 pmdrx->eb_in.len,
421 (int)priv->count_tx_between_fin);
422 priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
423 priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
424 }
425
426 priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
427 pmdrx->eb_out.token = priv->tx.next_out;
428 priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
429
430 pen = 0;
431 penbits = 0;
432 deflatePending(&priv->tx, &pen, &penbits);
433 pen = pen | (unsigned int)penbits;
434
435 if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
436 lwsl_ext("%s: no available in, pen: %u\n", __func__, pen);
437
438 if (!pen)
439 return PMDR_DID_NOTHING;
440 }
441
442 m = Z_NO_FLUSH;
443 if (!(len & LWS_WRITE_NO_FIN)) {
444 lwsl_ext("%s: deflate with SYNC_FLUSH, pkt len %d\n",
445 __func__, (int)wsi->ws->rx_packet_length);
446 m = Z_SYNC_FLUSH;
447 }
448
449 n = deflate(&priv->tx, m);
450 if (n == Z_STREAM_ERROR) {
451 lwsl_notice("%s: Z_STREAM_ERROR\n", __func__);
452 return PMDR_FAILED;
453 }
454
455 pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
456
457 lwsl_ext("%s: deflate ret %d, len 0x%x\n", __func__, n,
458 (unsigned int)len);
459
460 if ((len & 0xf) == LWS_WRITE_TEXT)
461 priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
462 if ((len & 0xf) == LWS_WRITE_BINARY)
463 priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
464
465 pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
466 pmdrx->eb_out.token);
467
468 if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
469 pmdrx->eb_out.len < 4) {
470 lwsl_err("%s: FAIL want to trim out length %d\n",
471 __func__, (int)pmdrx->eb_out.len);
472 assert(0);
473 }
474
475 if (!(len & LWS_WRITE_NO_FIN) &&
476 m == Z_SYNC_FLUSH &&
477 !pen &&
478 pmdrx->eb_out.len >= 4) {
479 // lwsl_err("%s: Trimming 4 from end of write\n", __func__);
480 priv->tx.next_out -= 4;
481 priv->tx.avail_out += 4;
482 priv->count_tx_between_fin = 0;
483
484 assert(priv->tx.next_out[0] == 0x00 &&
485 priv->tx.next_out[1] == 0x00 &&
486 priv->tx.next_out[2] == 0xff &&
487 priv->tx.next_out[3] == 0xff);
488 }
489
490
491 /*
492 * track how much input was used and advance it
493 */
494
495 pmdrx->eb_in.token = pmdrx->eb_in.token +
496 ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
497 pmdrx->eb_in.len = (int)priv->tx.avail_in;
498
499 priv->compressed_out = 1;
500 pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
501 pmdrx->eb_out.token);
502
503 lwsl_ext(" TX rewritten with new eb_in len %d, "
504 "eb_out len %d, deflatePending %d\n",
505 pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
506
507 if (pmdrx->eb_in.len || pen)
508 return PMDR_HAS_PENDING;
509
510 if (!(len & LWS_WRITE_NO_FIN))
511 return PMDR_EMPTY_FINAL;
512
513 return PMDR_EMPTY_NONFINAL;
514
515 case LWS_EXT_CB_PACKET_TX_PRESEND:
516 if (!priv->compressed_out)
517 break;
518 priv->compressed_out = 0;
519
520 /*
521 * we may have not produced any output for the actual "first"
522 * write... in that case, we need to fix up the inappropriate
523 * use of CONTINUATION when the first real write does come.
524 */
525 if (priv->tx_first_frame_type & 0xf) {
526 *pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
527 ((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
528 /*
529 * We have now written the "first" fragment, only
530 * do that once
531 */
532 priv->tx_first_frame_type = 0;
533 }
534
535 n = *(pmdrx->eb_in.token) & 15;
536
537 /* set RSV1, but not on CONTINUATION */
538 if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
539 *pmdrx->eb_in.token |= 0x40;
540
541 lwsl_ext("%s: PRESEND compressed: ws frame 0x%02X, len %d\n",
542 __func__, ((*pmdrx->eb_in.token) & 0xff),
543 pmdrx->eb_in.len);
544
545 if (((*pmdrx->eb_in.token) & 0x80) && /* fin */
546 priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
547 lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
548 (void)deflateEnd(&priv->tx);
549 priv->tx_init = 0;
550 }
551
552 break;
553
554 default:
555 break;
556 }
557
558 return 0;
559 }
560
561