• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #include "urldata.h"
28 #include <curl/curl.h>
29 #include <stddef.h>
30 
31 #ifdef HAVE_LIBZ
32 #include <zlib.h>
33 #endif
34 
35 #ifdef HAVE_BROTLI
36 #if defined(__GNUC__) || defined(__clang__)
37 /* Ignore -Wvla warnings in brotli headers */
38 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wvla"
40 #endif
41 #include <brotli/decode.h>
42 #if defined(__GNUC__) || defined(__clang__)
43 #pragma GCC diagnostic pop
44 #endif
45 #endif
46 
47 #ifdef HAVE_ZSTD
48 #include <zstd.h>
49 #endif
50 
51 #include "sendf.h"
52 #include "http.h"
53 #include "content_encoding.h"
54 #include "strdup.h"
55 #include "strcase.h"
56 
57 /* The last 3 #include files should be in this order */
58 #include "curl_printf.h"
59 #include "curl_memory.h"
60 #include "memdebug.h"
61 
62 #define CONTENT_ENCODING_DEFAULT  "identity"
63 
64 #ifndef CURL_DISABLE_HTTP
65 
66 /* allow no more than 5 "chained" compression steps */
67 #define MAX_ENCODE_STACK 5
68 #define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */
69 
70 #ifdef HAVE_LIBZ
71 
72 typedef enum {
73   ZLIB_UNINIT,               /* uninitialized */
74   ZLIB_INIT,                 /* initialized */
75   ZLIB_INFLATING,            /* inflating started. */
76   ZLIB_EXTERNAL_TRAILER,     /* reading external trailer */
77   ZLIB_INIT_GZIP             /* initialized in transparent gzip mode */
78 } zlibInitState;
79 
80 /* Deflate and gzip writer. */
81 struct zlib_writer {
82   struct Curl_cwriter super;
83   zlibInitState zlib_init;   /* zlib init state */
84   char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */
85   uInt trailerlen;           /* Remaining trailer byte count. */
86   z_stream z;                /* State structure for zlib. */
87 };
88 
89 
90 static voidpf
zalloc_cb(voidpf opaque,unsigned int items,unsigned int size)91 zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
92 {
93   (void) opaque;
94   /* not a typo, keep it calloc() */
95   return (voidpf) calloc(items, size);
96 }
97 
98 static void
zfree_cb(voidpf opaque,voidpf ptr)99 zfree_cb(voidpf opaque, voidpf ptr)
100 {
101   (void) opaque;
102   free(ptr);
103 }
104 
105 static CURLcode
process_zlib_error(struct Curl_easy * data,z_stream * z)106 process_zlib_error(struct Curl_easy *data, z_stream *z)
107 {
108   if(z->msg)
109     failf(data, "Error while processing content unencoding: %s",
110           z->msg);
111   else
112     failf(data, "Error while processing content unencoding: "
113           "Unknown failure within decompression software.");
114 
115   return CURLE_BAD_CONTENT_ENCODING;
116 }
117 
118 static CURLcode
exit_zlib(struct Curl_easy * data,z_stream * z,zlibInitState * zlib_init,CURLcode result)119 exit_zlib(struct Curl_easy *data,
120           z_stream *z, zlibInitState *zlib_init, CURLcode result)
121 {
122   if(*zlib_init != ZLIB_UNINIT) {
123     if(inflateEnd(z) != Z_OK && result == CURLE_OK)
124       result = process_zlib_error(data, z);
125     *zlib_init = ZLIB_UNINIT;
126   }
127 
128   return result;
129 }
130 
process_trailer(struct Curl_easy * data,struct zlib_writer * zp)131 static CURLcode process_trailer(struct Curl_easy *data,
132                                 struct zlib_writer *zp)
133 {
134   z_stream *z = &zp->z;
135   CURLcode result = CURLE_OK;
136   uInt len = z->avail_in < zp->trailerlen ? z->avail_in : zp->trailerlen;
137 
138   /* Consume expected trailer bytes. Terminate stream if exhausted.
139      Issue an error if unexpected bytes follow. */
140 
141   zp->trailerlen -= len;
142   z->avail_in -= len;
143   z->next_in += len;
144   if(z->avail_in)
145     result = CURLE_WRITE_ERROR;
146   if(result || !zp->trailerlen)
147     result = exit_zlib(data, z, &zp->zlib_init, result);
148   else {
149     /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */
150     zp->zlib_init = ZLIB_EXTERNAL_TRAILER;
151   }
152   return result;
153 }
154 
inflate_stream(struct Curl_easy * data,struct Curl_cwriter * writer,int type,zlibInitState started)155 static CURLcode inflate_stream(struct Curl_easy *data,
156                                struct Curl_cwriter *writer, int type,
157                                zlibInitState started)
158 {
159   struct zlib_writer *zp = (struct zlib_writer *) writer;
160   z_stream *z = &zp->z;         /* zlib state structure */
161   uInt nread = z->avail_in;
162   Bytef *orig_in = z->next_in;
163   bool done = FALSE;
164   CURLcode result = CURLE_OK;   /* Curl_client_write status */
165 
166   /* Check state. */
167   if(zp->zlib_init != ZLIB_INIT &&
168      zp->zlib_init != ZLIB_INFLATING &&
169      zp->zlib_init != ZLIB_INIT_GZIP)
170     return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
171 
172   /* because the buffer size is fixed, iteratively decompress and transfer to
173      the client via next_write function. */
174   while(!done) {
175     int status;                   /* zlib status */
176     done = TRUE;
177 
178     /* (re)set buffer for decompressed output for every iteration */
179     z->next_out = (Bytef *) zp->buffer;
180     z->avail_out = DECOMPRESS_BUFFER_SIZE;
181 
182 #ifdef Z_BLOCK
183     /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */
184     status = inflate(z, Z_BLOCK);
185 #else
186     /* fallback for zlib ver. < 1.2.0.5 */
187     status = inflate(z, Z_SYNC_FLUSH);
188 #endif
189 
190     /* Flush output data if some. */
191     if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
192       if(status == Z_OK || status == Z_STREAM_END) {
193         zp->zlib_init = started;      /* Data started. */
194         result = Curl_cwriter_write(data, writer->next, type, zp->buffer,
195                                     DECOMPRESS_BUFFER_SIZE - z->avail_out);
196         if(result) {
197           exit_zlib(data, z, &zp->zlib_init, result);
198           break;
199         }
200       }
201     }
202 
203     /* Dispatch by inflate() status. */
204     switch(status) {
205     case Z_OK:
206       /* Always loop: there may be unflushed latched data in zlib state. */
207       done = FALSE;
208       break;
209     case Z_BUF_ERROR:
210       /* No more data to flush: just exit loop. */
211       break;
212     case Z_STREAM_END:
213       result = process_trailer(data, zp);
214       break;
215     case Z_DATA_ERROR:
216       /* some servers seem to not generate zlib headers, so this is an attempt
217          to fix and continue anyway */
218       if(zp->zlib_init == ZLIB_INIT) {
219         /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */
220         (void) inflateEnd(z);     /* do not care about the return code */
221         if(inflateInit2(z, -MAX_WBITS) == Z_OK) {
222           z->next_in = orig_in;
223           z->avail_in = nread;
224           zp->zlib_init = ZLIB_INFLATING;
225           zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */
226           done = FALSE;
227           break;
228         }
229         zp->zlib_init = ZLIB_UNINIT;    /* inflateEnd() already called. */
230       }
231       result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
232       break;
233     default:
234       result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
235       break;
236     }
237   }
238 
239   /* We are about to leave this call so the `nread' data bytes will not be seen
240      again. If we are in a state that would wrongly allow restart in raw mode
241      at the next call, assume output has already started. */
242   if(nread && zp->zlib_init == ZLIB_INIT)
243     zp->zlib_init = started;      /* Cannot restart anymore. */
244 
245   return result;
246 }
247 
248 
249 /* Deflate handler. */
deflate_do_init(struct Curl_easy * data,struct Curl_cwriter * writer)250 static CURLcode deflate_do_init(struct Curl_easy *data,
251                                 struct Curl_cwriter *writer)
252 {
253   struct zlib_writer *zp = (struct zlib_writer *) writer;
254   z_stream *z = &zp->z;     /* zlib state structure */
255 
256   /* Initialize zlib */
257   z->zalloc = (alloc_func) zalloc_cb;
258   z->zfree = (free_func) zfree_cb;
259 
260   if(inflateInit(z) != Z_OK)
261     return process_zlib_error(data, z);
262   zp->zlib_init = ZLIB_INIT;
263   return CURLE_OK;
264 }
265 
deflate_do_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t nbytes)266 static CURLcode deflate_do_write(struct Curl_easy *data,
267                                  struct Curl_cwriter *writer, int type,
268                                  const char *buf, size_t nbytes)
269 {
270   struct zlib_writer *zp = (struct zlib_writer *) writer;
271   z_stream *z = &zp->z;     /* zlib state structure */
272 
273   if(!(type & CLIENTWRITE_BODY) || !nbytes)
274     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
275 
276   /* Set the compressed input when this function is called */
277   z->next_in = (Bytef *) buf;
278   z->avail_in = (uInt) nbytes;
279 
280   if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
281     return process_trailer(data, zp);
282 
283   /* Now uncompress the data */
284   return inflate_stream(data, writer, type, ZLIB_INFLATING);
285 }
286 
deflate_do_close(struct Curl_easy * data,struct Curl_cwriter * writer)287 static void deflate_do_close(struct Curl_easy *data,
288                              struct Curl_cwriter *writer)
289 {
290   struct zlib_writer *zp = (struct zlib_writer *) writer;
291   z_stream *z = &zp->z;     /* zlib state structure */
292 
293   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
294 }
295 
296 static const struct Curl_cwtype deflate_encoding = {
297   "deflate",
298   NULL,
299   deflate_do_init,
300   deflate_do_write,
301   deflate_do_close,
302   sizeof(struct zlib_writer)
303 };
304 
305 
306 /* Gzip handler. */
gzip_do_init(struct Curl_easy * data,struct Curl_cwriter * writer)307 static CURLcode gzip_do_init(struct Curl_easy *data,
308                              struct Curl_cwriter *writer)
309 {
310   struct zlib_writer *zp = (struct zlib_writer *) writer;
311   z_stream *z = &zp->z;     /* zlib state structure */
312   const char *v = zlibVersion();
313 
314   /* Initialize zlib */
315   z->zalloc = (alloc_func) zalloc_cb;
316   z->zfree = (free_func) zfree_cb;
317 
318   if(strcmp(v, "1.2.0.4") >= 0) {
319     /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */
320     if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
321       return process_zlib_error(data, z);
322     }
323     zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
324   }
325   else {
326     failf(data, "too old zlib version: %s", v);
327     return CURLE_FAILED_INIT;
328   }
329 
330   return CURLE_OK;
331 }
332 
gzip_do_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t nbytes)333 static CURLcode gzip_do_write(struct Curl_easy *data,
334                               struct Curl_cwriter *writer, int type,
335                               const char *buf, size_t nbytes)
336 {
337   struct zlib_writer *zp = (struct zlib_writer *) writer;
338   z_stream *z = &zp->z;     /* zlib state structure */
339 
340   if(!(type & CLIENTWRITE_BODY) || !nbytes)
341     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
342 
343   if(zp->zlib_init == ZLIB_INIT_GZIP) {
344     /* Let zlib handle the gzip decompression entirely */
345     z->next_in = (Bytef *) buf;
346     z->avail_in = (uInt) nbytes;
347     /* Now uncompress the data */
348     return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
349   }
350 
351   /* We are running with an old version: return error. */
352   return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
353 }
354 
gzip_do_close(struct Curl_easy * data,struct Curl_cwriter * writer)355 static void gzip_do_close(struct Curl_easy *data,
356                               struct Curl_cwriter *writer)
357 {
358   struct zlib_writer *zp = (struct zlib_writer *) writer;
359   z_stream *z = &zp->z;     /* zlib state structure */
360 
361   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
362 }
363 
364 static const struct Curl_cwtype gzip_encoding = {
365   "gzip",
366   "x-gzip",
367   gzip_do_init,
368   gzip_do_write,
369   gzip_do_close,
370   sizeof(struct zlib_writer)
371 };
372 
373 #endif /* HAVE_LIBZ */
374 
375 #ifdef HAVE_BROTLI
376 /* Brotli writer. */
377 struct brotli_writer {
378   struct Curl_cwriter super;
379   char buffer[DECOMPRESS_BUFFER_SIZE];
380   BrotliDecoderState *br;    /* State structure for brotli. */
381 };
382 
brotli_map_error(BrotliDecoderErrorCode be)383 static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
384 {
385   switch(be) {
386   case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:
387   case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:
388   case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:
389   case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:
390   case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:
391   case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:
392   case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:
393   case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:
394   case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:
395   case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:
396   case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:
397   case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:
398   case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:
399   case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:
400 #ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY
401   case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:
402 #endif
403 #ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET
404   case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:
405 #endif
406   case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:
407     return CURLE_BAD_CONTENT_ENCODING;
408   case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:
409   case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:
410   case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:
411   case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:
412   case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:
413   case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:
414     return CURLE_OUT_OF_MEMORY;
415   default:
416     break;
417   }
418   return CURLE_WRITE_ERROR;
419 }
420 
brotli_do_init(struct Curl_easy * data,struct Curl_cwriter * writer)421 static CURLcode brotli_do_init(struct Curl_easy *data,
422                                struct Curl_cwriter *writer)
423 {
424   struct brotli_writer *bp = (struct brotli_writer *) writer;
425   (void) data;
426 
427   bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
428   return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;
429 }
430 
brotli_do_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t nbytes)431 static CURLcode brotli_do_write(struct Curl_easy *data,
432                                 struct Curl_cwriter *writer, int type,
433                                 const char *buf, size_t nbytes)
434 {
435   struct brotli_writer *bp = (struct brotli_writer *) writer;
436   const uint8_t *src = (const uint8_t *) buf;
437   uint8_t *dst;
438   size_t dstleft;
439   CURLcode result = CURLE_OK;
440   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
441 
442   if(!(type & CLIENTWRITE_BODY) || !nbytes)
443     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
444 
445   if(!bp->br)
446     return CURLE_WRITE_ERROR;  /* Stream already ended. */
447 
448   while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
449         result == CURLE_OK) {
450     dst = (uint8_t *) bp->buffer;
451     dstleft = DECOMPRESS_BUFFER_SIZE;
452     r = BrotliDecoderDecompressStream(bp->br,
453                                       &nbytes, &src, &dstleft, &dst, NULL);
454     result = Curl_cwriter_write(data, writer->next, type,
455                                 bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);
456     if(result)
457       break;
458     switch(r) {
459     case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
460     case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
461       break;
462     case BROTLI_DECODER_RESULT_SUCCESS:
463       BrotliDecoderDestroyInstance(bp->br);
464       bp->br = NULL;
465       if(nbytes)
466         result = CURLE_WRITE_ERROR;
467       break;
468     default:
469       result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));
470       break;
471     }
472   }
473   return result;
474 }
475 
brotli_do_close(struct Curl_easy * data,struct Curl_cwriter * writer)476 static void brotli_do_close(struct Curl_easy *data,
477                                 struct Curl_cwriter *writer)
478 {
479   struct brotli_writer *bp = (struct brotli_writer *) writer;
480   (void) data;
481 
482   if(bp->br) {
483     BrotliDecoderDestroyInstance(bp->br);
484     bp->br = NULL;
485   }
486 }
487 
488 static const struct Curl_cwtype brotli_encoding = {
489   "br",
490   NULL,
491   brotli_do_init,
492   brotli_do_write,
493   brotli_do_close,
494   sizeof(struct brotli_writer)
495 };
496 #endif
497 
498 #ifdef HAVE_ZSTD
499 /* Zstd writer. */
500 struct zstd_writer {
501   struct Curl_cwriter super;
502   ZSTD_DStream *zds;    /* State structure for zstd. */
503   char buffer[DECOMPRESS_BUFFER_SIZE];
504 };
505 
506 #ifdef ZSTD_STATIC_LINKING_ONLY
Curl_zstd_alloc(void * opaque,size_t size)507 static void *Curl_zstd_alloc(void *opaque, size_t size)
508 {
509   (void)opaque;
510   return Curl_cmalloc(size);
511 }
512 
Curl_zstd_free(void * opaque,void * address)513 static void Curl_zstd_free(void *opaque, void *address)
514 {
515   (void)opaque;
516   Curl_cfree(address);
517 }
518 #endif
519 
zstd_do_init(struct Curl_easy * data,struct Curl_cwriter * writer)520 static CURLcode zstd_do_init(struct Curl_easy *data,
521                              struct Curl_cwriter *writer)
522 {
523   struct zstd_writer *zp = (struct zstd_writer *) writer;
524 
525   (void)data;
526 
527 #ifdef ZSTD_STATIC_LINKING_ONLY
528   zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {
529     .customAlloc = Curl_zstd_alloc,
530     .customFree  = Curl_zstd_free,
531     .opaque      = NULL
532   });
533 #else
534   zp->zds = ZSTD_createDStream();
535 #endif
536 
537   return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
538 }
539 
zstd_do_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t nbytes)540 static CURLcode zstd_do_write(struct Curl_easy *data,
541                               struct Curl_cwriter *writer, int type,
542                               const char *buf, size_t nbytes)
543 {
544   CURLcode result = CURLE_OK;
545   struct zstd_writer *zp = (struct zstd_writer *) writer;
546   ZSTD_inBuffer in;
547   ZSTD_outBuffer out;
548   size_t errorCode;
549 
550   if(!(type & CLIENTWRITE_BODY) || !nbytes)
551     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
552 
553   in.pos = 0;
554   in.src = buf;
555   in.size = nbytes;
556 
557   for(;;) {
558     out.pos = 0;
559     out.dst = zp->buffer;
560     out.size = DECOMPRESS_BUFFER_SIZE;
561 
562     errorCode = ZSTD_decompressStream(zp->zds, &out, &in);
563     if(ZSTD_isError(errorCode)) {
564       return CURLE_BAD_CONTENT_ENCODING;
565     }
566     if(out.pos > 0) {
567       result = Curl_cwriter_write(data, writer->next, type,
568                                   zp->buffer, out.pos);
569       if(result)
570         break;
571     }
572     if((in.pos == nbytes) && (out.pos < out.size))
573       break;
574   }
575 
576   return result;
577 }
578 
zstd_do_close(struct Curl_easy * data,struct Curl_cwriter * writer)579 static void zstd_do_close(struct Curl_easy *data,
580                               struct Curl_cwriter *writer)
581 {
582   struct zstd_writer *zp = (struct zstd_writer *) writer;
583   (void)data;
584 
585   if(zp->zds) {
586     ZSTD_freeDStream(zp->zds);
587     zp->zds = NULL;
588   }
589 }
590 
591 static const struct Curl_cwtype zstd_encoding = {
592   "zstd",
593   NULL,
594   zstd_do_init,
595   zstd_do_write,
596   zstd_do_close,
597   sizeof(struct zstd_writer)
598 };
599 #endif
600 
601 /* Identity handler. */
602 static const struct Curl_cwtype identity_encoding = {
603   "identity",
604   "none",
605   Curl_cwriter_def_init,
606   Curl_cwriter_def_write,
607   Curl_cwriter_def_close,
608   sizeof(struct Curl_cwriter)
609 };
610 
611 /* supported general content decoders. */
612 static const struct Curl_cwtype * const general_unencoders[] = {
613   &identity_encoding,
614 #ifdef HAVE_LIBZ
615   &deflate_encoding,
616   &gzip_encoding,
617 #endif
618 #ifdef HAVE_BROTLI
619   &brotli_encoding,
620 #endif
621 #ifdef HAVE_ZSTD
622   &zstd_encoding,
623 #endif
624   NULL
625 };
626 
627 /* supported content decoders only for transfer encodings */
628 static const struct Curl_cwtype * const transfer_unencoders[] = {
629 #ifndef CURL_DISABLE_HTTP
630   &Curl_httpchunk_unencoder,
631 #endif
632   NULL
633 };
634 
635 /* Provide a list of comma-separated names of supported encodings.
636 */
Curl_all_content_encodings(char * buf,size_t blen)637 void Curl_all_content_encodings(char *buf, size_t blen)
638 {
639   size_t len = 0;
640   const struct Curl_cwtype * const *cep;
641   const struct Curl_cwtype *ce;
642 
643   DEBUGASSERT(buf);
644   DEBUGASSERT(blen);
645   buf[0] = 0;
646 
647   for(cep = general_unencoders; *cep; cep++) {
648     ce = *cep;
649     if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
650       len += strlen(ce->name) + 2;
651   }
652 
653   if(!len) {
654     if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))
655       strcpy(buf, CONTENT_ENCODING_DEFAULT);
656   }
657   else if(blen > len) {
658     char *p = buf;
659     for(cep = general_unencoders; *cep; cep++) {
660       ce = *cep;
661       if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
662         strcpy(p, ce->name);
663         p += strlen(p);
664         *p++ = ',';
665         *p++ = ' ';
666       }
667     }
668     p[-2] = '\0';
669   }
670 }
671 
672 /* Deferred error dummy writer. */
error_do_init(struct Curl_easy * data,struct Curl_cwriter * writer)673 static CURLcode error_do_init(struct Curl_easy *data,
674                               struct Curl_cwriter *writer)
675 {
676   (void)data;
677   (void)writer;
678   return CURLE_OK;
679 }
680 
error_do_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t nbytes)681 static CURLcode error_do_write(struct Curl_easy *data,
682                                struct Curl_cwriter *writer, int type,
683                                const char *buf, size_t nbytes)
684 {
685   (void) writer;
686   (void) buf;
687   (void) nbytes;
688 
689   if(!(type & CLIENTWRITE_BODY) || !nbytes)
690     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
691   else {
692     char all[256];
693     (void)Curl_all_content_encodings(all, sizeof(all));
694     failf(data, "Unrecognized content encoding type. "
695           "libcurl understands %s content encodings.", all);
696   }
697   return CURLE_BAD_CONTENT_ENCODING;
698 }
699 
error_do_close(struct Curl_easy * data,struct Curl_cwriter * writer)700 static void error_do_close(struct Curl_easy *data,
701                                struct Curl_cwriter *writer)
702 {
703   (void) data;
704   (void) writer;
705 }
706 
707 static const struct Curl_cwtype error_writer = {
708   "ce-error",
709   NULL,
710   error_do_init,
711   error_do_write,
712   error_do_close,
713   sizeof(struct Curl_cwriter)
714 };
715 
716 /* Find the content encoding by name. */
find_unencode_writer(const char * name,size_t len,Curl_cwriter_phase phase)717 static const struct Curl_cwtype *find_unencode_writer(const char *name,
718                                                       size_t len,
719                                                       Curl_cwriter_phase phase)
720 {
721   const struct Curl_cwtype * const *cep;
722 
723   if(phase == CURL_CW_TRANSFER_DECODE) {
724     for(cep = transfer_unencoders; *cep; cep++) {
725       const struct Curl_cwtype *ce = *cep;
726       if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
727          (ce->alias && strncasecompare(name, ce->alias, len)
728                     && !ce->alias[len]))
729         return ce;
730     }
731   }
732   /* look among the general decoders */
733   for(cep = general_unencoders; *cep; cep++) {
734     const struct Curl_cwtype *ce = *cep;
735     if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
736        (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
737       return ce;
738   }
739   return NULL;
740 }
741 
742 /* Setup the unencoding stack from the Content-Encoding header value.
743  * See RFC 7231 section 3.1.2.2. */
Curl_build_unencoding_stack(struct Curl_easy * data,const char * enclist,int is_transfer)744 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
745                                      const char *enclist, int is_transfer)
746 {
747   Curl_cwriter_phase phase = is_transfer ?
748     CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;
749   CURLcode result;
750 
751   do {
752     const char *name;
753     size_t namelen;
754     bool is_chunked = FALSE;
755 
756     /* Parse a single encoding name. */
757     while(ISBLANK(*enclist) || *enclist == ',')
758       enclist++;
759 
760     name = enclist;
761 
762     for(namelen = 0; *enclist && *enclist != ','; enclist++)
763       if(!ISSPACE(*enclist))
764         namelen = enclist - name + 1;
765 
766     if(namelen) {
767       const struct Curl_cwtype *cwt;
768       struct Curl_cwriter *writer;
769 
770       CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
771                      is_transfer ? "transfer" : "content", (int)namelen, name);
772       is_chunked = (is_transfer && (namelen == 7) &&
773                     strncasecompare(name, "chunked", 7));
774       /* if we skip the decoding in this phase, do not look further.
775        * Exception is "chunked" transfer-encoding which always must happen */
776       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
777          (!is_transfer && data->set.http_ce_skip)) {
778         /* not requested, ignore */
779         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
780                        (int)namelen, name);
781         return CURLE_OK;
782       }
783 
784       if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
785         failf(data, "Reject response due to more than %u content encodings",
786               MAX_ENCODE_STACK);
787         return CURLE_BAD_CONTENT_ENCODING;
788       }
789 
790       cwt = find_unencode_writer(name, namelen, phase);
791       if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
792         /* A 'chunked' transfer encoding has already been added.
793          * Ignore duplicates. See #13451.
794          * Also RFC 9112, ch. 6.1:
795          * "A sender MUST NOT apply the chunked transfer coding more than
796          *  once to a message body."
797          */
798         CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");
799         return CURLE_OK;
800       }
801 
802       if(is_transfer && !is_chunked &&
803          Curl_cwriter_get_by_name(data, "chunked")) {
804         /* RFC 9112, ch. 6.1:
805          * "If any transfer coding other than chunked is applied to a
806          *  response's content, the sender MUST either apply chunked as the
807          *  final transfer coding or terminate the message by closing the
808          *  connection."
809          * "chunked" must be the last added to be the first in its phase,
810          *  reject this.
811          */
812         failf(data, "Reject response due to 'chunked' not being the last "
813               "Transfer-Encoding");
814         return CURLE_BAD_CONTENT_ENCODING;
815       }
816 
817       if(!cwt)
818         cwt = &error_writer;  /* Defer error at use. */
819 
820       result = Curl_cwriter_create(&writer, data, cwt, phase);
821       CURL_TRC_WRITE(data, "added %s decoder %s -> %d",
822                      is_transfer ? "transfer" : "content", cwt->name, result);
823       if(result)
824         return result;
825 
826       result = Curl_cwriter_add(data, writer);
827       if(result) {
828         Curl_cwriter_free(data, writer);
829         return result;
830       }
831     }
832   } while(*enclist);
833 
834   return CURLE_OK;
835 }
836 
837 #else
838 /* Stubs for builds without HTTP. */
Curl_build_unencoding_stack(struct Curl_easy * data,const char * enclist,int is_transfer)839 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
840                                      const char *enclist, int is_transfer)
841 {
842   (void) data;
843   (void) enclist;
844   (void) is_transfer;
845   return CURLE_NOT_BUILT_IN;
846 }
847 
Curl_all_content_encodings(char * buf,size_t blen)848 void Curl_all_content_encodings(char *buf, size_t blen)
849 {
850   DEBUGASSERT(buf);
851   DEBUGASSERT(blen);
852   if(blen < sizeof(CONTENT_ENCODING_DEFAULT))
853     buf[0] = 0;
854   else
855     strcpy(buf, CONTENT_ENCODING_DEFAULT);
856 }
857 
858 #endif /* CURL_DISABLE_HTTP */
859