From a8c85e8f00525975c5e60b54e15c6cfc0a2a87a0 Mon Sep 17 00:00:00 2001 From: Yifei Date: Tue, 16 Apr 2024 19:03:36 +0800 Subject: [PATCH] Upgrade curl to 8.5.0 and more options (#20) --- Dockerfile.template | 2 +- Makefile.in | 53 +- README.md | 20 +- chrome/Dockerfile | 2 +- chrome/Dockerfile.alpine | 2 +- chrome/curl_chrome100 | 1 + chrome/curl_chrome101 | 1 + chrome/curl_chrome104 | 1 + chrome/curl_chrome107 | 1 + chrome/curl_chrome110 | 1 + chrome/curl_chrome116 | 1 + chrome/curl_chrome119 | 1 + chrome/curl_chrome120 | 1 + chrome/curl_chrome99 | 1 + chrome/curl_chrome99_android | 1 + chrome/curl_edge101 | 1 + chrome/curl_edge99 | 1 + chrome/curl_firefox120 | 28 + chrome/curl_safari15_3 | 1 + chrome/curl_safari15_5 | 1 + chrome/curl_safari17_0 | 1 + chrome/curl_safari17_2_ios | 1 + chrome/patches/boringssl-old-ciphers.patch | 306 ++++- chrome/patches/curl-impersonate.patch | 1239 +++++++++++++------- firefox/Dockerfile | 2 +- firefox/Dockerfile.alpine | 2 +- tests/README.md | 4 +- tests/targets.yaml | 58 +- win/build.sh | 2 +- 29 files changed, 1196 insertions(+), 540 deletions(-) create mode 100755 chrome/curl_firefox120 diff --git a/Dockerfile.template b/Dockerfile.template index 899b1203..4229df71 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -102,7 +102,7 @@ RUN cd ${NGHTTP2_VERSION} && \ make && make install # Download curl. -ARG CURL_VERSION=curl-8.1.1 +ARG CURL_VERSION=curl-8.5.0 RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz RUN tar xf ${CURL_VERSION}.tar.xz diff --git a/Makefile.in b/Makefile.in index 8efcbd93..d5b136cb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -17,7 +17,7 @@ NSS_URL := https://ftp.mozilla.org/pub/security/nss/releases/NSS_3_92_RTM/src/ns BORING_SSL_COMMIT := d24a38200fef19150eef00cad35b138936c08767 NGHTTP2_VERSION := nghttp2-1.56.0 NGHTTP2_URL := https://github.com/nghttp2/nghttp2/releases/download/v1.56.0/nghttp2-1.56.0.tar.bz2 -CURL_VERSION := curl-8.1.1 +CURL_VERSION := curl-8.5.0 brotli_install_dir := $(abspath brotli-$(BROTLI_VERSION)/out/installed) brotli_static_libs := $(brotli_install_dir)/lib/libbrotlicommon-static.a $(brotli_install_dir)/lib/libbrotlidec-static.a @@ -65,55 +65,6 @@ help: ## Show this help message .PHONY: help .DEFAULT_GOAL := help -firefox-build: $(CURL_VERSION)/.firefox ## Build the Firefox version of curl-impersonate - cd $(CURL_VERSION) - # Don't pass this Makefile's MAKEFLAGS - $(MAKE) MAKEFLAGS=-j$(SUBJOBS) -.PHONY: firefox-build - -firefox-checkbuild: ## Run basic checks on the built binary -ifeq ($(host),$(build)) - cd $(CURL_VERSION) - # Make sure all needed features were compiled in - ./src/curl-impersonate-ff -V | grep -q zlib - ./src/curl-impersonate-ff -V | grep -q brotli - ./src/curl-impersonate-ff -V | grep -q nghttp2 - ./src/curl-impersonate-ff -V | grep -q NSS - $(info Build OK) -else - $(info Cross compiling, skipping checkbuild) -endif -.PHONY: firefox-checkbuild - -firefox-install: ## Install the Firefox version of curl-impersonate after build - cd $(CURL_VERSION) - $(MAKE) install-exec MAKEFLAGS= - # Wrapper scripts for the Firefox version (e.g. 'curl_ff98') - install $(srcdir)/firefox/curl_ff* @bindir@ -.PHONY: firefox-install - -firefox-install-strip: ## Like 'firefox-install', but strip binaries for smaller size - cd $(CURL_VERSION) - $(MAKE) install-exec MAKEFLAGS= - # We could have used 'install-strip' but then the docs would be installed as well. - # Instead strip manually. - $(STRIP) @bindir@/curl-impersonate-ff - # Wrapper scripts for the Firefox version (e.g. 'curl_ff98') - install $(srcdir)/firefox/curl_ff* @bindir@ -.PHONY: firefox-install-strip - -firefox-uninstall: ## Uninstall the Firefox version of curl-impersonate after 'make install' - cd $(CURL_VERSION) - $(MAKE) uninstall MAKEFLAGS= - rm -Rf @bindir@/curl_ff* -.PHONY: firefox-uninstall - -firefox-clean: ## Clean build artifacts of the Firefox version. Use after re-running './configure' - cd $(CURL_VERSION) - $(MAKE) clean MAKEFLAGS= - rm -f .firefox -.PHONY: firefox-clean - chrome-build: $(CURL_VERSION)/.chrome ## Build the Chrome version of curl-impersonate cd $(CURL_VERSION) # Don't pass this Makefile's MAKEFLAGS @@ -138,7 +89,7 @@ chrome-install: ## Install the Chrome version of curl-impersonate after build cd $(CURL_VERSION) $(MAKE) install-exec MAKEFLAGS= # Wrapper scripts for the Chrome version (e.g. 'curl_chrome99') - install $(srcdir)/chrome/curl_chrome* $(srcdir)/chrome/curl_edge* $(srcdir)/chrome/curl_safari* @bindir@ + install $(srcdir)/chrome/curl_* @bindir@ .PHONY: chrome-install chrome-install-strip: ## Like 'chrome-install', but strip binaries for smaller size diff --git a/README.md b/README.md index 48bf4f86..4d44f776 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# curl-impersonate ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") ![Safari](https://github.com/alrra/browser-logos/blob/main/src/safari/safari_24x24.png "Safari") +# curl-impersonate ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") ![Safari](https://github.com/alrra/browser-logos/blob/main/src/safari/safari_24x24.png "Safari") [![Build and test](https://github.com/yifeikong/curl-impersonate/actions/workflows/build-and-test-make.yml/badge.svg)](https://github.com/yifeikong/curl-impersonate/actions/workflows/build-and-test-make.yml) [![Docker images](https://github.com/yifekong/curl-impersonate/actions/workflows/build-and-test-docker.yml/badge.svg)](https://github.com/yifeikong/curl-impersonate/actions/workflows/build-and-test-docker.yml) > [!NOTE] > This is a maintained fork of [curl-impersonate](https://github.com/lwthiker/curl-impersonate). Chrome's fingerprints has changed a lot since the last update of upstream, use this fork if your impersonation is not working. -A special build of [curl](https://github.com/curl/curl) that can impersonate the four major browsers: Chrome, Edge, Safari & Firefox. curl-impersonate is able to perform TLS and HTTP handshakes that are identical to that of a real browser. +A special build of [curl](https://github.com/curl/curl) that can impersonate the four major browsers: Chrome, Edge, Safari. curl-impersonate is able to perform TLS and HTTP handshakes that are identical to that of a real browser. curl-impersonate can be used either as a command line tool, similar to the regular curl, or as a library that can be integrated instead of the regular libcurl. See [Usage](#Basic-usage) below. @@ -21,7 +21,7 @@ With the modified curl in this repository, the TLS and HTTP handshakes look *exa ## How? To make this work, `curl` was patched significantly to resemble a browser. Specifically, The modifications that were needed to make this work: -* Compiling curl with nss, the TLS library that Firefox uses, instead of OpenSSL. For the Chrome version, compiling with BoringSSL, Google's TLS library. +* For the Chrome version, compiling with BoringSSL, Google's TLS library. * Modifying the way curl configures various TLS extensions and SSL options. * Adding support for new TLS extensions. * Changing the settings that curl uses for its HTTP/2 connections. @@ -47,13 +47,6 @@ The following browsers can be impersonated. | ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_24x24.png "Chrome") | 99 | 99.0.4844.73 | Android 12 | `chrome99_android` | [curl_chrome99_android](chrome/curl_chrome99_android) | | ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") | 99 | 99.0.1150.30 | Windows 10 | `edge99` | [curl_edge99](chrome/curl_edge99) | | ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_24x24.png "Edge") | 101 | 101.0.1210.47 | Windows 10 | `edge101` | [curl_edge101](chrome/curl_edge101) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 91 ESR | 91.6.0esr | Windows 10 | `ff91esr` | [curl_ff91esr](firefox/curl_ff91esr) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 95 | 95.0.2 | Windows 10 | `ff95` | [curl_ff95](firefox/curl_ff95) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 98 | 98.0 | Windows 10 | `ff98` | [curl_ff98](firefox/curl_ff98) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 100 | 100.0 | Windows 10 | `ff100` | [curl_ff100](firefox/curl_ff100) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 102 | 102.0 | Windows 10 | `ff102` | [curl_ff102](firefox/curl_ff102) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 109 | 109.0 | Windows 10 | `ff109` | [curl_ff109](firefox/curl_ff109) | -| ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_24x24.png "Firefox") | 117 | 117.0.1 | Windows 10 | `ff117` | [curl_ff117](firefox/curl_ff117) | | ![Safari](https://github.com/alrra/browser-logos/blob/main/src/safari/safari_24x24.png "Safari") | 15.3 | 16612.4.9.1.8 | MacOS Big Sur | `safari15_3` | [curl_safari15_3](chrome/curl_safari15_3) | | ![Safari](https://github.com/alrra/browser-logos/blob/main/src/safari/safari_24x24.png "Safari") | 15.5 | 17613.2.7.1.8 | MacOS Monterey | `safari15_5` | [curl_safari15_5](chrome/curl_safari15_5) | | ![Safari](https://github.com/alrra/browser-logos/blob/main/src/safari/safari_24x24.png "Safari") | 17.0 | unclear | MacOS Sonoma | `safari17_0` | [curl_safari17_0](chrome/curl_safari17_0) | @@ -85,7 +78,7 @@ See [Advanced usage](#Advanced-usage) for more options, including using `libcurl More documentation is available in the [docs/](docs/README.md) directory. ## Installation -There are two versions of `curl-impersonate` for technical reasons. The **chrome** version is used to impersonate Chrome, Edge and Safari. The **firefox** version is used to impersonate Firefox. +There are two versions of `curl-impersonate` for technical reasons. The **chrome** version is used to impersonate Chrome, Edge and Safari. ### Pre-compiled binaries Pre-compiled binaries for Windows, Linux and macOS are available at the [GitHub releases](https://github.com/yifeikong/curl-impersonate/releases) page. Before you use them you need to install nss (Firefox's TLS library) and CA certificates: @@ -113,10 +106,6 @@ See [INSTALL.md](INSTALL.md). Docker images based on Alpine Linux and Debian with `curl-impersonate` compiled and ready to use are available on [Docker Hub](https://hub.docker.com/r/lwthiker/curl-impersonate). The images contain the binary and all the wrapper scripts. Use like the following: ```bash -# Firefox version, Alpine Linux -docker pull lwthiker/curl-impersonate:0.5-ff -docker run --rm lwthiker/curl-impersonate:0.5-ff curl_ff109 https://www.wikipedia.org - # Chrome version, Alpine Linux docker pull lwthiker/curl-impersonate:0.5-chrome docker run --rm lwthiker/curl-impersonate:0.5-chrome curl_chrome110 https://www.wikipedia.org @@ -176,7 +165,6 @@ In particular, see the [note about the Firefox version](INSTALL.md#a-note-about- This repository contains two main folders: * [chrome](chrome) - Scripts and patches for building the Chrome version of `curl-impersonate`. -* [firefox](firefox) - Scripts and patches for building the Firefox version of `curl-impersonate`. The layout is similar for both. For example, the Firefox directory contains: * [Dockerfile](firefox/Dockerfile) - Used to build `curl-impersonate` with all dependencies. diff --git a/chrome/Dockerfile b/chrome/Dockerfile index 89da312d..5b61d18d 100644 --- a/chrome/Dockerfile +++ b/chrome/Dockerfile @@ -74,7 +74,7 @@ RUN cd ${NGHTTP2_VERSION} && \ make && make install # Download curl. -ARG CURL_VERSION=curl-8.1.1 +ARG CURL_VERSION=curl-8.5.0 RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz RUN tar xf ${CURL_VERSION}.tar.xz diff --git a/chrome/Dockerfile.alpine b/chrome/Dockerfile.alpine index db288357..fd654e80 100644 --- a/chrome/Dockerfile.alpine +++ b/chrome/Dockerfile.alpine @@ -68,7 +68,7 @@ RUN cd ${NGHTTP2_VERSION} && \ make && make install # Download curl. -ARG CURL_VERSION=curl-8.1.1 +ARG CURL_VERSION=curl-8.5.0 RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz RUN tar xf ${CURL_VERSION}.tar.xz diff --git a/chrome/curl_chrome100 b/chrome/curl_chrome100 index 6f81a1f4..085e1a63 100755 --- a/chrome/curl_chrome100 +++ b/chrome/curl_chrome100 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome101 b/chrome/curl_chrome101 index 9921089d..d77ef41d 100755 --- a/chrome/curl_chrome101 +++ b/chrome/curl_chrome101 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome104 b/chrome/curl_chrome104 index 7b04cd81..0d9ed592 100755 --- a/chrome/curl_chrome104 +++ b/chrome/curl_chrome104 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome107 b/chrome/curl_chrome107 index 7ee02c2f..32fad580 100755 --- a/chrome/curl_chrome107 +++ b/chrome/curl_chrome107 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome110 b/chrome/curl_chrome110 index 79472092..50826dbd 100755 --- a/chrome/curl_chrome110 +++ b/chrome/curl_chrome110 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps --tls-permute-extensions \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome116 b/chrome/curl_chrome116 index df9fd879..d5b14d45 100755 --- a/chrome/curl_chrome116 +++ b/chrome/curl_chrome116 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps --tls-permute-extensions \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome119 b/chrome/curl_chrome119 index 7875027a..dd3629c9 100755 --- a/chrome/curl_chrome119 +++ b/chrome/curl_chrome119 @@ -27,4 +27,5 @@ dir=${0%/*} --ech GREASE \ --tlsv1.2 --alps --tls-permute-extensions \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome120 b/chrome/curl_chrome120 index a9b1d18b..8218e242 100755 --- a/chrome/curl_chrome120 +++ b/chrome/curl_chrome120 @@ -27,4 +27,5 @@ dir=${0%/*} --ech GREASE \ --tlsv1.2 --alps --tls-permute-extensions \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome99 b/chrome/curl_chrome99 index ea1c537a..20ef1cf4 100755 --- a/chrome/curl_chrome99 +++ b/chrome/curl_chrome99 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_chrome99_android b/chrome/curl_chrome99_android index 0e56cd7a..79277699 100755 --- a/chrome/curl_chrome99_android +++ b/chrome/curl_chrome99_android @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_edge101 b/chrome/curl_edge101 index ff3c261c..6193dec1 100755 --- a/chrome/curl_edge101 +++ b/chrome/curl_edge101 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_edge99 b/chrome/curl_edge99 index 696a848b..cd8e1a65 100755 --- a/chrome/curl_edge99 +++ b/chrome/curl_edge99 @@ -26,4 +26,5 @@ dir=${0%/*} --compressed \ --tlsv1.2 --alps \ --cert-compression brotli \ + --tls-grease \ "$@" diff --git a/chrome/curl_firefox120 b/chrome/curl_firefox120 new file mode 100755 index 00000000..68a1274c --- /dev/null +++ b/chrome/curl_firefox120 @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Find the directory of this script +dir=${0%/*} + +# The list of ciphers can be obtained by looking at the Client Hello message in +# Wireshark, then converting it using the cipherlist array at +# https://github.com/curl/curl/blob/master/lib/vtls/nss.c +"$dir/curl-impersonate-chrome" \ + --ciphers "TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA" \ + -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/120.0' \ + -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Accept-Encoding: gzip, deflate, br' \ + -H 'Upgrade-Insecure-Requests: 1' \ + -H 'Sec-Fetch-Dest: document' \ + -H 'Sec-Fetch-Mode: navigate' \ + -H 'Sec-Fetch-Site: none' \ + -H 'Sec-Fetch-User: ?1' \ + -H 'TE: Trailers' \ + --http2 \ + --http2-settings '1:65536;4:131072;5:16384' \ + --http2-streams '3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241' \ + --http2-window-update 12517377 \ + --http2-pseudo-headers-order 'mpas' \ + --compressed \ + --ech GREASE \ + "$@" diff --git a/chrome/curl_safari15_3 b/chrome/curl_safari15_3 index 982e3b28..0d78f445 100755 --- a/chrome/curl_safari15_3 +++ b/chrome/curl_safari15_3 @@ -20,4 +20,5 @@ dir=${0%/*} --http2-window-update 10485760 \ --compressed \ --tlsv1.0 --no-tls-session-ticket \ + --tls-grease \ "$@" diff --git a/chrome/curl_safari15_5 b/chrome/curl_safari15_5 index 83100dc3..a565cd6a 100755 --- a/chrome/curl_safari15_5 +++ b/chrome/curl_safari15_5 @@ -21,4 +21,5 @@ dir=${0%/*} --compressed \ --tlsv1.0 --no-tls-session-ticket \ --cert-compression zlib \ + --tls-grease \ "$@" diff --git a/chrome/curl_safari17_0 b/chrome/curl_safari17_0 index 66f7dc03..7c06c1dc 100755 --- a/chrome/curl_safari17_0 +++ b/chrome/curl_safari17_0 @@ -24,4 +24,5 @@ dir=${0%/*} --compressed \ --tlsv1.0 --no-tls-session-ticket \ --cert-compression zlib \ + --tls-grease \ "$@" diff --git a/chrome/curl_safari17_2_ios b/chrome/curl_safari17_2_ios index d579b09a..ca5c576d 100755 --- a/chrome/curl_safari17_2_ios +++ b/chrome/curl_safari17_2_ios @@ -24,4 +24,5 @@ dir=${0%/*} --compressed \ --tlsv1.0 --no-tls-session-ticket \ --cert-compression zlib \ + --tls-grease \ "$@" diff --git a/chrome/patches/boringssl-old-ciphers.patch b/chrome/patches/boringssl-old-ciphers.patch index 2b278299..098f63fe 100644 --- a/chrome/patches/boringssl-old-ciphers.patch +++ b/chrome/patches/boringssl-old-ciphers.patch @@ -1,5 +1,194 @@ +diff --git a/export.sh b/export.sh +new file mode 100755 +index 000000000..3375b870d +--- /dev/null ++++ b/export.sh +@@ -0,0 +1,4 @@ ++#!/bin/bash ++ ++git df d24a382 > boringssl-old-ciphers.patch ++mv boringssl-old-ciphers.patch ../curl-impersonate/chrome/patches/boringssl-old-ciphers.patch +diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h +index e500dd76e..487945969 100644 +--- a/include/openssl/ssl.h ++++ b/include/openssl/ssl.h +@@ -1560,6 +1560,9 @@ OPENSSL_EXPORT int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, + // garbage inputs, unless an empty cipher list results. + OPENSSL_EXPORT int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); + ++// curl-impersonate: set the extension order by given string ++OPENSSL_EXPORT int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order); ++ + // SSL_set_strict_cipher_list configures the cipher list for |ssl|, evaluating + // |str| as a cipher string and returning error if |str| contains anything + // meaningless. It returns one on success and zero on failure. +@@ -4583,6 +4586,9 @@ OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled); + // permute extensions. For now, this is only implemented for the ClientHello. + OPENSSL_EXPORT void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled); + ++// curl-impersonate ++OPENSSL_EXPORT int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order); ++ + // SSL_set_permute_extensions configures whether sockets on |ssl| should + // permute extensions. For now, this is only implemented for the ClientHello. + OPENSSL_EXPORT void SSL_set_permute_extensions(SSL *ssl, int enabled); +diff --git a/ssl/extensions.cc b/ssl/extensions.cc +index b13400097..a59db770c 100644 +--- a/ssl/extensions.cc ++++ b/ssl/extensions.cc +@@ -3313,6 +3313,7 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { + !permutation.Init(kNumExtensions)) { + return false; + } ++ // By default, nothing is permuted. + for (size_t i = 0; i < kNumExtensions; i++) { + permutation[i] = i; + } +@@ -3320,6 +3321,11 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { + // Set element |i| to a randomly-selected element 0 <= j <= i. + std::swap(permutation[i], permutation[seeds[i - 1] % (i + 1)]); + } ++ // fprintf(stderr, "the permuated order is set to:"); ++ // for (size_t i = 0; i < kNumExtensions; i++) { ++ // fprintf(stderr, "%d, ", permutation[i]); ++ // } ++ // fprintf(stderr, "\n"); + hs->extension_permutation = std::move(permutation); + return true; + } +@@ -3337,6 +3343,50 @@ static const struct tls_extension *tls_extension_find(uint32_t *out_index, + return NULL; + } + ++// curl-impersonate: set customized extension order ++// ++// Generate the extension_permutation array from a customized extension order string. ++// ++// The customized extension order string is a dash-separated list of extensions. ++// ++bool ssl_set_extension_order(SSL_HANDSHAKE *hs) { ++ if (hs->config->extension_order == nullptr) { ++ return true; ++ } ++ // fprintf(stderr, "order %s\n", hs->config->extension_order); ++ Array order; ++ if (!order.Init(kNumExtensions)) { ++ return false; ++ } ++ // By default, nothing is reordered. ++ for (size_t i = 0; i < kNumExtensions; i++) { ++ order[i] = 255; ++ } ++ // split the order string, and put there order in the table ++ const char *delimiter = "-"; ++ char *tmp = strdup(hs->config->extension_order); ++ char *ext = strtok(tmp, delimiter); ++ size_t idx = 0; ++ while (ext != nullptr) { ++ unsigned ext_index; ++ tls_extension_find(&ext_index, atoi(ext)); ++ // fprintf(stderr, "found %d -> %d, ", atoi(ext), ext_index); ++ order[idx] = ext_index; ++ ext = strtok(NULL, delimiter); ++ idx++; ++ } ++ // fprintf(stderr, "\n"); ++ // fprintf(stderr, "the order is set to:"); ++ // for (size_t i = 0; i < kNumExtensions; i++) { ++ // fprintf(stderr, "%d, ", order[i]); ++ // } ++ // fprintf(stderr, "\n"); ++ free(tmp); ++ ++ hs->extension_permutation = std::move(order); ++ return true; ++} ++ + static bool add_padding_extension(CBB *cbb, uint16_t ext, size_t len) { + CBB child; + if (!CBB_add_u16(cbb, ext) || // +@@ -3379,10 +3429,14 @@ static bool ssl_add_clienthello_tlsext_inner(SSL_HANDSHAKE *hs, CBB *out, + } + } + ++ fprintf(stderr, "starting to numeratez\n"); + for (size_t unpermuted = 0; unpermuted < kNumExtensions; unpermuted++) { + size_t i = hs->extension_permutation.empty() + ? unpermuted + : hs->extension_permutation[unpermuted]; ++ fprintf(stderr, "extension %zu,", i); ++ fprintf(stderr, "\n"); ++ if (i == 255) { continue; } // curl-impersonate: skip non-exist extensions + const size_t len_before = CBB_len(&extensions); + const size_t len_compressed_before = CBB_len(compressed.get()); + if (!kExtensions[i].add_clienthello(hs, &extensions, compressed.get(), +@@ -3492,6 +3546,7 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded, + size_t i = hs->extension_permutation.empty() + ? unpermuted + : hs->extension_permutation[unpermuted]; ++ if (i == 255) { continue; } // curl-impersonate: skip non-exist extensions + const size_t len_before = CBB_len(&extensions); + if (!kExtensions[i].add_clienthello(hs, &extensions, &extensions, type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); +diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc +index 971ebd0b1..0005c6d79 100644 +--- a/ssl/handshake_client.cc ++++ b/ssl/handshake_client.cc +@@ -215,14 +215,6 @@ static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs, + } + } + +-static bool ssl_add_tls13_cipher(CBB *cbb, uint16_t cipher_id, +- ssl_compliance_policy_t policy) { +- if (ssl_tls13_cipher_meets_policy(cipher_id, policy)) { +- return CBB_add_u16(cbb, cipher_id); +- } +- return true; +-} +- + static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, + ssl_client_hello_type_t type) { + const SSL *const ssl = hs->ssl; +@@ -240,28 +232,7 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, + return false; + } + +- // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on +- // hardware support. +- if (hs->max_version >= TLS1_3_VERSION) { +- const bool has_aes_hw = ssl->config->aes_hw_override +- ? ssl->config->aes_hw_override_value +- : EVP_has_aes_hardware(); +- +- if ((!has_aes_hw && // +- !ssl_add_tls13_cipher(&child, +- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, +- ssl->config->tls13_cipher_policy)) || +- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, +- ssl->config->tls13_cipher_policy) || +- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, +- ssl->config->tls13_cipher_policy) || +- (has_aes_hw && // +- !ssl_add_tls13_cipher(&child, +- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, +- ssl->config->tls13_cipher_policy))) { +- return false; +- } +- } ++ // curl-impersonate: TLS 1.3 suites are added uniformly with other suites. + + if (hs->min_version < TLS1_3_VERSION && type != ssl_client_hello_inner) { + bool any_enabled = false; +@@ -539,6 +510,7 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { + + if (!ssl_setup_key_shares(hs, /*override_group_id=*/0) || + !ssl_setup_extension_permutation(hs) || ++ !ssl_set_extension_order(hs) || + !ssl_encrypt_client_hello(hs, MakeConstSpan(ech_enc, ech_enc_len)) || + !ssl_add_client_hello(hs)) { + return ssl_hs_error; diff --git a/ssl/internal.h b/ssl/internal.h -index c9facb699..ec3c21fed 100644 +index c9facb699..eab61611e 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -574,9 +574,14 @@ BSSL_NAMESPACE_BEGIN @@ -18,8 +207,38 @@ index c9facb699..ec3c21fed 100644 // Bits for |algorithm_prf| (handshake digest). #define SSL_HANDSHAKE_MAC_DEFAULT 0x1 +@@ -2161,6 +2166,9 @@ bssl::UniquePtr tls13_create_session_with_ticket(SSL *ssl, + // for |hs|, if applicable. It returns true on success and false on error. + bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs); + ++// curl-impersonate ++bool ssl_set_extension_order(SSL_HANDSHAKE *hs); ++ + // ssl_setup_key_shares computes client key shares and saves them in |hs|. It + // returns true on success and false on failure. If |override_group_id| is zero, + // it offers the default groups, including GREASE. If it is non-zero, it offers +@@ -3033,6 +3041,9 @@ struct SSL_CONFIG { + // crypto + UniquePtr cipher_list; + ++ // curl-impersonate ++ char *extension_order = nullptr; ++ + // This is used to hold the local certificate used (i.e. the server + // certificate for a server or the client certificate for a client). + UniquePtr cert; +@@ -3490,6 +3501,9 @@ struct ssl_ctx_st { + + bssl::UniquePtr cipher_list; + ++ // curl-impersonate ++ char *extension_order = nullptr; ++ + X509_STORE *cert_store = nullptr; + LHASH_OF(SSL_SESSION) *sessions = nullptr; + // Most session-ids that will be cached, default is diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc -index fd8cef95d..1d6ffe88b 100644 +index fd8cef95d..3d2c8ff6d 100644 --- a/ssl/ssl_cipher.cc +++ b/ssl/ssl_cipher.cc @@ -197,6 +197,37 @@ static constexpr SSL_CIPHER kCiphers[] = { @@ -161,7 +380,24 @@ index fd8cef95d..1d6ffe88b 100644 // GCM based TLS v1.2 ciphersuites from RFC 5289 -@@ -554,6 +658,11 @@ static const CIPHER_ALIAS kCipherAliases[] = { +@@ -467,16 +571,6 @@ Span AllCiphers() { + return MakeConstSpan(kCiphers, OPENSSL_ARRAY_SIZE(kCiphers)); + } + +-static constexpr size_t NumTLS13Ciphers() { +- size_t num = 0; +- for (const auto &cipher : kCiphers) { +- if (cipher.algorithm_mkey == SSL_kGENERIC) { +- num++; +- } +- } +- return num; +-} +- + #define CIPHER_ADD 1 + #define CIPHER_KILL 2 + #define CIPHER_DEL 3 +@@ -554,6 +648,11 @@ static const CIPHER_ALIAS kCipherAliases[] = { // MAC aliases {"SHA1", ~0u, ~0u, ~0u, SSL_SHA1, 0}, {"SHA", ~0u, ~0u, ~0u, SSL_SHA1, 0}, @@ -173,15 +409,17 @@ index fd8cef95d..1d6ffe88b 100644 // Legacy protocol minimum version aliases. "TLSv1" is intentionally the // same as "SSLv3". -@@ -1154,13 +1263,20 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1154,13 +1253,22 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 & 0xffff, }; static const uint16_t kLegacyCiphers[] = { + TLS1_CK_RSA_WITH_AES_128_SHA256 & 0xffff, + TLS1_CK_RSA_WITH_AES_256_SHA256 & 0xffff, + 0x0300C008 & 0xffff, ++ // TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA & 0xffff, TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA & 0xffff, + 0x0300C012 & 0xffff, ++ // TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA & 0xffff, TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA & 0xffff, TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA & 0xffff, TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA & 0xffff, @@ -194,6 +432,66 @@ index fd8cef95d..1d6ffe88b 100644 TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff, TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff, TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff, +@@ -1169,11 +1277,16 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, + TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff, + SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff, + }; ++ // curl-impersonate: add TLS 1.3 ciphers here for ordering ++ static const uint16_t kTLS13Ciphers[] = { ++ TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, ++ TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, ++ TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, ++ }; + + // Set up a linked list of ciphers. +- CIPHER_ORDER co_list[OPENSSL_ARRAY_SIZE(kAESCiphers) + +- OPENSSL_ARRAY_SIZE(kChaChaCiphers) + +- OPENSSL_ARRAY_SIZE(kLegacyCiphers)]; ++ CIPHER_ORDER co_list[OPENSSL_ARRAY_SIZE(kCiphers)]; ++ + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(co_list); i++) { + co_list[i].next = + i + 1 < OPENSSL_ARRAY_SIZE(co_list) ? &co_list[i + 1] : nullptr; +@@ -1209,8 +1322,13 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, + co_list[num++].cipher = SSL_get_cipher_by_value(id); + assert(co_list[num - 1].cipher != nullptr); + } ++ // curl-impersonate: add TLS 1.3 ciphers here for ordering ++ for (uint16_t id: kTLS13Ciphers) { ++ co_list[num++].cipher = SSL_get_cipher_by_value(id); ++ assert(co_list[num - 1].cipher != nullptr); ++ } + assert(num == OPENSSL_ARRAY_SIZE(co_list)); +- static_assert(OPENSSL_ARRAY_SIZE(co_list) + NumTLS13Ciphers() == ++ static_assert(OPENSSL_ARRAY_SIZE(co_list) == + OPENSSL_ARRAY_SIZE(kCiphers), + "Not all ciphers are included in the cipher order"); + +diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc +index 58b68e675..15eb823b3 100644 +--- a/ssl/ssl_lib.cc ++++ b/ssl/ssl_lib.cc +@@ -657,6 +657,7 @@ SSL *SSL_new(SSL_CTX *ctx) { + ssl->config->retain_only_sha256_of_client_certs = + ctx->retain_only_sha256_of_client_certs; + ssl->config->permute_extensions = ctx->permute_extensions; ++ ssl->config->extension_order = ctx->extension_order; + ssl->config->aes_hw_override = ctx->aes_hw_override; + ssl->config->aes_hw_override_value = ctx->aes_hw_override_value; + ssl->config->tls13_cipher_policy = ctx->tls13_cipher_policy; +@@ -3015,6 +3016,12 @@ void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) { + ctx->permute_extensions = !!enabled; + } + ++// curl-impersonate: set extensions order ++int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order) { ++ ctx->extension_order = order; ++ return 0; ++} ++ + void SSL_set_permute_extensions(SSL *ssl, int enabled) { + if (!ssl->config) { + return; diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc index 57116cd6c..fa1652832 100644 --- a/ssl/ssl_privkey.cc diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index 2bc8422d..dcd7b9b3 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -1,8 +1,8 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 49a44eabf..fe8b38915 100644 +index a54c2fff9..7d22d4e96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -550,6 +550,29 @@ if(CURL_ZSTD) +@@ -619,6 +619,29 @@ if(CURL_ZSTD) endif() endif() @@ -29,14 +29,14 @@ index 49a44eabf..fe8b38915 100644 + endif() +endif() + - option(USE_NGHTTP2 "Use Nghttp2 library" OFF) - if(USE_NGHTTP2) - find_package(NGHTTP2 REQUIRED) + # Check symbol in OpenSSL-like TLS backends. + macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE) + cmake_push_check_state() diff --git a/Makefile.am b/Makefile.am -index f25e4e2f0..ff0c5630b 100644 +index c8afcb505..234125083 100644 --- a/Makefile.am +++ b/Makefile.am -@@ -156,13 +156,13 @@ CLEANFILES = $(VC10_LIBVCXPROJ) $(VC10_SRCVCXPROJ) $(VC11_LIBVCXPROJ) \ +@@ -131,13 +131,13 @@ CLEANFILES = $(VC14_LIBVCXPROJ) \ $(VC14_SRCVCXPROJ) $(VC14_10_LIBVCXPROJ) $(VC14_10_SRCVCXPROJ) \ $(VC14_30_LIBVCXPROJ) $(VC14_30_SRCVCXPROJ) @@ -53,10 +53,10 @@ index f25e4e2f0..ff0c5630b 100644 # 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 75a882b12..8697a6f74 100644 +index d9b396376..cd3b845f4 100644 --- a/configure.ac +++ b/configure.ac -@@ -1493,7 +1493,8 @@ if test X"$OPT_BROTLI" != Xno; then +@@ -1422,7 +1422,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 @@ -66,7 +66,7 @@ index 75a882b12..8697a6f74 100644 LD_BROTLI=-L${PREFIX_BROTLI}/lib$libsuff CPP_BROTLI=-I${PREFIX_BROTLI}/include DIR_BROTLI=${PREFIX_BROTLI}/lib$libsuff -@@ -1503,7 +1504,11 @@ if test X"$OPT_BROTLI" != Xno; then +@@ -1432,7 +1433,11 @@ if test X"$OPT_BROTLI" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_BROTLI" LIBS="$LIB_BROTLI $LIBS" @@ -79,7 +79,7 @@ index 75a882b12..8697a6f74 100644 AC_CHECK_HEADERS(brotli/decode.h, curl_brotli_msg="enabled (libbrotlidec)" -@@ -4306,14 +4311,23 @@ if test "x$want_ech" != "xno"; then +@@ -4383,14 +4388,23 @@ if test "x$want_ech" != "xno"; then ECH_ENABLED=0 ECH_SUPPORT='' @@ -108,7 +108,7 @@ index 75a882b12..8697a6f74 100644 fi dnl now deal with whatever we found -@@ -4706,8 +4720,8 @@ AC_CONFIG_FILES([Makefile \ +@@ -4795,8 +4809,8 @@ AC_CONFIG_FILES([Makefile \ tests/http/clients/Makefile \ packages/Makefile \ packages/vms/Makefile \ @@ -144,11 +144,21 @@ index 54f92d931..ea5895e9b 100644 else echo "curl was built with static libraries disabled" >&2 exit 1 +diff --git a/export.sh b/export.sh +new file mode 100755 +index 000000000..34b44cd2a +--- /dev/null ++++ b/export.sh +@@ -0,0 +1,4 @@ ++#!/bin/bash ++ ++git df curl-8_5_0 > chrome.patch ++mv chrome.patch ../curl-impersonate/chrome/patches/curl-impersonate.patch diff --git a/include/curl/curl.h b/include/curl/curl.h -index 944352421..8d81e9a8d 100644 +index cc24c0506..9cba4dd14 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h -@@ -640,6 +640,7 @@ typedef enum { +@@ -631,6 +631,7 @@ typedef enum { CURLE_PROXY, /* 97 - proxy handshake error */ CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */ CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */ @@ -156,32 +166,32 @@ index 944352421..8d81e9a8d 100644 CURL_LAST /* never use! */ } CURLcode; -@@ -2207,6 +2208,59 @@ typedef enum { - /* Can leak things, gonna exit() soon */ - CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322), +@@ -2201,6 +2202,77 @@ typedef enum { + /* set a specific client IP for HAProxy PROXY protocol header? */ + CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323), + /* curl-impersonate: A list of headers used by the impersonated browser. + * If given, merged with CURLOPT_HTTPHEADER. */ -+ CURLOPT(CURLOPT_HTTPBASEHEADER, CURLOPTTYPE_SLISTPOINT, 323), ++ CURLOPT(CURLOPT_HTTPBASEHEADER, CURLOPTTYPE_SLISTPOINT, 324), + + /* 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, 324), ++ CURLOPT(CURLOPT_SSL_SIG_HASH_ALGS, CURLOPTTYPE_STRINGPOINT, 325), + + /* 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, 325), ++ CURLOPT(CURLOPT_SSL_ENABLE_ALPS, CURLOPTTYPE_LONG, 326), + + /* 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, 326), ++ CURLOPT(CURLOPT_SSL_CERT_COMPRESSION, CURLOPTTYPE_STRINGPOINT, 327), + + /* Enable/disable TLS session ticket extension (RFC5077) */ -+ CURLOPT(CURLOPT_SSL_ENABLE_TICKET, CURLOPTTYPE_LONG, 327), ++ CURLOPT(CURLOPT_SSL_ENABLE_TICKET, CURLOPTTYPE_LONG, 328), + + /* + * curl-impersonate: @@ -190,28 +200,46 @@ index 944352421..8d81e9a8d 100644 + * ":method", ":authority", ":scheme", ":path" in the desired order of + * appearance in the HTTP/2 HEADERS frame. + */ -+ CURLOPT(CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, CURLOPTTYPE_STRINGPOINT, 328), ++ CURLOPT(CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, CURLOPTTYPE_STRINGPOINT, 329), + + /* + * curl-impersonate: + * HTTP2 settings frame keys and values, format: 1:v;2:v;3:v + */ -+ CURLOPT(CURLOPT_HTTP2_SETTINGS, CURLOPTTYPE_STRINGPOINT, 329), ++ CURLOPT(CURLOPT_HTTP2_SETTINGS, CURLOPTTYPE_STRINGPOINT, 330), + + /* + * curl-impersonate: Whether to enable Boringssl permute extensions + * See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_permute_extensions. + */ -+ CURLOPT(CURLOPT_SSL_PERMUTE_EXTENSIONS, CURLOPTTYPE_LONG, 330), ++ CURLOPT(CURLOPT_SSL_PERMUTE_EXTENSIONS, CURLOPTTYPE_LONG, 331), + + /* + * curl-impersonate: + * HTTP2 initial window update + */ -+ CURLOPT(CURLOPT_HTTP2_WINDOW_UPDATE, CURLOPTTYPE_LONG, 331), ++ CURLOPT(CURLOPT_HTTP2_WINDOW_UPDATE, CURLOPTTYPE_LONG, 332), ++ ++ /* curl-impersonate: ++ * set ECH configuration, XXX, the official one is 324 ++ */ ++ CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 333), ++ ++ /* ++ * curl-impersonate: ++ * Set the initial streams settings for http2. ++ */ ++ CURLOPT(CURLOPT_HTTP2_STREAMS, CURLOPTTYPE_STRINGPOINT, 334), + -+ /* set ECH configuration, XXX, the official one is 324 */ -+ CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 332), ++ /* curl-impersonate: ++ * enable tls grease ++ */ ++ CURLOPT(CURLOPT_TLS_GREASE, CURLOPTTYPE_LONG, 335), ++ ++ /* curl-impersonate: ++ * set tls extension order ++ */ ++ CURLOPT(CURLOPT_TLS_EXTENSION_ORDER, CURLOPTTYPE_STRINGPOINT, 336), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -238,7 +266,7 @@ index 1285101c5..c620065dc 100644 * NAME curl_easy_getinfo() * diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h -index bc8d7a78c..033c994ca 100644 +index b880f3dc6..79074e011 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -275,6 +275,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, @@ -250,7 +278,7 @@ index bc8d7a78c..033c994ca 100644 (option) == CURLOPT_FTP_ACCOUNT || \ (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ diff --git a/lib/Makefile.am b/lib/Makefile.am -index 3c0a70912..61a9eb90b 100644 +index 1237c8e99..6b2961018 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -31,7 +31,7 @@ EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \ @@ -322,19 +350,19 @@ index 3c0a70912..61a9eb90b 100644 endif -libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) --libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(LDFLAGS) $(LIBCURL_LIBS) +-libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(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_LDFLAGS = $(AM_LDFLAGS) $(libcurl_impersonate_chrome_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(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) diff --git a/lib/Makefile.inc b/lib/Makefile.inc -index f815170a7..9d9417edc 100644 +index e568ef953..298b16050 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc -@@ -174,6 +174,7 @@ LIB_CFILES = \ +@@ -171,6 +171,7 @@ LIB_CFILES = \ idn.c \ if2ip.c \ imap.c \ @@ -343,21 +371,21 @@ index f815170a7..9d9417edc 100644 inet_pton.c \ krb5.c \ diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake -index eca71bdef..ae9f02aca 100644 +index 339358ea3..a9cf400fb 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake -@@ -783,3 +783,6 @@ ${SIZEOF_TIME_T_CODE} +@@ -811,3 +811,6 @@ ${SIZEOF_TIME_T_CODE} - /* Define to 1 to enable websocket support. */ - #cmakedefine USE_WEBSOCKETS 1 + /* Define to 1 to enable TLS-SRP support. */ + #cmakedefine USE_TLS_SRP 1 + +/* if ECH support is available */ +#cmakedefine USE_ECH 1 diff --git a/lib/dynhds.c b/lib/dynhds.c -index b325e0060..4c8a73bab 100644 +index d7548959b..00f97506b 100644 --- a/lib/dynhds.c +++ b/lib/dynhds.c -@@ -52,6 +52,8 @@ entry_new(const char *name, size_t namelen, +@@ -56,6 +56,8 @@ entry_new(const char *name, size_t namelen, e->valuelen = valuelen; if(opts & DYNHDS_OPT_LOWERCASE) Curl_strntolower(e->name, e->name, e->namelen); @@ -366,7 +394,7 @@ index b325e0060..4c8a73bab 100644 return e; } -@@ -134,6 +136,16 @@ void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts) +@@ -138,6 +140,16 @@ void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts) dynhds->opts = opts; } @@ -384,7 +412,7 @@ index b325e0060..4c8a73bab 100644 { DEBUGASSERT(dynhds); diff --git a/lib/dynhds.h b/lib/dynhds.h -index 777baa58a..2d542dfd6 100644 +index 3b536000a..d7135698f 100644 --- a/lib/dynhds.h +++ b/lib/dynhds.h @@ -53,6 +53,7 @@ struct dynhds { @@ -405,10 +433,10 @@ index 777baa58a..2d542dfd6 100644 /** * Return the n-th header entry or NULL if it does not exist. diff --git a/lib/easy.c b/lib/easy.c -index d36cc03d1..ec25400c5 100644 +index 322d1a41b..805613e6f 100644 --- a/lib/easy.c +++ b/lib/easy.c -@@ -73,6 +73,8 @@ +@@ -74,6 +74,8 @@ #include "dynbuf.h" #include "altsvc.h" #include "hsts.h" @@ -417,29 +445,23 @@ index d36cc03d1..ec25400c5 100644 #include "easy_lock.h" -@@ -330,6 +332,146 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, +@@ -341,6 +343,203 @@ 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. ++ * Actually call curl_easy_setopt() with all the needed options + * */ -+CURLcode curl_easy_impersonate(struct Curl_easy *data, const char *target, -+ int default_headers) ++CURLcode _do_impersonate(struct Curl_easy *data, ++ const struct impersonate_opts *opts, ++ int default_headers) +{ + int i; + int ret; -+ const struct impersonate_opts *opts = NULL; + struct curl_slist *headers = NULL; + -+ for(opts = impersonations; opts->target != NULL; opts++) { -+ if (strcasecompare(target, opts->target)) { -+ break; -+ } -+ } -+ + if(opts->target == NULL) { + DEBUGF(fprintf(stderr, "Error: unknown impersonation target '%s'\n", + target)); @@ -547,12 +569,27 @@ index d36cc03d1..ec25400c5 100644 + return ret; + } + ++ if(opts->http2_streams) { ++ ret = curl_easy_setopt(data, CURLOPT_HTTP2_STREAMS, opts->http2_streams); ++ if(ret) ++ return ret; ++ } ++ + if(opts->ech) { + ret = curl_easy_setopt(data, CURLOPT_ECH, opts->ech); + if(ret) + return ret; + } + ++ if(opts->tls_grease) { ++ ret = curl_easy_setopt(data, CURLOPT_TLS_GREASE, opts->tls_grease); ++ } ++ ++ if(opts->tls_extension_order) { ++ // printf("setting extension order as: %s\n", opts->tls_extension_order); ++ ret = curl_easy_setopt(data, CURLOPT_TLS_EXTENSION_ORDER, opts->tls_extension_order); ++ } ++ + /* Always enable all supported compressions. */ + ret = curl_easy_setopt(data, CURLOPT_ACCEPT_ENCODING, ""); + if(ret) @@ -560,11 +597,59 @@ index d36cc03d1..ec25400c5 100644 + + return CURLE_OK; +} ++ ++ ++/* ++ * curl-impersonate: ++ * Call curl_easy_setopt() with all the needed options as defined by the target ++ * */ ++CURLcode curl_easy_impersonate_customized(struct Curl_easy *data, ++ const struct impersonate_opts *opts, ++ int default_headers) ++{ ++ int ret; ++ ++ ret = _do_impersonate(data, opts, default_headers); ++ if(ret) ++ return ret; ++ ++ return CURLE_OK; ++} ++ ++/* ++ * 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 default_headers) ++{ ++ int ret; ++ const struct impersonate_opts *opts = NULL; ++ ++ for(opts = impersonations; opts->target != NULL; opts++) { ++ if (strcasecompare(target, opts->target)) { ++ break; ++ } ++ } ++ ++ if(opts->target == NULL) { ++ DEBUGF(fprintf(stderr, "Error: unknown impersonation target '%s'\n", ++ target)); ++ return CURLE_BAD_FUNCTION_ARGUMENT; ++ } ++ ++ ret = _do_impersonate(data, opts, default_headers); ++ 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. -@@ -338,6 +480,8 @@ struct Curl_easy *curl_easy_init(void) +@@ -349,6 +548,8 @@ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -573,7 +658,7 @@ index d36cc03d1..ec25400c5 100644 /* Make sure we inited the global SSL stuff */ global_init_lock(); -@@ -360,6 +504,29 @@ struct Curl_easy *curl_easy_init(void) +@@ -371,6 +572,29 @@ struct Curl_easy *curl_easy_init(void) return NULL; } @@ -603,7 +688,7 @@ index d36cc03d1..ec25400c5 100644 return data; } -@@ -930,6 +1097,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +@@ -945,6 +1169,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.referer_alloc = TRUE; } @@ -617,7 +702,7 @@ index d36cc03d1..ec25400c5 100644 /* 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]) { -@@ -1019,6 +1193,9 @@ fail: +@@ -1004,6 +1235,9 @@ fail: */ void curl_easy_reset(struct Curl_easy *data) { @@ -627,8 +712,8 @@ index d36cc03d1..ec25400c5 100644 Curl_free_request_state(data); /* zero out UserDefined data: */ -@@ -1043,6 +1220,23 @@ void curl_easy_reset(struct Curl_easy *data) - #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +@@ -1028,6 +1262,23 @@ void curl_easy_reset(struct Curl_easy *data) + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif + @@ -652,7 +737,7 @@ index d36cc03d1..ec25400c5 100644 /* diff --git a/lib/easyoptions.c b/lib/easyoptions.c -index a9c1efd00..b81f3a671 100644 +index e69c658b0..027dd6f93 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -86,6 +86,7 @@ struct curl_easyoption Curl_easyopts[] = { @@ -663,7 +748,7 @@ index a9c1efd00..b81f3a671 100644 {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0}, {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS}, {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0}, -@@ -132,8 +133,13 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -133,8 +134,14 @@ 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}, @@ -671,13 +756,14 @@ index a9c1efd00..b81f3a671 100644 + CURLOT_STRING, 0}, + {"HTTP2_SETTINGS", CURLOPT_HTTP2_SETTINGS, CURLOT_STRING, 0}, + {"HTTP2_WINDOW_UPDATE", CURLOPT_HTTP2_WINDOW_UPDATE, CURLOT_LONG, 0}, ++ {"HTTP2_STREAMS", CURLOPT_HTTP2_STREAMS, 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}, -@@ -302,18 +308,23 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -305,18 +312,23 @@ 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}, @@ -701,16 +787,25 @@ index a9c1efd00..b81f3a671 100644 {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0}, {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, -@@ -370,6 +381,6 @@ struct curl_easyoption Curl_easyopts[] = { +@@ -340,6 +352,8 @@ struct curl_easyoption Curl_easyopts[] = { + {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0}, + {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0}, + {"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0}, ++ {"TLS_GREASE", CURLOPT_TLS_GREASE, CURLOT_LONG, 0}, ++ {"TLS_EXTENSION_ORDER", CURLOPT_TLS_EXTENSION_ORDER, CURLOT_STRING, 0}, + {"TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0}, + {"TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0}, + {"TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0}, +@@ -373,6 +387,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { -- return ((CURLOPT_LASTENTRY%10000) != (322 + 1)); -+ return ((CURLOPT_LASTENTRY%10000) != (332 + 1)); +- return ((CURLOPT_LASTENTRY%10000) != (323 + 1)); ++ return ((CURLOPT_LASTENTRY%10000) != (334 + 1)); } #endif diff --git a/lib/http.c b/lib/http.c -index 219dcc2c0..7b04c6c36 100644 +index be6d442e8..ca537314a 100644 --- a/lib/http.c +++ b/lib/http.c @@ -90,6 +90,7 @@ @@ -721,7 +816,7 @@ index 219dcc2c0..7b04c6c36 100644 /* The last 3 #include files should be in this order */ #include "curl_printf.h" -@@ -1881,6 +1882,15 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, +@@ -1937,6 +1938,15 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, int numlists = 1; /* by default */ int i; @@ -737,7 +832,7 @@ index 219dcc2c0..7b04c6c36 100644 #ifndef CURL_DISABLE_PROXY enum proxy_use proxy; -@@ -1892,10 +1902,10 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, +@@ -1948,10 +1958,10 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, switch(proxy) { case HEADER_SERVER: @@ -750,7 +845,7 @@ index 219dcc2c0..7b04c6c36 100644 if(data->set.sep_headers) { h[1] = data->set.proxyheaders; numlists++; -@@ -1905,12 +1915,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, +@@ -1961,12 +1971,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, if(data->set.sep_headers) h[0] = data->set.proxyheaders; else @@ -765,7 +860,7 @@ index 219dcc2c0..7b04c6c36 100644 #endif /* loop through one or two lists */ -@@ -2146,6 +2156,108 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, +@@ -2202,6 +2212,108 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, *reqp = httpreq; } @@ -874,7 +969,7 @@ index 219dcc2c0..7b04c6c36 100644 CURLcode Curl_http_useragent(struct Curl_easy *data) { /* The User-Agent string might have been allocated in url.c already, because -@@ -3165,6 +3277,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) +@@ -3210,6 +3322,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) http = data->req.p.http; DEBUGASSERT(http); @@ -886,7 +981,7 @@ index 219dcc2c0..7b04c6c36 100644 result = Curl_http_host(data, conn); if(result) return result; -@@ -4777,12 +4894,41 @@ static bool h2_non_field(const char *name, size_t namelen) +@@ -4847,12 +4964,41 @@ static bool h2_non_field(const char *name, size_t namelen) return FALSE; } @@ -928,7 +1023,7 @@ index 219dcc2c0..7b04c6c36 100644 CURLcode result; DEBUGASSERT(req); -@@ -4816,25 +4962,56 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, +@@ -4886,25 +5032,56 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, Curl_dynhds_reset(h2_headers); Curl_dynhds_set_opts(h2_headers, DYNHDS_OPT_LOWERCASE); @@ -998,10 +1093,10 @@ index 219dcc2c0..7b04c6c36 100644 } diff --git a/lib/http2.c b/lib/http2.c -index c666192fc..5a90b28ea 100644 +index 973848484..36654499d 100644 --- a/lib/http2.c +++ b/lib/http2.c -@@ -50,6 +50,7 @@ +@@ -51,6 +51,7 @@ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" @@ -1009,16 +1104,16 @@ index c666192fc..5a90b28ea 100644 #if (NGHTTP2_VERSION_NUM < 0x010c00) #error too old nghttp2 version, upgrade! -@@ -68,7 +69,7 @@ +@@ -69,7 +70,7 @@ * use 16K as chunk size, as that fits H2 DATA frames well */ #define H2_CHUNK_SIZE (16 * 1024) /* this is how much we want "in flight" for a stream */ -#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) +#define H2_STREAM_WINDOW_SIZE (1024 * 1024) - /* on receving from TLS, we prep for holding a full stream window */ + /* on receiving from TLS, we prep for holding a full stream window */ #define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) /* on send into TLS, we just want to accumulate small frames */ -@@ -86,24 +87,84 @@ +@@ -87,26 +88,87 @@ * will block their received QUOTA in the connection window. And if we * run out of space, the server is blocked from sending us any data. * See #10988 for an issue with this. */ @@ -1054,10 +1149,13 @@ index c666192fc..5a90b28ea 100644 - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_STREAM_WINDOW_SIZE; + // printf("USING settings %s\n", http2_settings); -+ + +- iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; +- iv[2].value = data->multi->push_cb != NULL; + char *tmp = strdup(http2_settings); + char *setting = strtok(tmp, delimiter); -+ + +- return 3; + // loop through the string to extract all other tokens + while(setting != NULL) { + // deal with each setting @@ -1097,22 +1195,98 @@ index c666192fc..5a90b28ea 100644 + setting = strtok(NULL, delimiter); + } + free(tmp); - -- 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)); - -- return 3; ++ + return i; } - static size_t populate_binsettings(uint8_t *binsettings, -@@ -483,8 +544,22 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, ++ + static ssize_t populate_binsettings(uint8_t *binsettings, + struct Curl_easy *data) + { +@@ -165,6 +227,75 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) + } + } + ++static CURLcode http2_set_stream_priority(struct Curl_cfilter *cf, ++ struct Curl_easy *data, ++ int32_t stream_id, ++ int32_t dep_stream_id, ++ int32_t weight, ++ int exclusive ++ ) ++{ ++ int rv; ++ struct cf_h2_ctx *ctx = cf->ctx; ++ nghttp2_priority_spec pri_spec; ++ ++ nghttp2_priority_spec_init(&pri_spec, dep_stream_id, weight, exclusive); ++ rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, ++ stream_id, &pri_spec); ++ if(rv) { ++ failf(data, "nghttp2_submit_priority() failed: %s(%d)", ++ nghttp2_strerror(rv), rv); ++ return CURLE_HTTP2; ++ } ++ ++ return CURLE_OK; ++} ++ ++/* ++ * curl-impersonate: Firefox uses an elaborate scheme of http/2 streams to ++ * split the load for html/js/css/images. It builds a tree of streams with ++ * different weights (priorities) by default and communicates this to the ++ * server. Imitate that behavior. ++ */ ++static CURLcode http2_set_stream_priorities(struct Curl_cfilter *cf, ++ struct Curl_easy *data) ++{ ++ CURLcode result; ++ char *stream_delimiter = ","; ++ char *value_delimiter = ":"; ++ ++ if(!data->set.str[STRING_HTTP2_STREAMS]) ++ return CURLE_OK; ++ ++ char *tmp1 = strdup(data->set.str[STRING_HTTP2_STREAMS]); ++ char *end1; ++ char *stream = strtok_r(tmp1, stream_delimiter, &end1); ++ ++ while(stream != NULL) { ++ ++ char *tmp2 = strdup(stream); ++ char *end2; ++ ++ int32_t stream_id = atoi(strtok_r(tmp2, value_delimiter, &end2)); ++ int exclusive = atoi(strtok_r(NULL, value_delimiter, &end2)); ++ int32_t dep_stream_id = atoi(strtok_r(NULL, value_delimiter, &end2)); ++ int32_t weight = atoi(strtok_r(NULL, value_delimiter, &end2)); ++ ++ free(tmp2); ++ ++ result = http2_set_stream_priority(cf, data, stream_id, dep_stream_id, weight, exclusive); ++ if(result) { ++ free(tmp1); ++ return result; ++ } ++ ++ stream = strtok_r(NULL, stream_delimiter, &end1); ++ } ++ ++ free(tmp1); ++ return CURLE_OK; ++} ++ + static CURLcode h2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data); + +@@ -504,8 +635,22 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, } } @@ -1137,7 +1311,24 @@ index c666192fc..5a90b28ea 100644 if(rc) { failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", nghttp2_strerror(rc), rc); -@@ -1616,11 +1691,17 @@ out: +@@ -513,6 +658,16 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, + goto out; + } + ++ // curl-impersonate: set stream priorities ++ result = http2_set_stream_priorities(cf, data); ++ if(result) ++ goto out; ++ ++#define FIREFOX_DEFAULT_STREAM_ID (15) ++ /* Best effort to set the request's stream id to 15, like Firefox does. */ ++ // Let's ignore this for now, as it seems not to be targeted as fingerprints. ++ // nghttp2_session_set_next_stream_id(ctx->h2, FIREFOX_DEFAULT_STREAM_ID); ++ + /* all set, traffic will be send on connect */ + result = CURLE_OK; + CURL_TRC_CF(data, cf, "[0] created h2 session%s", +@@ -1747,11 +1902,19 @@ out: return rv; } @@ -1146,6 +1337,7 @@ index c666192fc..5a90b28ea 100644 + * instead of NGINX default stream weight. + */ +#define CHROME_DEFAULT_STREAM_WEIGHT (256) ++#define FIREFOX_DEFAULT_STREAM_WEIGHT (42) + static int sweight_wanted(const struct Curl_easy *data) { @@ -1153,13 +1345,35 @@ index c666192fc..5a90b28ea 100644 return data->set.priority.weight? - data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; + data->set.priority.weight : CHROME_DEFAULT_STREAM_WEIGHT; ++ // data->set.priority.weight : FIREFOX_DEFAULT_STREAM_WEIGHT; } static int sweight_in_effect(const struct Curl_easy *data) -@@ -1642,9 +1723,11 @@ static void h2_pri_spec(struct Curl_easy *data, +@@ -1759,6 +1922,7 @@ static int sweight_in_effect(const struct Curl_easy *data) + /* 0 weight is not set by user and we take the nghttp2 default one */ + return data->state.priority.weight? + data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; ++ // data->state.priority.weight : FIREFOX_DEFAULT_STREAM_WEIGHT; + } + + /* +@@ -1767,15 +1931,24 @@ static int sweight_in_effect(const struct Curl_easy *data) + * struct. + */ + ++/* ++ * curl-impersonate: By default Firefox uses stream 13 as the "parent" of the ++ * stream that fetches the main html resource of the web page. ++ */ ++#define FIREFOX_DEFAULT_STREAM_DEP (13) ++ + static void h2_pri_spec(struct Curl_easy *data, + nghttp2_priority_spec *pri_spec) + { struct Curl_data_priority *prio = &data->set.priority; struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent); int32_t depstream_id = depstream? depstream->id:0; ++ // int32_t depstream_id = depstream? depstream->id:FIREFOX_DEFAULT_STREAM_DEP; + /* curl-impersonate: Set stream exclusive flag to true. */ + int exclusive = 1; nghttp2_priority_spec_init(pri_spec, depstream_id, @@ -1169,24 +1383,22 @@ index c666192fc..5a90b28ea 100644 data->state.priority = *prio; } -@@ -1661,20 +1744,25 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, +@@ -1792,20 +1965,24 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, struct stream_ctx *stream = H2_STREAM_CTX(data); int rv = 0; -- if((sweight_wanted(data) != sweight_in_effect(data)) || -- (data->set.priority.exclusive != data->state.priority.exclusive) || -- (data->set.priority.parent != data->state.priority.parent) ) { + /* curl-impersonate: Check if stream exclusive flag is true. */ -+ if(stream && stream->id > 0 && -+ ((sweight_wanted(data) != sweight_in_effect(data)) || + if(stream && stream->id > 0 && + ((sweight_wanted(data) != sweight_in_effect(data)) || +- (data->set.priority.exclusive != data->state.priority.exclusive) || +- (data->set.priority.parent != data->state.priority.parent)) ) { + (data->set.priority.exclusive != 1) || + (data->set.priority.parent != data->state.priority.parent))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; h2_pri_spec(data, &pri_spec); -- DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY", -- stream->id)); +- CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); - DEBUGASSERT(stream->id != -1); - rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, - stream->id, &pri_spec); @@ -1194,8 +1406,7 @@ index c666192fc..5a90b28ea 100644 - goto out; + /* curl-impersonate: Don't send PRIORITY frames for main stream. */ + if(stream->id != 1) { -+ DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY", -+ stream->id)); ++ CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); + DEBUGASSERT(stream->id != -1); + rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, &pri_spec); @@ -1204,9 +1415,9 @@ index c666192fc..5a90b28ea 100644 + } } - while(!rv && nghttp2_session_want_write(ctx->h2)) + ctx->nw_out_blocked = 0; diff --git a/lib/http2.h b/lib/http2.h -index 562c05c99..b99c085d5 100644 +index 80e183480..8ee390b7e 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -31,7 +31,8 @@ @@ -1221,10 +1432,10 @@ index 562c05c99..b99c085d5 100644 * Store nghttp2 version info in this buffer. diff --git a/lib/impersonate.c b/lib/impersonate.c new file mode 100644 -index 000000000..00a2ba9c3 +index 000000000..4609b17a0 --- /dev/null +++ b/lib/impersonate.c -@@ -0,0 +1,745 @@ +@@ -0,0 +1,876 @@ +#include "curl_setup.h" + +#include @@ -1272,7 +1483,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome100", @@ -1314,7 +1527,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome101", @@ -1356,7 +1571,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome104", @@ -1398,7 +1615,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome107", @@ -1440,181 +1659,191 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome110", -+ .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_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"", -+ "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/110.0.0.0 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.7", -+ "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" -+ }, -+ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .target = "chrome110", ++ .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_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"", ++ "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/110.0.0.0 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.7", ++ "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" ++ }, ++ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome116", -+ .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_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"", -+ "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/116.0.0.0 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.7", -+ "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" -+ }, -+ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .target = "chrome116", ++ .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_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"", ++ "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/116.0.0.0 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.7", ++ "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" ++ }, ++ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome119", -+ .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_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"macOS\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 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.7", -+ "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" -+ }, -+ .http2_settings = "1:65536;2:0;4:6291456;6:262144", -+ .http2_window_update = 15663105, -+ .ech = "GREASE" ++ .target = "chrome119", ++ .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_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"macOS\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 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.7", ++ "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" ++ }, ++ .http2_settings = "1:65536;2:0;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .ech = "GREASE", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome120", -+ .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_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"macOS\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 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.7", -+ "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" -+ }, -+ .http2_settings = "1:65536;2:0;4:6291456;6:262144", -+ .http2_window_update = 15663105, -+ .ech = "GREASE" ++ .target = "chrome120", ++ .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_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"macOS\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 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.7", ++ "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" ++ }, ++ .http2_settings = "1:65536;2:0;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .ech = "GREASE", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome99_android", @@ -1656,7 +1885,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "edge99", @@ -1698,7 +1929,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "edge101", @@ -1740,7 +1973,9 @@ index 000000000..00a2ba9c3 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari15_3", @@ -1798,7 +2033,9 @@ index 000000000..00a2ba9c3 + }, + .http2_settings = "4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari15_5", @@ -1851,7 +2088,9 @@ index 000000000..00a2ba9c3 + }, + .http2_settings = "4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari17_2_ios", @@ -1907,7 +2146,9 @@ index 000000000..00a2ba9c3 + }, + .http2_settings = "2:0;4:2097152;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari17_0", @@ -1963,7 +2204,108 @@ index 000000000..00a2ba9c3 + }, + .http2_settings = "2:0;4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true ++ }, ++ { ++ .target = "okhttp4", /* not working */ ++ .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 = { ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", ++ "Sec-Fetch-Site: none", ++ "Accept-Encoding: gzip, deflate, br", ++ "Sec-Fetch-Mode: navigate", ++ "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", ++ "Accept-Language: en-US,en;q=0.9", ++ "Sec-Fetch-Dest: document" ++ }, ++ .http2_settings = "2:0;4:4194304;3:100", ++ .http2_window_update = 10485760, ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true ++ }, ++ { ++ .target = "firefox120", /* not working */ ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," ++ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256," ++ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256," ++ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256," ++ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384," ++ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," ++ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA," ++ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA," ++ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA," ++ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA," ++ "TLS_RSA_WITH_AES_128_GCM_SHA256," ++ "TLS_RSA_WITH_AES_256_GCM_SHA384," ++ "TLS_RSA_WITH_AES_128_CBC_SHA," ++ "TLS_RSA_WITH_AES_256_CBC_SHA", ++ .http_headers = { ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", ++ "Accept-Language: en-US,en;q=0.5", ++ "Accept-Encoding: gzip, deflate, br", ++ "Upgrade-Insecure-Requests: 1", ++ "Sec-Fetch-Dest: document", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-User: ?1", ++ "Te: trailers" ++ }, ++ .http2_settings = "1:65536;4:131072;5:16384", ++ .http2_window_update = 12517377, ++ .http2_pseudo_headers_order = "mpas", ++ .http2_streams = "3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + /* Last one must be NULL. */ @@ -1972,10 +2314,10 @@ index 000000000..00a2ba9c3 +}; diff --git a/lib/impersonate.h b/lib/impersonate.h new file mode 100644 -index 000000000..0158d5477 +index 000000000..a9e1e4b45 --- /dev/null +++ b/lib/impersonate.h -@@ -0,0 +1,47 @@ +@@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_IMPERSONATE_H +#define HEADER_CURL_IMPERSONATE_H + @@ -2010,8 +2352,11 @@ index 000000000..0158d5477 + const char *http2_pseudo_headers_order; + const char *http2_settings; + int http2_window_update; ++ const char *http2_streams; + bool tls_permute_extensions; + const char *ech; ++ const char *tls_extension_order; ++ bool tls_grease; + /* Other TLS options will come here in the future once they are + * configurable through curl_easy_setopt() */ +}; @@ -2024,13 +2369,13 @@ index 000000000..0158d5477 + +#endif /* HEADER_CURL_IMPERSONATE_H */ diff --git a/lib/multi.c b/lib/multi.c -index d1d32b793..3b49a1b4c 100644 +index 5456113be..85841f769 100644 --- a/lib/multi.c +++ b/lib/multi.c -@@ -424,7 +424,8 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ +@@ -396,7 +396,8 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ + Curl_llist_init(&multi->msgsent, NULL); - /* -1 means it not set by user, use the default value */ - multi->maxconnects = -1; + multi->multiplexing = TRUE; - multi->max_concurrent_streams = 100; + /* curl-impersonate: Use 1000 concurrent streams like Chrome. */ + multi->max_concurrent_streams = 1000; @@ -2038,18 +2383,18 @@ index d1d32b793..3b49a1b4c 100644 #ifdef USE_WINSOCK multi->wsa_event = WSACreateEvent(); diff --git a/lib/setopt.c b/lib/setopt.c -index 0c3b9634d..a65c5d99e 100644 +index a08140cce..05468ff49 100644 --- a/lib/setopt.c +++ b/lib/setopt.c -@@ -50,6 +50,7 @@ - #include "multiif.h" +@@ -51,6 +51,7 @@ #include "altsvc.h" #include "hsts.h" + #include "tftp.h" +#include "slist.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" -@@ -712,6 +713,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -710,6 +711,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) va_arg(param, char *)); break; @@ -2073,7 +2418,7 @@ index 0c3b9634d..a65c5d99e 100644 #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXYHEADER: /* -@@ -2410,6 +2428,27 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -2394,6 +2412,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; @@ -2101,9 +2446,9 @@ index 0c3b9634d..a65c5d99e 100644 #endif case CURLOPT_IPRESOLVE: arg = va_arg(param, long); -@@ -2953,6 +2992,31 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -2936,6 +2975,42 @@ 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; + data->set.ssl_enable_alpn = (0 != va_arg(param, long)); break; + case CURLOPT_SSL_ENABLE_ALPS: + data->set.ssl_enable_alps = (0 != va_arg(param, long)) ? TRUE : FALSE; @@ -2114,6 +2459,13 @@ index 0c3b9634d..a65c5d99e 100644 + case CURLOPT_SSL_PERMUTE_EXTENSIONS: + data->set.ssl_permute_extensions = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; ++ case CURLOPT_TLS_GREASE: ++ data->set.tls_grease = (0 != va_arg(param, long)) ? TRUE : FALSE; ++ break; ++ case CURLOPT_TLS_EXTENSION_ORDER: ++ result = Curl_setstropt(&data->set.str[STRING_TLS_EXTENSION_ORDER], ++ va_arg(param, char *)); ++ break; +#ifdef USE_HTTP2 + case CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER: + result = Curl_setstropt(&data->set.str[STRING_HTTP2_PSEUDO_HEADERS_ORDER], @@ -2129,11 +2481,15 @@ index 0c3b9634d..a65c5d99e 100644 + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.http2_window_update = arg; + break; ++ case CURLOPT_HTTP2_STREAMS: ++ result = Curl_setstropt(&data->set.str[STRING_HTTP2_STREAMS], ++ va_arg(param, char *)); ++ break; +#endif #ifdef USE_UNIX_SOCKETS case CURLOPT_UNIX_SOCKET_PATH: data->set.abstract_unix_socket = FALSE; -@@ -3146,6 +3210,31 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +@@ -3128,6 +3203,31 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.ws_raw_mode = raw; break; } @@ -2166,7 +2522,7 @@ index 0c3b9634d..a65c5d99e 100644 case CURLOPT_QUICK_EXIT: data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L; diff --git a/lib/strerror.c b/lib/strerror.c -index bd9cc535c..971568792 100644 +index 0d5f9276f..232116383 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -319,6 +319,11 @@ curl_easy_strerror(CURLcode error) @@ -2182,10 +2538,10 @@ index bd9cc535c..971568792 100644 case CURLE_OBSOLETE20: case CURLE_OBSOLETE24: diff --git a/lib/transfer.c b/lib/transfer.c -index d2ff0c24c..56e2090b6 100644 +index 96f1fde75..4a57497c7 100644 --- a/lib/transfer.c +++ b/lib/transfer.c -@@ -106,7 +106,15 @@ char *Curl_checkheaders(const struct Curl_easy *data, +@@ -104,7 +104,15 @@ char *Curl_checkheaders(const struct Curl_easy *data, DEBUGASSERT(thislen); DEBUGASSERT(thisheader[thislen-1] != ':'); @@ -2203,10 +2559,10 @@ index d2ff0c24c..56e2090b6 100644 Curl_headersep(head->data[thislen]) ) return head->data; diff --git a/lib/url.c b/lib/url.c -index b37d13f8f..f1b3b5440 100644 +index b81785fe2..699e8037a 100644 --- a/lib/url.c +++ b/lib/url.c -@@ -444,6 +444,11 @@ CURLcode Curl_close(struct Curl_easy **datap) +@@ -322,6 +322,11 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->state.aptr.proxyuser); Curl_safefree(data->state.aptr.proxypasswd); @@ -2218,7 +2574,7 @@ index b37d13f8f..f1b3b5440 100644 #ifndef CURL_DISABLE_DOH if(data->req.doh) { Curl_dyn_free(&data->req.doh->probe[0].serverdoh); -@@ -595,6 +600,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) +@@ -468,6 +473,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->tcp_fastopen = FALSE; set->tcp_nodelay = TRUE; set->ssl_enable_alpn = TRUE; @@ -2226,17 +2582,7 @@ index b37d13f8f..f1b3b5440 100644 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; -@@ -3584,6 +3590,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]; -@@ -3695,6 +3704,11 @@ static CURLcode create_conn(struct Curl_easy *data, +@@ -3672,6 +3678,11 @@ static CURLcode create_conn(struct Curl_easy *data, (default) */ if(data->set.ssl_enable_alpn) conn->bits.tls_enable_alpn = TRUE; @@ -2249,7 +2595,7 @@ index b37d13f8f..f1b3b5440 100644 if(waitpipe) diff --git a/lib/urldata.h b/lib/urldata.h -index f02e66541..058ace33c 100644 +index ff661482e..29b9d37fa 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -53,6 +53,15 @@ @@ -2268,7 +2614,7 @@ index f02e66541..058ace33c 100644 #ifdef USE_WEBSOCKETS /* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, * the rest are internal information. If we use higher bits we only do this on -@@ -277,6 +286,8 @@ struct ssl_primary_config { +@@ -290,6 +299,8 @@ struct ssl_primary_config { char *password; /* TLS password (for, e.g., SRP) */ #endif char *curves; /* list of curves to use */ @@ -2277,7 +2623,7 @@ index f02e66541..058ace33c 100644 unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ unsigned int version_max; /* max supported version the client wants to use */ unsigned char version; /* what version the client wants to use */ -@@ -526,6 +537,9 @@ struct ConnectBits { +@@ -541,6 +552,9 @@ struct ConnectBits { BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ @@ -2287,7 +2633,7 @@ index f02e66541..058ace33c 100644 #ifndef CURL_DISABLE_DOH BIT(doh); #endif -@@ -1395,6 +1409,19 @@ struct UrlState { +@@ -1452,6 +1466,19 @@ struct UrlState { CURLcode hresult; /* used to pass return codes back from hyper callbacks */ #endif @@ -2307,20 +2653,22 @@ index f02e66541..058ace33c 100644 /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { -@@ -1563,6 +1590,12 @@ enum dupstring { - STRING_DNS_LOCAL_IP6, +@@ -1628,6 +1655,14 @@ enum dupstring { STRING_SSL_EC_CURVES, STRING_AWS_SIGV4, /* Parameters for V4 signature */ + STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */ + STRING_SSL_SIG_HASH_ALGS, + STRING_SSL_CERT_COMPRESSION, + STRING_HTTP2_PSEUDO_HEADERS_ORDER, + STRING_HTTP2_SETTINGS, ++ STRING_HTTP2_STREAMS, + STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */ + STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */ ++ STRING_TLS_EXTENSION_ORDER, /* -- end of null-terminated strings -- */ -@@ -1857,6 +1890,9 @@ struct UserDefined { +@@ -1921,6 +1956,9 @@ struct UserDefined { BIT(tcp_keepalive); /* use TCP keepalives */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(ssl_enable_alpn);/* TLS ALPN extension? */ @@ -2330,7 +2678,7 @@ index f02e66541..058ace33c 100644 BIT(path_as_is); /* allow dotdots? */ BIT(pipewait); /* wait for multiplex status before starting a new connection */ -@@ -1877,6 +1913,10 @@ struct UserDefined { +@@ -1941,6 +1979,11 @@ struct UserDefined { #ifdef USE_WEBSOCKETS BIT(ws_raw_mode); #endif @@ -2338,19 +2686,22 @@ index f02e66541..058ace33c 100644 + int tls_ech; /* TLS ECH configuration */ +#endif + int http2_window_update; ++ BIT(tls_grease); }; struct Names { diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index 6543fb19a..58d243f1e 100644 +index 8c8f43e83..030832aeb 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c -@@ -79,6 +79,21 @@ +@@ -79,9 +79,24 @@ #include #include #include +#include -+ + #include + #include + +#ifdef HAVE_LIBZ +#include +#endif @@ -2364,10 +2715,11 @@ index 6543fb19a..58d243f1e 100644 +# endif +# include "curl_base64.h" +#endif /* USE_ECH */ - ++ #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) #include -@@ -266,6 +281,113 @@ + #endif +@@ -276,6 +291,113 @@ #define HAVE_OPENSSL_VERSION #endif @@ -2478,10 +2830,10 @@ index 6543fb19a..58d243f1e 100644 + +#endif + - #ifdef OPENSSL_IS_BORINGSSL + #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) typedef uint32_t sslerr_t; #else -@@ -2583,6 +2705,151 @@ static const char *tls_rt_type(int type) +@@ -2601,6 +2723,151 @@ static const char *tls_rt_type(int type) } } @@ -2633,7 +2985,7 @@ index 6543fb19a..58d243f1e 100644 /* * Our callback from the SSL/TLS layers. */ -@@ -3536,7 +3803,14 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3571,7 +3838,14 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, ctx_options = SSL_OP_ALL; #ifdef SSL_OP_NO_TICKET @@ -2649,7 +3001,7 @@ index 6543fb19a..58d243f1e 100644 #endif #ifdef SSL_OP_NO_COMPRESSION -@@ -3603,6 +3877,16 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3638,6 +3912,18 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2658,15 +3010,17 @@ index 6543fb19a..58d243f1e 100644 + SSL_CTX_set_mode(backend->ctx, + SSL_MODE_CBC_RECORD_SPLITTING | SSL_MODE_ENABLE_FALSE_START); + -+ /* curl-impersonate: Enable TLS extensions 5 - status_request and -+ * 18 - signed_certificate_timestamp. */ ++ /* curl-impersonate: Enable TLS extensions 18 - signed_certificate_timestamp. */ ++ // XXX: Firefox does not enable this + SSL_CTX_enable_signed_cert_timestamps(backend->ctx); ++ ++ /* curl-impersonate: Enable TLS extensions 5 - status_request */ + SSL_CTX_enable_ocsp_stapling(backend->ctx); + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { if(!result && !cert_stuff(data, backend->ctx, -@@ -3656,6 +3940,35 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3691,6 +3977,35 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2702,7 +3056,7 @@ index 6543fb19a..58d243f1e 100644 #ifdef USE_OPENSSL_SRP if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { char * const ssl_username = ssl_config->primary.username; -@@ -3681,6 +3994,30 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3716,6 +4031,38 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2712,8 +3066,10 @@ index 6543fb19a..58d243f1e 100644 + * and SSLClientSocketImpl::Init() + * in the Chromium's source code. */ + -+ /* Enable TLS GREASE. */ -+ SSL_CTX_set_grease_enabled(backend->ctx, 1); ++ /* curl-impersonate: Enable TLS GREASE. */ ++ if(data->set.tls_grease) { ++ SSL_CTX_set_grease_enabled(backend->ctx, 1); ++ } + + /* + * curl-impersonate: Enable TLS extension permutation, enabled by default @@ -2723,6 +3079,12 @@ index 6543fb19a..58d243f1e 100644 + SSL_CTX_set_permute_extensions(backend->ctx, 1); + } + ++ /* curl-impersonate: Set TLS extensions order. ++ */ ++ if(data->set.str[STRING_TLS_EXTENSION_ORDER]) { ++ SSL_CTX_set_extension_order(backend->ctx, data->set.str[STRING_TLS_EXTENSION_ORDER]); ++ } ++ + if(conn_config->cert_compression && + add_cert_compression(data, + backend->ctx, @@ -2733,7 +3095,7 @@ index 6543fb19a..58d243f1e 100644 /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with -@@ -3727,6 +4064,23 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3771,6 +4118,24 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_app_data(backend->handle, cf); @@ -2744,6 +3106,7 @@ index 6543fb19a..58d243f1e 100644 + + for(i = 0; i < connssl->alps->count; ++i) { + /* curl-impersonate: Add the ALPS extension (17513) like Chrome does. */ ++ // XXX: Firefox does not enable this. + SSL_add_application_settings(backend->handle, connssl->alps->entries[i], + strlen(connssl->alps->entries[i]), NULL, + 0); @@ -2757,7 +3120,7 @@ index 6543fb19a..58d243f1e 100644 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) -@@ -3755,6 +4109,21 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3794,6 +4159,21 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2778,10 +3141,10 @@ index 6543fb19a..58d243f1e 100644 + SSL_set_app_data(backend->handle, cf); - if(ssl_config->primary.sessionid) { -@@ -3946,6 +4315,60 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, - SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle)); + connssl->reused_session = FALSE; +@@ -4005,6 +4385,60 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, + negotiated_group_name? negotiated_group_name : "[blank]", + OBJ_nid2sn(psigtype_nid)); +#ifdef USE_ECH +# ifndef OPENSSL_IS_BORINGSSL @@ -2841,10 +3204,10 @@ index 6543fb19a..58d243f1e 100644 /* Sets data and len to negotiated protocol, len is 0 if no protocol was * negotiated diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c -index 32334016b..1a8a75ade 100644 +index 34eda3e5a..eda3f6d58 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c -@@ -141,6 +141,9 @@ static const struct alpn_spec ALPN_SPEC_H11 = { +@@ -139,6 +139,9 @@ static const struct alpn_spec ALPN_SPEC_H11 = { static const struct alpn_spec ALPN_SPEC_H2_H11 = { { ALPN_H2, ALPN_HTTP_1_1 }, 2 }; @@ -2855,7 +3218,7 @@ index 32334016b..1a8a75ade 100644 static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) @@ -155,6 +158,17 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) - #endif + Avoid "http/1.0" because some servers don't support it. */ return &ALPN_SPEC_H11; } + @@ -2872,16 +3235,16 @@ index 32334016b..1a8a75ade 100644 #endif /* USE_SSL */ -@@ -182,6 +196,8 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, - strcasecompare(data->cipher_list, needle->cipher_list) && - strcasecompare(data->cipher_list13, needle->cipher_list13) && - strcasecompare(data->curves, needle->curves) && -+ strcasecompare(data->sig_hash_algs, needle->sig_hash_algs) && -+ strcasecompare(data->cert_compression, needle->cert_compression) && - strcasecompare(data->CRLfile, needle->CRLfile) && - strcasecompare(data->pinned_key, needle->pinned_key)) +@@ -198,6 +212,8 @@ match_ssl_primary_config(struct Curl_easy *data, + strcasecompare(c1->cipher_list, c2->cipher_list) && + strcasecompare(c1->cipher_list13, c2->cipher_list13) && + strcasecompare(c1->curves, c2->curves) && ++ strcasecompare(c1->sig_hash_algs, c2->sig_hash_algs) && ++ strcasecompare(c1->cert_compression, c2->cert_compression) && + strcasecompare(c1->CRLfile, c2->CRLfile) && + strcasecompare(c1->pinned_key, c2->pinned_key)) return TRUE; -@@ -212,6 +228,8 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, +@@ -242,6 +258,8 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, CLONE_STRING(cipher_list13); CLONE_STRING(pinned_key); CLONE_STRING(curves); @@ -2890,7 +3253,7 @@ index 32334016b..1a8a75ade 100644 CLONE_STRING(CRLfile); #ifdef USE_TLS_SRP CLONE_STRING(username); -@@ -234,6 +252,8 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +@@ -264,6 +282,8 @@ static 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); @@ -2899,7 +3262,16 @@ index 32334016b..1a8a75ade 100644 Curl_safefree(sslc->CRLfile); #ifdef USE_TLS_SRP Curl_safefree(sslc->username); -@@ -318,7 +338,8 @@ static bool ssl_prefs_check(struct Curl_easy *data) +@@ -287,6 +307,8 @@ CURLcode Curl_ssl_easy_config_complete(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]; + #ifdef USE_TLS_SRP + data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +@@ -453,7 +475,8 @@ static bool ssl_prefs_check(struct Curl_easy *data) } static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, @@ -2909,7 +3281,7 @@ index 32334016b..1a8a75ade 100644 { struct ssl_connect_data *ctx; -@@ -328,6 +349,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, +@@ -463,6 +486,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, return NULL; ctx->alpn = alpn; @@ -2917,7 +3289,7 @@ index 32334016b..1a8a75ade 100644 ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); -@@ -1760,8 +1782,11 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, +@@ -1883,8 +1907,11 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, DEBUGASSERT(data->conn); @@ -2931,7 +3303,7 @@ index 32334016b..1a8a75ade 100644 if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; -@@ -1811,6 +1836,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, +@@ -1934,6 +1961,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, struct ssl_connect_data *ctx; CURLcode result; bool use_alpn = conn->bits.tls_enable_alpn; @@ -2939,7 +3311,7 @@ index 32334016b..1a8a75ade 100644 int httpwant = CURL_HTTP_VERSION_1_1; #ifdef USE_HTTP2 -@@ -1820,7 +1846,8 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, +@@ -1943,7 +1971,8 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } #endif @@ -2950,28 +3322,28 @@ index 32334016b..1a8a75ade 100644 result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h -index f24dca15b..595d437f9 100644 +index f1856bd33..fe9f5c266 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -44,6 +44,8 @@ struct Curl_ssl_session; "ALPN: server did not agree on a protocol. Uses default." #define VTLS_INFOF_ALPN_OFFER_1STR \ - "ALPN: offers %s" + "ALPN: curl offers %s" +#define VTLS_INFOF_ALPS_OFFER_1STR \ + "ALPS: offers %s" #define VTLS_INFOF_ALPN_ACCEPTED_1STR \ ALPN_ACCEPTED "%s" #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h -index ed49339e4..9fddc7494 100644 +index af7ae552e..f22147e22 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h -@@ -73,6 +73,7 @@ struct ssl_connect_data { - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ +@@ -70,6 +70,7 @@ struct ssl_connect_data { + ssl_connect_state connecting_state; + struct ssl_peer peer; const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ + const struct alpn_spec *alps; /* ALPS to use or NULL for none */ - struct ssl_backend_data *backend; /* vtls backend specific props */ + void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ diff --git a/libcurl.pc.in b/libcurl.pc.in @@ -2987,10 +3359,10 @@ index 9db6b0f89..14c2f23e0 100644 Libs.private: @LIBCURL_LIBS@ Cflags: -I${includedir} @CPPFLAG_CURL_STATICLIB@ diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 -index caa2b14cb..0a0af4361 100644 +index 35ba19866..3bab99f62 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 -@@ -373,42 +373,55 @@ AC_DEFUN([CURL_CONVERT_INCLUDE_TO_ISYSTEM], [ +@@ -382,42 +382,55 @@ AC_DEFUN([CURL_CONVERT_INCLUDE_TO_ISYSTEM], [ AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl AC_REQUIRE([CURL_CHECK_COMPILER])dnl AC_MSG_CHECKING([convert -I options to -isystem]) @@ -3082,7 +3454,7 @@ index caa2b14cb..0a0af4361 100644 diff --git a/src/Makefile.am b/src/Makefile.am -index f24cb6924..30b4fdb0a 100644 +index dced53e0f..dee8a2fc3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,7 +43,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \ @@ -3094,7 +3466,7 @@ index f24cb6924..30b4fdb0a 100644 SUBDIRS = ../docs -@@ -54,9 +54,9 @@ endif +@@ -55,9 +55,9 @@ AM_CPPFLAGS += -DBUILDING_CURL include Makefile.inc # CURL_FILES comes from Makefile.inc @@ -3106,23 +3478,39 @@ index f24cb6924..30b4fdb0a 100644 $(CURL_RCFILES): tool_version.h endif -@@ -67,9 +67,9 @@ CFLAGS += @CURL_CFLAG_EXTRAS@ +@@ -70,9 +70,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@ +-curl_LDADD = $(top_builddir)/lib/libcurl.la @SSL_LIBS@ @ZLIB_LIBS@ @CURL_NETWORK_AND_TIME_LIBS@ ++curl_impersonate_chrome_LDADD = $(top_builddir)/lib/libcurl-impersonate-chrome.la @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.c b/src/tool_cfgable.c -index ec5698ba2..df8d5913c 100644 +index 906e23e14..5c6613a93 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c -@@ -172,6 +172,14 @@ static void free_config_fields(struct OperationConfig *config) +@@ -95,6 +95,15 @@ static void free_config_fields(struct OperationConfig *config) + Curl_safefree(config->proto_str); + Curl_safefree(config->proto_redir_str); + ++ // Impersonate ++ Curl_safefree(config->ssl_sig_hash_algs); ++ Curl_safefree(config->ssl_cert_compression); ++ Curl_safefree(config->http2_pseudo_headers_order); ++ Curl_safefree(config->http2_settings); ++ Curl_safefree(config->http2_streams); ++ Curl_safefree(config->tls_extension_order); ++ // End Impersonate ++ + urlnode = config->url_list; + while(urlnode) { + struct getout *next = urlnode->next; +@@ -175,6 +184,14 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->aws_sigv4); Curl_safefree(config->proto_str); Curl_safefree(config->proto_redir_str); @@ -3138,10 +3526,10 @@ index ec5698ba2..df8d5913c 100644 void config_free(struct OperationConfig *config) diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h -index 9a15659bc..d7355cfd8 100644 +index 57e8fce52..b5d7019b1 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h -@@ -160,8 +160,13 @@ struct OperationConfig { +@@ -161,8 +161,16 @@ struct OperationConfig { bool crlf; char *customrequest; char *ssl_ec_curves; @@ -3152,10 +3540,13 @@ index 9a15659bc..d7355cfd8 100644 + char *http2_pseudo_headers_order; + char *http2_settings; + long http2_window_update; ++ char *http2_streams; ++ bool tls_grease; ++ char *tls_extension_order; long httpversion; bool http09_allowed; bool nobuffer; -@@ -191,6 +196,7 @@ struct OperationConfig { +@@ -192,6 +200,7 @@ struct OperationConfig { struct curl_slist *prequote; long ssl_version; long ssl_version_max; @@ -3163,7 +3554,7 @@ index 9a15659bc..d7355cfd8 100644 long proxy_ssl_version; long ip_version; long create_file_mode; /* CURLOPT_NEW_FILE_PERMS */ -@@ -266,6 +272,8 @@ struct OperationConfig { +@@ -268,6 +277,8 @@ struct OperationConfig { bool proxy_ssl_auto_client_cert; /* proxy version of ssl_auto_client_cert */ char *oauth_bearer; /* OAuth 2.0 bearer token */ bool noalpn; /* enable/disable TLS ALPN extension */ @@ -3172,7 +3563,7 @@ index 9a15659bc..d7355cfd8 100644 char *unix_socket_path; /* path to Unix domain socket */ bool abstract_unix_socket; /* path to an abstract Unix domain socket */ bool falsestart; -@@ -295,6 +303,11 @@ struct OperationConfig { +@@ -298,6 +309,11 @@ struct OperationConfig { struct State state; /* for create_transfer() */ bool rm_partial; /* on error, remove partially written output files */ @@ -3185,15 +3576,15 @@ index 9a15659bc..d7355cfd8 100644 struct GlobalConfig { diff --git a/src/tool_getparam.c b/src/tool_getparam.c -index c9810e9d4..8bcd914db 100644 +index 5fa1ace10..80277bbd9 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c -@@ -287,6 +287,17 @@ static const struct LongShort aliases[]= { +@@ -296,6 +296,20 @@ 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}, ++ {"ET", "signature-hashes", ARG_STRING}, ++ {"EU", "alps", ARG_BOOL}, + {"EI", "cert-compression", ARG_STRING}, + {"EJ", "tls-session-ticket", ARG_BOOL}, + {"EK", "http2-pseudo-headers-order", ARG_STRING}, @@ -3203,19 +3594,22 @@ index c9810e9d4..8bcd914db 100644 +#ifdef USE_ECH + {"ER", "ech", ARG_STRING}, +#endif ++ {"EV", "http2-streams", ARG_STRING}, ++ {"EW", "tls-extension-order", ARG_STRING}, ++ {"EX", "tls-grease", ARG_BOOL}, {"f", "fail", ARG_BOOL}, {"fa", "fail-early", ARG_BOOL}, {"fb", "styled-output", ARG_BOOL}, -@@ -1940,6 +1951,62 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ +@@ -2124,6 +2138,78 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ GetStr(&config->ssl_ec_curves, nextarg); break; -+ case 'G': ++ case 'T': + /* --signature-hashes */ + GetStr(&config->ssl_sig_hash_algs, nextarg); + break; + -+ case 'H': ++ case 'U': + /* --alps */ + config->alps = toggle; + break; @@ -3241,9 +3635,9 @@ index c9810e9d4..8bcd914db 100644 + break; + + case 'M': -+ /* --tls-permute-extensions */ -+ config->ssl_permute_extensions = toggle; -+ break; ++ /* --tls-permute-extensions */ ++ config->ssl_permute_extensions = toggle; ++ break; + + case 'N': + /* --http2-window-update */ @@ -3254,6 +3648,22 @@ index c9810e9d4..8bcd914db 100644 + return PARAM_BAD_NUMERIC; + break; + ++ case 'V': ++ /* --http2-streams */ ++ GetStr(&config->http2_streams, nextarg); ++ break; ++ ++ case 'W': ++ /* --tls-extension-order */ ++ GetStr(&config->tls_extension_order, nextarg); ++ // printf("setting is %s\n", config->tls_extension_order); ++ break; ++ ++ case 'X': ++ /* --tls-grease */ ++ config->tls_grease = toggle; ++ break; ++ +#ifdef USE_ECH + case 'R': + if(strlen(nextarg) != 6 || !strncasecompare("GREASE", nextarg, 6)) { @@ -3267,13 +3677,13 @@ index c9810e9d4..8bcd914db 100644 + break; +#endif default: /* unknown flag */ - return PARAM_OPTION_UNKNOWN; - } + err = PARAM_OPTION_UNKNOWN; + break; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c -index 61550de72..c3b99ab72 100644 +index 4e7a6dd63..8093b7f8e 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c -@@ -108,6 +108,27 @@ const struct helptxt helptext[] = { +@@ -111,6 +111,27 @@ const struct helptxt helptext[] = { {" --curves ", "(EC) TLS key exchange algorithm(s) to request", CURLHELP_TLS}, @@ -3301,7 +3711,7 @@ index 61550de72..c3b99ab72 100644 {"-d, --data ", "HTTP POST data", CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, -@@ -165,6 +186,11 @@ const struct helptxt helptext[] = { +@@ -168,6 +189,11 @@ const struct helptxt helptext[] = { {"-D, --dump-header ", "Write the received headers to ", CURLHELP_HTTP | CURLHELP_FTP}, @@ -3313,7 +3723,7 @@ index 61550de72..c3b99ab72 100644 {" --egd-file ", "EGD socket path for random data", CURLHELP_TLS}, -@@ -387,6 +413,9 @@ const struct helptxt helptext[] = { +@@ -396,6 +422,9 @@ const struct helptxt helptext[] = { {" --no-alpn", "Disable the ALPN TLS extension", CURLHELP_TLS | CURLHELP_HTTP}, @@ -3324,10 +3734,10 @@ index 61550de72..c3b99ab72 100644 "Disable buffering of the output stream", CURLHELP_CURL}, diff --git a/src/tool_operate.c b/src/tool_operate.c -index ead7dca63..c3d1aecf5 100644 +index c805b7732..4f948b09c 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c -@@ -1493,6 +1493,22 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -1522,6 +1522,26 @@ static CURLcode single_transfer(struct GlobalConfig *global, return result; } @@ -3346,11 +3756,15 @@ index ead7dca63..c3d1aecf5 100644 + CURLOPT_HTTP2_WINDOW_UPDATE, + config->http2_window_update); + ++ if(config->http2_streams) ++ my_setopt_str(curl, ++ CURLOPT_HTTP2_STREAMS, ++ config->http2_streams); + } /* (proto_http) */ if(proto_ftp) -@@ -1581,6 +1597,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -1610,6 +1630,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); @@ -3365,18 +3779,27 @@ index ead7dca63..c3d1aecf5 100644 if(config->writeout) my_setopt_str(curl, CURLOPT_CERTINFO, 1L); -@@ -1914,6 +1938,10 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -1942,6 +1970,19 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_str(curl, CURLOPT_PROXY_TLS13_CIPHERS, config->proxy_cipher13_list); -+ /* curl-impersonate */ ++ /* curl-impersonate */ + if(config->ssl_permute_extensions) -+ my_setopt(curl, CURLOPT_SSL_PERMUTE_EXTENSIONS, 1L); ++ my_setopt(curl, CURLOPT_SSL_PERMUTE_EXTENSIONS, 1L); ++ ++ /* curl-impersonate */ ++ if (config->tls_grease) ++ my_setopt(curl, CURLOPT_TLS_GREASE, 1L); ++ ++ /* curl-impersonate */ ++ if(config->tls_extension_order) ++ // printf("setting is %s\n", config->tls_extension_order); ++ my_setopt_str(curl, CURLOPT_TLS_EXTENSION_ORDER, config->tls_extension_order); + /* new in libcurl 7.9.2: */ if(config->disable_epsv) /* disable it */ -@@ -2123,6 +2151,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -2151,6 +2192,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); } @@ -3391,7 +3814,7 @@ index ead7dca63..c3d1aecf5 100644 /* new in 7.40.0, abstract support added in 7.53.0 */ if(config->unix_socket_path) { if(config->abstract_unix_socket) { -@@ -2166,6 +2202,16 @@ static CURLcode single_transfer(struct GlobalConfig *global, +@@ -2199,6 +2248,16 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->hsts) my_setopt_str(curl, CURLOPT_HSTS, config->hsts); @@ -3409,7 +3832,7 @@ index ead7dca63..c3d1aecf5 100644 per->retry_sleep_default = (config->retry_delay) ? config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ diff --git a/src/tool_setopt.c b/src/tool_setopt.c -index 0f3cc83b9..1c14e9854 100644 +index de3b78fab..e034c9848 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -153,6 +153,8 @@ static const struct NameValue setopt_nv_CURLNONZERODEFAULTS[] = { diff --git a/firefox/Dockerfile b/firefox/Dockerfile index 87377c41..2f657758 100644 --- a/firefox/Dockerfile +++ b/firefox/Dockerfile @@ -69,7 +69,7 @@ RUN cd ${NGHTTP2_VERSION} && \ make && make install # Download curl. -ARG CURL_VERSION=curl-8.1.1 +ARG CURL_VERSION=curl-8.5.0 RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz RUN tar xf ${CURL_VERSION}.tar.xz diff --git a/firefox/Dockerfile.alpine b/firefox/Dockerfile.alpine index c702d150..8fb0bce6 100644 --- a/firefox/Dockerfile.alpine +++ b/firefox/Dockerfile.alpine @@ -58,7 +58,7 @@ RUN cd ${NGHTTP2_VERSION} && \ make && make install # Download curl. -ARG CURL_VERSION=curl-8.1.1 +ARG CURL_VERSION=curl-8.5.0 RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz RUN tar xf ${CURL_VERSION}.tar.xz diff --git a/tests/README.md b/tests/README.md index ddab00b3..072ad6ab 100644 --- a/tests/README.md +++ b/tests/README.md @@ -21,7 +21,7 @@ For each supported browser, the following tests are performed: ## What's missing The following tests are still missing: -- [ ] Test that `curl-impersonate` sends the same HTTP/2 SETTINGS as the browser. -- [ ] Capture traffic automatically from different browsers + +- [x] Test that `curl-impersonate` sends the same HTTP/2 SETTINGS as the browser. - [x] Update safari versions, double `rsa_pss_rsae_sha384` diff --git a/tests/targets.yaml b/tests/targets.yaml index b5086778..0c16a234 100644 --- a/tests/targets.yaml +++ b/tests/targets.yaml @@ -63,34 +63,10 @@ - null - null - safari_17.2_iOS -# - - curl_ff91esr +# - - curl_firefox120 # - null # - null -# - firefox_91.6.0esr_win10 -# - - curl_ff95 -# - null -# - null -# - firefox_95.0.2_win10 -# - - curl_ff98 -# - null -# - null -# - firefox_98.0_win10 -# - - curl_ff100 -# - null -# - null -# - firefox_100.0_win10 -# - - curl_ff102 -# - null -# - null -# - firefox_102.0_win10 -# - - curl_ff109 -# - null -# - null -# - firefox_109.0_win10 -# - - curl_ff117 -# - null -# - null -# - firefox_117.0.1_win10 +# - firefox_120_macOS # Test libcurl-impersonate by loading it with LD_PRELOAD to an app # linked against the regular libcurl and setting the @@ -160,30 +136,6 @@ - libcurl-impersonate-chrome - safari_17.2_iOS # - - minicurl -# - CURL_IMPERSONATE: ff91esr -# - libcurl-impersonate-ff -# - firefox_91.6.0esr_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff95 -# - libcurl-impersonate-ff -# - firefox_95.0.2_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff98 -# - libcurl-impersonate-ff -# - firefox_98.0_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff100 -# - libcurl-impersonate-ff -# - firefox_100.0_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff102 -# - libcurl-impersonate-ff -# - firefox_102.0_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff109 -# - libcurl-impersonate-ff -# - firefox_109.0_win10 -# - - minicurl -# - CURL_IMPERSONATE: ff117 -# - libcurl-impersonate-ff -# - firefox_117.0.1_win10 +# - CURL_IMPERSONATE: firefox120 +# - libcurl-impersonate-chrome +# - firefox_120_macOS diff --git a/win/build.sh b/win/build.sh index 66e7e53f..53aa3efc 100644 --- a/win/build.sh +++ b/win/build.sh @@ -38,7 +38,7 @@ export OPENSSL_LIBPATH=$PWD/boringssl/lib export OPENSSL_LIBS='-lssl -lcrypto' -CURL_VERSION=curl-8_1_1 +CURL_VERSION=curl-8_5_0 curl -L https://github.com/curl/curl/archive/${CURL_VERSION}.zip -o curl.zip unzip -q -o curl.zip