Files
MoFin/venv/lib/python3.12/site-packages/curl_cffi/requests/impersonate.py
T
知微 fa45d8aa5f fix: 小果地址统一node122(兼容LAN+EasyTier)
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
  Privoxy对node122:18003返回500,直连正常
2026-06-30 02:56:35 +08:00

456 lines
13 KiB
Python

import warnings
from dataclasses import dataclass
from enum import Enum
from typing import Literal, Optional, TypedDict
from ..const import CurlOpt, CurlSslVersion
from ..utils import CurlCffiWarning
BrowserTypeLiteral = Literal[
# Edge
"edge99",
"edge101",
# Chrome
"chrome99",
"chrome100",
"chrome101",
"chrome104",
"chrome107",
"chrome110",
"chrome116",
"chrome119",
"chrome120",
"chrome123",
"chrome124",
"chrome131",
"chrome133a",
"chrome136",
"chrome142",
"chrome145",
"chrome146",
"chrome99_android",
"chrome131_android",
# Safari
"safari153",
"safari155",
"safari170",
"safari172_ios",
"safari180",
"safari180_ios",
"safari184",
"safari184_ios",
"safari260",
"safari2601",
"safari260_ios",
# Firefox
"firefox133",
"firefox135",
"firefox144",
"firefox147",
"tor145",
# alias
"chrome",
"edge",
"safari",
"safari_ios",
"safari_beta",
"safari_ios_beta",
"chrome_android",
"firefox",
# deprecated aliases
"safari15_3",
"safari15_5",
"safari17_0",
"safari17_2_ios",
"safari18_0",
"safari18_0_ios",
"safari18_4",
"safari18_4_ios",
# Canonical names
# "edge_99",
# "edge_101",
# "safari_15.3_macos",
# "safari_15.5_macos",
# "safari_17.2_ios",
# "safari_17.0_macos",
# "safari_18.0_ios",
# "safari_18.0_macos",
]
DEFAULT_CHROME = "chrome146"
DEFAULT_EDGE = "edge101"
DEFAULT_SAFARI = "safari2601"
DEFAULT_SAFARI_IOS = "safari260_ios"
DEFAULT_SAFARI_BETA = "safari2601"
DEFAULT_SAFARI_IOS_BETA = "safari260_ios"
DEFAULT_CHROME_ANDROID = "chrome131_android"
DEFAULT_FIREFOX = "firefox147"
DEFAULT_TOR = "tor145"
REAL_TARGET_MAP = {
"chrome": "chrome146",
"edge": "edge101",
"safari": "safari2601",
"safari_ios": "safari260_ios",
"safari_beta": "safari2601",
"safari_ios_beta": "safari260_ios",
"chrome_android": "chrome131_android",
"firefox": "firefox147",
"tor": "tor145",
}
def normalize_browser_type(item):
if item == "chrome": # noqa: SIM116
return DEFAULT_CHROME
elif item == "edge":
return DEFAULT_EDGE
elif item == "safari":
return DEFAULT_SAFARI
elif item == "safari_ios":
return DEFAULT_SAFARI_IOS
elif item == "safari_beta":
return DEFAULT_SAFARI_BETA
elif item == "safari_ios_beta":
return DEFAULT_SAFARI_IOS_BETA
elif item == "chrome_android":
return DEFAULT_CHROME_ANDROID
elif item == "firefox":
return DEFAULT_FIREFOX
elif item == "tor":
return DEFAULT_TOR
else:
return item
class BrowserType(str, Enum): # TODO: remove in version 1.x
edge99 = "edge99"
edge101 = "edge101"
chrome99 = "chrome99"
chrome100 = "chrome100"
chrome101 = "chrome101"
chrome104 = "chrome104"
chrome107 = "chrome107"
chrome110 = "chrome110"
chrome116 = "chrome116"
chrome119 = "chrome119"
chrome120 = "chrome120"
chrome123 = "chrome123"
chrome124 = "chrome124"
chrome131 = "chrome131"
chrome133a = "chrome133a"
chrome136 = "chrome136"
chrome142 = "chrome142"
chrome145 = "chrome145"
chrome146 = "chrome146"
chrome99_android = "chrome99_android"
chrome131_android = "chrome131_android"
safari153 = "safari153"
safari155 = "safari155"
safari170 = "safari170"
safari172_ios = "safari172_ios"
safari180 = "safari180"
safari180_ios = "safari180_ios"
safari184 = "safari184"
safari184_ios = "safari184_ios"
safari260 = "safari260"
safari260_ios = "safari260_ios"
safari2601 = "safari2601"
firefox133 = "firefox133"
firefox135 = "firefox135"
firefox144 = "firefox144"
firefox147 = "firefox147"
tor145 = "tor145"
# deprecated aliases
safari15_3 = "safari15_3"
safari15_5 = "safari15_5"
safari17_0 = "safari17_0"
safari17_2_ios = "safari17_2_ios"
safari18_0 = "safari18_0"
safari18_0_ios = "safari18_0_ios"
@dataclass
class ExtraFingerprints:
tls_min_version: int = CurlSslVersion.TLSv1_2
tls_grease: bool = False
tls_permute_extensions: bool = False
tls_cert_compression: Literal["zlib", "brotli"] = "brotli"
tls_signature_algorithms: Optional[list[str]] = None
tls_delegated_credential: str = ""
tls_record_size_limit: int = 0
http2_stream_weight: int = 256
http2_stream_exclusive: int = 1
http2_no_priority: bool = False
split_cookies: Optional[bool] = None
form_boundary: Optional[bool] = None
http3_sig_hash_algs: Optional[str] = None
http3_tls_extension_order: Optional[str] = None
class ExtraFpDict(TypedDict, total=False):
tls_min_version: int
tls_grease: bool
tls_permute_extensions: bool
tls_cert_compression: Literal["zlib", "brotli"]
tls_signature_algorithms: Optional[list[str]]
tls_delegated_credential: str
tls_record_size_limit: int
http2_stream_weight: int
http2_stream_exclusive: int
http2_no_priority: bool
split_cookies: Optional[bool]
form_boundary: Optional[bool]
http3_sig_hash_algs: Optional[str]
http3_tls_extension_order: Optional[str]
# TLS version are in the format of 0xAABB, where AA is major version and BB is minor
# version. As of today, the major version is always 03.
TLS_VERSION_MAP = {
0x0301: CurlSslVersion.TLSv1_0, # 769
0x0302: CurlSslVersion.TLSv1_1, # 770
0x0303: CurlSslVersion.TLSv1_2, # 771
0x0304: CurlSslVersion.TLSv1_3, # 772
}
# A list of the possible cipher suite ids. Taken from
# http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
# via BoringSSL
TLS_CIPHER_NAME_MAP = {
0x000A: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
0x002F: "TLS_RSA_WITH_AES_128_CBC_SHA",
0x0033: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
0x0035: "TLS_RSA_WITH_AES_256_CBC_SHA",
0x0039: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
0x003C: "TLS_RSA_WITH_AES_128_CBC_SHA256",
0x003D: "TLS_RSA_WITH_AES_256_CBC_SHA256",
0x0067: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
0x006B: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
0x008C: "TLS_PSK_WITH_AES_128_CBC_SHA",
0x008D: "TLS_PSK_WITH_AES_256_CBC_SHA",
0x009C: "TLS_RSA_WITH_AES_128_GCM_SHA256",
0x009D: "TLS_RSA_WITH_AES_256_GCM_SHA384",
0x009E: "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
0x009F: "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
0x1301: "TLS_AES_128_GCM_SHA256",
0x1302: "TLS_AES_256_GCM_SHA384",
0x1303: "TLS_CHACHA20_POLY1305_SHA256",
0xC008: "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
0xC009: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
0xC00A: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
0xC012: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
0xC013: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
0xC014: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
0xC023: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
0xC024: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
0xC027: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
0xC028: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
0xC02B: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
0xC02C: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
0xC02F: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
0xC030: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
0xC035: "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
0xC036: "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
0xCCA8: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
0xCCA9: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
0xCCAC: "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
}
# RFC tls extensions: https://datatracker.ietf.org/doc/html/rfc6066
# IANA list: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
TLS_EXTENSION_NAME_MAP = {
0: "server_name",
1: "max_fragment_length",
2: "client_certificate_url",
3: "trusted_ca_keys",
4: "truncated_hmac",
5: "status_request",
6: "user_mapping",
7: "client_authz",
8: "server_authz",
9: "cert_type",
10: "supported_groups", # (renamed from "elliptic_curves")
11: "ec_point_formats",
12: "srp",
13: "signature_algorithms",
14: "use_srtp",
15: "heartbeat",
16: "application_layer_protocol_negotiation",
17: "status_request_v2",
18: "signed_certificate_timestamp",
19: "client_certificate_type",
20: "server_certificate_type",
21: "padding",
22: "encrypt_then_mac",
23: "extended_master_secret",
24: "token_binding",
25: "cached_info",
26: "tls_lts",
27: "compress_certificate",
28: "record_size_limit",
29: "pwd_protect",
30: "pwd_clear",
31: "password_salt",
32: "ticket_pinning",
33: "tls_cert_with_extern_psk",
34: "delegated_credential",
35: "session_ticket", # (renamed from "SessionTicket TLS")
36: "TLMSP",
37: "TLMSP_proxying",
38: "TLMSP_delegate",
39: "supported_ekt_ciphers",
# 40:"Reserved",
41: "pre_shared_key",
42: "early_data",
43: "supported_versions",
44: "cookie",
45: "psk_key_exchange_modes",
# 46:"Reserved",
47: "certificate_authorities",
48: "oid_filters",
49: "post_handshake_auth",
50: "signature_algorithms_cert",
51: "key_share",
52: "transparency_info",
# 53:"connection_id", # (deprecated)
54: "connection_id",
55: "external_id_hash",
56: "external_session_id",
57: "quic_transport_parameters",
58: "ticket_request",
59: "dnssec_chain",
60: "sequence_number_encryption_algorithms",
61: "rrc",
17513: "application_settings", # BoringSSL private usage
17613: "application_settings new", # BoringSSL private usage
# 62-2569:"Unassigned
# 2570:"Reserved
# 2571-6681:"Unassigned
# 6682:"Reserved
# 6683-10793:"Unassigned
# 10794:"Reserved
# 10795-14905:"Unassigned
# 14906:"Reserved
# 14907-19017:"Unassigned
# 19018:"Reserved
# 19019-23129:"Unassigned
# 23130:"Reserved
# 23131-27241:"Unassigned
# 27242:"Reserved
# 27243-31353:"Unassigned
# 31354:"Reserved
# 31355-35465:"Unassigned
# 35466:"Reserved
# 35467-39577:"Unassigned
# 39578:"Reserved
# 39579-43689:"Unassigned
# 43690:"Reserved
# 43691-47801:"Unassigned
# 47802:"Reserved
# 47803-51913:"Unassigned
# 51914:"Reserved
# 51915-56025:"Unassigned
# 56026:"Reserved
# 56027-60137:"Unassigned
# 60138:"Reserved
# 60139-64249:"Unassigned
# 64250:"Reserved
# 64251-64767:"Unassigned
64768: "ech_outer_extensions",
# 64769-65036:"Unassigned
65037: "encrypted_client_hello",
# 65038-65279:"Unassigned
# 65280:"Reserved for Private Use
65281: "renegotiation_info",
# 65282-65535:"Reserved for Private Use
}
TLS_EC_CURVES_MAP = {
19: "P-192",
21: "P-224",
23: "P-256",
24: "P-384",
25: "P-521",
29: "X25519",
256: "ffdhe2048",
257: "ffdhe3072",
4588: "X25519MLKEM768",
25497: "X25519Kyber768Draft00",
}
def toggle_extension(curl, extension_id: int, enable: bool):
# ECH
if extension_id == 65037:
if enable:
curl.setopt(CurlOpt.ECH, "grease")
else:
curl.setopt(CurlOpt.ECH, "")
# compress certificate
elif extension_id == 27:
if enable:
warnings.warn(
"Cert compression setting to brotli, "
"you had better specify which to use: zlib/brotli",
CurlCffiWarning,
stacklevel=1,
)
curl.setopt(CurlOpt.SSL_CERT_COMPRESSION, "brotli")
else:
curl.setopt(CurlOpt.SSL_CERT_COMPRESSION, "")
# ALPS: application settings
elif extension_id == 17513:
if enable:
curl.setopt(CurlOpt.SSL_ENABLE_ALPS, 1)
else:
curl.setopt(CurlOpt.SSL_ENABLE_ALPS, 0)
elif extension_id == 17613:
if enable:
curl.setopt(CurlOpt.SSL_ENABLE_ALPS, 1)
curl.setopt(CurlOpt.TLS_USE_NEW_ALPS_CODEPOINT, 1)
else:
curl.setopt(CurlOpt.SSL_ENABLE_ALPS, 0)
curl.setopt(CurlOpt.TLS_USE_NEW_ALPS_CODEPOINT, 0)
# server_name
elif extension_id == 0:
raise NotImplementedError(
"It's unlikely that the server_name(0) extension being changed."
)
# ALPN
elif extension_id == 16:
if enable:
curl.setopt(CurlOpt.SSL_ENABLE_ALPN, 1)
else:
curl.setopt(CurlOpt.SSL_ENABLE_ALPN, 0)
# status_request
elif extension_id == 5:
if enable:
curl.setopt(CurlOpt.TLS_STATUS_REQUEST, 1)
# signed_certificate_timestamps
elif extension_id == 18:
if enable:
curl.setopt(CurlOpt.TLS_SIGNED_CERT_TIMESTAMPS, 1)
# session_ticket
elif extension_id == 35:
if enable:
curl.setopt(CurlOpt.SSL_ENABLE_TICKET, 1)
else:
curl.setopt(CurlOpt.SSL_ENABLE_TICKET, 0)
# padding, should be ignored
elif extension_id == 21:
pass # type: ignore
# firefox extension, toggled by extra_fp
elif extension_id in [34, 28]:
pass
else:
raise NotImplementedError(
f"This extension({extension_id}) can not be toggled for now, it may be "
"updated later."
)