rocksdb报错解决:librocksdb.a(format.o): In function `Zlib_Uncompress':

在运用RocksDB时遇到错误提示'librocksdb.a(format.o): In function `Zlib_Uncompress':'。解决此问题的关键在于,在编译阶段需添加参数,确保正确链接了Zlib库。

在使用rocksdb的时候遇到了报错:

build64_release.rep125/my_kv/rocksdb-6.1.2/librocksdb.a(format.o): In function `Zlib_Uncompress':
/home/xxx/rocksdb-6.1.2/./util/compression.h:723: undefined reference to `inflateInit2_'
/home/xxx/rocksdb-6.1.2/./util/compression.h:733: undefined reference to `inflateSetDictionary'
/home/xxx/rocksdb-6.1.2/./util/compression.h:749: undefined reference to `inflate'
/home/xxx/rocksdb-6.1.2/./util/compression.h:781: undefined reference to `inflateEnd'
/home/xxx/rocksdb-6.1.2/./util/compression.h:773: undefined reference to `inflateEnd'
build64_release.rep125/my_kv/rocksdb-6.1.2/librocksdb.a(block_based_table_builder.o): In function `Zlib_Compress':
/home/xxx/rocksdb-6.1.2/./util/compression.h:642: undefined reference to `deflateInit2_'
/home/xxx/rocksdb-6.1.2/./util/compression.h:669: undefined reference to `deflate'
/home/xxx/rocksdb-6.1.2/./util/compression.h:678: undefined reference to `deflateEnd'
/home/xxx/rocksdb-6.1.2/./util/compression.h:642: undefined reference to `deflateInit2_'
/home/xxx/rocksdb-6.1.2/./util/compression.h:669: undefined reference to `deflate'
/home/xxx/rocksdb-6.1.2/./util/compression.h:655: undefined reference to `deflateEnd'
/home/xxx/rocksdb-6.1.2/./util/compression.h:653: undefined reference to `deflateSetDictionary'
build64_release.rep125/my_kv/rocksdb-6.1.2/librocksdb.a(block_based_table_builder.o): In function `CompressBlockInternal':
/home/xxx/rocksdb-6.1.2/./util/compression.h:653: undefined reference to `deflateSetDictionary'
/home/xxx/rocksdb-6.1.2/./util/compression.h:678: undefined reference to `deflateEnd'
collect2: error: ld returned 1 exit status

这个报错的解决方法是在编译时加上参数-lz,意思是把zlib库链接上。

From 76f83f0db23846e254d940ec7fe141010077eb88 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg <daniel@haxx.se> Date: Fri, 24 Jan 2025 11:13:24 +0100 Subject: [PATCH] content_encoding: drop support for zlib before 1.2.0.4 zlib 1.2.0.4 was released on 10 August 2003 Closes #16079 --- docs/INTERNALS.md | 2 +- lib/content_encoding.c | 276 ++++------------------------------------- 2 files changed, 25 insertions(+), 253 deletions(-) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index ae77f0e54b05..4e42f4fd1015 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -26,7 +26,7 @@ versions of libs and build tools. - OpenSSL 0.9.7 - GnuTLS 3.1.10 - - zlib 1.1.4 + - zlib 1.2.0.4 - libssh2 1.0 - c-ares 1.16.0 - libidn2 2.0.0 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index e19595d5ec42..d2b17297890d 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -68,33 +68,13 @@ #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ - #ifdef HAVE_LIBZ -/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 - (doing so will reduce code size slightly). */ -#define OLD_ZLIB_SUPPORT 1 - -#define GZIP_MAGIC_0 0x1f -#define GZIP_MAGIC_1 0x8b - -/* gzip flag byte */ -#define CURL_GZIPFLAG_ASCII 0x01 /* bit 0 set: file probably ASCII - text */ -#define CURL_GZIPFLAG_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ -#define CURL_GZIPFLAG_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define CURL_GZIPFLAG_ORIG_NAME 0x08 /* bit 3 set: original filename - present */ -#define CURL_GZIPFLAG_COMMENT 0x10 /* bit 4 set: file comment present */ -#define CURL_GZIPFLAG_RESERVED 0xE0 /* bits 5..7: reserved */ - typedef enum { ZLIB_UNINIT, /* uninitialized */ ZLIB_INIT, /* initialized */ ZLIB_INFLATING, /* inflating started. */ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ - ZLIB_GZIP_HEADER, /* reading gzip header */ - ZLIB_GZIP_INFLATING, /* inflating gzip stream */ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ } zlibInitState; @@ -139,9 +119,6 @@ static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, zlibInitState *zlib_init, CURLcode result) { - if(*zlib_init == ZLIB_GZIP_HEADER) - Curl_safefree(z->next_in); - if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) result = process_zlib_error(data, z); @@ -190,8 +167,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Check state. */ if(zp->zlib_init != ZLIB_INIT && zp->zlib_init != ZLIB_INFLATING && - zp->zlib_init != ZLIB_INIT_GZIP && - zp->zlib_init != ZLIB_GZIP_INFLATING) + zp->zlib_init != ZLIB_INIT_GZIP) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); /* Dynamically allocate a buffer for decompression because it is uncommonly @@ -280,7 +256,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Deflate handler. */ static CURLcode deflate_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -296,8 +272,8 @@ static CURLcode deflate_do_init(struct Curl_easy *data, } static CURLcode deflate_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -317,7 +293,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, } static void deflate_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -337,124 +313,34 @@ static const struct Curl_cwtype deflate_encoding = { /* Gzip handler. */ static CURLcode gzip_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + const char *v = zlibVersion(); /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; - if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { - /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ + if(strcmp(v, "1.2.0.4") >= 0) { + /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { return process_zlib_error(data, z); } zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ } else { - /* we must parse the gzip header and trailer ourselves */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - return process_zlib_error(data, z); - } - zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ - zp->zlib_init = ZLIB_INIT; /* Initial call state */ + failf(data, "too old zlib version: %s", v); + return CURLE_FAILED_INIT; } return CURLE_OK; } -#ifdef OLD_ZLIB_SUPPORT -/* Skip over the gzip header */ -typedef enum { - GZIP_OK, - GZIP_BAD, - GZIP_UNDERFLOW -} gzip_status; - -static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, - ssize_t *headerlen) -{ - int method, flags; - const ssize_t totallen = len; - - /* The shortest header is 10 bytes */ - if(len < 10) - return GZIP_UNDERFLOW; - - if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) - return GZIP_BAD; - - method = data[2]; - flags = data[3]; - - if(method != Z_DEFLATED || (flags & CURL_GZIPFLAG_RESERVED) != 0) { - /* cannot handle this compression method or unknown flag */ - return GZIP_BAD; - } - - /* Skip over time, xflags, OS code and all previous bytes */ - len -= 10; - data += 10; - - if(flags & CURL_GZIPFLAG_EXTRA_FIELD) { - ssize_t extra_len; - - if(len < 2) - return GZIP_UNDERFLOW; - - extra_len = (data[1] << 8) | data[0]; - - if(len < (extra_len + 2)) - return GZIP_UNDERFLOW; - - len -= (extra_len + 2); - data += (extra_len + 2); - } - - if(flags & CURL_GZIPFLAG_ORIG_NAME) { - /* Skip over NUL-terminated filename */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - ++data; - } - - if(flags & CURL_GZIPFLAG_COMMENT) { - /* Skip over NUL-terminated comment */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - } - - if(flags & CURL_GZIPFLAG_HEAD_CRC) { - if(len < 2) - return GZIP_UNDERFLOW; - - len -= 2; - } - - *headerlen = totallen - len; - return GZIP_OK; -} -#endif - static CURLcode gzip_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -470,117 +356,8 @@ static CURLcode gzip_do_write(struct Curl_easy *data, return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); } -#ifndef OLD_ZLIB_SUPPORT - /* Support for old zlib versions is compiled away and we are running with - an old version, so return an error. */ + /* We are running with an old version: return error. */ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - -#else - /* This next mess is to get around the potential case where there is not - * enough data passed in to skip over the gzip header. If that happens, we - * malloc a block and copy what we have then wait for the next call. If - * there still is not enough (this is definitely a worst-case scenario), we - * make the block bigger, copy the next part in and keep waiting. - * - * This is only required with zlib versions < 1.2.0.4 as newer versions - * can handle the gzip header themselves. - */ - - switch(zp->zlib_init) { - /* Skip over gzip header? */ - case ZLIB_INIT: - { - /* Initial call state */ - ssize_t hlen; - - switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { - case GZIP_OK: - z->next_in = (Bytef *) buf + hlen; - z->avail_in = (uInt) (nbytes - hlen); - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We need more data so we can find the end of the gzip header. it is - * possible that the memory block we malloc here will never be freed if - * the transfer abruptly aborts after this point. Since it is unlikely - * that circumstances will be right for this code path to be followed in - * the first place, and it is even more unlikely for a transfer to fail - * immediately afterwards, it should seldom be a problem. - */ - z->avail_in = (uInt) nbytes; - z->next_in = malloc(z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - memcpy(z->next_in, buf, z->avail_in); - zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ - /* We do not have any data to inflate yet */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_GZIP_HEADER: - { - /* Need more gzip header data state */ - ssize_t hlen; - z->avail_in += (uInt) nbytes; - z->next_in = Curl_saferealloc(z->next_in, z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - /* Append the new block of data to the previous one */ - memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); - - switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) { - case GZIP_OK: - /* This is the zlib stream data */ - free(z->next_in); - /* Do not point into the malloced block since we just freed it */ - z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; - z->avail_in = z->avail_in - (uInt)hlen; - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We still do not have any data to inflate! */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_EXTERNAL_TRAILER: - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - return process_trailer(data, zp); - - case ZLIB_GZIP_INFLATING: - default: - /* Inflating stream state */ - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - break; - } - - if(z->avail_in == 0) { - /* We do not have any data to inflate; wait until next time */ - return CURLE_OK; - } - - /* We have parsed the header, now uncompress the data */ - return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); -#endif } static void gzip_do_close(struct Curl_easy *data, @@ -603,7 +380,6 @@ static const struct Curl_cwtype gzip_encoding = { #endif /* HAVE_LIBZ */ - #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { @@ -650,7 +426,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) } static CURLcode brotli_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -660,8 +436,8 @@ static CURLcode brotli_do_init(struct Curl_easy *data, } static CURLcode brotli_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; const uint8_t *src = (const uint8_t *) buf; @@ -733,7 +509,6 @@ static const struct Curl_cwtype brotli_encoding = { }; #endif - #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { @@ -757,7 +532,7 @@ static void Curl_zstd_free(void *opaque, void *address) #endif static CURLcode zstd_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -778,8 +553,8 @@ static CURLcode zstd_do_init(struct Curl_easy *data, } static CURLcode zstd_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -810,7 +585,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, } if(out.pos > 0) { result = Curl_cwriter_write(data, writer->next, type, - zp->decomp, out.pos); + zp->decomp, out.pos); if(result) break; } @@ -848,7 +623,6 @@ static const struct Curl_cwtype zstd_encoding = { }; #endif - /* Identity handler. */ static const struct Curl_cwtype identity_encoding = { "identity", @@ -859,7 +633,6 @@ static const struct Curl_cwtype identity_encoding = { sizeof(struct Curl_cwriter) }; - /* supported general content decoders. */ static const struct Curl_cwtype * const general_unencoders[] = { &identity_encoding, @@ -923,7 +696,7 @@ void Curl_all_content_encodings(char *buf, size_t blen) /* Deferred error dummy writer. */ static CURLcode error_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { (void)data; (void)writer; @@ -931,8 +704,8 @@ static CURLcode error_do_init(struct Curl_easy *data, } static CURLcode error_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { (void) writer; (void) buf; @@ -1107,5 +880,4 @@ void Curl_all_content_encodings(char *buf, size_t blen) strcpy(buf, CONTENT_ENCODING_DEFAULT); } - #endif /* CURL_DISABLE_HTTP */ 上边的这个是个patch 下边的是我的源码,但是是以压缩包的形式存在的,不能修改源码 /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "curl_setup.h" #include "urldata.h" #include <curl/curl.h> #include <stddef.h> #ifdef HAVE_LIBZ #include <zlib.h> #endif #ifdef HAVE_BROTLI #include <brotli/decode.h> #endif #ifdef HAVE_ZSTD #include <zstd.h> #endif #include "sendf.h" #include "http.h" #include "content_encoding.h" #include "strdup.h" #include "strcase.h" #include "curl_memory.h" #include "memdebug.h" #define CONTENT_ENCODING_DEFAULT "identity" #ifndef CURL_DISABLE_HTTP #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ #ifdef HAVE_LIBZ /* Comment this out if zlib is always going to be at least ver. 1.2.0.4 (doing so will reduce code size slightly). */ #define OLD_ZLIB_SUPPORT 1 #define GZIP_MAGIC_0 0x1f #define GZIP_MAGIC_1 0x8b /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ typedef enum { ZLIB_UNINIT, /* uninitialized */ ZLIB_INIT, /* initialized */ ZLIB_INFLATING, /* inflating started. */ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ ZLIB_GZIP_HEADER, /* reading gzip header */ ZLIB_GZIP_INFLATING, /* inflating gzip stream */ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ } zlibInitState; /* Deflate and gzip writer. */ struct zlib_writer { struct contenc_writer super; zlibInitState zlib_init; /* zlib init state */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ }; static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void) opaque; /* not a typo, keep it calloc() */ return (voidpf) calloc(items, size); } static void zfree_cb(voidpf opaque, voidpf ptr) { (void) opaque; free(ptr); } static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) failf(data, "Error while processing content unencoding: %s", z->msg); else failf(data, "Error while processing content unencoding: " "Unknown failure within decompression software."); return CURLE_BAD_CONTENT_ENCODING; } static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, zlibInitState *zlib_init, CURLcode result) { if(*zlib_init == ZLIB_GZIP_HEADER) Curl_safefree(z->next_in); if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) result = process_zlib_error(data, z); *zlib_init = ZLIB_UNINIT; } return result; } static CURLcode process_trailer(struct Curl_easy *data, struct zlib_writer *zp) { z_stream *z = &zp->z; CURLcode result = CURLE_OK; uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen; /* Consume expected trailer bytes. Terminate stream if exhausted. Issue an error if unexpected bytes follow. */ zp->trailerlen -= len; z->avail_in -= len; z->next_in += len; if(z->avail_in) result = CURLE_WRITE_ERROR; if(result || !zp->trailerlen) result = exit_zlib(data, z, &zp->zlib_init, result); else { /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */ zp->zlib_init = ZLIB_EXTERNAL_TRAILER; } return result; } static CURLcode inflate_stream(struct Curl_easy *data, struct contenc_writer *writer, zlibInitState started) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ uInt nread = z->avail_in; Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ char *decomp; /* Put the decompressed data here. */ /* Check state. */ if(zp->zlib_init != ZLIB_INIT && zp->zlib_init != ZLIB_INFLATING && zp->zlib_init != ZLIB_INIT_GZIP && zp->zlib_init != ZLIB_GZIP_INFLATING) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); /* Dynamically allocate a buffer for decompression because it's uncommonly large to hold on the stack */ decomp = malloc(DSIZ); if(!decomp) return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); /* because the buffer size is fixed, iteratively decompress and transfer to the client via downstream_write function. */ while(!done) { int status; /* zlib status */ done = TRUE; /* (re)set buffer for decompressed output for every iteration */ z->next_out = (Bytef *) decomp; z->avail_out = DSIZ; #ifdef Z_BLOCK /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ status = inflate(z, Z_BLOCK); #else /* fallback for zlib ver. < 1.2.0.5 */ status = inflate(z, Z_SYNC_FLUSH); #endif /* Flush output data if some. */ if(z->avail_out != DSIZ) { if(status == Z_OK || status == Z_STREAM_END) { zp->zlib_init = started; /* Data started. */ result = Curl_unencode_write(data, writer->downstream, decomp, DSIZ - z->avail_out); if(result) { exit_zlib(data, z, &zp->zlib_init, result); break; } } } /* Dispatch by inflate() status. */ switch(status) { case Z_OK: /* Always loop: there may be unflushed latched data in zlib state. */ done = FALSE; break; case Z_BUF_ERROR: /* No more data to flush: just exit loop. */ break; case Z_STREAM_END: result = process_trailer(data, zp); break; case Z_DATA_ERROR: /* some servers seem to not generate zlib headers, so this is an attempt to fix and continue anyway */ if(zp->zlib_init == ZLIB_INIT) { /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ (void) inflateEnd(z); /* don't care about the return code */ if(inflateInit2(z, -MAX_WBITS) == Z_OK) { z->next_in = orig_in; z->avail_in = nread; zp->zlib_init = ZLIB_INFLATING; zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */ done = FALSE; break; } zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; default: result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; } } free(decomp); /* We're about to leave this call so the `nread' data bytes won't be seen again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) zp->zlib_init = started; /* Cannot restart anymore. */ return result; } /* Deflate handler. */ static CURLcode deflate_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) return CURLE_WRITE_ERROR; /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; if(inflateInit(z) != Z_OK) return process_zlib_error(data, z); zp->zlib_init = ZLIB_INIT; return CURLE_OK; } static CURLcode deflate_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ /* Set the compressed input when this function is called */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER) return process_trailer(data, zp); /* Now uncompress the data */ return inflate_stream(data, writer, ZLIB_INFLATING); } static void deflate_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } static const struct content_encoding deflate_encoding = { "deflate", NULL, deflate_init_writer, deflate_unencode_write, deflate_close_writer, sizeof(struct zlib_writer) }; /* Gzip handler. */ static CURLcode gzip_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) return CURLE_WRITE_ERROR; /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { return process_zlib_error(data, z); } zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ } else { /* we must parse the gzip header and trailer ourselves */ if(inflateInit2(z, -MAX_WBITS) != Z_OK) { return process_zlib_error(data, z); } zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ zp->zlib_init = ZLIB_INIT; /* Initial call state */ } return CURLE_OK; } #ifdef OLD_ZLIB_SUPPORT /* Skip over the gzip header */ static enum { GZIP_OK, GZIP_BAD, GZIP_UNDERFLOW } check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen) { int method, flags; const ssize_t totallen = len; /* The shortest header is 10 bytes */ if(len < 10) return GZIP_UNDERFLOW; if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) return GZIP_BAD; method = data[2]; flags = data[3]; if(method != Z_DEFLATED || (flags & RESERVED) != 0) { /* Can't handle this compression method or unknown flag */ return GZIP_BAD; } /* Skip over time, xflags, OS code and all previous bytes */ len -= 10; data += 10; if(flags & EXTRA_FIELD) { ssize_t extra_len; if(len < 2) return GZIP_UNDERFLOW; extra_len = (data[1] << 8) | data[0]; if(len < (extra_len + 2)) return GZIP_UNDERFLOW; len -= (extra_len + 2); data += (extra_len + 2); } if(flags & ORIG_NAME) { /* Skip over NUL-terminated file name */ while(len && *data) { --len; ++data; } if(!len || *data) return GZIP_UNDERFLOW; /* Skip over the NUL */ --len; ++data; } if(flags & COMMENT) { /* Skip over NUL-terminated comment */ while(len && *data) { --len; ++data; } if(!len || *data) return GZIP_UNDERFLOW; /* Skip over the NUL */ --len; } if(flags & HEAD_CRC) { if(len < 2) return GZIP_UNDERFLOW; len -= 2; } *headerlen = totallen - len; return GZIP_OK; } #endif static CURLcode gzip_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(zp->zlib_init == ZLIB_INIT_GZIP) { /* Let zlib handle the gzip decompression entirely */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; /* Now uncompress the data */ return inflate_stream(data, writer, ZLIB_INIT_GZIP); } #ifndef OLD_ZLIB_SUPPORT /* Support for old zlib versions is compiled away and we are running with an old version, so return an error. */ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); #else /* This next mess is to get around the potential case where there isn't * enough data passed in to skip over the gzip header. If that happens, we * malloc a block and copy what we have then wait for the next call. If * there still isn't enough (this is definitely a worst-case scenario), we * make the block bigger, copy the next part in and keep waiting. * * This is only required with zlib versions < 1.2.0.4 as newer versions * can handle the gzip header themselves. */ switch(zp->zlib_init) { /* Skip over gzip header? */ case ZLIB_INIT: { /* Initial call state */ ssize_t hlen; switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { case GZIP_OK: z->next_in = (Bytef *) buf + hlen; z->avail_in = (uInt) (nbytes - hlen); zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: /* We need more data so we can find the end of the gzip header. It's * possible that the memory block we malloc here will never be freed if * the transfer abruptly aborts after this point. Since it's unlikely * that circumstances will be right for this code path to be followed in * the first place, and it's even more unlikely for a transfer to fail * immediately afterwards, it should seldom be a problem. */ z->avail_in = (uInt) nbytes; z->next_in = malloc(z->avail_in); if(!z->next_in) { return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } memcpy(z->next_in, buf, z->avail_in); zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ /* We don't have any data to inflate yet */ return CURLE_OK; case GZIP_BAD: default: return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); } } break; case ZLIB_GZIP_HEADER: { /* Need more gzip header data state */ ssize_t hlen; z->avail_in += (uInt) nbytes; z->next_in = Curl_saferealloc(z->next_in, z->avail_in); if(!z->next_in) { return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } /* Append the new block of data to the previous one */ memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { case GZIP_OK: /* This is the zlib stream data */ free(z->next_in); /* Don't point into the malloced block since we just freed it */ z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; z->avail_in = (uInt) (z->avail_in - hlen); zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: /* We still don't have any data to inflate! */ return CURLE_OK; case GZIP_BAD: default: return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); } } break; case ZLIB_EXTERNAL_TRAILER: z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; return process_trailer(data, zp); case ZLIB_GZIP_INFLATING: default: /* Inflating stream state */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; break; } if(z->avail_in == 0) { /* We don't have any data to inflate; wait until next time */ return CURLE_OK; } /* We've parsed the header, now uncompress the data */ return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); #endif } static void gzip_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } static const struct content_encoding gzip_encoding = { "gzip", "x-gzip", gzip_init_writer, gzip_unencode_write, gzip_close_writer, sizeof(struct zlib_writer) }; #endif /* HAVE_LIBZ */ #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { struct contenc_writer super; BrotliDecoderState *br; /* State structure for brotli. */ }; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) { switch(be) { case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: #ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: #endif #ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: #endif case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: return CURLE_BAD_CONTENT_ENCODING; case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: return CURLE_OUT_OF_MEMORY; default: break; } return CURLE_WRITE_ERROR; } static CURLcode brotli_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; if(!writer->downstream) return CURLE_WRITE_ERROR; bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; } static CURLcode brotli_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; const uint8_t *src = (const uint8_t *) buf; char *decomp; uint8_t *dst; size_t dstleft; CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; if(!bp->br) return CURLE_WRITE_ERROR; /* Stream already ended. */ decomp = malloc(DSIZ); if(!decomp) return CURLE_OUT_OF_MEMORY; while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { dst = (uint8_t *) decomp; dstleft = DSIZ; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); result = Curl_unencode_write(data, writer->downstream, decomp, DSIZ - dstleft); if(result) break; switch(r) { case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: break; case BROTLI_DECODER_RESULT_SUCCESS: BrotliDecoderDestroyInstance(bp->br); bp->br = NULL; if(nbytes) result = CURLE_WRITE_ERROR; break; default: result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br)); break; } } free(decomp); return result; } static void brotli_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; if(bp->br) { BrotliDecoderDestroyInstance(bp->br); bp->br = NULL; } } static const struct content_encoding brotli_encoding = { "br", NULL, brotli_init_writer, brotli_unencode_write, brotli_close_writer, sizeof(struct brotli_writer) }; #endif #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { struct contenc_writer super; ZSTD_DStream *zds; /* State structure for zstd. */ void *decomp; }; static CURLcode zstd_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; if(!writer->downstream) return CURLE_WRITE_ERROR; zp->zds = ZSTD_createDStream(); zp->decomp = NULL; return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; } static CURLcode zstd_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; struct zstd_writer *zp = (struct zstd_writer *) writer; ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; if(!zp->decomp) { zp->decomp = malloc(DSIZ); if(!zp->decomp) return CURLE_OUT_OF_MEMORY; } in.pos = 0; in.src = buf; in.size = nbytes; for(;;) { out.pos = 0; out.dst = zp->decomp; out.size = DSIZ; errorCode = ZSTD_decompressStream(zp->zds, &out, &in); if(ZSTD_isError(errorCode)) { return CURLE_BAD_CONTENT_ENCODING; } if(out.pos > 0) { result = Curl_unencode_write(data, writer->downstream, zp->decomp, out.pos); if(result) break; } if((in.pos == nbytes) && (out.pos < out.size)) break; } return result; } static void zstd_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; if(zp->decomp) { free(zp->decomp); zp->decomp = NULL; } if(zp->zds) { ZSTD_freeDStream(zp->zds); zp->zds = NULL; } } static const struct content_encoding zstd_encoding = { "zstd", NULL, zstd_init_writer, zstd_unencode_write, zstd_close_writer, sizeof(struct zstd_writer) }; #endif /* Identity handler. */ static CURLcode identity_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode identity_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { return Curl_unencode_write(data, writer->downstream, buf, nbytes); } static void identity_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding identity_encoding = { "identity", "none", identity_init_writer, identity_unencode_write, identity_close_writer, sizeof(struct contenc_writer) }; /* supported content encodings table. */ static const struct content_encoding * const encodings[] = { &identity_encoding, #ifdef HAVE_LIBZ &deflate_encoding, &gzip_encoding, #endif #ifdef HAVE_BROTLI &brotli_encoding, #endif #ifdef HAVE_ZSTD &zstd_encoding, #endif NULL }; /* Return a list of comma-separated names of supported encodings. */ char *Curl_all_content_encodings(void) { size_t len = 0; const struct content_encoding * const *cep; const struct content_encoding *ce; char *ace; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) len += strlen(ce->name) + 2; } if(!len) return strdup(CONTENT_ENCODING_DEFAULT); ace = malloc(len); if(ace) { char *p = ace; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { strcpy(p, ce->name); p += strlen(p); *p++ = ','; *p++ = ' '; } } p[-2] = '\0'; } return ace; } /* Real client writer: no downstream. */ static CURLcode client_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; } static CURLcode client_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct SingleRequest *k = &data->req; (void) writer; if(!nbytes || k->ignorebody) return CURLE_OK; return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes); } static void client_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding client_encoding = { NULL, NULL, client_init_writer, client_unencode_write, client_close_writer, sizeof(struct contenc_writer) }; /* Deferred error dummy writer. */ static CURLcode error_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode error_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { char *all = Curl_all_content_encodings(); (void) writer; (void) buf; (void) nbytes; if(!all) return CURLE_OUT_OF_MEMORY; failf(data, "Unrecognized content encoding type. " "libcurl understands %s content encodings.", all); free(all); return CURLE_BAD_CONTENT_ENCODING; } static void error_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding error_encoding = { NULL, NULL, error_init_writer, error_unencode_write, error_close_writer, sizeof(struct contenc_writer) }; /* Create an unencoding writer stage using the given handler. */ static struct contenc_writer * new_unencoding_writer(struct Curl_easy *data, const struct content_encoding *handler, struct contenc_writer *downstream, int order) { struct contenc_writer *writer; DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer)); writer = (struct contenc_writer *) calloc(1, handler->writersize); if(writer) { writer->handler = handler; writer->downstream = downstream; writer->order = order; if(handler->init_writer(data, writer)) { free(writer); writer = NULL; } } return writer; } /* Write data using an unencoding writer stack. "nbytes" is not allowed to be 0. */ CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { if(!nbytes) return CURLE_OK; return writer->handler->unencode_write(data, writer, buf, nbytes); } /* Close and clean-up the connection's writer stack. */ void Curl_unencode_cleanup(struct Curl_easy *data) { struct SingleRequest *k = &data->req; struct contenc_writer *writer = k->writer_stack; while(writer) { k->writer_stack = writer->downstream; writer->handler->close_writer(data, writer); free(writer); writer = k->writer_stack; } } /* Find the content encoding by name. */ static const struct content_encoding *find_encoding(const char *name, size_t len) { const struct content_encoding * const *cep; for(cep = encodings; *cep; cep++) { const struct content_encoding *ce = *cep; if((strncasecompare(name, ce->name, len) && !ce->name[len]) || (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) return ce; } return NULL; } /* allow no more than 5 "chained" compression steps */ #define MAX_ENCODE_STACK 5 /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; unsigned int order = is_transfer? 2: 1; do { const char *name; size_t namelen; /* Parse a single encoding name. */ while(ISBLANK(*enclist) || *enclist == ',') enclist++; name = enclist; for(namelen = 0; *enclist && *enclist != ','; enclist++) if(!ISSPACE(*enclist)) namelen = enclist - name + 1; /* Special case: chunked encoding is handled at the reader level. */ if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) { k->chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data); /* init our chunky engine. */ } else if(namelen) { const struct content_encoding *encoding = find_encoding(name, namelen); struct contenc_writer *writer; if(!k->writer_stack) { k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL, 0); if(!k->writer_stack) return CURLE_OUT_OF_MEMORY; } if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { failf(data, "Reject response due to more than %u content encodings", MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; } /* Stack the unencoding stage. */ if(order >= k->writer_stack->order) { writer = new_unencoding_writer(data, encoding, k->writer_stack, order); if(!writer) return CURLE_OUT_OF_MEMORY; k->writer_stack = writer; } else { struct contenc_writer *w = k->writer_stack; while(w->downstream && order < w->downstream->order) w = w->downstream; writer = new_unencoding_writer(data, encoding, w->downstream, order); if(!writer) return CURLE_OUT_OF_MEMORY; w->downstream = writer; } } } while(*enclist); return CURLE_OK; } #else /* Stubs for builds without HTTP. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { (void) data; (void) enclist; (void) is_transfer; return CURLE_NOT_BUILT_IN; } CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { (void) data; (void) writer; (void) buf; (void) nbytes; return CURLE_NOT_BUILT_IN; } void Curl_unencode_cleanup(struct Curl_easy *data) { (void) data; } char *Curl_all_content_encodings(void) { return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ } #endif /* CURL_DISABLE_HTTP */ 但是patch的行数对不上,那要如何修复?可不可以修复?
09-12
/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "curl_setup.h" #include "urldata.h" #include <curl/curl.h> #include <stddef.h> #ifdef HAVE_LIBZ #include <zlib.h> #endif #ifdef HAVE_BROTLI #include <brotli/decode.h> #endif #ifdef HAVE_ZSTD #include <zstd.h> #endif #include "sendf.h" #include "http.h" #include "content_encoding.h" #include "strdup.h" #include "strcase.h" #include "curl_memory.h" #include "memdebug.h" #define CONTENT_ENCODING_DEFAULT "identity" #ifndef CURL_DISABLE_HTTP #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ #ifdef HAVE_LIBZ /* Comment this out if zlib is always going to be at least ver. 1.2.0.4 (doing so will reduce code size slightly). */ #define OLD_ZLIB_SUPPORT 1 #define GZIP_MAGIC_0 0x1f #define GZIP_MAGIC_1 0x8b /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ typedef enum { ZLIB_UNINIT, /* uninitialized */ ZLIB_INIT, /* initialized */ ZLIB_INFLATING, /* inflating started. */ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ ZLIB_GZIP_HEADER, /* reading gzip header */ ZLIB_GZIP_INFLATING, /* inflating gzip stream */ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ } zlibInitState; /* Deflate and gzip writer. */ struct zlib_writer { struct contenc_writer super; zlibInitState zlib_init; /* zlib init state */ uInt trailerlen; /* Remaining trailer byte count. */ z_stream z; /* State structure for zlib. */ }; static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void) opaque; /* not a typo, keep it calloc() */ return (voidpf) calloc(items, size); } static void zfree_cb(voidpf opaque, voidpf ptr) { (void) opaque; free(ptr); } static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) failf(data, "Error while processing content unencoding: %s", z->msg); else failf(data, "Error while processing content unencoding: " "Unknown failure within decompression software."); return CURLE_BAD_CONTENT_ENCODING; } static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, zlibInitState *zlib_init, CURLcode result) { if(*zlib_init == ZLIB_GZIP_HEADER) Curl_safefree(z->next_in); if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) result = process_zlib_error(data, z); *zlib_init = ZLIB_UNINIT; } return result; } static CURLcode process_trailer(struct Curl_easy *data, struct zlib_writer *zp) { z_stream *z = &zp->z; CURLcode result = CURLE_OK; uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen; /* Consume expected trailer bytes. Terminate stream if exhausted. Issue an error if unexpected bytes follow. */ zp->trailerlen -= len; z->avail_in -= len; z->next_in += len; if(z->avail_in) result = CURLE_WRITE_ERROR; if(result || !zp->trailerlen) result = exit_zlib(data, z, &zp->zlib_init, result); else { /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */ zp->zlib_init = ZLIB_EXTERNAL_TRAILER; } return result; } static CURLcode inflate_stream(struct Curl_easy *data, struct contenc_writer *writer, zlibInitState started) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ uInt nread = z->avail_in; Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ char *decomp; /* Put the decompressed data here. */ /* Check state. */ if(zp->zlib_init != ZLIB_INIT && zp->zlib_init != ZLIB_INFLATING && zp->zlib_init != ZLIB_INIT_GZIP && zp->zlib_init != ZLIB_GZIP_INFLATING) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); /* Dynamically allocate a buffer for decompression because it's uncommonly large to hold on the stack */ decomp = malloc(DSIZ); if(!decomp) return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); /* because the buffer size is fixed, iteratively decompress and transfer to the client via downstream_write function. */ while(!done) { int status; /* zlib status */ done = TRUE; /* (re)set buffer for decompressed output for every iteration */ z->next_out = (Bytef *) decomp; z->avail_out = DSIZ; #ifdef Z_BLOCK /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ status = inflate(z, Z_BLOCK); #else /* fallback for zlib ver. < 1.2.0.5 */ status = inflate(z, Z_SYNC_FLUSH); #endif /* Flush output data if some. */ if(z->avail_out != DSIZ) { if(status == Z_OK || status == Z_STREAM_END) { zp->zlib_init = started; /* Data started. */ result = Curl_unencode_write(data, writer->downstream, decomp, DSIZ - z->avail_out); if(result) { exit_zlib(data, z, &zp->zlib_init, result); break; } } } /* Dispatch by inflate() status. */ switch(status) { case Z_OK: /* Always loop: there may be unflushed latched data in zlib state. */ done = FALSE; break; case Z_BUF_ERROR: /* No more data to flush: just exit loop. */ break; case Z_STREAM_END: result = process_trailer(data, zp); break; case Z_DATA_ERROR: /* some servers seem to not generate zlib headers, so this is an attempt to fix and continue anyway */ if(zp->zlib_init == ZLIB_INIT) { /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ (void) inflateEnd(z); /* don't care about the return code */ if(inflateInit2(z, -MAX_WBITS) == Z_OK) { z->next_in = orig_in; z->avail_in = nread; zp->zlib_init = ZLIB_INFLATING; zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */ done = FALSE; break; } zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; default: result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; } } free(decomp); /* We're about to leave this call so the `nread' data bytes won't be seen again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) zp->zlib_init = started; /* Cannot restart anymore. */ return result; } /* Deflate handler. */ static CURLcode deflate_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) return CURLE_WRITE_ERROR; /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; if(inflateInit(z) != Z_OK) return process_zlib_error(data, z); zp->zlib_init = ZLIB_INIT; return CURLE_OK; } static CURLcode deflate_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ /* Set the compressed input when this function is called */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER) return process_trailer(data, zp); /* Now uncompress the data */ return inflate_stream(data, writer, ZLIB_INFLATING); } static void deflate_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } static const struct content_encoding deflate_encoding = { "deflate", NULL, deflate_init_writer, deflate_unencode_write, deflate_close_writer, sizeof(struct zlib_writer) }; /* Gzip handler. */ static CURLcode gzip_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(!writer->downstream) return CURLE_WRITE_ERROR; /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { return process_zlib_error(data, z); } zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ } else { /* we must parse the gzip header and trailer ourselves */ if(inflateInit2(z, -MAX_WBITS) != Z_OK) { return process_zlib_error(data, z); } zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ zp->zlib_init = ZLIB_INIT; /* Initial call state */ } return CURLE_OK; } #ifdef OLD_ZLIB_SUPPORT /* Skip over the gzip header */ static enum { GZIP_OK, GZIP_BAD, GZIP_UNDERFLOW } check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen) { int method, flags; const ssize_t totallen = len; /* The shortest header is 10 bytes */ if(len < 10) return GZIP_UNDERFLOW; if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) return GZIP_BAD; method = data[2]; flags = data[3]; if(method != Z_DEFLATED || (flags & RESERVED) != 0) { /* Can't handle this compression method or unknown flag */ return GZIP_BAD; } /* Skip over time, xflags, OS code and all previous bytes */ len -= 10; data += 10; if(flags & EXTRA_FIELD) { ssize_t extra_len; if(len < 2) return GZIP_UNDERFLOW; extra_len = (data[1] << 8) | data[0]; if(len < (extra_len + 2)) return GZIP_UNDERFLOW; len -= (extra_len + 2); data += (extra_len + 2); } if(flags & ORIG_NAME) { /* Skip over NUL-terminated file name */ while(len && *data) { --len; ++data; } if(!len || *data) return GZIP_UNDERFLOW; /* Skip over the NUL */ --len; ++data; } if(flags & COMMENT) { /* Skip over NUL-terminated comment */ while(len && *data) { --len; ++data; } if(!len || *data) return GZIP_UNDERFLOW; /* Skip over the NUL */ --len; } if(flags & HEAD_CRC) { if(len < 2) return GZIP_UNDERFLOW; len -= 2; } *headerlen = totallen - len; return GZIP_OK; } #endif static CURLcode gzip_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ if(zp->zlib_init == ZLIB_INIT_GZIP) { /* Let zlib handle the gzip decompression entirely */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; /* Now uncompress the data */ return inflate_stream(data, writer, ZLIB_INIT_GZIP); } #ifndef OLD_ZLIB_SUPPORT /* Support for old zlib versions is compiled away and we are running with an old version, so return an error. */ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); #else /* This next mess is to get around the potential case where there isn't * enough data passed in to skip over the gzip header. If that happens, we * malloc a block and copy what we have then wait for the next call. If * there still isn't enough (this is definitely a worst-case scenario), we * make the block bigger, copy the next part in and keep waiting. * * This is only required with zlib versions < 1.2.0.4 as newer versions * can handle the gzip header themselves. */ switch(zp->zlib_init) { /* Skip over gzip header? */ case ZLIB_INIT: { /* Initial call state */ ssize_t hlen; switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { case GZIP_OK: z->next_in = (Bytef *) buf + hlen; z->avail_in = (uInt) (nbytes - hlen); zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: /* We need more data so we can find the end of the gzip header. It's * possible that the memory block we malloc here will never be freed if * the transfer abruptly aborts after this point. Since it's unlikely * that circumstances will be right for this code path to be followed in * the first place, and it's even more unlikely for a transfer to fail * immediately afterwards, it should seldom be a problem. */ z->avail_in = (uInt) nbytes; z->next_in = malloc(z->avail_in); if(!z->next_in) { return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } memcpy(z->next_in, buf, z->avail_in); zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ /* We don't have any data to inflate yet */ return CURLE_OK; case GZIP_BAD: default: return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); } } break; case ZLIB_GZIP_HEADER: { /* Need more gzip header data state */ ssize_t hlen; z->avail_in += (uInt) nbytes; z->next_in = Curl_saferealloc(z->next_in, z->avail_in); if(!z->next_in) { return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); } /* Append the new block of data to the previous one */ memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { case GZIP_OK: /* This is the zlib stream data */ free(z->next_in); /* Don't point into the malloced block since we just freed it */ z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; z->avail_in = (uInt) (z->avail_in - hlen); zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: /* We still don't have any data to inflate! */ return CURLE_OK; case GZIP_BAD: default: return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); } } break; case ZLIB_EXTERNAL_TRAILER: z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; return process_trailer(data, zp); case ZLIB_GZIP_INFLATING: default: /* Inflating stream state */ z->next_in = (Bytef *) buf; z->avail_in = (uInt) nbytes; break; } if(z->avail_in == 0) { /* We don't have any data to inflate; wait until next time */ return CURLE_OK; } /* We've parsed the header, now uncompress the data */ return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); #endif } static void gzip_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } static const struct content_encoding gzip_encoding = { "gzip", "x-gzip", gzip_init_writer, gzip_unencode_write, gzip_close_writer, sizeof(struct zlib_writer) }; #endif /* HAVE_LIBZ */ #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { struct contenc_writer super; BrotliDecoderState *br; /* State structure for brotli. */ }; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) { switch(be) { case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: #ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: #endif #ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: #endif case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: return CURLE_BAD_CONTENT_ENCODING; case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: return CURLE_OUT_OF_MEMORY; default: break; } return CURLE_WRITE_ERROR; } static CURLcode brotli_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; if(!writer->downstream) return CURLE_WRITE_ERROR; bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; } static CURLcode brotli_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; const uint8_t *src = (const uint8_t *) buf; char *decomp; uint8_t *dst; size_t dstleft; CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; if(!bp->br) return CURLE_WRITE_ERROR; /* Stream already ended. */ decomp = malloc(DSIZ); if(!decomp) return CURLE_OUT_OF_MEMORY; while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { dst = (uint8_t *) decomp; dstleft = DSIZ; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); result = Curl_unencode_write(data, writer->downstream, decomp, DSIZ - dstleft); if(result) break; switch(r) { case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: break; case BROTLI_DECODER_RESULT_SUCCESS: BrotliDecoderDestroyInstance(bp->br); bp->br = NULL; if(nbytes) result = CURLE_WRITE_ERROR; break; default: result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br)); break; } } free(decomp); return result; } static void brotli_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; if(bp->br) { BrotliDecoderDestroyInstance(bp->br); bp->br = NULL; } } static const struct content_encoding brotli_encoding = { "br", NULL, brotli_init_writer, brotli_unencode_write, brotli_close_writer, sizeof(struct brotli_writer) }; #endif #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { struct contenc_writer super; ZSTD_DStream *zds; /* State structure for zstd. */ void *decomp; }; static CURLcode zstd_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; if(!writer->downstream) return CURLE_WRITE_ERROR; zp->zds = ZSTD_createDStream(); zp->decomp = NULL; return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; } static CURLcode zstd_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; struct zstd_writer *zp = (struct zstd_writer *) writer; ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; if(!zp->decomp) { zp->decomp = malloc(DSIZ); if(!zp->decomp) return CURLE_OUT_OF_MEMORY; } in.pos = 0; in.src = buf; in.size = nbytes; for(;;) { out.pos = 0; out.dst = zp->decomp; out.size = DSIZ; errorCode = ZSTD_decompressStream(zp->zds, &out, &in); if(ZSTD_isError(errorCode)) { return CURLE_BAD_CONTENT_ENCODING; } if(out.pos > 0) { result = Curl_unencode_write(data, writer->downstream, zp->decomp, out.pos); if(result) break; } if((in.pos == nbytes) && (out.pos < out.size)) break; } return result; } static void zstd_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; if(zp->decomp) { free(zp->decomp); zp->decomp = NULL; } if(zp->zds) { ZSTD_freeDStream(zp->zds); zp->zds = NULL; } } static const struct content_encoding zstd_encoding = { "zstd", NULL, zstd_init_writer, zstd_unencode_write, zstd_close_writer, sizeof(struct zstd_writer) }; #endif /* Identity handler. */ static CURLcode identity_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode identity_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { return Curl_unencode_write(data, writer->downstream, buf, nbytes); } static void identity_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding identity_encoding = { "identity", "none", identity_init_writer, identity_unencode_write, identity_close_writer, sizeof(struct contenc_writer) }; /* supported content encodings table. */ static const struct content_encoding * const encodings[] = { &identity_encoding, #ifdef HAVE_LIBZ &deflate_encoding, &gzip_encoding, #endif #ifdef HAVE_BROTLI &brotli_encoding, #endif #ifdef HAVE_ZSTD &zstd_encoding, #endif NULL }; /* Return a list of comma-separated names of supported encodings. */ char *Curl_all_content_encodings(void) { size_t len = 0; const struct content_encoding * const *cep; const struct content_encoding *ce; char *ace; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) len += strlen(ce->name) + 2; } if(!len) return strdup(CONTENT_ENCODING_DEFAULT); ace = malloc(len); if(ace) { char *p = ace; for(cep = encodings; *cep; cep++) { ce = *cep; if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { strcpy(p, ce->name); p += strlen(p); *p++ = ','; *p++ = ' '; } } p[-2] = '\0'; } return ace; } /* Real client writer: no downstream. */ static CURLcode client_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; } static CURLcode client_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { struct SingleRequest *k = &data->req; (void) writer; if(!nbytes || k->ignorebody) return CURLE_OK; return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes); } static void client_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding client_encoding = { NULL, NULL, client_init_writer, client_unencode_write, client_close_writer, sizeof(struct contenc_writer) }; /* Deferred error dummy writer. */ static CURLcode error_init_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; } static CURLcode error_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { char *all = Curl_all_content_encodings(); (void) writer; (void) buf; (void) nbytes; if(!all) return CURLE_OUT_OF_MEMORY; failf(data, "Unrecognized content encoding type. " "libcurl understands %s content encodings.", all); free(all); return CURLE_BAD_CONTENT_ENCODING; } static void error_close_writer(struct Curl_easy *data, struct contenc_writer *writer) { (void) data; (void) writer; } static const struct content_encoding error_encoding = { NULL, NULL, error_init_writer, error_unencode_write, error_close_writer, sizeof(struct contenc_writer) }; /* Create an unencoding writer stage using the given handler. */ static struct contenc_writer * new_unencoding_writer(struct Curl_easy *data, const struct content_encoding *handler, struct contenc_writer *downstream, int order) { struct contenc_writer *writer; DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer)); writer = (struct contenc_writer *) calloc(1, handler->writersize); if(writer) { writer->handler = handler; writer->downstream = downstream; writer->order = order; if(handler->init_writer(data, writer)) { free(writer); writer = NULL; } } return writer; } /* Write data using an unencoding writer stack. "nbytes" is not allowed to be 0. */ CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { if(!nbytes) return CURLE_OK; return writer->handler->unencode_write(data, writer, buf, nbytes); } /* Close and clean-up the connection's writer stack. */ void Curl_unencode_cleanup(struct Curl_easy *data) { struct SingleRequest *k = &data->req; struct contenc_writer *writer = k->writer_stack; while(writer) { k->writer_stack = writer->downstream; writer->handler->close_writer(data, writer); free(writer); writer = k->writer_stack; } } /* Find the content encoding by name. */ static const struct content_encoding *find_encoding(const char *name, size_t len) { const struct content_encoding * const *cep; for(cep = encodings; *cep; cep++) { const struct content_encoding *ce = *cep; if((strncasecompare(name, ce->name, len) && !ce->name[len]) || (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) return ce; } return NULL; } /* allow no more than 5 "chained" compression steps */ #define MAX_ENCODE_STACK 5 /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; unsigned int order = is_transfer? 2: 1; do { const char *name; size_t namelen; /* Parse a single encoding name. */ while(ISBLANK(*enclist) || *enclist == ',') enclist++; name = enclist; for(namelen = 0; *enclist && *enclist != ','; enclist++) if(!ISSPACE(*enclist)) namelen = enclist - name + 1; /* Special case: chunked encoding is handled at the reader level. */ if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) { k->chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data); /* init our chunky engine. */ } else if(namelen) { const struct content_encoding *encoding = find_encoding(name, namelen); struct contenc_writer *writer; if(!k->writer_stack) { k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL, 0); if(!k->writer_stack) return CURLE_OUT_OF_MEMORY; } if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { failf(data, "Reject response due to more than %u content encodings", MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; } /* Stack the unencoding stage. */ if(order >= k->writer_stack->order) { writer = new_unencoding_writer(data, encoding, k->writer_stack, order); if(!writer) return CURLE_OUT_OF_MEMORY; k->writer_stack = writer; } else { struct contenc_writer *w = k->writer_stack; while(w->downstream && order < w->downstream->order) w = w->downstream; writer = new_unencoding_writer(data, encoding, w->downstream, order); if(!writer) return CURLE_OUT_OF_MEMORY; w->downstream = writer; } } } while(*enclist); return CURLE_OK; } #else /* Stubs for builds without HTTP. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { (void) data; (void) enclist; (void) is_transfer; return CURLE_NOT_BUILT_IN; } CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes) { (void) data; (void) writer; (void) buf; (void) nbytes; return CURLE_NOT_BUILT_IN; } void Curl_unencode_cleanup(struct Curl_easy *data) { (void) data; } char *Curl_all_content_encodings(void) { return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ } #endif /* CURL_DISABLE_HTTP */ 上边的是patch 下边的是源码: From 76f83f0db23846e254d940ec7fe141010077eb88 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg <daniel@haxx.se> Date: Fri, 24 Jan 2025 11:13:24 +0100 Subject: [PATCH] content_encoding: drop support for zlib before 1.2.0.4 zlib 1.2.0.4 was released on 10 August 2003 Closes #16079 --- docs/INTERNALS.md | 2 +- lib/content_encoding.c | 276 ++++------------------------------------- 2 files changed, 25 insertions(+), 253 deletions(-) diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index ae77f0e54b05..4e42f4fd1015 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -23,7 +23,7 @@ versions of libs and build tools. - OpenSSL 0.9.7 - GnuTLS 3.1.10 - - zlib 1.1.4 + - zlib 1.2.0.4 - libssh2 1.0 - c-ares 1.16.0 - libidn2 2.0.0 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index e19595d5ec42..d2b17297890d 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -55,33 +55,13 @@ #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ - #ifdef HAVE_LIBZ -/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 - (doing so will reduce code size slightly). */ -#define OLD_ZLIB_SUPPORT 1 - -#define GZIP_MAGIC_0 0x1f -#define GZIP_MAGIC_1 0x8b - -/* gzip flag byte */ -#define CURL_GZIPFLAG_ASCII 0x01 /* bit 0 set: file probably ASCII - text */ -#define CURL_GZIPFLAG_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ -#define CURL_GZIPFLAG_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define CURL_GZIPFLAG_ORIG_NAME 0x08 /* bit 3 set: original filename - present */ -#define CURL_GZIPFLAG_COMMENT 0x10 /* bit 4 set: file comment present */ -#define CURL_GZIPFLAG_RESERVED 0xE0 /* bits 5..7: reserved */ - typedef enum { ZLIB_UNINIT, /* uninitialized */ ZLIB_INIT, /* initialized */ ZLIB_INFLATING, /* inflating started. */ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ - ZLIB_GZIP_HEADER, /* reading gzip header */ - ZLIB_GZIP_INFLATING, /* inflating gzip stream */ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ } zlibInitState; @@ -139,9 +119,6 @@ static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, zlibInitState *zlib_init, CURLcode result) { - if(*zlib_init == ZLIB_GZIP_HEADER) - Curl_safefree(z->next_in); - if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) result = process_zlib_error(data, z); @@ -190,8 +167,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Check state. */ if(zp->zlib_init != ZLIB_INIT && zp->zlib_init != ZLIB_INFLATING && - zp->zlib_init != ZLIB_INIT_GZIP && - zp->zlib_init != ZLIB_GZIP_INFLATING) + zp->zlib_init != ZLIB_INIT_GZIP) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); /* Dynamically allocate a buffer for decompression because it is uncommonly @@ -280,7 +256,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Deflate handler. */ static CURLcode deflate_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -296,8 +272,8 @@ static CURLcode deflate_do_init(struct Curl_easy *data, } static CURLcode deflate_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -317,7 +293,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, } static void deflate_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -337,124 +313,34 @@ static const struct Curl_cwtype deflate_encoding = { /* Gzip handler. */ static CURLcode gzip_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ + const char *v = zlibVersion(); /* Initialize zlib */ z->zalloc = (alloc_func) zalloc_cb; z->zfree = (free_func) zfree_cb; - if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { - /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ + if(strcmp(v, "1.2.0.4") >= 0) { + /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { return process_zlib_error(data, z); } zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ } else { - /* we must parse the gzip header and trailer ourselves */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - return process_zlib_error(data, z); - } - zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ - zp->zlib_init = ZLIB_INIT; /* Initial call state */ + failf(data, "too old zlib version: %s", v); + return CURLE_FAILED_INIT; } return CURLE_OK; } -#ifdef OLD_ZLIB_SUPPORT -/* Skip over the gzip header */ -typedef enum { - GZIP_OK, - GZIP_BAD, - GZIP_UNDERFLOW -} gzip_status; - -static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, - ssize_t *headerlen) -{ - int method, flags; - const ssize_t totallen = len; - - /* The shortest header is 10 bytes */ - if(len < 10) - return GZIP_UNDERFLOW; - - if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) - return GZIP_BAD; - - method = data[2]; - flags = data[3]; - - if(method != Z_DEFLATED || (flags & CURL_GZIPFLAG_RESERVED) != 0) { - /* cannot handle this compression method or unknown flag */ - return GZIP_BAD; - } - - /* Skip over time, xflags, OS code and all previous bytes */ - len -= 10; - data += 10; - - if(flags & CURL_GZIPFLAG_EXTRA_FIELD) { - ssize_t extra_len; - - if(len < 2) - return GZIP_UNDERFLOW; - - extra_len = (data[1] << 8) | data[0]; - - if(len < (extra_len + 2)) - return GZIP_UNDERFLOW; - - len -= (extra_len + 2); - data += (extra_len + 2); - } - - if(flags & CURL_GZIPFLAG_ORIG_NAME) { - /* Skip over NUL-terminated filename */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - ++data; - } - - if(flags & CURL_GZIPFLAG_COMMENT) { - /* Skip over NUL-terminated comment */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - } - - if(flags & CURL_GZIPFLAG_HEAD_CRC) { - if(len < 2) - return GZIP_UNDERFLOW; - - len -= 2; - } - - *headerlen = totallen - len; - return GZIP_OK; -} -#endif - static CURLcode gzip_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -470,117 +356,8 @@ static CURLcode gzip_do_write(struct Curl_easy *data, return inflate_stream(data, writer, type, ZLIB_INIT_GZIP); } -#ifndef OLD_ZLIB_SUPPORT - /* Support for old zlib versions is compiled away and we are running with - an old version, so return an error. */ + /* We are running with an old version: return error. */ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - -#else - /* This next mess is to get around the potential case where there is not - * enough data passed in to skip over the gzip header. If that happens, we - * malloc a block and copy what we have then wait for the next call. If - * there still is not enough (this is definitely a worst-case scenario), we - * make the block bigger, copy the next part in and keep waiting. - * - * This is only required with zlib versions < 1.2.0.4 as newer versions - * can handle the gzip header themselves. - */ - - switch(zp->zlib_init) { - /* Skip over gzip header? */ - case ZLIB_INIT: - { - /* Initial call state */ - ssize_t hlen; - - switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { - case GZIP_OK: - z->next_in = (Bytef *) buf + hlen; - z->avail_in = (uInt) (nbytes - hlen); - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We need more data so we can find the end of the gzip header. it is - * possible that the memory block we malloc here will never be freed if - * the transfer abruptly aborts after this point. Since it is unlikely - * that circumstances will be right for this code path to be followed in - * the first place, and it is even more unlikely for a transfer to fail - * immediately afterwards, it should seldom be a problem. - */ - z->avail_in = (uInt) nbytes; - z->next_in = malloc(z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - memcpy(z->next_in, buf, z->avail_in); - zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ - /* We do not have any data to inflate yet */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_GZIP_HEADER: - { - /* Need more gzip header data state */ - ssize_t hlen; - z->avail_in += (uInt) nbytes; - z->next_in = Curl_saferealloc(z->next_in, z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - /* Append the new block of data to the previous one */ - memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); - - switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) { - case GZIP_OK: - /* This is the zlib stream data */ - free(z->next_in); - /* Do not point into the malloced block since we just freed it */ - z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; - z->avail_in = z->avail_in - (uInt)hlen; - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We still do not have any data to inflate! */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_EXTERNAL_TRAILER: - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - return process_trailer(data, zp); - - case ZLIB_GZIP_INFLATING: - default: - /* Inflating stream state */ - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - break; - } - - if(z->avail_in == 0) { - /* We do not have any data to inflate; wait until next time */ - return CURLE_OK; - } - - /* We have parsed the header, now uncompress the data */ - return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); -#endif } static void gzip_do_close(struct Curl_easy *data, @@ -603,7 +380,6 @@ static const struct Curl_cwtype gzip_encoding = { #endif /* HAVE_LIBZ */ - #ifdef HAVE_BROTLI /* Brotli writer. */ struct brotli_writer { @@ -650,7 +426,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) } static CURLcode brotli_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -660,8 +436,8 @@ static CURLcode brotli_do_init(struct Curl_easy *data, } static CURLcode brotli_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { struct brotli_writer *bp = (struct brotli_writer *) writer; const uint8_t *src = (const uint8_t *) buf; @@ -733,7 +509,6 @@ static const struct Curl_cwtype brotli_encoding = { }; #endif - #ifdef HAVE_ZSTD /* Zstd writer. */ struct zstd_writer { @@ -757,7 +532,7 @@ static void Curl_zstd_free(void *opaque, void *address) #endif static CURLcode zstd_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -778,8 +553,8 @@ static CURLcode zstd_do_init(struct Curl_easy *data, } static CURLcode zstd_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; struct zstd_writer *zp = (struct zstd_writer *) writer; @@ -810,7 +585,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, } if(out.pos > 0) { result = Curl_cwriter_write(data, writer->next, type, - zp->decomp, out.pos); + zp->decomp, out.pos); if(result) break; } @@ -848,7 +623,6 @@ static const struct Curl_cwtype zstd_encoding = { }; #endif - /* Identity handler. */ static const struct Curl_cwtype identity_encoding = { "identity", @@ -859,7 +633,6 @@ static const struct Curl_cwtype identity_encoding = { sizeof(struct Curl_cwriter) }; - /* supported general content decoders. */ static const struct Curl_cwtype * const general_unencoders[] = { &identity_encoding, @@ -923,7 +696,7 @@ void Curl_all_content_encodings(char *buf, size_t blen) /* Deferred error dummy writer. */ static CURLcode error_do_init(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { (void)data; (void)writer; @@ -931,8 +704,8 @@ static CURLcode error_do_init(struct Curl_easy *data, } static CURLcode error_do_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { (void) writer; (void) buf; @@ -1107,5 +880,4 @@ void Curl_all_content_encodings(char *buf, size_t blen) strcpy(buf, CONTENT_ENCODING_DEFAULT); } - #endif /* CURL_DISABLE_HTTP */ 现在就是想做的就是根据源码相应的函数的行数来修改patch中要修复的行数,不修改源码
09-12
/******************************************************************************/ /* */ /* Copyright (c) 2018 TP-Link Systems Inc. */ /* */ /* File : devmgr_sock.c */ /* */ /* Description : functions refered to socket */ /* */ /* Author : libin */ /* */ /******************************************************************************/ #include <uci.h> #include <uci_blobmsg.h> #include <libubox/crc32.h> #include <zlib.h> #include "devmgr_sock.h" #include "devmgr_dev_ctrl.h" #include "devmgr_global.h" #include "devmgr_crypto.h" #include "devmgr_config.h" #include "devmgr_dev_list.h" #ifdef CFG_DEVMGR_SLAVER #include "devmgr_devctl_slaver.h" #include "devmgr_config_slaver.h" #else #include "devmgr_ubus_master.h" #endif /* MACRO DEFINE */ #define DM_RECV_USOCK_TYPE (USOCK_UDP | USOCK_IPV4ONLY | USOCK_SERVER) #define DM_SEND_USOCK_TYPE (USOCK_UDP | USOCK_IPV4ONLY) #define DM_DEVCTL_PORT "4700" #define DM_ULOOP_FD_ADD_FLAG (ULOOP_READ | ULOOP_ERROR_CB) #ifdef CFG_DEVMGR_MASTER #define DM_NET_IFNAME "br-lan" #else #define DM_NET_IFNAME "wlan0" #endif /* GLOBAL VARIABLES */ static struct tpsocket_fd* g_listen_fd; static unsigned int g_seq; static struct recv_pkt_queue g_rx; int devmgr_time(int* out) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); if (out) { *out = ts.tv_sec; } return ts.tv_sec; } #ifdef CFG_DEVMGR_MASTER int devmgr_get_dev_mac(char *out, int len, const char *ip) { if (!out || !ip) { return -1; } struct blob_buf b = {0}; blobmsg_buf_init(&b); master_call_ipmgr_get_ip_table(1000, master_ipmgr_get_ip_table_cb, &b); struct blobmsg( blobmsg_array ip_table ) (ipmgr_rtn, &b); if (ipmgr_rtn.ip_table) { struct blob_attr *cur = NULL; int rem; blobmsg_for_each_attr(cur, ipmgr_rtn.ip_table, rem) { struct blobmsg( blobmsg_string ip, blobmsg_string mac ) (ip_table, cur, false); if (!ip_table.ip || !ip_table.mac || strcmp(ip, blobmsg_get_string(ip_table.ip))) { continue; } strncpy(out, blobmsg_get_string(ip_table.mac), len); out[len - 1] = '\0'; blob_buf_free(&b); return 0; } } blob_buf_free(&b); return -1; } int devmgr_get_dev_psk(char *out, int len, const char *mac) { if (!out || !mac) { return -1; } char mac_lower_case[18] = {}; string_to_lower_case(mac, mac_lower_case, sizeof(mac_lower_case)); struct blob_buf b = {0}; blobmsg_buf_init(&b); master_call_unetwork_get_psk(10000, master_unetwork_get_psk_cb, &b); struct blobmsg( blobmsg_array psk_list ) (unetwork_rtn, &b); if (unetwork_rtn.psk_list) { struct blob_attr *cur = NULL; int rem; blobmsg_for_each_attr(cur, unetwork_rtn.psk_list, rem) { if (cur) { struct blobmsg( blobmsg_string macaddr, blobmsg_string psk ) (psk_list, cur, false); if (!psk_list.macaddr || !psk_list.psk || strcmp(mac_lower_case, blobmsg_get_string(psk_list.macaddr))) { continue; } strncpy(out, blobmsg_get_string(psk_list.psk), len); out[len - 1] = '\0'; blob_buf_free(&b); return 0; } } DBG_ERR("can not find psk in psk list\n"); } else { DBG_ERR("ubus call unetwork get psk failed\n"); } out[0] = '\0'; blob_buf_free(&b); return -1; } #else int devmgr_get_dev_mac(char *out, int len, const char *ip) { DEV_NODE *dev = device_list_search(NULL); if (dev) { strncpy(out, dev->mac, len); return 0; } return -1; } int devmgr_get_dev_psk(char *out, int len, const char *mac) { struct blob_buf b = {0}; blobmsg_buf_init(&b); devmgr_config_get("wireless", &b, NULL); struct blobmsg ( blobmsg_section sta ) (cfg, &b); if (cfg.sta) { struct blobmsg( blobmsg_option psk_key ) (sta, cfg.sta, false); if (sta.psk_key) { strncpy(out, blobuci_get_string(sta.psk_key), len); out[len - 1] = '\0'; blob_buf_free(&b); return 0; } } out[0] = '\0'; blob_buf_free(&b); return -1; } #endif /******************************************************************************* Function : devmgr_packet_recv_cb Description : receive packet function Input : ufd @ uloop fd events @ events Output : void Return : void *******************************************************************************/ static void devmgr_packet_recv_cb(struct tpsocket_fd*sock, char*data, int len) { const char* ip = sock->addr; kdcp_t pkt = {{0}}; struct blob_buf rblob; struct blob_buf *sblob = NULL; char dev_psk[32+1] = {}; char mac[18] = {}; unsigned char key[16] = {}; unsigned char iv[16] = {}; unsigned short pkt_cmd = 0; unsigned short pkt_id = 0; unsigned char *original_buf = NULL; unsigned char *compress_buf = NULL; unsigned long original_len = 0; unsigned long compress_len = 0; int type_compress = 0; int cap_compress = 0; if (len > sizeof(pkt) - 1) { len = sizeof(pkt) - 1; } memcpy(&pkt, data, len); struct blob_buf b = {0}; blobmsg_buf_init(&b); blobmsg_add_string(&b, "ip", ip); DEV_NODE *dev = device_list_search(b.head); blob_buf_free(&b); if ((pkt.hdr.type & 0x1) != DEVMGR_REQUEST) { DBG_ERR("the packet received is not request\n"); return; } if (!(pkt.hdr.type & DEVMGR_TYPE_CRYPTO)) { DBG_ERR("the packet should be encrypt\n"); return; } type_compress = (pkt.hdr.type & DEVMGR_TYPE_COMPRESS) ? 1 : 0; cap_compress = (pkt.hdr.cap & DEVMGR_CAP_COMPRESS) ? 1 : 0; if (len < sizeof(pkt.hdr) || len < sizeof(pkt.hdr) + ntohs(pkt.hdr.len)) { DBG_ERR("devctl packet is too short\n"); return; } else { uint32_t crc; memset(&rblob, 0, sizeof(rblob)); blobmsg_buf_init(&rblob); if (dev) { dev->compress_support = cap_compress; if (dev->psk[0] == '\0') { devmgr_get_dev_psk(dev->psk, sizeof(dev->psk), dev->mac); devmgr_aes_128_key_gen(dev->psk, dev->crypto_key, sizeof(dev->crypto_key)); } memcpy(mac, dev->mac, sizeof(mac)); memcpy(key, dev->crypto_key, sizeof(key)); } else { devmgr_get_dev_mac(mac, sizeof(mac), ip); devmgr_get_dev_psk(dev_psk, sizeof(dev_psk), mac); devmgr_aes_128_key_gen(dev_psk, key, sizeof(key)); } /* aes decode */ pkt_cmd = pkt.hdr.cmd; pkt_id = pkt.hdr.id; devmgr_aes128_iv_init(iv, sizeof(iv), mac, &pkt_cmd, &pkt_id); devmgr_aes_128_cbc_decrypt((unsigned char *)pkt.data, ntohs(pkt.hdr.len), (unsigned char *)pkt.data, sizeof(pkt.data), iv, (unsigned char *)key ); if (type_compress) { data = (char *)(pkt.data + 6); /* uncompress */ original_len = ntohs(*(uint16_t *)(pkt.data + 4)); compress_len = ntohs(pkt.hdr.len) - 6; original_buf = malloc(sizeof(unsigned char) * original_len + 1); if (!original_buf) { DBG_ERR("packet_recv_cb: malloc failed\n"); goto ERR_RTN; } memset(original_buf, 0, sizeof(unsigned char) * original_len + 1); if (uncompress(original_buf, &original_len, (unsigned char *)data, compress_len) != Z_OK) { DBG_ERR("packet_recv_cb: the packet uncompress failed\n"); goto ERR_RTN; } data = (char *)original_buf; } else { data = (char *)(pkt.data + 4); } /* check crc */ crc = (uint8_t)pkt.data[0]; crc |= (uint8_t)pkt.data[1] << 8; crc |= (uint8_t)pkt.data[2] << 16; crc |= (uint8_t)pkt.data[3] << 24; if (crc != crc32_sum(data, strlen(data))) { DBG_ERR("packet_recv_cb: crc error\n"); goto ERR_RTN; } if (data[0] && !blobmsg_add_json_from_string(&rblob, data)) { DBG_ERR("packet_recv_cb: the packet format is invalid\n"); goto ERR_RTN; } else { struct recv_pkt_queue *rx = dev ? &dev->rx_queue : &g_rx; struct blobmsg( blobmsg_int32 seq ) (recv_pkt, &rblob); if (recv_pkt.seq) { int seq = blobmsg_get_u32(recv_pkt.seq); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); if (dev) { int i; for (i = 0; i < 8; i++) { if (now.tv_sec - rx->ts[i] < 5) { if (seq == rx->seq[i]) { sblob = &rx->response[i]; goto RTN; } } } } rx->idx = (rx->idx + 1) & 0x7; rx->seq[rx->idx] = seq; rx->ts[rx->idx] = now.tv_sec; sblob = &rx->response[rx->idx]; blobmsg_buf_init(sblob); } else { sblob = &rx->response[rx->idx]; blobmsg_buf_init(sblob); } blobmsg_add_string(&rblob, "ip", ip); uint16_t cmd = ntohs(pkt.hdr.cmd); switch (cmd) { #ifdef CFG_DEVMGR_MASTER case DEVMGR_CMD_BIND: device_bind_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_GET_SUB1G_PARAM: device_get_sub1g_param_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_WAKE_UP: device_wake_up_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_HEARTBEAT: device_heartbeat_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_STATE_UPDATE: device_state_update_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_SUB1G_REJOIN: device_sub1g_rejoin_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_INVALID_EVENT_ID: device_invalid_event_id_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_UPGRADE_DONE: device_upgrade_done_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_STATE_LIST_UPDATE: device_state_list_update_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_UPLOAD_TO_CLOUD: //ask homebase to help the subdevice to send data to cloud device_upload_data_to_cloud_proc(dev, rblob.head, sblob); break; #else case DEVMGR_CMD_CTRL: device_control_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_FORCE_IDR: device_force_idr_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_MCU_DEV_CTL: device_mcu_dev_ctl_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_UBUS_CALL: device_ubus_call_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_RESET_BIND: device_reset_bind_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_ONBOARD_CLEAR: device_onboard_clear_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_RESET_WIFI: device_reset_wifi_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_GET_COMPONENT: device_get_component_proc(dev, rblob.head, sblob); break; case DEVMGR_CMD_GET_ALL_CONFIG: device_get_all_config_proc(dev, rblob.head, sblob); break; #endif case DEVMGR_CMD_UNBIND: /* device node will be freed, use rblob instead of rx_queue */ sblob = &rblob; blobmsg_buf_init(sblob); device_unbind_proc(dev, rblob.head, sblob); break; default: common_end(sblob, DEVICE_CMD_NOT_SUPPORT); break; } } } RTN: len = 0; pkt.hdr.type = DEVMGR_RESPOND | DEVMGR_TYPE_CRYPTO; if (type_compress || cap_compress) { pkt.hdr.type |= DEVMGR_TYPE_COMPRESS; } pkt.hdr.cap = DEVMGR_CAP_CRYPTO | DEVMGR_CAP_COMPRESS; pkt.hdr.len = ntohs(0); if (sblob) { char *buf = blobmsg_format_json(sblob->head, true); if (buf) { uint32_t crc = 0; memset(pkt.data, 0, sizeof(pkt.data)); original_len = strlen(buf) + 1; /* calc crc */ crc = crc32_sum((uint8_t *)buf, original_len - 1); pkt.data[0] = crc & 0xFF; pkt.data[1] = (crc >> 8) & 0xFF; pkt.data[2] = (crc >> 16) & 0xFF; pkt.data[3] = (crc >> 24) & 0xFF; /* if compress is supported by peer, send compress packet */ if (type_compress || cap_compress) { /* original len */ *(uint16_t *)(pkt.data + 4) = ntohs(original_len); /* compress */ compress_len = compressBound(original_len); compress_buf = malloc(sizeof(unsigned char) * compress_len); if (compress_buf && compress(compress_buf, &compress_len, (unsigned char *)buf, original_len) == Z_OK) { len = compress_len < (sizeof(pkt.data) - 6) ? compress_len : (sizeof(pkt.data) - 6); memcpy((char *)(pkt.data + 6), compress_buf, len); len += 6; } } else { len = original_len < (sizeof(pkt.data) - 4) ? original_len : (sizeof(pkt.data) - 4); memcpy((char *)(pkt.data + 4), buf, len); len += 4; } /* aes encrypt */ pkt_cmd = pkt.hdr.cmd; pkt_id = pkt.hdr.id; devmgr_aes128_iv_init(iv, sizeof(iv), mac, &pkt_cmd, &pkt_id); len = devmgr_aes128_cbc_encrypt((unsigned char *)pkt.data, len, (unsigned char *)pkt.data, sizeof(pkt.data), iv, (unsigned char *)key); pkt.hdr.len = ntohs(len); free(buf); } } len += sizeof(pkt.hdr); struct tpsocket_buf* tbuf = tpbuf_new(len); if (tbuf) { memcpy(tpbuf_tail(tbuf), &pkt, len); tpbuf_put(tbuf, len); tpsocket_write(sock, tbuf); } ERR_RTN: if (original_buf) { free(original_buf); } if (compress_buf) { free(compress_buf); } blob_buf_free(&rblob); } /******************************************************************************* Function : devmgr_task_send_packet Description : task send timer Input : tm @ pointer to uloop_timeout struct Output : void Return : void *******************************************************************************/ static void devmgr_task_send_packet(struct send_task *task) { struct tpsocket_buf* tbuf = tpbuf_new(sizeof(kdcp_t)); int time_now = devmgr_time(NULL); int len = 0; kdcp_t *pkt = NULL; char mac[18] = {}; unsigned char key[16] = {}; unsigned char iv[16] = {}; char dev_psk[32+1] = {}; unsigned short pkt_cmd = 0; unsigned short pkt_id = 0; unsigned long original_len = 0; unsigned long compress_len = 0; int type_compress = 0; unsigned char *compress_buf = NULL; if (!tbuf) { return; } else { pkt = tpbuf_tail(tbuf); } pkt->hdr.cmd = htons(task->cmd_id); pkt->hdr.id = htons(time_now % 10000); pkt->hdr.type = DEVMGR_REQUEST; pkt->hdr.cap = DEVMGR_CAP_CRYPTO | DEVMGR_CAP_COMPRESS; if (task->encrypt_enable) { pkt->hdr.type |= DEVMGR_TYPE_CRYPTO; } struct blob_buf b = {0}; blobmsg_buf_init(&b); blobmsg_add_string(&b, "ip", task->ip); DEV_NODE *dev = device_list_search(b.head); blob_buf_free(&b); if (dev) { if (dev->compress_support && task->compress_enable) { pkt->hdr.type |= DEVMGR_TYPE_COMPRESS; type_compress = 1; } if (dev->psk[0] == '\0') { devmgr_get_dev_psk(dev->psk, sizeof(dev->psk), dev->mac); devmgr_aes_128_key_gen(dev->psk, dev->crypto_key, sizeof(dev->crypto_key)); } memcpy(mac, dev->mac, sizeof(mac)); memcpy(key, dev->crypto_key, sizeof(key)); } else { devmgr_get_dev_mac(mac, sizeof(mac), task->ip); devmgr_get_dev_psk(dev_psk, sizeof(dev_psk), mac); devmgr_aes_128_key_gen(dev_psk, key, sizeof(key)); } if (task->encrypt_enable) { pkt_cmd = pkt->hdr.cmd; pkt_id = pkt->hdr.id; devmgr_aes128_iv_init(iv, sizeof(iv), mac, &pkt_cmd, &pkt_id); } char *buf = blobmsg_format_json(task->b.head, true); if (buf) { /* calc crc */ original_len = strlen(buf) + 1; uint32_t crc = crc32_sum((uint8_t *)buf, original_len - 1); pkt->data[0] = crc & 0xFF; pkt->data[1] = (crc >> 8) & 0xFF; pkt->data[2] = (crc >> 16) & 0xFF; pkt->data[3] = (crc >> 24) & 0xFF; if (type_compress) { /* original_len */ *(uint16_t *)(pkt->data + 4) = ntohs(original_len); /* compress */ compress_len = compressBound(original_len); compress_buf = malloc(sizeof(unsigned char) * compress_len); if (compress_buf && compress(compress_buf, &compress_len, (unsigned char *)buf, original_len) == Z_OK) { /* aes encrypt */ len = compress_len < (sizeof(pkt->data) - 6) ? compress_len : (sizeof(pkt->data) - 6); memcpy((char *)(pkt->data + 6), compress_buf, len); len += 6; } //DBG_DBG("send compress packet: cmd = %d, origin len = %lu, compress len = %lu\n", // task->cmd_id, original_len, compress_len); } else { len = original_len < (sizeof(pkt->data) - 4) ? original_len : (sizeof(pkt->data) - 4); memcpy((char *)(pkt->data + 4), buf, len); len += 4; } if (task->encrypt_enable) { len = devmgr_aes128_cbc_encrypt((unsigned char *)pkt->data, len, (unsigned char *)pkt->data, sizeof(pkt->data), iv, (unsigned char *)key ); } free(buf); } else { len = 0; } pkt->hdr.len = ntohs(len); len += sizeof(kdcp_hdr_t); tpbuf_put(tbuf, len); tpsocket_write(task->sock, tbuf); if (compress_buf) { free(compress_buf); } } bool tpsocket_devmgr_sock_event(struct tpsocket_handler * handler, struct list_head * buf, int event); static void devmgr_task_resend_cb(struct uloop_timeout *tm) { struct send_task *task = container_of(tm, struct send_task, tmr); int interval_threshold = 0; if (!task->sock) { char url[128] = {0}; struct tpsocket_handler sockhdler = { .cb = tpsocket_devmgr_sock_event, .priv = task, .msg_len_max = KDCP_MAX_DATA_LEN }; snprintf(url, sizeof(url), "udp://%s:%d/", task->ip, task->dst_port); task->sock = tpsocket_from_url(url, &sockhdler); } if (task->sock) { devmgr_task_send_packet(task); } else { task->timeout = devmgr_time(NULL) + 1; task->retry = DEVMGR_UDP_RETRY + 1; } if (devmgr_time(NULL) <= task->timeout || task->retry <= DEVMGR_UDP_RETRY) { task->retry++; if (task->fixed_interval == 0) { task->interval *= 2; interval_threshold = (task->dst_port == atoi(DM_DEVCTL_PORT_NEW)) ? 400 : 1000; task->interval = task->interval > interval_threshold ? interval_threshold : task->interval; } uloop_timeout_set(&task->tmr, task->interval); } else { if (task->handler) { struct blob_buf b = {0}; blobmsg_buf_init(&b); common_end(&b, DEVICE_PACKET_RECV_TIMEOUT); task->handler(b.head, task->arg); task->handler = NULL; blob_buf_free(&b); } devmgr_send_task_free(task); } } /******************************************************************************* Function : devmgr_task_recv_cb Description : ustream task receive callback Input : s @ pointer to ustream struct bytes @ receive bytes Output : void Return : void *******************************************************************************/ static bool devmgr_task_recv_cb(struct send_task *task, char*data, int bytes) { kdcp_t pkt = {{0}}; struct blob_buf b = {0}; int type_compress = 0; char mac[18] = {}; unsigned char key[16] = {}; unsigned char iv[16] = {}; char dev_psk[32+1] = {}; uint32_t crc = 0; unsigned short pkt_cmd = 0; unsigned short pkt_id = 0; unsigned long original_len = 0; unsigned long compress_len = 0; unsigned char *original_buf = NULL; if (bytes > sizeof(pkt) - 1) { bytes = sizeof(pkt) - 1; } memcpy((char *)&pkt, data, bytes); if ((pkt.hdr.type & 0x1) != DEVMGR_RESPOND) { DBG_ERR("task_recv_cb: packet is not respond\n"); return false; } if (task->encrypt_enable && !(pkt.hdr.type & DEVMGR_TYPE_CRYPTO)) { DBG_ERR("task_recv_cb: the packet should be encrypt\n"); return false; } type_compress = (pkt.hdr.type & DEVMGR_TYPE_COMPRESS) ? 1 : 0; blobmsg_buf_init(&b); blobmsg_add_string(&b, "ip", task->ip); DEV_NODE *dev = device_list_search(b.head); blob_buf_free(&b); if (dev) { dev->support_new_port = (pkt.hdr.type & DEVMGR_TYPE_NEW_PORT) ? 1 : 0; if (task->compress_enable) { dev->compress_support = type_compress; } if (dev->psk[0] == '\0') { devmgr_get_dev_psk(dev->psk, sizeof(dev->psk), dev->mac); devmgr_aes_128_key_gen(dev->psk, dev->crypto_key, sizeof(dev->crypto_key)); } memcpy(mac, dev->mac, sizeof(mac)); memcpy(key, dev->crypto_key, sizeof(key)); } else { devmgr_get_dev_mac(mac, sizeof(mac), task->ip); devmgr_get_dev_psk(dev_psk, sizeof(dev_psk), mac); devmgr_aes_128_key_gen(dev_psk, key, sizeof(key)); } if (task->encrypt_enable) { /* aes decrypt */ pkt_cmd = pkt.hdr.cmd; pkt_id = pkt.hdr.id; devmgr_aes128_iv_init(iv, sizeof(iv), mac, &pkt_cmd, &pkt_id); devmgr_aes_128_cbc_decrypt((unsigned char *)pkt.data, ntohs(pkt.hdr.len), pkt.data, sizeof(pkt.data), iv, (unsigned char *)key ); } if (type_compress) { data = (char *)(pkt.data + 6); /* uncompress */ original_len = ntohs(*(uint16_t *)(pkt.data + 4)); compress_len = ntohs(pkt.hdr.len) - 6; original_buf = malloc(sizeof(unsigned char) * original_len + 1); if (!original_buf) { DBG_ERR("task_recv_cb: malloc failed\n"); return false; } memset(original_buf, 0, sizeof(unsigned char) * original_len + 1); if (uncompress(original_buf, &original_len, (unsigned char *)data, compress_len) != Z_OK) { DBG_ERR("task_recv_cb: the packet uncompress failed\n"); free(original_buf); return false; } data = (char *)original_buf; } else { data = (char *)(pkt.data + 4); } /* check crc */ crc = (uint8_t)pkt.data[0]; crc |= (uint8_t)pkt.data[1] << 8; crc |= (uint8_t)pkt.data[2] << 16; crc |= (uint8_t)pkt.data[3] << 24; if (crc != crc32_sum(data, strlen(data))) { DBG_ERR("task_recv_cb: crc error\n"); if (original_buf) { free(original_buf); } return false; } blobmsg_buf_init(&b); if (!blobmsg_add_json_from_string(&b, data)) { common_end(&b, DEVICE_PACKET_FORMAT_ERR); } if (original_buf) { free(original_buf); } if (task->handler) { task->handler(b.head, task->arg); task->handler = NULL; } blob_buf_free(&b); return true; } bool tpsocket_devmgr_server_event(struct tpsocket_handler*handler, struct list_head*buf, int event) { struct tpsocket_buf * pbuf = NULL; struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); switch(event) { case TPSOCKET_EVENT_LISTEN: break; case TPSOCKET_EVENT_ACCEPT: break; case TPSOCKET_EVENT_CONNECTED: break; case TPSOCKET_EVENT_REQ_HEAD: break; case TPSOCKET_EVENT_RSP_HEAD: break; case TPSOCKET_EVENT_SUB_HEAD: break; case TPSOCKET_EVENT_UPGRADE: break; case TPSOCKET_EVENT_MESSAGE: pbuf = tpsocket_merge_buf(buf); if (pbuf) { devmgr_packet_recv_cb(sock, tpbuf_data(pbuf), tpbuf_data_len(pbuf)); } tpsocket_close(sock); break; case TPSOCKET_EVENT_WRITABLE: break; case TPSOCKET_EVENT_STREAM: break; case TPSOCKET_EVENT_RESET: break; case TPSOCKET_EVENT_CLOSED: break; case TPSOCKET_EVENT_SHUTDOWN: if (sock == g_listen_fd) { g_listen_fd = NULL; } break; case TPSOCKET_EVENT_ERROR: default: break; } tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; } bool tpsocket_devmgr_sock_event(struct tpsocket_handler*handler, struct list_head*buf, int event) { struct tpsocket_buf * pbuf = NULL; struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); struct send_task * task = handler->priv; switch(event) { case TPSOCKET_EVENT_CONNECTED: break; case TPSOCKET_EVENT_REQ_HEAD: break; case TPSOCKET_EVENT_RSP_HEAD: break; case TPSOCKET_EVENT_SUB_HEAD: break; case TPSOCKET_EVENT_UPGRADE: break; case TPSOCKET_EVENT_MESSAGE: if (task) { pbuf = tpsocket_merge_buf(buf); if (pbuf) { if (devmgr_task_recv_cb(task, tpbuf_data(pbuf), tpbuf_data_len(pbuf))) { tpsocket_unbind(sock, &task->sock); devmgr_send_task_free(task); tpsocket_close(sock); } } } break; case TPSOCKET_EVENT_WRITABLE: break; case TPSOCKET_EVENT_STREAM: break; case TPSOCKET_EVENT_RESET: break; case TPSOCKET_EVENT_CLOSED: if (task) { tpsocket_unbind(sock, &task->sock); //devmgr_send_task_free(task); } break; case TPSOCKET_EVENT_ERROR: default: break; } tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; } /******************************************************************************* Function : devmgr_send_task_new Description : create a new task to send packet Input : ip @ destination ip address buf @ send buffer id @ packet id timeout @ timeout value in sec handler @ response handler arg @ handler argument Output : void Return : void *******************************************************************************/ struct send_task * devmgr_send_task_new(char *ip, char *buf, int id, int timeout, void(*handler)(struct blob_attr *msg, void *arg), void *arg) { if (!ip) { DM_PRINT("param[*ip] is null\n"); return NULL; } struct send_task *task = malloc(sizeof(struct send_task)); if (!task) { DM_PRINT("create task failed\n"); return NULL; } else { memset(task, 0, sizeof(struct send_task)); } INIT_LIST_HEAD(&task->list); task->cmd_id = id; task->timeout = timeout + devmgr_time(NULL); task->interval = 100; task->arg = arg; task->seq = g_seq++; task->encrypt_enable = true; task->compress_enable = true; task->dst_port = atoi(DM_DEVCTL_PORT); task->tmr.cb = devmgr_task_resend_cb; uloop_timeout_set(&task->tmr, task->interval); strncpy(task->ip, ip, sizeof(task->ip) - 1); memset(&task->b, 0, sizeof(task->b)); blob_buf_init(&task->b, 0); if (buf && buf[0] && !blobmsg_add_json_from_string(&task->b, buf)) { DM_PRINT("set blobmsg failed\n"); devmgr_send_task_free(task); return NULL; } else { char url[128] = {0}; struct tpsocket_handler sockhdler = { .cb = tpsocket_devmgr_sock_event, .priv = task, .msg_len_max = KDCP_MAX_DATA_LEN }; snprintf(url, sizeof(url), "udp://%s:%s/", ip, DM_DEVCTL_PORT); task->sock = tpsocket_from_url(url, &sockhdler); if (!task->sock) { DM_PRINT("create socket failed\n"); devmgr_send_task_free(task); return NULL; } } task->handler = handler; blobmsg_add_u32(&task->b, "seq", task->seq); devmgr_task_send_packet(task); return task; } struct send_task * devmgr_send_task_new2(char *ip, char *buf, int id, int timeout, void(*handler)(struct blob_attr *msg, void *arg), void *arg, int interval) { if (!ip) { DM_PRINT("param[*ip] is null\n"); return NULL; } struct send_task *task = malloc(sizeof(struct send_task)); if (!task) { DM_PRINT("create task failed\n"); return NULL; } else { memset(task, 0, sizeof(struct send_task)); } INIT_LIST_HEAD(&task->list); task->cmd_id = id; task->timeout = timeout + devmgr_time(NULL); task->interval = interval; task->fixed_interval = 1; task->arg = arg; task->seq = g_seq++; task->encrypt_enable = true; task->compress_enable = true; task->dst_port = atoi(DM_DEVCTL_PORT); task->tmr.cb = devmgr_task_resend_cb; uloop_timeout_set(&task->tmr, task->interval); strncpy(task->ip, ip, sizeof(task->ip) - 1); memset(&task->b, 0, sizeof(task->b)); blob_buf_init(&task->b, 0); if (buf && buf[0] && !blobmsg_add_json_from_string(&task->b, buf)) { DM_PRINT("set blobmsg failed\n"); devmgr_send_task_free(task); return NULL; } else { char url[128] = {0}; struct tpsocket_handler sockhdler = { .cb = tpsocket_devmgr_sock_event, .priv = task, .msg_len_max = KDCP_MAX_DATA_LEN }; snprintf(url, sizeof(url), "udp://%s:%s/", ip, DM_DEVCTL_PORT); task->sock = tpsocket_from_url(url, &sockhdler); if (!task->sock) { DM_PRINT("create socket failed\n"); devmgr_send_task_free(task); return NULL; } } task->handler = handler; blobmsg_add_u32(&task->b, "seq", task->seq); devmgr_task_send_packet(task); return task; } struct send_task * devmgr_send_task_new3(char *ip, char *buf, int id, int timeout, void(*handler)(struct blob_attr *msg, void *arg), void *arg, int port, int seq, bool encrypt_enable, bool compress_enable) { /* send udp packet to child dev falcon, not compress and encrypt */ if (!ip) { DM_PRINT("param[*ip] is null\n"); return NULL; } struct send_task *task = malloc(sizeof(struct send_task)); if (!task) { DM_PRINT("create task failed\n"); return NULL; } else { memset(task, 0, sizeof(struct send_task)); } INIT_LIST_HEAD(&task->list); task->cmd_id = id; task->timeout = timeout + devmgr_time(NULL); task->interval = 100; task->arg = arg; task->seq = seq; task->encrypt_enable = encrypt_enable; task->compress_enable = compress_enable; task->dst_port = port; task->tmr.cb = devmgr_task_resend_cb; uloop_timeout_set(&task->tmr, task->interval); strncpy(task->ip, ip, sizeof(task->ip) - 1); memset(&task->b, 0, sizeof(task->b)); blob_buf_init(&task->b, 0); DBG_NOTICE("signal:%d, buf:%s\n", id, buf); if (buf && buf[0] && !blobmsg_add_json_from_string(&task->b, buf)) { DM_PRINT("set blobmsg failed\n"); devmgr_send_task_free(task); return NULL; } else { char url[128] = {0}; struct tpsocket_handler sockhdler = { .cb = tpsocket_devmgr_sock_event, .priv = task, .msg_len_max = KDCP_MAX_DATA_LEN }; snprintf(url, sizeof(url), "udp://%s:%d/", ip, port); task->sock = tpsocket_from_url(url, &sockhdler); if (!task->sock) { DM_PRINT("create socket failed\n"); devmgr_send_task_free(task); return NULL; } } task->handler = handler; blobmsg_add_u32(&task->b, "seq", task->seq); devmgr_task_send_packet(task); return task; } /******************************************************************************* Function : devmgr_send_task_free Description : create a new task to send packet Input : ip @ destination ip address buf @ send buffer id @ packet id retry @ retry times handler @ response handler arg @ handler argument Output : void Return : void *******************************************************************************/ void devmgr_send_task_free(struct send_task *task) { if (!task) { return; } list_del(&task->list); if (task->handler) { struct blob_buf b = {0}; blobmsg_buf_init(&b); common_end(&b, DEVICE_PACKET_RECV_TIMEOUT); task->handler(b.head, task->arg); task->handler = NULL; blob_buf_free(&b); } tpsocket_free2(&task->sock, task); blob_buf_free(&task->b); uloop_timeout_cancel(&task->tmr); free(task); } void devmgr_sensor_post_request(struct sensor_task *task) { char *data = NULL; int data_len = 0; struct list_head buf_head; struct tpsocket_buf *hbuf = NULL, *dbuf = NULL; if (!task) { return; } data = blobmsg_format_json(task->b.head, true); if (data) { data_len = strlen(data); dbuf = tpbuf_new(data_len); memcpy(tpbuf_tail(dbuf), data, data_len); tpbuf_put(dbuf, data_len); hbuf = tpbuf_snprintf(1024, "POST /local HTTP/1.1\r\n"\ "Content-Type: application/json; charset=UTF-8\r\n"\ "User-Agent: Device=%s %s/1.3\r\n"\ "Host: %s:%s\r\n"\ "Content-Length: %d\r\n"\ "\r\n", "H200", "1.2.1", task->sock->host ? task->sock->host : "*", task->sock->port ? task->sock->port : "14614", data_len ); INIT_LIST_HEAD(&buf_head); list_add_tail(&hbuf->list, &buf_head); list_add_tail(&dbuf->list, &buf_head); tpsocket_write_list_force(task->sock, &buf_head, true); free(data); } } void devmgr_sensor_ubus_relay(struct sensor_task *task, const char *data) { if (task && task->call) { blobmsg_buf_init(&task->b); blobmsg_add_json_from_string(&task->b, data); ubus_app_reply(task->call, task->b.head); task->call = NULL; } } void devmgr_sensor_ubus_relay_error(struct sensor_task *task, int error_code) { if (task && task->call) { blobmsg_buf_init(&task->b); common_end(&task->b, error_code); ubus_app_reply(task->call, task->b.head); task->call = NULL; } } bool devmgr_sensor_sock_proc(struct tpsocket_handler *handler, struct list_head *buf, int event) { struct sensor_task *task = handler->priv; struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); struct tpsocket_buf *mbuf = NULL; switch (event) { case TPSOCKET_EVENT_LISTEN: break; case TPSOCKET_EVENT_SHUTDOWN: break; case TPSOCKET_EVENT_ACCEPT: break; case TPSOCKET_EVENT_CONNECTED: if (!task) { return false; } devmgr_sensor_post_request(task); break; case TPSOCKET_EVENT_REQ_HEAD: break; case TPSOCKET_EVENT_RSP_HEAD: if (200 != sock->parser.http.code) { return false; } break; case TPSOCKET_EVENT_SUB_HEAD: break; case TPSOCKET_EVENT_UPGRADE: break; case TPSOCKET_EVENT_STREAM: return true; case TPSOCKET_EVENT_MESSAGE: mbuf = tpsocket_merge_buf(buf); if (task) { devmgr_sensor_ubus_relay(task, tpbuf_data(mbuf)); tpsocket_unbind(sock, &task->sock); devmgr_sensor_task_free(task); tpsocket_close(sock); } break; case TPSOCKET_EVENT_RESET: break; case TPSOCKET_EVENT_TIMEOUT: if (task) { devmgr_sensor_ubus_relay_error(task, -1); tpsocket_unbind(sock, &task->sock); devmgr_sensor_task_free(task); tpsocket_close(sock); } break; case TPSOCKET_EVENT_CLOSED: if (task) { devmgr_sensor_ubus_relay_error(task, -1); tpsocket_unbind(sock, &task->sock); devmgr_sensor_task_free(task); } break; case TPSOCKET_EVENT_ERROR: default: break; } tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; } struct sensor_task * devmgr_sensor_task_new(struct blob_attr *msg, struct ubus_call *call) { struct sensor_task *task = NULL; char url[128] = {0}; struct blob_attr *cur = NULL; int rem; struct tpsocket_handler sensor_sock_handle = { .timeout = 5000, .write_buf_max = 32*1024, .cb = devmgr_sensor_sock_proc, }; task = (struct sensor_task *)malloc(sizeof(struct sensor_task)); if (!task) { return NULL; } else { memset(task, 0, sizeof(struct sensor_task)); } snprintf(url, sizeof(url), "http://127.0.0.1:14614/local"); sensor_sock_handle.priv = task; task->call = call; task->sock = tpsocket_from_url(url, &sensor_sock_handle); if (!task->sock) { free(task); return NULL; } blobmsg_buf_init(&task->b); blobmsg_for_each_attr(cur, msg, rem) { blobmsg_add_blob(&task->b, cur); } return task; } void devmgr_sensor_task_free(struct sensor_task *task) { if (task) { tpsocket_free2(&task->sock, task); blob_buf_free(&task->b); free(task); } } void devmgr_sock_init() { if (g_listen_fd) { tpsocket_free(g_listen_fd); g_listen_fd = NULL; } devmgr_socket_init(); } void devmgr_socket_init() { char url[128] = {0}; struct tpsocket_handler sockhdler = { .session = 100, .cb = tpsocket_devmgr_server_event, .msg_len_max = KDCP_MAX_DATA_LEN }; snprintf(url, sizeof(url), "udpd://%s:%s/", "0.0.0.0", DM_DEVCTL_PORT); g_listen_fd = tpsocket_from_url(url, &sockhdler); }
最新发布
09-18
这个错误信息 `libavcodec.a undefined reference to symbol inflateEnd` 表明在链接阶段,FFmpeg 使用的 zlib 压缩库中的函数 `inflateEnd` 未被正确链接。通常这是因为编译时没有链接 `zlib` 库。 ### 原因分析: `inflateEnd` 是 zlib 提供的一个函数,用于释放解压过程中使用的资源。FFmpeg 的某些编解码器(如 `H.264`、`VP8` 等)依赖 zlib 来处理压缩数据。如果你在编译时使用了静态链接的 `libavcodec.a`,但没有链接 `zlib`,就会出现这个错误。 --- ### 解决方案: #### ✅ 方法一:在编译命令中手动链接 zlib 库 在你的编译命令中添加 `-lz` 参数,确保链接器能找到 zlib 函数。 例如,如果你使用的是 g++ 编译 Qt 项目,命令如下: ```bash g++ your_program.cpp -o your_program $(pkg-config --cflags --libs Qt5Core Qt5Gui Qt5Widgets) -lavformat -lavcodec -lavutil -lswscale -lz ``` 如果你使用 `.pro` 文件构建 Qt 项目,确保添加以下内容: ```qmake LIBS += -lz ``` #### ✅ 方法二:确认 FFmpeg 是否启用 zlib 编译 如果你是自己编译 FFmpeg,请确保配置时启用了 zlib: ```bash ./configure --enable-zlib ... ``` 否则 FFmpeg 可能会忽略内部依赖 zlib 的模块。 #### ✅ 方法三:使用动态链接 FFmpeg 库 如果你使用的是系统安装的 FFmpeg 动态库(如通过 `apt install libavcodec-dev` 安装),通常会自动处理依赖库的链接。可以尝试使用动态链接方式,而不是静态链接。 --- ### 示例修复后的 `.pro` 文件内容: ```qmake QT += core gui widgets TARGET = RTSPPlayer TEMPLATE = app SOURCES += main.cpp \ rtspstreamhandler.cpp INCLUDEPATH += /usr/local/include LIBS += -L/usr/local/lib \ -lavformat \ -lavcodec \ -lavutil \ -lswscale \ -lz ``` --- ### 验证 zlib 是否安装 在 Linux 上,你可以运行以下命令检查 zlib 是否已安装: ```bash pkg-config --libs zlib ``` 如果没有输出,你需要安装 zlib 开发包: - Ubuntu/Debian: ```bash sudo apt-get install zlib1g-dev ``` - CentOS/RHEL: ```bash sudo yum install zlib-devel ``` --- ### 总结 该错误是由于缺少 `zlib` 库导致的,解决方法是: 1. 在编译时添加 `-lz` 链接选项; 2. 确保 FFmpeg 配置时启用了 `--enable-zlib`; 3. 确保系统中安装了 zlib 开发库; 4. 或使用动态链接 FFmpeg 库。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值