From c02ece6b61784d997e417781e7c1e7a8f2cd7edf Mon Sep 17 00:00:00 2001 From: depler Date: Sat, 3 Sep 2022 20:21:58 +0300 Subject: [PATCH] code --- patch/boringssl-old-ciphers.patch | 154 -- patch/curl-impersonate.patch | 2209 ----------------------------- 2 files changed, 2363 deletions(-) delete mode 100644 patch/boringssl-old-ciphers.patch delete mode 100644 patch/curl-impersonate.patch diff --git a/patch/boringssl-old-ciphers.patch b/patch/boringssl-old-ciphers.patch deleted file mode 100644 index 4db24cc..0000000 --- a/patch/boringssl-old-ciphers.patch +++ /dev/null @@ -1,154 +0,0 @@ -diff -u1 -Nar --exclude build --exclude tags boringssl-3a667d10e94186fd503966f5638e134fe9fb4080/ssl/internal.h boringssl/ssl/internal.h ---- boringssl-3a667d10e94186fd503966f5638e134fe9fb4080/ssl/internal.h 2021-11-22 19:06:04.000000000 +0200 -+++ boringssl/ssl/internal.h 2022-02-27 12:20:25.308284303 +0200 -@@ -566,4 +566,10 @@ - #define SSL_SHA1 0x00000001u -+// curl-impersonate: -+// SSL_SHA256 and SSL_SHA384 were removed in -+// https://boringssl-review.googlesource.com/c/boringssl/+/27944/ -+// but restored to impersonate browsers with older ciphers. -+#define SSL_SHA256 0x00000002u -+#define SSL_SHA384 0x00000004u - // SSL_AEAD is set for all AEADs. --#define SSL_AEAD 0x00000002u -+#define SSL_AEAD 0x00000008u - -diff -u1 -Nar --exclude build --exclude tags boringssl-3a667d10e94186fd503966f5638e134fe9fb4080/ssl/ssl_cipher.cc boringssl/ssl/ssl_cipher.cc ---- boringssl-3a667d10e94186fd503966f5638e134fe9fb4080/ssl/ssl_cipher.cc 2021-11-22 19:06:04.000000000 +0200 -+++ boringssl/ssl/ssl_cipher.cc 2022-02-27 13:54:05.378053046 +0200 -@@ -210,2 +210,33 @@ - -+ // curl-impersonate: Ciphers 3C, 3D were removed in -+ // https://boringssl-review.googlesource.com/c/boringssl/+/27944/ -+ // but restored here to impersonate browsers with older ciphers. They are -+ // not expected to actually work; but just to be included in the TLS -+ // Client Hello. -+ -+ // TLS v1.2 ciphersuites -+ -+ // Cipher 3C -+ { -+ TLS1_TXT_RSA_WITH_AES_128_SHA256, -+ "TLS_RSA_WITH_AES_128_CBC_SHA256", -+ TLS1_CK_RSA_WITH_AES_128_SHA256, -+ SSL_kRSA, -+ SSL_aRSA, -+ SSL_AES128, -+ SSL_SHA256, -+ SSL_HANDSHAKE_MAC_SHA256, -+ }, -+ // Cipher 3D -+ { -+ TLS1_TXT_RSA_WITH_AES_256_SHA256, -+ "TLS_RSA_WITH_AES_256_CBC_SHA256", -+ TLS1_CK_RSA_WITH_AES_256_SHA256, -+ SSL_kRSA, -+ SSL_aRSA, -+ SSL_AES256, -+ SSL_SHA256, -+ SSL_HANDSHAKE_MAC_SHA256, -+ }, -+ - // PSK cipher suites. -@@ -300,2 +331,19 @@ - -+ // curl-impersonate: Cipher C008 was missing from BoringSSL, -+ // probably because it is weak. Add it back from OpenSSL (ssl/s3_lib.c) -+ // where it is called ECDHE-ECDSA-DES-CBC3-SHA. -+ // It's not supposed to really work but just appear in the TLS client hello. -+ -+ // Cipher C008 -+ { -+ "ECDHE-ECDSA-DES-CBC3-SHA", -+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", -+ 0x0300C008, -+ SSL_kECDHE, -+ SSL_aECDSA, -+ SSL_3DES, -+ SSL_SHA1, -+ SSL_HANDSHAKE_MAC_DEFAULT, -+ }, -+ - // Cipher C009 -@@ -324,2 +372,17 @@ - -+ // curl-impersonate: Cipher C012 was missing from BoringSSL, -+ // probably because it is weak. Add it back from OpenSSL (ssl/s3_lib.c) -+ // where it is called ECDHE-RSA-DES-CBC3-SHA -+ // It's not supposed to really work but just appear in the TLS client hello. -+ { -+ "ECDHE-RSA-DES-CBC3-SHA", -+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", -+ 0x0300C012, -+ SSL_kECDHE, -+ SSL_aRSA, -+ SSL_3DES, -+ SSL_SHA1, -+ SSL_HANDSHAKE_MAC_DEFAULT, -+ }, -+ - // Cipher C013 -@@ -348,2 +411,55 @@ - -+ // curl-impersonate: Ciphers C023, C024, C027, C028 were removed in -+ // https://boringssl-review.googlesource.com/c/boringssl/+/27944/ -+ // but restored here to impersonate browsers with older ciphers. They are -+ // not expected to actually work; but just to be included in the TLS -+ // Client Hello. -+ -+ // HMAC based TLS v1.2 ciphersuites from RFC5289 -+ -+ // Cipher C023 -+ { -+ TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256, -+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", -+ TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, -+ SSL_kECDHE, -+ SSL_aECDSA, -+ SSL_AES128, -+ SSL_SHA256, -+ SSL_HANDSHAKE_MAC_SHA256, -+ }, -+ // Cipher C024 -+ { -+ TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384, -+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", -+ TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, -+ SSL_kECDHE, -+ SSL_aECDSA, -+ SSL_AES256, -+ SSL_SHA384, -+ SSL_HANDSHAKE_MAC_SHA384, -+ }, -+ // Cipher C027 -+ { -+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256, -+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", -+ TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, -+ SSL_kECDHE, -+ SSL_aRSA, -+ SSL_AES128, -+ SSL_SHA256, -+ SSL_HANDSHAKE_MAC_SHA256, -+ }, -+ // Cipher C028 -+ { -+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384, -+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", -+ TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, -+ SSL_kECDHE, -+ SSL_aRSA, -+ SSL_AES256, -+ SSL_SHA384, -+ SSL_HANDSHAKE_MAC_SHA384, -+ }, -+ - // GCM based TLS v1.2 ciphersuites from RFC 5289 -@@ -539,2 +655,7 @@ - {"SHA", ~0u, ~0u, ~0u, SSL_SHA1, 0}, -+ // curl-impersonate: -+ // Removed in https://boringssl-review.googlesource.com/c/boringssl/+/27944/ -+ // but restored to impersonate browsers with older ciphers. -+ {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0}, -+ {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0}, - diff --git a/patch/curl-impersonate.patch b/patch/curl-impersonate.patch deleted file mode 100644 index b4fca3e..0000000 --- a/patch/curl-impersonate.patch +++ /dev/null @@ -1,2209 +0,0 @@ -diff --git a/Makefile.am b/Makefile.am -index 40771ed38..a7c51eea7 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -155,13 +155,13 @@ CLEANFILES = $(VC10_LIBVCXPROJ) $(VC10_SRCVCXPROJ) $(VC11_LIBVCXPROJ) \ - $(VC14_SRCVCXPROJ) $(VC14_10_LIBVCXPROJ) $(VC14_10_SRCVCXPROJ) \ - $(VC14_30_LIBVCXPROJ) $(VC14_30_SRCVCXPROJ) - --bin_SCRIPTS = curl-config -+bin_SCRIPTS = curl-impersonate-chrome-config - - SUBDIRS = lib src - DIST_SUBDIRS = $(SUBDIRS) tests packages scripts include docs - - pkgconfigdir = $(libdir)/pkgconfig --pkgconfig_DATA = libcurl.pc -+pkgconfig_DATA = libcurl-impersonate-chrome.pc - - # List of files required to generate VC IDE .dsp, .vcproj and .vcxproj files - include lib/Makefile.inc -diff --git a/configure.ac b/configure.ac -index de2dee5a4..ab8a92db4 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1331,7 +1331,8 @@ if test X"$OPT_BROTLI" != Xno; then - - dnl if given with a prefix, we set -L and -I based on that - if test -n "$PREFIX_BROTLI"; then -- LIB_BROTLI="-lbrotlidec" -+ # curl-impersonate: Use static libbrotli -+ LIB_BROTLI="-lbrotlidec-static -lbrotlicommon-static" - LD_BROTLI=-L${PREFIX_BROTLI}/lib$libsuff - CPP_BROTLI=-I${PREFIX_BROTLI}/include - DIR_BROTLI=${PREFIX_BROTLI}/lib$libsuff -@@ -1341,7 +1342,11 @@ if test X"$OPT_BROTLI" != Xno; then - CPPFLAGS="$CPPFLAGS $CPP_BROTLI" - LIBS="$LIB_BROTLI $LIBS" - -- AC_CHECK_LIB(brotlidec, BrotliDecoderDecompress) -+ AC_CHECK_LIB(brotlidec, BrotliDecoderDecompress, -+ # curl-impersonate: Define 'action-if-found' explicitly to prevent -+ # -lbrotlidec from being added to LIBS (already added before) -+ AC_DEFINE(HAVE_LIBBROTLI, 1, [Define to 1 if libbrotli exists]) -+ ) - - AC_CHECK_HEADERS(brotli/decode.h, - curl_brotli_msg="enabled (libbrotlidec)" -@@ -4426,8 +4431,8 @@ AC_CONFIG_FILES([Makefile \ - tests/unit/Makefile \ - packages/Makefile \ - packages/vms/Makefile \ -- curl-config \ -- libcurl.pc -+ curl-impersonate-chrome-config:curl-config.in \ -+ libcurl-impersonate-chrome.pc:libcurl.pc.in - ]) - AC_OUTPUT - -diff --git a/curl-config.in b/curl-config.in -index aaf2b8a43..ccfa52985 100644 ---- a/curl-config.in -+++ b/curl-config.in -@@ -163,9 +163,9 @@ while test $# -gt 0; do - CURLLIBDIR="" - fi - if test "X@ENABLE_SHARED@" = "Xno"; then -- echo ${CURLLIBDIR}-lcurl @LIBCURL_LIBS@ -+ echo ${CURLLIBDIR}-lcurl-impersonate-chrome @LIBCURL_LIBS@ - else -- echo ${CURLLIBDIR}-lcurl -+ echo ${CURLLIBDIR}-lcurl-impersonate-chrome - fi - ;; - --ssl-backends) -@@ -174,7 +174,7 @@ while test $# -gt 0; do - - --static-libs) - if test "X@ENABLE_STATIC@" != "Xno" ; then -- echo @libdir@/libcurl.@libext@ @LDFLAGS@ @LIBCURL_LIBS@ -+ echo @libdir@/libcurl-impersonate-chrome.@libext@ @LDFLAGS@ @LIBCURL_LIBS@ - else - echo "curl was built with static libraries disabled" >&2 - exit 1 -diff --git a/include/curl/curl.h b/include/curl/curl.h -index b00648e79..8f8f19799 100644 ---- a/include/curl/curl.h -+++ b/include/curl/curl.h -@@ -2143,6 +2143,38 @@ typedef enum { - /* set the SSH host key callback custom pointer */ - CURLOPT(CURLOPT_SSH_HOSTKEYDATA, CURLOPTTYPE_CBPOINT, 317), - -+ /* curl-impersonate: A list of headers used by the impersonated browser. -+ * If given, merged with CURLOPT_HTTPHEADER. */ -+ CURLOPT(CURLOPT_HTTPBASEHEADER, CURLOPTTYPE_SLISTPOINT, 318), -+ -+ /* curl-impersonate: A list of TLS signature hash algorithms. -+ * See https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1 */ -+ CURLOPT(CURLOPT_SSL_SIG_HASH_ALGS, CURLOPTTYPE_STRINGPOINT, 319), -+ -+ /* curl-impersonate: Whether to enable ALPS in TLS or not. -+ * See https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps. -+ * Support for ALPS is minimal and is intended only for the TLS client -+ * hello to match. */ -+ CURLOPT(CURLOPT_SSL_ENABLE_ALPS, CURLOPTTYPE_LONG, 320), -+ -+ /* curl-impersonate: Comma-separated list of certificate compression -+ * algorithms to use. These are published in the client hello. -+ * Supported algorithms are "zlib" and "brotli". -+ * See https://datatracker.ietf.org/doc/html/rfc8879 */ -+ CURLOPT(CURLOPT_SSL_CERT_COMPRESSION, CURLOPTTYPE_STRINGPOINT, 321), -+ -+ /* Enable/disable TLS session ticket extension (RFC5077) */ -+ CURLOPT(CURLOPT_SSL_ENABLE_TICKET, CURLOPTTYPE_LONG, 322), -+ -+ /* -+ * curl-impersonate: -+ * Set the order of the HTTP/2 pseudo headers. The value must contain -+ * the letters 'm', 'a', 's', 'p' representing the pseudo-headers -+ * ":method", ":authority", ":scheme", ":path" in the desired order of -+ * appearance in the HTTP/2 HEADERS frame. -+ */ -+ CURLOPT(CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, CURLOPTTYPE_STRINGPOINT, 323), -+ - CURLOPT_LASTENTRY /* the last unused */ - } CURLoption; - -diff --git a/include/curl/easy.h b/include/curl/easy.h -index 9c7e63ada..d93353c69 100644 ---- a/include/curl/easy.h -+++ b/include/curl/easy.h -@@ -43,6 +43,15 @@ CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); - CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); - CURL_EXTERN void curl_easy_cleanup(CURL *curl); - -+/* -+ * curl-impersonate: Tell libcurl to impersonate a browser. -+ * This is a wrapper function that calls curl_easy_setopt() -+ * multiple times with all the parameters required. That's also why it was -+ * created as a separate API function and not just as another option to -+ * curl_easy_setopt(). -+ */ -+CURL_EXTERN CURLcode curl_easy_impersonate(CURL *curl, const char *target); -+ - /* - * NAME curl_easy_getinfo() - * -diff --git a/lib/Makefile.am b/lib/Makefile.am -index 18ce47ea9..9cbd81534 100644 ---- a/lib/Makefile.am -+++ b/lib/Makefile.am -@@ -30,7 +30,7 @@ EXTRA_DIST = Makefile.m32 config-win32.h config-win32ce.h config-plan9.h \ - libcurl.plist libcurl.rc config-amigaos.h makefile.amiga config-win32ce.h \ - config-os400.h setup-os400.h $(CMAKE_DIST) setup-win32.h .checksrc - --lib_LTLIBRARIES = libcurl.la -+lib_LTLIBRARIES = libcurl-impersonate-chrome.la - - if BUILD_UNITTESTS - noinst_LTLIBRARIES = libcurlu.la -@@ -75,43 +75,43 @@ AM_CPPFLAGS += -DBUILDING_LIBCURL - AM_LDFLAGS = - AM_CFLAGS = - --libcurl_la_CPPFLAGS_EXTRA = --libcurl_la_LDFLAGS_EXTRA = --libcurl_la_CFLAGS_EXTRA = -+libcurl_impersonate_chrome_la_CPPFLAGS_EXTRA = -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA = -+libcurl_impersonate_chrome_la_CFLAGS_EXTRA = - - if CURL_LT_SHLIB_USE_VERSION_INFO --libcurl_la_LDFLAGS_EXTRA += $(VERSIONINFO) -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA += $(VERSIONINFO) - endif - - if CURL_LT_SHLIB_USE_NO_UNDEFINED --libcurl_la_LDFLAGS_EXTRA += -no-undefined -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA += -no-undefined - endif - - if CURL_LT_SHLIB_USE_MIMPURE_TEXT --libcurl_la_LDFLAGS_EXTRA += -mimpure-text -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA += -mimpure-text - endif - - if CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS --libcurl_la_LDFLAGS_EXTRA += -Wl,--version-script=libcurl.vers -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA += -Wl,--version-script=libcurl.vers - else - # if symbol-hiding is enabled, hide them! - if DOING_CURL_SYMBOL_HIDING --libcurl_la_LDFLAGS_EXTRA += -export-symbols-regex '^curl_.*' -+libcurl_impersonate_chrome_la_LDFLAGS_EXTRA += -export-symbols-regex '^curl_.*' - endif - endif - - if USE_CPPFLAG_CURL_STATICLIB --libcurl_la_CPPFLAGS_EXTRA += -DCURL_STATICLIB -+libcurl_impersonate_chrome_la_CPPFLAGS_EXTRA += -DCURL_STATICLIB - endif - - if DOING_CURL_SYMBOL_HIDING --libcurl_la_CPPFLAGS_EXTRA += -DCURL_HIDDEN_SYMBOLS --libcurl_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING) -+libcurl_impersonate_chrome_la_CPPFLAGS_EXTRA += -DCURL_HIDDEN_SYMBOLS -+libcurl_impersonate_chrome_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING) - endif - --libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) --libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(LDFLAGS) $(LIBCURL_LIBS) --libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) -+libcurl_impersonate_chrome_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_impersonate_chrome_la_CPPFLAGS_EXTRA) -+libcurl_impersonate_chrome_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_impersonate_chrome_la_LDFLAGS_EXTRA) $(LDFLAGS) $(LIBCURL_LIBS) -+libcurl_impersonate_chrome_la_CFLAGS = $(AM_CFLAGS) $(libcurl_impersonate_chrome_la_CFLAGS_EXTRA) - - libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS - libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS) -@@ -120,7 +120,7 @@ libcurlu_la_CFLAGS = $(AM_CFLAGS) - # Makefile.inc provides the CSOURCES and HHEADERS defines - include Makefile.inc - --libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) -+libcurl_impersonate_chrome_la_SOURCES = $(CSOURCES) $(HHEADERS) - libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS) - - CHECKSRC = $(CS_$(V)) -diff --git a/lib/Makefile.inc b/lib/Makefile.inc -index 9bd8e324b..bfd5e90e2 100644 ---- a/lib/Makefile.inc -+++ b/lib/Makefile.inc -@@ -165,6 +165,7 @@ LIB_CFILES = \ - idn_win32.c \ - if2ip.c \ - imap.c \ -+ impersonate.c \ - inet_ntop.c \ - inet_pton.c \ - krb5.c \ -diff --git a/lib/easy.c b/lib/easy.c -index 704a59df6..c3ee9ac97 100644 ---- a/lib/easy.c -+++ b/lib/easy.c -@@ -81,6 +81,8 @@ - #include "dynbuf.h" - #include "altsvc.h" - #include "hsts.h" -+#include "strcase.h" -+#include "impersonate.h" - - /* The last 3 #include files should be in this order */ - #include "curl_printf.h" -@@ -332,6 +334,119 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, - return rc; - } - -+/* -+ * curl-impersonate: -+ * Call curl_easy_setopt() with all the needed options as defined in the -+ * 'impersonations' array. -+ * */ -+CURLcode curl_easy_impersonate(struct Curl_easy *data, const char *target) -+{ -+ int i; -+ int ret; -+ const struct impersonate_opts *opts = NULL; -+ struct curl_slist *headers = NULL; -+ -+ for(opts = impersonations; opts->target != NULL; opts++) { -+ if (Curl_safe_strcasecompare(target, opts->target)) { -+ break; -+ } -+ } -+ -+ if(opts->target == NULL) { -+ DEBUGF(fprintf(stderr, "Error: unknown impersonation target '%s'\n", -+ target)); -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ } -+ -+ if(opts->httpversion != CURL_HTTP_VERSION_NONE) { -+ ret = curl_easy_setopt(data, CURLOPT_HTTP_VERSION, opts->httpversion); -+ if(ret) -+ return ret; -+ } -+ -+ if (opts->ssl_version != CURL_SSLVERSION_DEFAULT) { -+ ret = curl_easy_setopt(data, CURLOPT_SSLVERSION, opts->ssl_version); -+ if(ret) -+ return ret; -+ } -+ -+ if(opts->ciphers) { -+ ret = curl_easy_setopt(data, CURLOPT_SSL_CIPHER_LIST, opts->ciphers); -+ if (ret) -+ return ret; -+ } -+ -+ if(opts->curves) { -+ ret = curl_easy_setopt(data, CURLOPT_SSL_EC_CURVES, opts->curves); -+ if(ret) -+ return ret; -+ } -+ -+ if(opts->sig_hash_algs) { -+ ret = curl_easy_setopt(data, CURLOPT_SSL_SIG_HASH_ALGS, -+ opts->sig_hash_algs); -+ if(ret) -+ return ret; -+ } -+ -+ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_NPN, opts->npn ? 1 : 0); -+ if(ret) -+ return ret; -+ -+ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_ALPN, opts->alpn ? 1 : 0); -+ if(ret) -+ return ret; -+ -+ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_ALPS, opts->alps ? 1 : 0); -+ if(ret) -+ return ret; -+ -+ ret = curl_easy_setopt(data, CURLOPT_SSL_ENABLE_TICKET, -+ opts->tls_session_ticket ? 1 : 0); -+ if(ret) -+ return ret; -+ -+ if(opts->cert_compression) { -+ ret = curl_easy_setopt(data, -+ CURLOPT_SSL_CERT_COMPRESSION, -+ opts->cert_compression); -+ if(ret) -+ return ret; -+ } -+ -+ /* Build a linked list out of the static array of headers. */ -+ for(i = 0; i < IMPERSONATE_MAX_HEADERS; i++) { -+ if(opts->http_headers[i]) { -+ headers = curl_slist_append(headers, opts->http_headers[i]); -+ if(!headers) { -+ return CURLE_OUT_OF_MEMORY; -+ } -+ } -+ } -+ -+ if(headers) { -+ ret = curl_easy_setopt(data, CURLOPT_HTTPBASEHEADER, headers); -+ curl_slist_free_all(headers); -+ if(ret) -+ return ret; -+ } -+ -+ if(opts->http2_pseudo_headers_order) { -+ ret = curl_easy_setopt(data, -+ CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, -+ opts->http2_pseudo_headers_order); -+ if(ret) -+ return ret; -+ } -+ -+ /* Always enable all supported compressions. */ -+ ret = curl_easy_setopt(data, CURLOPT_ACCEPT_ENCODING, ""); -+ if(ret) -+ return ret; -+ -+ return CURLE_OK; -+} -+ - /* - * curl_easy_init() is the external interface to alloc, setup and init an - * easy handle that is returned. If anything goes wrong, NULL is returned. -@@ -340,6 +455,7 @@ struct Curl_easy *curl_easy_init(void) - { - CURLcode result; - struct Curl_easy *data; -+ char *target; - - /* Make sure we inited the global SSL stuff */ - global_init_lock(); -@@ -362,6 +478,22 @@ struct Curl_easy *curl_easy_init(void) - return NULL; - } - -+ /* -+ * curl-impersonate: Hook into curl_easy_init() to set the required options -+ * from an environment variable. -+ * This is a bit hacky but allows seamless integration of libcurl-impersonate -+ * without code modifications to the app. -+ */ -+ target = curl_getenv("CURL_IMPERSONATE"); -+ if(target) { -+ result = curl_easy_impersonate(data, target); -+ free(target); -+ if(result) { -+ Curl_close(&data); -+ return NULL; -+ } -+ } -+ - return data; - } - -@@ -936,6 +1068,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) - outcurl->state.referer_alloc = TRUE; - } - -+ if(data->state.base_headers) { -+ outcurl->state.base_headers = -+ Curl_slist_duplicate(data->state.base_headers); -+ if(!outcurl->state.base_headers) -+ goto fail; -+ } -+ - /* Reinitialize an SSL engine for the new handle - * note: the engine name has already been copied by dupset */ - if(outcurl->set.str[STRING_SSL_ENGINE]) { -@@ -1025,6 +1164,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) - */ - void curl_easy_reset(struct Curl_easy *data) - { -+ char *target; -+ - Curl_free_request_state(data); - - /* zero out UserDefined data: */ -@@ -1049,6 +1190,12 @@ void curl_easy_reset(struct Curl_easy *data) - #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) - Curl_http_auth_cleanup_digest(data); - #endif -+ -+ target = curl_getenv("CURL_IMPERSONATE"); -+ if(target) { -+ curl_easy_impersonate(data, target); -+ free(target); -+ } - } - - /* -diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index c99f135ff..f3ebffa3e 100644 ---- a/lib/easyoptions.c -+++ b/lib/easyoptions.c -@@ -130,8 +130,11 @@ struct curl_easyoption Curl_easyopts[] = { - {"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0}, - {"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0}, - {"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0}, -+ {"HTTP2_PSEUDO_HEADERS_ORDER", CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, -+ CURLOT_STRING, 0}, - {"HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0}, - {"HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0}, -+ {"HTTPBASEHEADER", CURLOPT_HTTPBASEHEADER, CURLOT_SLIST, 0}, - {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, - {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, - {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, -@@ -297,15 +300,19 @@ struct curl_easyoption Curl_easyopts[] = { - {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0}, - {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0}, - {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0}, -+ {"SSL_CERT_COMPRESSION", CURLOPT_SSL_CERT_COMPRESSION, CURLOT_STRING, 0}, - {"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0}, - {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, - {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, - {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, - {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, -+ {"SSL_ENABLE_ALPS", CURLOPT_SSL_ENABLE_ALPS, CURLOT_LONG, 0}, - {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, -+ {"SSL_ENABLE_TICKET", CURLOPT_SSL_ENABLE_TICKET, CURLOT_LONG, 0}, - {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, - {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, - {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, -+ {"SSL_SIG_HASH_ALGS", CURLOPT_SSL_SIG_HASH_ALGS, CURLOT_STRING, 0}, - {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, -@@ -364,6 +371,6 @@ struct curl_easyoption Curl_easyopts[] = { - */ - int Curl_easyopts_check(void) - { -- return ((CURLOPT_LASTENTRY%10000) != (317 + 1)); -+ return ((CURLOPT_LASTENTRY%10000) != (323 + 1)); - } - #endif -diff --git a/lib/h2h3.c b/lib/h2h3.c -index 9453cf55b..01f8918ea 100644 ---- a/lib/h2h3.c -+++ b/lib/h2h3.c -@@ -41,10 +41,6 @@ - - #if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) - --/* Index where :authority header field will appear in request header -- field list. */ --#define AUTHORITY_DST_IDX 3 -- - /* USHRT_MAX is 65535 == 0xffff */ - #define HEADER_OVERFLOW(x) \ - (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) -@@ -115,6 +111,53 @@ static header_instruction inspect_header(const char *name, size_t namelen, - } - } - -+/* -+ * curl-impersonate: -+ * Determine the position of HTTP/2 pseudo headers. -+ * The pseudo headers ":method", ":path", ":scheme", ":authority" -+ * are sent in different order by different browsers. An important part of the -+ * impersonation is ordering them like the browser does. -+ */ -+static int http2_pseudo_header_index(struct Curl_easy *data, -+ const char *header, -+ size_t *index) -+{ -+ char *off; -+ // Use the Chrome ordering by default: -+ // :method, :authority, :scheme, :path -+ char *order = "masp"; -+ if(data->set.str[STRING_HTTP2_PSEUDO_HEADERS_ORDER]) -+ order = data->set.str[STRING_HTTP2_PSEUDO_HEADERS_ORDER]; -+ -+ if(strlen(order) != 4) -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ -+ // :method should always be first -+ if(order[0] != 'm') -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ -+ // All pseudo-headers must be present -+ if(!strchr(order, 'm') || -+ !strchr(order, 'a') || -+ !strchr(order, 's') || -+ !strchr(order, 'p')) -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ -+ if(strcasecompare(header, ":method")) -+ off = strchr(order, 'm'); -+ else if(strcasecompare(header, ":authority")) -+ off = strchr(order, 'a'); -+ else if(strcasecompare(header, ":scheme")) -+ off = strchr(order, 's'); -+ else if(strcasecompare(header, ":path")) -+ off = strchr(order, 'p'); -+ else -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ -+ *index = off - order; -+ return CURLE_OK; -+} -+ - CURLcode Curl_pseudo_headers(struct Curl_easy *data, - const char *mem, /* the request */ - const size_t len /* size of request */, -@@ -123,6 +166,7 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data, - struct connectdata *conn = data->conn; - size_t nheader = 0; - size_t i; -+ size_t header_idx; - size_t authority_idx; - char *hdbuf = (char *)mem; - char *end, *line_end; -@@ -164,10 +208,19 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data, - end = memchr(hdbuf, ' ', line_end - hdbuf); - if(!end || end == hdbuf) - goto fail; -- nva[0].name = H2H3_PSEUDO_METHOD; -- nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1; -- nva[0].value = hdbuf; -- nva[0].valuelen = (size_t)(end - hdbuf); -+ /* curl-impersonate: Set the index of ":method" based on libcurl option */ -+ if(http2_pseudo_header_index(data, ":authority", &authority_idx)) -+ goto fail; -+ if(http2_pseudo_header_index(data, ":method", &header_idx)) -+ goto fail; -+ /* This is needed to overcome the fact that curl will only move the authority -+ * header into its place after all other headers have been placed. */ -+ if(header_idx > authority_idx) -+ header_idx--; -+ nva[header_idx].name = H2H3_PSEUDO_METHOD; -+ nva[header_idx].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1; -+ nva[header_idx].value = hdbuf; -+ nva[header_idx].valuelen = (size_t)(end - hdbuf); - - hdbuf = end + 1; - -@@ -181,28 +234,38 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data, - } - if(!end || end == hdbuf) - goto fail; -- nva[1].name = H2H3_PSEUDO_PATH; -- nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1; -- nva[1].value = hdbuf; -- nva[1].valuelen = (end - hdbuf); -- -- nva[2].name = H2H3_PSEUDO_SCHEME; -- nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1; -+ /* curl-impersonate: Set the index of ":path" based on libcurl option */ -+ if(http2_pseudo_header_index(data, ":path", &header_idx)) -+ goto fail; -+ if(header_idx > authority_idx) -+ header_idx--; -+ nva[header_idx].name = H2H3_PSEUDO_PATH; -+ nva[header_idx].namelen = sizeof(H2H3_PSEUDO_PATH) - 1; -+ nva[header_idx].value = hdbuf; -+ nva[header_idx].valuelen = (end - hdbuf); -+ -+ /* curl-impersonate: Set the index of ":scheme" based on libcurl option */ -+ if(http2_pseudo_header_index(data, ":scheme", &header_idx)) -+ goto fail; -+ if(header_idx > authority_idx) -+ header_idx--; -+ nva[header_idx].name = H2H3_PSEUDO_SCHEME; -+ nva[header_idx].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1; - vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME)); - if(vptr) { - vptr += sizeof(H2H3_PSEUDO_SCHEME); - while(*vptr && ISSPACE(*vptr)) - vptr++; -- nva[2].value = vptr; -+ nva[header_idx].value = vptr; - infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr); - } - else { - if(conn->handler->flags & PROTOPT_SSL) -- nva[2].value = "https"; -+ nva[header_idx].value = "https"; - else -- nva[2].value = "http"; -+ nva[header_idx].value = "http"; - } -- nva[2].valuelen = strlen((char *)nva[2].value); -+ nva[header_idx].valuelen = strlen((char *)nva[header_idx].value); - - authority_idx = 0; - i = 3; -@@ -258,16 +321,16 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data, - nva[i].valuelen = (end - hdbuf); - } - -- nva[i].value = hdbuf; -- nva[i].valuelen = (end - hdbuf); -- - ++i; - } - -+ /* curl-impersonate: Set the index of ":authority" based on libcurl option */ -+ if(http2_pseudo_header_index(data, ":authority", &header_idx)) -+ goto fail; - /* :authority must come before non-pseudo header fields */ -- if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { -+ if(authority_idx && authority_idx != header_idx) { - struct h2h3pseudo authority = nva[authority_idx]; -- for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { -+ for(i = authority_idx; i > header_idx; --i) { - nva[i] = nva[i - 1]; - } - nva[i] = authority; -diff --git a/lib/http.c b/lib/http.c -index 258722a60..b8990121d 100644 ---- a/lib/http.c -+++ b/lib/http.c -@@ -85,6 +85,7 @@ - #include "altsvc.h" - #include "hsts.h" - #include "c-hyper.h" -+#include "slist.h" - - /* The last 3 #include files should be in this order */ - #include "curl_printf.h" -@@ -1804,6 +1805,15 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, - int numlists = 1; /* by default */ - int i; - -+ /* -+ * curl-impersonate: Use the merged list of headers if it exists (i.e. when -+ * the CURLOPT_HTTPBASEHEADER option was set. -+ */ -+ struct curl_slist *noproxyheaders = -+ (data->state.merged_headers ? -+ data->state.merged_headers : -+ data->set.headers); -+ - #ifndef CURL_DISABLE_PROXY - enum proxy_use proxy; - -@@ -1815,10 +1825,10 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, - - switch(proxy) { - case HEADER_SERVER: -- h[0] = data->set.headers; -+ h[0] = noproxyheaders; - break; - case HEADER_PROXY: -- h[0] = data->set.headers; -+ h[0] = noproxyheaders; - if(data->set.sep_headers) { - h[1] = data->set.proxyheaders; - numlists++; -@@ -1828,12 +1838,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, - if(data->set.sep_headers) - h[0] = data->set.proxyheaders; - else -- h[0] = data->set.headers; -+ h[0] = noproxyheaders; - break; - } - #else - (void)is_connect; -- h[0] = data->set.headers; -+ h[0] = noproxyheaders; - #endif - - /* loop through one or two lists */ -@@ -2069,6 +2079,92 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, - *reqp = httpreq; - } - -+/* -+ * curl-impersonate: -+ * Create a new linked list of headers. -+ * The new list is a merge between the "base" headers and the application given -+ * headers. The "base" headers contain curl-impersonate's list of headers -+ * used by default by the impersonated browser. -+ * -+ * The application given headers will override the "base" headers if supplied. -+ */ -+CURLcode Curl_http_merge_headers(struct Curl_easy *data) -+{ -+ int i; -+ int ret; -+ struct curl_slist *head; -+ struct curl_slist *dup = NULL; -+ struct curl_slist *new_list = NULL; -+ -+ if (!data->state.base_headers) -+ return CURLE_OK; -+ -+ /* Duplicate the list for temporary use. */ -+ if (data->set.headers) { -+ dup = Curl_slist_duplicate(data->set.headers); -+ if(!dup) -+ return CURLE_OUT_OF_MEMORY; -+ } -+ -+ for(head = data->state.base_headers; head; head = head->next) { -+ char *sep; -+ size_t prefix_len; -+ bool found = FALSE; -+ struct curl_slist *head2; -+ -+ sep = strchr(head->data, ':'); -+ if(!sep) -+ continue; -+ -+ prefix_len = sep - head->data; -+ -+ /* Check if this header was added by the application. */ -+ for(head2 = dup; head2; head2 = head2->next) { -+ if(head2->data && -+ strncasecompare(head2->data, head->data, prefix_len) && -+ Curl_headersep(head2->data[prefix_len]) ) { -+ new_list = curl_slist_append(new_list, head2->data); -+ /* Free and set to NULL to mark that it's been added. */ -+ Curl_safefree(head2->data); -+ found = TRUE; -+ break; -+ } -+ } -+ -+ if (!found) { -+ new_list = curl_slist_append(new_list, head->data); -+ } -+ -+ if (!new_list) { -+ ret = CURLE_OUT_OF_MEMORY; -+ goto fail; -+ } -+ } -+ -+ /* Now go over any additional application-supplied headers. */ -+ for(head = dup; head; head = head->next) { -+ if(head->data) { -+ new_list = curl_slist_append(new_list, head->data); -+ if(!new_list) { -+ ret = CURLE_OUT_OF_MEMORY; -+ goto fail; -+ } -+ } -+ } -+ -+ curl_slist_free_all(dup); -+ /* Save the new, merged list separately, so it can be freed later. */ -+ curl_slist_free_all(data->state.merged_headers); -+ data->state.merged_headers = new_list; -+ -+ return CURLE_OK; -+ -+fail: -+ Curl_safefree(dup); -+ curl_slist_free_all(new_list); -+ return ret; -+} -+ - CURLcode Curl_http_useragent(struct Curl_easy *data) - { - /* The User-Agent string might have been allocated in url.c already, because -@@ -3088,6 +3184,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) - http = data->req.p.http; - DEBUGASSERT(http); - -+ /* curl-impersonate: Add HTTP headers to impersonate real browsers. */ -+ result = Curl_http_merge_headers(data); -+ if (result) -+ return result; -+ - result = Curl_http_host(data, conn); - if(result) - return result; -diff --git a/lib/http.h b/lib/http.h -index 9eff6b1ff..912fa3c23 100644 ---- a/lib/http.h -+++ b/lib/http.h -@@ -327,7 +327,8 @@ struct http_conn { - struct h2settings settings; - - /* list of settings that will be sent */ -- nghttp2_settings_entry local_settings[3]; -+ /* curl-impersonate: Align HTTP/2 settings to Chrome's */ -+ nghttp2_settings_entry local_settings[4]; - size_t local_settings_num; - #else - int unused; /* prevent a compiler warning */ -diff --git a/lib/http2.c b/lib/http2.c -index f6364d0e0..740d19535 100644 ---- a/lib/http2.c -+++ b/lib/http2.c -@@ -46,6 +46,7 @@ - #include "curl_printf.h" - #include "curl_memory.h" - #include "memdebug.h" -+#include "rand.h" - - #define H2_BUFSIZE 32768 - -@@ -61,7 +62,7 @@ - #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 - #endif - --#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ -+#define HTTP2_HUGE_WINDOW_SIZE (15 * 1024 * 1024) /* 15 MB */ - - #ifdef DEBUG_HTTP2 - #define H2BUGF(x) x -@@ -79,13 +80,20 @@ static int h2_process_pending_input(struct Curl_easy *data, - struct http_conn *httpc, - CURLcode *err); - -+/* -+ * curl-impersonate: Use Chrome's default HTTP/2 stream weight. -+ */ -+ -+#define CHROME_DEFAULT_STREAM_WEIGHT (256) -+ - /* - * Curl_http2_init_state() is called when the easy handle is created and - * allows for HTTP/2 specific init of state. - */ - void Curl_http2_init_state(struct UrlState *state) - { -- state->stream_weight = NGHTTP2_DEFAULT_WEIGHT; -+ state->stream_weight = CHROME_DEFAULT_STREAM_WEIGHT; -+ state->stream_depends_e = TRUE; - } - - /* -@@ -94,7 +102,8 @@ void Curl_http2_init_state(struct UrlState *state) - */ - void Curl_http2_init_userset(struct UserDefined *set) - { -- set->stream_weight = NGHTTP2_DEFAULT_WEIGHT; -+ set->stream_weight = CHROME_DEFAULT_STREAM_WEIGHT; -+ set->stream_depends_e = TRUE; - } - - static int http2_getsock(struct Curl_easy *data, -@@ -1212,16 +1221,30 @@ static void populate_settings(struct Curl_easy *data, - { - nghttp2_settings_entry *iv = httpc->local_settings; - -- iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; -- iv[0].value = Curl_multi_max_concurrent_streams(data->multi); -+ /* curl-impersonate: Align HTTP/2 settings to Chrome's */ -+ iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; -+ iv[0].value = 0x10000; -+ -+ iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; -+ iv[1].value = Curl_multi_max_concurrent_streams(data->multi); -+ -+ iv[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; -+ iv[2].value = 0x600000; - -- iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; -- iv[1].value = HTTP2_HUGE_WINDOW_SIZE; -+ iv[3].settings_id = NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE; -+ iv[3].value = 0x40000; - -- iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; -- iv[2].value = data->multi->push_cb != NULL; -+ // iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; -+ // iv[2].value = data->multi->push_cb != NULL; -+ -+ // curl-impersonate: -+ // Up until Chrome 98, there was a randomly chosen setting number in the -+ // HTTP2 SETTINGS frame. This might be something similar to TLS GREASE. -+ // However, it seems to have been removed since. -+ // Curl_rand(data, (unsigned char *)&iv[4].settings_id, sizeof(iv[4].settings_id)); -+ // Curl_rand(data, (unsigned char *)&iv[4].value, sizeof(iv[4].value)); - -- httpc->local_settings_num = 3; -+ httpc->local_settings_num = 4; - } - - void Curl_http2_done(struct Curl_easy *data, bool premature) -diff --git a/lib/http2.h b/lib/http2.h -index f0390596c..cf9b7a9d5 100644 ---- a/lib/http2.h -+++ b/lib/http2.h -@@ -31,7 +31,8 @@ - - /* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting - from the peer */ --#define DEFAULT_MAX_CONCURRENT_STREAMS 100 -+/* curl-impersonate: Use 1000 concurrent streams like Chrome. */ -+#define DEFAULT_MAX_CONCURRENT_STREAMS 1000 - - /* - * Store nghttp2 version info in this buffer. -diff --git a/lib/impersonate.c b/lib/impersonate.c -new file mode 100644 -index 000000000..bba3f5788 ---- /dev/null -+++ b/lib/impersonate.c -@@ -0,0 +1,357 @@ -+#include "curl_setup.h" -+ -+#include "impersonate.h" -+ -+const struct impersonate_opts impersonations[] = { -+ { -+ .target = "chrome99", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"99\", \"Google Chrome\";v=\"99\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "chrome100", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"100\", \"Google Chrome\";v=\"100\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "chrome101", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Google Chrome\";v=\"101\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "chrome99_android", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"99\", \"Google Chrome\";v=\"99\"", -+ "sec-ch-ua-mobile: ?1", -+ "sec-ch-ua-platform: \"Android\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.58 Mobile Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "edge99", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"99\", \"Microsoft Edge\";v=\"99\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.30", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "edge101", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Microsoft Edge\";v=\"101\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.47", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ } -+ }, -+ { -+ .target = "safari15_3", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384," -+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," -+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256," -+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384," -+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA," -+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384," -+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256," -+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA," -+ "TLS_RSA_WITH_AES_256_GCM_SHA384," -+ "TLS_RSA_WITH_AES_128_GCM_SHA256," -+ "TLS_RSA_WITH_AES_256_CBC_SHA256," -+ "TLS_RSA_WITH_AES_128_CBC_SHA256," -+ "TLS_RSA_WITH_AES_256_CBC_SHA," -+ "TLS_RSA_WITH_AES_128_CBC_SHA," -+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA," -+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA,", -+ .curves = "X25519:P-256:P-384:P-521", -+ .sig_hash_algs = -+ "ecdsa_secp256r1_sha256," -+ "rsa_pss_rsae_sha256," -+ "rsa_pkcs1_sha256," -+ "ecdsa_secp384r1_sha384," -+ "ecdsa_sha1," -+ "rsa_pss_rsae_sha384," -+ "rsa_pss_rsae_sha384," -+ "rsa_pkcs1_sha384," -+ "rsa_pss_rsae_sha512," -+ "rsa_pkcs1_sha512," -+ "rsa_pkcs1_sha1", -+ .npn = false, -+ .alpn = true, -+ .alps = false, -+ .tls_session_ticket = false, -+ .http_headers = { -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", -+ "Accept-Language: en-us", -+ "Accept-Encoding: gzip, deflate, br" -+ }, -+ .http2_pseudo_headers_order = "mspa" -+ }, -+ { -+ .target = "safari15_5", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384," -+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," -+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256," -+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256," -+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA," -+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA," -+ "TLS_RSA_WITH_AES_256_GCM_SHA384," -+ "TLS_RSA_WITH_AES_128_GCM_SHA256," -+ "TLS_RSA_WITH_AES_256_CBC_SHA," -+ "TLS_RSA_WITH_AES_128_CBC_SHA," -+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA," -+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA," -+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA", -+ .curves = "X25519:P-256:P-384:P-521", -+ .sig_hash_algs = -+ "ecdsa_secp256r1_sha256," -+ "rsa_pss_rsae_sha256," -+ "rsa_pkcs1_sha256," -+ "ecdsa_secp384r1_sha384," -+ "ecdsa_sha1," -+ "rsa_pss_rsae_sha384," -+ "rsa_pss_rsae_sha384," -+ "rsa_pkcs1_sha384," -+ "rsa_pss_rsae_sha512," -+ "rsa_pkcs1_sha512," -+ "rsa_pkcs1_sha1", -+ .npn = false, -+ .alpn = true, -+ .alps = false, -+ .tls_session_ticket = false, -+ .cert_compression = "zlib", -+ .http_headers = { -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", -+ "Accept-Language: en-GB,en-US;q=0.9,en;q=0.8", -+ "Accept-Encoding: gzip, deflate, br" -+ }, -+ .http2_pseudo_headers_order = "mspa" -+ }, -+ { -+ /* Last one must be NULL. */ -+ .target = NULL -+ } -+}; -diff --git a/lib/impersonate.h b/lib/impersonate.h -new file mode 100644 -index 000000000..9546a7833 ---- /dev/null -+++ b/lib/impersonate.h -@@ -0,0 +1,43 @@ -+#ifndef HEADER_CURL_IMPERSONATE_H -+#define HEADER_CURL_IMPERSONATE_H -+ -+#define IMPERSONATE_MAX_HEADERS 32 -+ -+/* -+ * curl-impersonate: Options to be set for each supported target browser. -+ */ -+struct impersonate_opts { -+ const char *target; -+ int httpversion; -+ int ssl_version; -+ const char *ciphers; -+ /* Elliptic curves (TLS extension 10). -+ * Passed to CURLOPT_SSL_EC_CURVES */ -+ const char *curves; -+ /* Signature hash algorithms (TLS extension 13). -+ * Passed to CURLOPT_SSL_SIG_HASH_ALGS */ -+ const char *sig_hash_algs; -+ /* Enable TLS NPN extension. */ -+ bool npn; -+ /* Enable TLS ALPN extension. */ -+ bool alpn; -+ /* Enable TLS ALPS extension. */ -+ bool alps; -+ /* Enable TLS session ticket extension. */ -+ bool tls_session_ticket; -+ /* TLS certificate compression algorithms. -+ * (TLS extension 27) */ -+ const char *cert_compression; -+ const char *http_headers[IMPERSONATE_MAX_HEADERS]; -+ const char *http2_pseudo_headers_order; -+ /* Other TLS options will come here in the future once they are -+ * configurable through curl_easy_setopt() */ -+}; -+ -+/* -+ * curl-impersonate: Global array of supported browsers and their -+ * impersonation options. -+ */ -+extern const struct impersonate_opts impersonations[]; -+ -+#endif /* HEADER_CURL_IMPERSONATE_H */ -diff --git a/lib/multi.c b/lib/multi.c -index e0280447c..dc1fdab68 100644 ---- a/lib/multi.c -+++ b/lib/multi.c -@@ -395,7 +395,8 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ - - /* -1 means it not set by user, use the default value */ - multi->maxconnects = -1; -- multi->max_concurrent_streams = 100; -+ /* curl-impersonate: Use 1000 concurrent streams like Chrome. */ -+ multi->max_concurrent_streams = 1000; - multi->ipv6_works = Curl_ipv6works(NULL); - - #ifdef USE_WINSOCK -diff --git a/lib/setopt.c b/lib/setopt.c -index 6b16e1c7c..75dddebd8 100644 ---- a/lib/setopt.c -+++ b/lib/setopt.c -@@ -50,6 +50,7 @@ - #include "multiif.h" - #include "altsvc.h" - #include "hsts.h" -+#include "slist.h" - - /* The last 3 #include files should be in this order */ - #include "curl_printf.h" -@@ -674,6 +675,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) - va_arg(param, char *)); - break; - -+ case CURLOPT_HTTPBASEHEADER: -+ /* -+ * curl-impersonate: -+ * Set a list of "base" headers. These will be merged with any headers -+ * set by CURLOPT_HTTPHEADER. curl-impersonate uses this option in order -+ * to set a list of default browser headers. -+ * -+ * Unlike CURLOPT_HTTPHEADER, -+ * the list is copied and can be immediately freed by the user. -+ */ -+ curl_slist_free_all(data->state.base_headers); -+ data->state.base_headers = \ -+ Curl_slist_duplicate(va_arg(param, struct curl_slist *)); -+ if (!data->state.base_headers) -+ result = CURLE_OUT_OF_MEMORY; -+ break; -+ - case CURLOPT_HTTPHEADER: - /* - * Set a list with HTTP headers to use (or replace internals with) -@@ -2318,6 +2336,27 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) - result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], - va_arg(param, char *)); - break; -+ -+ case CURLOPT_SSL_SIG_HASH_ALGS: -+ /* -+ * Set the list of hash algorithms we want to use in the SSL connection. -+ * Specify comma-delimited list of algorithms to use. -+ */ -+ result = Curl_setstropt(&data->set.str[STRING_SSL_SIG_HASH_ALGS], -+ va_arg(param, char *)); -+ break; -+ -+ case CURLOPT_SSL_CERT_COMPRESSION: -+ /* -+ * Set the list of ceritifcate compression algorithms we support in the TLS -+ * connection. -+ * Specify comma-delimited list of algorithms to use. Options are "zlib" -+ * and "brotli". -+ */ -+ result = Curl_setstropt(&data->set.str[STRING_SSL_CERT_COMPRESSION], -+ va_arg(param, char *)); -+ break; -+ - #endif - case CURLOPT_IPRESOLVE: - arg = va_arg(param, long); -@@ -2861,6 +2900,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) - case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -+ case CURLOPT_SSL_ENABLE_ALPS: -+ data->set.ssl_enable_alps = (0 != va_arg(param, long)) ? TRUE : FALSE; -+ break; -+ case CURLOPT_SSL_ENABLE_TICKET: -+ data->set.ssl_enable_ticket = (0 != va_arg(param, long)) ? TRUE : FALSE; -+ break; -+ case CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER: -+ result = Curl_setstropt(&data->set.str[STRING_HTTP2_PSEUDO_HEADERS_ORDER], -+ va_arg(param, char *)); -+ break; - #ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - data->set.abstract_unix_socket = FALSE; -diff --git a/lib/transfer.c b/lib/transfer.c -index 1720b24b1..dcae3c143 100644 ---- a/lib/transfer.c -+++ b/lib/transfer.c -@@ -104,7 +104,15 @@ char *Curl_checkheaders(const struct Curl_easy *data, - DEBUGASSERT(thislen); - DEBUGASSERT(thisheader[thislen-1] != ':'); - -- for(head = data->set.headers; head; head = head->next) { -+ /* -+ * curl-impersonate: -+ * Check if we have overriden the user-supplied list of headers. -+ */ -+ head = data->set.headers; -+ if (data->state.merged_headers) -+ head = data->state.merged_headers; -+ -+ for(; head; head = head->next) { - if(strncasecompare(head->data, thisheader, thislen) && - Curl_headersep(head->data[thislen]) ) - return head->data; -diff --git a/lib/url.c b/lib/url.c -index 1114c6c12..b16628e96 100644 ---- a/lib/url.c -+++ b/lib/url.c -@@ -465,6 +465,11 @@ CURLcode Curl_close(struct Curl_easy **datap) - Curl_safefree(data->state.aptr.proxyuser); - Curl_safefree(data->state.aptr.proxypasswd); - -+ /* curl-impersonate: Free the list set by CURLOPT_HTTPBASEHEADER. */ -+ curl_slist_free_all(data->state.base_headers); -+ /* curl-impersonate: Free the dynamic list of headers. */ -+ curl_slist_free_all(data->state.merged_headers); -+ - #ifndef CURL_DISABLE_DOH - if(data->req.doh) { - Curl_dyn_free(&data->req.doh->probe[0].serverdoh); -@@ -620,6 +625,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) - set->tcp_nodelay = TRUE; - set->ssl_enable_npn = TRUE; - set->ssl_enable_alpn = TRUE; -+ set->ssl_enable_ticket = TRUE; - set->expect_100_timeout = 1000L; /* Wait for a second by default. */ - set->sep_headers = TRUE; /* separated header lists by default */ - set->buffer_size = READBUFFER_SIZE; -@@ -3883,6 +3889,9 @@ static CURLcode create_conn(struct Curl_easy *data, - data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; - data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; -+ data->set.ssl.primary.sig_hash_algs = data->set.str[STRING_SSL_SIG_HASH_ALGS]; -+ data->set.ssl.primary.cert_compression = -+ data->set.str[STRING_SSL_CERT_COMPRESSION]; - - #ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; -@@ -3996,8 +4005,17 @@ static CURLcode create_conn(struct Curl_easy *data, - conn->bits.tls_enable_alpn = TRUE; - if(data->set.ssl_enable_npn) - conn->bits.tls_enable_npn = TRUE; -+ -+ /* curl-impersonate: Turn on ALPS if ALPN is enabled and the bit is -+ * enabled. */ -+ if(data->set.ssl_enable_alps) -+ conn->bits.tls_enable_alps = TRUE; - } - -+ /* curl-impersonate: Add the TLS session ticket extension. */ -+ if(data->set.ssl_enable_ticket) -+ conn->bits.tls_enable_ticket = TRUE; -+ - if(waitpipe) - /* There is a connection that *might* become usable for multiplexing - "soon", and we wait for that */ -diff --git a/lib/urldata.h b/lib/urldata.h -index bcb4d460c..b98d5977d 100644 ---- a/lib/urldata.h -+++ b/lib/urldata.h -@@ -254,6 +254,8 @@ struct ssl_primary_config { - enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ - #endif - char *curves; /* list of curves to use */ -+ char *sig_hash_algs; /* List of signature hash algorithms to use */ -+ char *cert_compression; /* List of certificate compression algorithms. */ - unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - BIT(verifypeer); /* set TRUE if this is desired */ - BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ -@@ -509,6 +511,8 @@ struct ConnectBits { - BIT(tcp_fastopen); /* use TCP Fast Open */ - BIT(tls_enable_npn); /* TLS NPN extension? */ - BIT(tls_enable_alpn); /* TLS ALPN extension? */ -+ BIT(tls_enable_alps); /* TLS ALPS extension? */ -+ BIT(tls_enable_ticket); /* TLS session ticket extension? */ - BIT(connect_only); - #ifndef CURL_DISABLE_DOH - BIT(doh); -@@ -1453,6 +1457,19 @@ struct UrlState { - CURLcode hresult; /* used to pass return codes back from hyper callbacks */ - #endif - -+ /* -+ * curl-impersonate: -+ * List of "base" headers set by CURLOPT_HTTPBASEHEADER. -+ */ -+ struct curl_slist *base_headers; -+ /* -+ * curl-impersonate: -+ * Dynamically-constructed list of HTTP headers. -+ * This list is a merge of the default HTTP headers needed to impersonate a -+ * browser, together with any user-supplied headers. -+ */ -+ struct curl_slist *merged_headers; -+ - /* Dynamically allocated strings, MUST be freed before this struct is - killed. */ - struct dynamically_allocated_data { -@@ -1608,6 +1625,9 @@ enum dupstring { - STRING_DNS_LOCAL_IP4, - STRING_DNS_LOCAL_IP6, - STRING_SSL_EC_CURVES, -+ STRING_SSL_SIG_HASH_ALGS, -+ STRING_SSL_CERT_COMPRESSION, -+ STRING_HTTP2_PSEUDO_HEADERS_ORDER, - - /* -- end of null-terminated strings -- */ - -@@ -1893,6 +1913,8 @@ struct UserDefined { - BIT(tcp_fastopen); /* use TCP Fast Open */ - BIT(ssl_enable_npn); /* TLS NPN extension? */ - BIT(ssl_enable_alpn);/* TLS ALPN extension? */ -+ BIT(ssl_enable_alps);/* TLS ALPS extension? */ -+ BIT(ssl_enable_ticket); /* TLS session ticket extension */ - BIT(path_as_is); /* allow dotdots? */ - BIT(pipewait); /* wait for multiplex status before starting a new - connection */ -diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index 78aacd022..a29ca8055 100644 ---- a/lib/vtls/openssl.c -+++ b/lib/vtls/openssl.c -@@ -78,6 +78,13 @@ - #include - #include - -+#ifdef HAVE_ZLIB_H -+#include -+#endif -+#ifdef HAVE_BROTLI -+#include -+#endif -+ - #ifdef USE_AMISSL - #include "amigaos.h" - #endif -@@ -262,6 +269,113 @@ - #define HAVE_OPENSSL_VERSION - #endif - -+#if defined(OPENSSL_IS_BORINGSSL) -+#define HAVE_SSL_CTX_SET_VERIFY_ALGORITHM_PREFS -+ -+/* -+ * kMaxSignatureAlgorithmNameLen and kSignatureAlgorithmNames -+ * Taken from BoringSSL, see ssl/ssl_privkey.cc -+ * */ -+static const size_t kMaxSignatureAlgorithmNameLen = 23; -+ -+static const struct { -+ uint16_t signature_algorithm; -+ const char *name; -+} kSignatureAlgorithmNames[] = { -+ {SSL_SIGN_RSA_PKCS1_MD5_SHA1, "rsa_pkcs1_md5_sha1"}, -+ {SSL_SIGN_RSA_PKCS1_SHA1, "rsa_pkcs1_sha1"}, -+ {SSL_SIGN_RSA_PKCS1_SHA256, "rsa_pkcs1_sha256"}, -+ {SSL_SIGN_RSA_PKCS1_SHA384, "rsa_pkcs1_sha384"}, -+ {SSL_SIGN_RSA_PKCS1_SHA512, "rsa_pkcs1_sha512"}, -+ {SSL_SIGN_ECDSA_SHA1, "ecdsa_sha1"}, -+ {SSL_SIGN_ECDSA_SECP256R1_SHA256, "ecdsa_secp256r1_sha256"}, -+ {SSL_SIGN_ECDSA_SECP384R1_SHA384, "ecdsa_secp384r1_sha384"}, -+ {SSL_SIGN_ECDSA_SECP521R1_SHA512, "ecdsa_secp521r1_sha512"}, -+ {SSL_SIGN_RSA_PSS_RSAE_SHA256, "rsa_pss_rsae_sha256"}, -+ {SSL_SIGN_RSA_PSS_RSAE_SHA384, "rsa_pss_rsae_sha384"}, -+ {SSL_SIGN_RSA_PSS_RSAE_SHA512, "rsa_pss_rsae_sha512"}, -+ {SSL_SIGN_ED25519, "ed25519"}, -+}; -+ -+#define MAX_SIG_ALGS \ -+ sizeof(kSignatureAlgorithmNames) / sizeof(kSignatureAlgorithmNames[0]) -+ -+/* Default signature hash algorithms taken from Chrome/Chromium. -+ * See kVerifyPeers @ net/socket/ssl_client_socket_impl.cc */ -+static const uint16_t default_sig_algs[] = { -+ SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256, -+ SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ECDSA_SECP384R1_SHA384, -+ SSL_SIGN_RSA_PSS_RSAE_SHA384, SSL_SIGN_RSA_PKCS1_SHA384, -+ SSL_SIGN_RSA_PSS_RSAE_SHA512, SSL_SIGN_RSA_PKCS1_SHA512, -+}; -+ -+#define DEFAULT_SIG_ALGS_LENGTH \ -+ sizeof(default_sig_algs) / sizeof(default_sig_algs[0]) -+ -+static CURLcode parse_sig_algs(struct Curl_easy *data, -+ const char *sigalgs, -+ uint16_t *algs, -+ size_t *nalgs) -+{ -+ *nalgs = 0; -+ while (sigalgs && sigalgs[0]) { -+ int i; -+ bool found = FALSE; -+ const char *end; -+ size_t len; -+ char algname[kMaxSignatureAlgorithmNameLen + 1]; -+ -+ end = strpbrk(sigalgs, ":,"); -+ if (end) -+ len = end - sigalgs; -+ else -+ len = strlen(sigalgs); -+ -+ if (len > kMaxSignatureAlgorithmNameLen) { -+ failf(data, "Bad signature hash algorithm list"); -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ } -+ -+ if (!len) { -+ ++sigalgs; -+ continue; -+ } -+ -+ if (*nalgs == MAX_SIG_ALGS) { -+ /* Reached the maximum number of possible algorithms, but more data -+ * available in the list. */ -+ failf(data, "Bad signature hash algorithm list"); -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ } -+ -+ memcpy(algname, sigalgs, len); -+ algname[len] = 0; -+ -+ for (i = 0; i < MAX_SIG_ALGS; i++) { -+ if (strcasecompare(algname, kSignatureAlgorithmNames[i].name)) { -+ algs[*nalgs] = kSignatureAlgorithmNames[i].signature_algorithm; -+ (*nalgs)++; -+ found = TRUE; -+ break; -+ } -+ } -+ -+ if (!found) { -+ failf(data, "Unknown signature hash algorithm: '%s'", algname); -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ } -+ -+ if (end) -+ sigalgs = ++end; -+ else -+ break; -+ } -+ -+ return CURLE_OK; -+} -+ -+#endif -+ - struct ssl_backend_data { - struct Curl_easy *logger; /* transfer handle to pass trace logs to, only - using sockindex 0 */ -@@ -2623,6 +2737,151 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, - return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); - } - -+#ifdef HAVE_LIBZ -+int DecompressZlibCert(SSL *ssl, -+ CRYPTO_BUFFER** out, -+ size_t uncompressed_len, -+ const uint8_t* in, -+ size_t in_len) -+{ -+ z_stream strm; -+ uint8_t* data; -+ CRYPTO_BUFFER* decompressed = CRYPTO_BUFFER_alloc(&data, uncompressed_len); -+ if(!decompressed) { -+ return 0; -+ } -+ -+ strm.zalloc = NULL; -+ strm.zfree = NULL; -+ strm.opaque = NULL; -+ strm.next_in = (Bytef *)in; -+ strm.avail_in = in_len; -+ strm.next_out = (Bytef *)data; -+ strm.avail_out = uncompressed_len; -+ -+ if(inflateInit(&strm) != Z_OK) { -+ CRYPTO_BUFFER_free(decompressed); -+ return 0; -+ } -+ -+ if(inflate(&strm, Z_FINISH) != Z_STREAM_END || -+ strm.avail_in != 0 || -+ strm.avail_out != 0) { -+ inflateEnd(&strm); -+ CRYPTO_BUFFER_free(decompressed); -+ return 0; -+ } -+ -+ inflateEnd(&strm); -+ *out = decompressed; -+ return 1; -+} -+#endif -+ -+#ifdef HAVE_BROTLI -+ -+/* Taken from Chromium and adapted to C, -+ * see net/ssl/cert_compression.cc -+ */ -+int DecompressBrotliCert(SSL* ssl, -+ CRYPTO_BUFFER** out, -+ size_t uncompressed_len, -+ const uint8_t* in, -+ size_t in_len) { -+ uint8_t* data; -+ CRYPTO_BUFFER* decompressed = CRYPTO_BUFFER_alloc(&data, uncompressed_len); -+ if (!decompressed) { -+ return 0; -+ } -+ -+ size_t output_size = uncompressed_len; -+ if (BrotliDecoderDecompress(in_len, in, &output_size, data) != -+ BROTLI_DECODER_RESULT_SUCCESS || -+ output_size != uncompressed_len) { -+ CRYPTO_BUFFER_free(decompressed); -+ return 0; -+ } -+ -+ *out = decompressed; -+ return 1; -+} -+#endif -+ -+#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) -+static struct { -+ char *alg_name; -+ uint16_t alg_id; -+ ssl_cert_compression_func_t compress; -+ ssl_cert_decompression_func_t decompress; -+} cert_compress_algs[] = { -+#ifdef HAVE_LIBZ -+ {"zlib", TLSEXT_cert_compression_zlib, NULL, DecompressZlibCert}, -+#endif -+#ifdef HAVE_BROTLI -+ {"brotli", TLSEXT_cert_compression_brotli, NULL, DecompressBrotliCert}, -+#endif -+}; -+ -+#define NUM_CERT_COMPRESSION_ALGS \ -+ sizeof(cert_compress_algs) / sizeof(cert_compress_algs[0]) -+ -+/* -+ * curl-impersonate: -+ * Add support for TLS extension 27 - compress_certificate. -+ * This calls the BoringSSL-specific API SSL_CTX_add_cert_compression_alg -+ * for each algorithm specified in cert_compression, which is a comma separated list. -+ */ -+static CURLcode add_cert_compression(struct Curl_easy *data, -+ SSL_CTX *ctx, -+ const char *algorithms) -+{ -+ int i; -+ const char *s = algorithms; -+ char *alg_name; -+ size_t alg_name_len; -+ bool found; -+ -+ while (s && s[0]) { -+ found = FALSE; -+ -+ for(i = 0; i < NUM_CERT_COMPRESSION_ALGS; i++) { -+ alg_name = cert_compress_algs[i].alg_name; -+ alg_name_len = strlen(alg_name); -+ if(strlen(s) >= alg_name_len && -+ strncasecompare(s, alg_name, alg_name_len) && -+ (s[alg_name_len] == ',' || s[alg_name_len] == 0)) { -+ if(!SSL_CTX_add_cert_compression_alg(ctx, -+ cert_compress_algs[i].alg_id, -+ cert_compress_algs[i].compress, -+ cert_compress_algs[i].decompress)) { -+ failf(data, "Error adding certificate compression algorithm '%s'", -+ alg_name); -+ return CURLE_SSL_CIPHER; -+ } -+ s += alg_name_len; -+ if(*s == ',') -+ s += 1; -+ found = TRUE; -+ break; -+ } -+ } -+ -+ if(!found) { -+ failf(data, "Invalid compression algorithm list"); -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+ } -+ } -+ -+ return CURLE_OK; -+} -+#else -+static CURLcode add_cert_compression(SSL_CTX *ctx, const char *algorithms) -+{ -+ /* No compression algorithms are available. */ -+ return CURLE_BAD_FUNCTION_ARGUMENT; -+} -+#endif -+ - static CURLcode ossl_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) - { -@@ -2762,7 +3021,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, - ctx_options = SSL_OP_ALL; - - #ifdef SSL_OP_NO_TICKET -- ctx_options |= SSL_OP_NO_TICKET; -+ if(conn->bits.tls_enable_ticket) { -+ /* curl-impersonate: -+ * Turn off SSL_OP_NO_TICKET, we want TLS extension 35 (session_ticket) -+ * to be present in the client hello. */ -+ ctx_options &= ~SSL_OP_NO_TICKET; -+ } else { -+ ctx_options |= SSL_OP_NO_TICKET; -+ } - #endif - - #ifdef SSL_OP_NO_COMPRESSION -@@ -2907,6 +3173,35 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, - } - #endif - -+#ifdef HAVE_SSL_CTX_SET_VERIFY_ALGORITHM_PREFS -+ { -+ uint16_t algs[MAX_SIG_ALGS]; -+ size_t nalgs; -+ /* curl-impersonate: Set the signature algorithms (TLS extension 13). -+ * See net/socket/ssl_client_socket_impl.cc in Chromium's source. */ -+ char *sig_hash_algs = SSL_CONN_CONFIG(sig_hash_algs); -+ if (sig_hash_algs) { -+ CURLcode result = parse_sig_algs(data, sig_hash_algs, algs, &nalgs); -+ if (result) -+ return result; -+ if (!SSL_CTX_set_verify_algorithm_prefs(backend->ctx, algs, nalgs)) { -+ failf(data, "failed setting signature hash algorithms list: '%s'", -+ sig_hash_algs); -+ return CURLE_SSL_CIPHER; -+ } -+ } else { -+ /* Use defaults from Chrome. */ -+ if (!SSL_CTX_set_verify_algorithm_prefs(backend->ctx, -+ default_sig_algs, -+ DEFAULT_SIG_ALGS_LENGTH)) { -+ failf(data, "failed setting signature hash algorithms list: '%s'", -+ sig_hash_algs); -+ return CURLE_SSL_CIPHER; -+ } -+ } -+ } -+#endif -+ - #ifdef USE_OPENSSL_SRP - if((ssl_authtype == CURL_TLSAUTH_SRP) && - Curl_allow_auth_to_host(data)) { -@@ -2933,6 +3228,20 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, - } - #endif - -+ /* curl-impersonate: -+ * Configure BoringSSL to behave like Chrome. -+ * See Constructor of SSLContext at net/socket/ssl_client_socket_impl.cc -+ * and SSLClientSocketImpl::Init() -+ * in the Chromium's source code. */ -+ -+ /* Enable TLS GREASE. */ -+ SSL_CTX_set_grease_enabled(backend->ctx, 1); -+ -+ if(SSL_CONN_CONFIG(cert_compression) && -+ add_cert_compression(data, -+ backend->ctx, -+ SSL_CONN_CONFIG(cert_compression))) -+ return CURLE_SSL_CIPHER; - - #if defined(USE_WIN32_CRYPTO) - /* Import certificates from the Windows root certificate store if requested. -@@ -3232,6 +3541,33 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, - - SSL_set_connect_state(backend->handle); - -+#if defined(HAS_ALPN) && defined(USE_HTTP2) -+ if(conn->bits.tls_enable_alpn && -+ data->state.httpwant >= CURL_HTTP_VERSION_2 && -+ conn->bits.tls_enable_alps) { -+ /* curl-impersonate: This adds the ALPS extension (17513). -+ * Chromium calls this function as well in SSLClientSocketImpl::Init(). -+ * The 4th parameter is called "settings", and I don't know what it -+ * should contain. For now, use an empty string. */ -+ SSL_add_application_settings(backend->handle, ALPN_H2, ALPN_H2_LENGTH, -+ NULL, 0); -+ infof(data, "ALPS, offering %s", ALPN_H2); -+ } -+#endif -+ -+ SSL_set_options(backend->handle, -+ SSL_OP_LEGACY_SERVER_CONNECT); -+ SSL_set_mode(backend->handle, -+ SSL_MODE_CBC_RECORD_SPLITTING | SSL_MODE_ENABLE_FALSE_START); -+ -+ /* curl-impersonate: Enable TLS extensions 5 - status_request and -+ * 18 - signed_certificate_timestamp. */ -+ SSL_enable_signed_cert_timestamps(backend->handle); -+ SSL_enable_ocsp_stapling(backend->handle); -+ -+ /* curl-impersonate: Some SSL settings copied over from Chrome. */ -+ SSL_set_shed_handshake_config(backend->handle, 1); -+ - backend->server_cert = 0x0; - #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c -index faa1b5141..dbb170d72 100644 ---- a/lib/vtls/vtls.c -+++ b/lib/vtls/vtls.c -@@ -153,6 +153,9 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, - Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && - Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && - Curl_safe_strcasecompare(data->curves, needle->curves) && -+ Curl_safe_strcasecompare(data->sig_hash_algs, needle->sig_hash_algs) && -+ Curl_safe_strcasecompare(data->cert_compression, -+ needle->cert_compression) && - Curl_safe_strcasecompare(data->CRLfile, needle->CRLfile) && - Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) - return TRUE; -@@ -186,6 +189,8 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - CLONE_STRING(cipher_list13); - CLONE_STRING(pinned_key); - CLONE_STRING(curves); -+ CLONE_STRING(sig_hash_algs); -+ CLONE_STRING(cert_compression); - CLONE_STRING(CRLfile); - #ifdef USE_TLS_SRP - CLONE_STRING(username); -@@ -208,6 +213,8 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) - Curl_safefree(sslc->ca_info_blob); - Curl_safefree(sslc->issuercert_blob); - Curl_safefree(sslc->curves); -+ Curl_safefree(sslc->sig_hash_algs); -+ Curl_safefree(sslc->cert_compression); - Curl_safefree(sslc->CRLfile); - #ifdef USE_TLS_SRP - Curl_safefree(sslc->username); -diff --git a/libcurl.pc.in b/libcurl.pc.in -index 49485f192..7f6590b36 100644 ---- a/libcurl.pc.in -+++ b/libcurl.pc.in -@@ -36,6 +36,6 @@ Name: libcurl - URL: https://curl.se/ - Description: Library to transfer files with ftp, http, etc. - Version: @CURLVERSION@ --Libs: -L${libdir} -lcurl @LIBCURL_NO_SHARED@ -+Libs: -L${libdir} -lcurl-impersonate-chrome @LIBCURL_NO_SHARED@ - Libs.private: @LIBCURL_LIBS@ - Cflags: -I${includedir} @CPPFLAG_CURL_STATICLIB@ -diff --git a/src/Makefile.am b/src/Makefile.am -index 706f0aac3..7124bf13e 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -43,7 +43,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/src - --bin_PROGRAMS = curl -+bin_PROGRAMS = curl-impersonate-chrome - - SUBDIRS = ../docs - -@@ -54,7 +54,7 @@ endif - include Makefile.inc - - # CURL_FILES comes from Makefile.inc --curl_SOURCES = $(CURL_FILES) -+curl_impersonate_chrome_SOURCES = $(CURL_FILES) - - # This might hold -Werror - CFLAGS += @CURL_CFLAG_EXTRAS@ -@@ -63,9 +63,9 @@ CFLAGS += @CURL_CFLAG_EXTRAS@ - LIBS = $(BLANK_AT_MAKETIME) - - if USE_EXPLICIT_LIB_DEPS --curl_LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_LIBS@ -+curl_impersonate_chrome_LDADD = $(top_builddir)/lib/libcurl-impersonate-chrome.la @LIBCURL_LIBS@ - else --curl_LDADD = $(top_builddir)/lib/libcurl.la @NSS_LIBS@ @SSL_LIBS@ @ZLIB_LIBS@ @CURL_NETWORK_AND_TIME_LIBS@ -+curl_impersonate_chrome_LDADD = $(top_builddir)/lib/libcurl-impersonate-chrome.la @NSS_LIBS@ @SSL_LIBS@ @ZLIB_LIBS@ @CURL_NETWORK_AND_TIME_LIBS@ - endif - - # if unit tests are enabled, build a static library to link them with -diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h -index 7e43fe754..823f72414 100644 ---- a/src/tool_cfgable.h -+++ b/src/tool_cfgable.h -@@ -165,8 +165,11 @@ struct OperationConfig { - bool crlf; - char *customrequest; - char *ssl_ec_curves; -+ char *ssl_sig_hash_algs; -+ char *ssl_cert_compression; - char *krblevel; - char *request_target; -+ char *http2_pseudo_headers_order; - long httpversion; - bool http09_allowed; - bool nobuffer; -@@ -275,6 +278,8 @@ struct OperationConfig { - char *oauth_bearer; /* OAuth 2.0 bearer token */ - bool nonpn; /* enable/disable TLS NPN extension */ - bool noalpn; /* enable/disable TLS ALPN extension */ -+ bool alps; /* enable/disable TLS ALPS extension */ -+ bool noticket; /* enable/disable TLS session ticket */ - char *unix_socket_path; /* path to Unix domain socket */ - bool abstract_unix_socket; /* path to an abstract Unix domain socket */ - bool falsestart; -diff --git a/src/tool_getparam.c b/src/tool_getparam.c -index 27e801a98..047f1c6c8 100644 ---- a/src/tool_getparam.c -+++ b/src/tool_getparam.c -@@ -282,6 +282,11 @@ static const struct LongShort aliases[]= { - {"EC", "etag-save", ARG_FILENAME}, - {"ED", "etag-compare", ARG_FILENAME}, - {"EE", "curves", ARG_STRING}, -+ {"EG", "signature-hashes", ARG_STRING}, -+ {"EH", "alps", ARG_BOOL}, -+ {"EI", "cert-compression", ARG_STRING}, -+ {"EJ", "tls-session-ticket", ARG_BOOL}, -+ {"EK", "http2-pseudo-headers-order", ARG_STRING}, - {"f", "fail", ARG_BOOL}, - {"fa", "fail-early", ARG_BOOL}, - {"fb", "styled-output", ARG_BOOL}, -@@ -1859,6 +1864,31 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ - GetStr(&config->ssl_ec_curves, nextarg); - break; - -+ case 'G': -+ /* --signature-hashes */ -+ GetStr(&config->ssl_sig_hash_algs, nextarg); -+ break; -+ -+ case 'H': -+ /* --alps */ -+ config->alps = toggle; -+ break; -+ -+ case 'I': -+ /* --cert-compression */ -+ GetStr(&config->ssl_cert_compression, nextarg); -+ break; -+ -+ case 'J': -+ /* --tls-session-ticket */ -+ config->noticket = (!toggle)?TRUE:FALSE; -+ break; -+ -+ case 'K': -+ /* --http2-pseudo-headers-order */ -+ GetStr(&config->http2_pseudo_headers_order, nextarg); -+ break; -+ - default: /* unknown flag */ - return PARAM_OPTION_UNKNOWN; - } -diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c -index 266f9b0bd..721a2c5f0 100644 ---- a/src/tool_listhelp.c -+++ b/src/tool_listhelp.c -@@ -108,6 +108,18 @@ const struct helptxt helptext[] = { - {" --curves ", - "(EC) TLS key exchange algorithm(s) to request", - CURLHELP_TLS}, -+ {" --signature-hashes ", -+ "TLS signature hash algorithm(s) to use", -+ CURLHELP_TLS}, -+ {" --cert-compression ", -+ "TLS cert compressions algorithm(s) to use", -+ CURLHELP_TLS}, -+ {" --no-tls-session-ticket", -+ "Disable the TLS session ticket extension", -+ CURLHELP_TLS}, -+ {" --http2-pseudo-headers-order", -+ "Change the order of the HTTP2 pseudo headers", -+ CURLHELP_HTTP}, - {"-d, --data ", - "HTTP POST data", - CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, -@@ -384,6 +396,9 @@ const struct helptxt helptext[] = { - {" --no-alpn", - "Disable the ALPN TLS extension", - CURLHELP_TLS | CURLHELP_HTTP}, -+ {" --alps", -+ "Enable the ALPS TLS extension", -+ CURLHELP_TLS | CURLHELP_HTTP}, - {"-N, --no-buffer", - "Disable buffering of the output stream", - CURLHELP_CURL}, -diff --git a/src/tool_operate.c b/src/tool_operate.c -index c317b3ba7..e325479a0 100644 ---- a/src/tool_operate.c -+++ b/src/tool_operate.c -@@ -1433,6 +1433,11 @@ static CURLcode single_transfer(struct GlobalConfig *global, - return result; - } - -+ if(config->http2_pseudo_headers_order) -+ my_setopt_str(curl, -+ CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, -+ config->http2_pseudo_headers_order); -+ - } /* (built_in_protos & CURLPROTO_HTTP) */ - - my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); -@@ -1520,6 +1525,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, - if(config->ssl_ec_curves) - my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); - -+ if(config->ssl_sig_hash_algs) -+ my_setopt_str(curl, CURLOPT_SSL_SIG_HASH_ALGS, -+ config->ssl_sig_hash_algs); -+ -+ if(config->ssl_cert_compression) -+ my_setopt_str(curl, CURLOPT_SSL_CERT_COMPRESSION, -+ config->ssl_cert_compression); -+ - if(curlinfo->features & CURL_VERSION_SSL) { - /* Check if config->cert is a PKCS#11 URI and set the - * config->cert_type if necessary */ -@@ -2057,6 +2070,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, - my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); - } - -+ if(config->alps) { -+ my_setopt(curl, CURLOPT_SSL_ENABLE_ALPS, 1L); -+ } -+ -+ if (config->noticket) { -+ my_setopt(curl, CURLOPT_SSL_ENABLE_TICKET, 0L); -+ } -+ - /* new in 7.40.0, abstract support added in 7.53.0 */ - if(config->unix_socket_path) { - if(config->abstract_unix_socket) { -diff --git a/src/tool_setopt.c b/src/tool_setopt.c -index 5ff86c7f5..e7b093d2d 100644 ---- a/src/tool_setopt.c -+++ b/src/tool_setopt.c -@@ -180,6 +180,7 @@ static const struct NameValue setopt_nv_CURLNONZERODEFAULTS[] = { - NV1(CURLOPT_SSL_VERIFYHOST, 1), - NV1(CURLOPT_SSL_ENABLE_NPN, 1), - NV1(CURLOPT_SSL_ENABLE_ALPN, 1), -+ NV1(CURLOPT_SSL_ENABLE_TICKET, 1), - NV1(CURLOPT_TCP_NODELAY, 1), - NV1(CURLOPT_PROXY_SSL_VERIFYPEER, 1), - NV1(CURLOPT_PROXY_SSL_VERIFYHOST, 1),