UNPKG

node-libcurl-ja3

Version:

Node.js native bindings for libcurl-impersonate. Impersonate Chrome, Edge, Firefox and Safari TLS fingerprints.

363 lines (338 loc) 15.8 kB
const fs = require('fs') const path = require('path') const { inspect } = require('util') const { execSync } = require('child_process') const { optionKindMap, optionKindValueMap } = require('./data/options') const { createConstantsFile, getDescriptionCommentForOption, } = require('./utils/createConstantsFile') const { createSetOptOverloads } = require('./utils/createSetOptOverloads') const { curlOptionsBlacklist } = require('./utils/curlOptionsBlacklist') const { multiOptionsBlacklist } = require('./utils/multiOptionsBlacklist') const { retrieveConstantList } = require('./utils/retrieveConstantList') const run = async () => { const curlOptionsFilePath = path.resolve( __dirname, '../lib/generated/CurlOption.ts', ) const curlInfoFilePath = path.resolve( __dirname, '../lib/generated/CurlInfo.ts', ) const multiOptionFilePath = path.resolve( __dirname, '../lib/generated/MultiOption.ts', ) const allowedCurlOptions = await retrieveConstantList({ url: 'https://curl.se/libcurl/c/curl_easy_setopt.html', constantPrefix: 'CURLOPT_', blacklist: curlOptionsBlacklist, }) allowedCurlOptions.push( ...[ { constantOriginal: 'CURLOPT_IMPERSONATE', constantName: 'IMPERSONATE', constantNameCamelCase: 'impersonate', description: 'curl-impersonate: The master option for setting an impersonate target', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L107', }, { constantOriginal: 'CURLOPT_HTTPBASEHEADER', constantName: 'HTTPBASEHEADER', constantNameCamelCase: 'httpBaseHeader', description: 'curl-impersonate: A list of headers used by the impersonated browser. ' + 'If given, merged with CURLOPT_HTTPHEADER.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L111', }, { constantOriginal: 'CURLOPT_SSL_SIG_HASH_ALGS', constantName: 'SSL_SIG_HASH_ALGS', constantNameCamelCase: 'sslSigHashAlgs', description: 'curl-impersonate: A list of TLS signature hash algorithms. ' + 'This has been implemented by curl as option 328, but we will keep it for compatibility. ' + 'See https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.1.4.1.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L116', }, { constantOriginal: 'CURLOPT_SSL_ENABLE_ALPS', constantName: 'SSL_ENABLE_ALPS', constantNameCamelCase: 'sslEnableAlps', description: '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.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L122', }, { constantOriginal: 'CURLOPT_SSL_CERT_COMPRESSION', constantName: 'SSL_CERT_COMPRESSION', constantNameCamelCase: 'sslCertCompression', description: '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.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L128', }, { constantOriginal: 'CURLOPT_SSL_ENABLE_TICKET', constantName: 'SSL_ENABLE_TICKET', constantNameCamelCase: 'sslEnableTicket', description: 'curl-impersonate: Enable/disable TLS session ticket extension (RFC5077).', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L131', }, { constantOriginal: 'CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER', constantName: 'HTTP2_PSEUDO_HEADERS_ORDER', constantNameCamelCase: 'http2PseudoHeadersOrder', description: 'curl-impersonate: Set the order of the HTTP/2 pseudo headers. ' + "The value must contain the letters 'm', 'a', 's', 'p' " + 'representing the pseudo-headers ":method", ":authority", ":scheme", ":path" ' + 'in the desired order of appearance in the HTTP/2 HEADERS frame.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L140', }, { constantOriginal: 'CURLOPT_HTTP2_SETTINGS', constantName: 'HTTP2_SETTINGS', constantNameCamelCase: 'http2Settings', description: 'curl-impersonate: HTTP2 settings frame keys and values, format: 1:v;2:v;3:v.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L143', }, { constantOriginal: 'CURLOPT_SSL_PERMUTE_EXTENSIONS', constantName: 'SSL_PERMUTE_EXTENSIONS', constantNameCamelCase: 'sslPermuteExtensions', description: 'curl-impersonate: Whether to enable Boringssl permute extensions. ' + 'See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_permute_extensions.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L149', }, { constantOriginal: 'CURLOPT_HTTP2_WINDOW_UPDATE', constantName: 'HTTP2_WINDOW_UPDATE', constantNameCamelCase: 'http2WindowUpdate', description: 'curl-impersonate: HTTP2 initial window update.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L152', }, { constantOriginal: 'CURLOPT_HTTP2_STREAMS', constantName: 'HTTP2_STREAMS', constantNameCamelCase: 'http2Streams', description: 'curl-impersonate: Set the initial streams settings for http2.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L155', }, { constantOriginal: 'CURLOPT_TLS_GREASE', constantName: 'TLS_GREASE', constantNameCamelCase: 'tlsGrease', description: 'curl-impersonate: enable tls grease.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L158', }, { constantOriginal: 'CURLOPT_TLS_EXTENSION_ORDER', constantName: 'TLS_EXTENSION_ORDER', constantNameCamelCase: 'tlsExtensionOrder', description: 'curl-impersonate: set tls extension order.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L161', }, { constantOriginal: 'CURLOPT_STREAM_EXCLUSIVE', constantName: 'STREAM_EXCLUSIVE', constantNameCamelCase: 'streamExclusive', description: 'curl-impersonate: Set stream exclusiveness, 0 or 1.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L164', }, { constantOriginal: 'CURLOPT_TLS_KEY_USAGE_NO_CHECK', constantName: 'TLS_KEY_USAGE_NO_CHECK', constantNameCamelCase: 'tlsKeyUsageNoCheck', description: 'curl-impersonate: enable tls key usage check, defaults: on.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L167', }, { constantOriginal: 'CURLOPT_TLS_SIGNED_CERT_TIMESTAMPS', constantName: 'TLS_SIGNED_CERT_TIMESTAMPS', constantNameCamelCase: 'tlsSignedCertTimestamps', description: 'curl-impersonate: enable tls signed cert stamps.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L170', }, { constantOriginal: 'CURLOPT_TLS_STATUS_REQUEST', constantName: 'TLS_STATUS_REQUEST', constantNameCamelCase: 'tlsStatusRequest', description: 'curl-impersonate: enable tls status request.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L173', }, { constantOriginal: 'CURLOPT_TLS_DELEGATED_CREDENTIALS', constantName: 'TLS_DELEGATED_CREDENTIALS', constantNameCamelCase: 'tlsDelegatedCredentials', description: 'curl-impersonate: firefox delegated credentials.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L176', }, { constantOriginal: 'CURLOPT_TLS_RECORD_SIZE_LIMIT', constantName: 'TLS_RECORD_SIZE_LIMIT', constantNameCamelCase: 'tlsRecordSizeLimit', description: 'curl-impersonate: firefox record size limit.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L179', }, { constantOriginal: 'CURLOPT_TLS_KEY_SHARES_LIMIT', constantName: 'TLS_KEY_SHARES_LIMIT', constantNameCamelCase: 'tlsKeySharesLimit', description: 'curl-impersonate: firefox key_shares_limit.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L182', }, { constantOriginal: 'CURLOPT_TLS_USE_NEW_ALPS_CODEPOINT', constantName: 'TLS_USE_NEW_ALPS_CODEPOINT', constantNameCamelCase: 'tlsUseNewAlpsCodepoint', description: 'curl-impersonate: : Use the new ALPS code point.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L185', }, { constantOriginal: 'CURLOPT_HTTP2_NO_PRIORITY', constantName: 'HTTP2_NO_PRIORITY', constantNameCamelCase: 'http2NoPriority', description: 'curl-impersonate: Do not set the priority bit in http2 header frame.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L188', }, { constantOriginal: 'CURLOPT_PROXY_CREDENTIAL_NO_REUSE', constantName: 'PROXY_CREDENTIAL_NO_REUSE', constantNameCamelCase: 'proxyCredentialNoReuse', description: 'curl-impersonate: Do not reuse TLS sessions or connections from different proxy credentials.', url: 'https://github.com/lexiforest/curl-impersonate/blob/2447279b1a388ba907f4bfe4fe02aadd1dc24376/patches/curl.patch#L191', }, ], ) allowedCurlOptions.sort((a, b) => a.constantName.localeCompare(b.constantName), ) await createConstantsFile({ constants: allowedCurlOptions, variableName: 'CurlOption', filePath: curlOptionsFilePath, shouldGenerateCamelCaseMap: true, extraHeaderText: ` import { CurlChunk } from '../enum/CurlChunk' import { CurlFnMatchFunc } from '../enum/CurlFnMatchFunc' import { CurlFtpMethod } from '../enum/CurlFtpMethod' import { CurlFtpSsl } from '../enum/CurlFtpSsl' import { CurlGssApi } from '../enum/CurlGssApi' import { CurlHeader } from '../enum/CurlHeader' import { CurlHsts, CurlHstsCacheCount, CurlHstsCacheEntry } from '../enum/CurlHsts' import { CurlHttpVersion } from '../enum/CurlHttpVersion' import { CurlInfoDebug } from '../enum/CurlInfoDebug' import { CurlIpResolve } from '../enum/CurlIpResolve' import { CurlNetrc } from '../enum/CurlNetrc' import { CurlPreReqFunc } from '../enum/CurlPreReqFunc' import { CurlProgressFunc } from '../enum/CurlProgressFunc' import { CurlProtocol } from '../enum/CurlProtocol' import { CurlProxy } from '../enum/CurlProxy' import { CurlRtspRequest } from '../enum/CurlRtspRequest' import { CurlSshAuth } from '../enum/CurlSshAuth' import { CurlSslOpt } from '../enum/CurlSslOpt' import { CurlSslVersion } from '../enum/CurlSslVersion' import { CurlTimeCond } from '../enum/CurlTimeCond' import { CurlUseSsl } from '../enum/CurlUseSsl' import { EasyNativeBinding } from "../types/EasyNativeBinding" import { Share } from "../Share" `, }) const allowedCurlInfos = await retrieveConstantList({ url: 'https://curl.se/libcurl/c/curl_easy_getinfo.html', constantPrefix: 'CURLINFO_', blacklist: [ // time constants at the bottom 'NAMELOOKUP', 'CONNECT', 'APPCONNECT', 'PRETRANSFER', 'STARTTRANSFER', 'TOTAL', 'REDIRECT', ], }) await createConstantsFile({ constants: allowedCurlInfos, variableName: 'CurlInfo', filePath: curlInfoFilePath, }) const allowedMultiOptions = await retrieveConstantList({ url: 'https://curl.se/libcurl/c/curl_multi_setopt.html', constantPrefix: 'CURLMOPT_', blacklist: multiOptionsBlacklist, }) await createConstantsFile({ constants: allowedMultiOptions, variableName: 'MultiOption', filePath: multiOptionFilePath, }) // add extra types to CurlOption const union = (arr) => arr.map((i) => inspect(i)).join(' | ') let optionsValueTypeData = [ 'import { FileInfo, HttpPostField } from "../types"', `export type DataCallbackOptions = ${union(optionKindMap.dataCallback)}`, `export type ProgressCallbackOptions = ${union( optionKindMap.progressCallback, )}`, `export type StringListOptions = ${union(optionKindMap.stringList)}`, `export type BlobOptions = ${union(optionKindMap.blob)}`, `export type SpecificOptions = DataCallbackOptions | ProgressCallbackOptions | StringListOptions | BlobOptions | ${union( optionKindMap.other, )}`, ] // Now we must create the type for the curl.<http-verb> options param optionsValueTypeData = [ ...optionsValueTypeData, ` /** * @public */ export type CurlOptionValueType = {`, ] for (const option of allowedCurlOptions) { const optionDescription = getDescriptionCommentForOption(option) const optionValueType = Object.entries(optionKindMap).reduce((acc, [kind, kindOptions]) => { if (acc) return acc return ( kindOptions.includes(option.constantName) && (optionKindValueMap[kind] || optionKindValueMap[option.constantName]) ) }, null) || optionKindValueMap._ optionsValueTypeData = [ ...optionsValueTypeData, `${optionDescription}${option.constantName}?: ${optionValueType} | null`, `${optionDescription}${option.constantNameCamelCase}?: ${optionValueType} | null`, ] } optionsValueTypeData = [...optionsValueTypeData, '}'] fs.writeFileSync(curlOptionsFilePath, optionsValueTypeData.join('\n'), { flag: 'a+', }) const easyBindingFilePath = path.resolve( __dirname, '../lib/types/EasyNativeBinding.ts', ) const curlClassFilePath = path.resolve(__dirname, '../lib/Curl.ts') createSetOptOverloads(easyBindingFilePath) createSetOptOverloads(curlClassFilePath, 'this') execSync( `yarn prettier ${curlOptionsFilePath} ${curlInfoFilePath} ${multiOptionFilePath} ${easyBindingFilePath} ${curlClassFilePath}`, ) } run()