mtcute/packages/mtproxy/index.ts

250 lines
8.3 KiB
TypeScript

// /* eslint-disable no-restricted-globals */
// todo fixme
// import { connect } from 'node:net'
// import type {
// IPacketCodec,
// tl,
// } from '@mtcute/node'
// import {
// BaseTcpTransport,
// IntermediatePacketCodec,
// MtSecurityError,
// MtUnsupportedError,
// MtcuteError,
// ObfuscatedPacketCodec,
// PaddedIntermediatePacketCodec,
// TransportState,
// } from '@mtcute/node'
// import { buffersEqual } from '@mtcute/node/utils.js'
// import { FakeTlsPacketCodec, generateFakeTlsHeader } from './fake-tls.js'
// /**
// * MTProto proxy settings
// */
// export interface MtProxySettings {
// /**
// * Host or IP of the proxy (e.g. `proxy.example.com`, `1.2.3.4`)
// */
// host: string
// /**
// * Port of the proxy (e.g. `8888`)
// */
// port: number
// /**
// * Secret of the proxy, optionally encoded either as hex or base64
// */
// secret: string | Buffer
// }
// const MAX_DOMAIN_LENGTH = 182 // must be small enough not to overflow TLS-hello length
// const TLS_START = [Buffer.from('160303', 'hex'), Buffer.from('140303000101170303', 'hex')]
// /**
// * TCP transport that connects via an MTProxy
// */
// export class MtProxyTcpTransport extends BaseTcpTransport {
// readonly _proxy: MtProxySettings
// private _rawSecret: Buffer
// private _randomPadding = false
// private _fakeTlsDomain: string | null = null
// /**
// * @param proxy Information about the proxy
// */
// constructor(proxy: MtProxySettings) {
// super()
// this._proxy = proxy
// // validate and parse secret
// let secret: Buffer
// if (Buffer.isBuffer(proxy.secret)) {
// secret = proxy.secret
// } else if (proxy.secret.match(/^[0-9a-f]+$/i)) {
// secret = Buffer.from(proxy.secret, 'hex')
// } else {
// secret = Buffer.from(proxy.secret, 'base64url')
// }
// if (secret.length > 17 + MAX_DOMAIN_LENGTH) {
// throw new MtSecurityError('Invalid secret: too long')
// }
// if (secret.length < 16) {
// throw new MtSecurityError('Invalid secret: too short')
// }
// if (secret.length === 16) {
// this._rawSecret = secret
// } else if (secret.length === 17 && secret[0] === 0xDD) {
// this._rawSecret = secret.slice(1)
// this._randomPadding = true
// } else if (secret.length >= 18 && secret[0] === 0xEE) {
// this._rawSecret = secret.slice(1, 17)
// this._fakeTlsDomain = secret.slice(17).toString()
// } else {
// throw new MtUnsupportedError('Unsupported secret')
// }
// }
// getMtproxyInfo(): tl.RawInputClientProxy {
// return {
// _: 'inputClientProxy',
// address: this._proxy.host,
// port: this._proxy.port,
// }
// }
// _packetCodec!: IPacketCodec
// connect(dc: tl.RawDcOption, testMode: boolean): void {
// if (this._state !== TransportState.Idle) {
// throw new MtcuteError('Transport is not IDLE')
// }
// if (this._packetCodec && this._currentDc?.id !== dc.id) {
// // dc changed, thus the codec's init will change too
// // clean up to avoid memory leaks
// this.packetCodecInitialized = false
// this._packetCodec.reset()
// this._packetCodec.removeAllListeners()
// delete (this as Partial<MtProxyTcpTransport>)._packetCodec
// }
// if (!this._packetCodec) {
// const proxy = {
// dcId: dc.id,
// media: dc.mediaOnly!,
// test: testMode,
// secret: this._rawSecret,
// }
// if (!this._fakeTlsDomain) {
// let inner: IPacketCodec
// if (this._randomPadding) {
// inner = new PaddedIntermediatePacketCodec()
// } else {
// inner = new IntermediatePacketCodec()
// }
// this._packetCodec = new ObfuscatedPacketCodec(inner, proxy)
// } else {
// this._packetCodec = new FakeTlsPacketCodec(
// new ObfuscatedPacketCodec(new PaddedIntermediatePacketCodec(), proxy),
// )
// }
// this._packetCodec.setup?.(this._crypto, this.log)
// this._packetCodec.on('error', err => this.emit('error', err))
// this._packetCodec.on('packet', buf => this.emit('message', buf))
// }
// this._state = TransportState.Connecting
// this._currentDc = dc
// if (this._fakeTlsDomain) {
// this._socket = connect(
// this._proxy.port,
// this._proxy.host,
// // MTQ-55
// // eslint-disable-next-line ts/no-misused-promises
// this._handleConnectFakeTls.bind(this),
// )
// } else {
// this._socket = connect(
// this._proxy.port,
// this._proxy.host,
// // MTQ-55
// this.handleConnect.bind(this),
// )
// this._socket.on('data', data => this._packetCodec.feed(data))
// }
// this._socket.on('error', this.handleError.bind(this))
// this._socket.on('close', this.close.bind(this))
// }
// private async _handleConnectFakeTls(): Promise<void> {
// try {
// const hello = await generateFakeTlsHeader(this._fakeTlsDomain!, this._rawSecret, this._crypto)
// const helloRand = hello.slice(11, 11 + 32)
// let serverHelloBuffer: Buffer | null = null
// const checkHelloResponse = async (buf: Buffer): Promise<boolean> => {
// if (serverHelloBuffer) {
// buf = Buffer.concat([serverHelloBuffer, buf])
// }
// const resp = buf
// for (const first of TLS_START) {
// if (buf.length < first.length + 2) {
// throw new MtSecurityError('Server hello is too short')
// }
// if (!buffersEqual(buf.slice(0, first.length), first)) {
// throw new MtSecurityError('Server hello is invalid')
// }
// buf = buf.slice(first.length)
// const skipSize = buf.readUInt16BE()
// buf = buf.slice(2)
// if (buf.length < skipSize) {
// // likely got split into multiple packets
// if (serverHelloBuffer) {
// throw new MtSecurityError('Server hello is too short')
// }
// serverHelloBuffer = resp
// return false
// }
// buf = buf.slice(skipSize)
// }
// const respRand = resp.slice(11, 11 + 32)
// const hash = await this._crypto.hmacSha256(
// Buffer.concat([helloRand, resp.slice(0, 11), Buffer.alloc(32, 0), resp.slice(11 + 32)]),
// this._rawSecret,
// )
// if (!buffersEqual(hash, respRand)) {
// throw new MtSecurityError('Response hash is invalid')
// }
// return true
// }
// const packetHandler = (buf: Buffer): void => {
// checkHelloResponse(buf)
// .then((done) => {
// if (!done) return
// this._socket!.off('data', packetHandler)
// this._socket!.on('data', (data) => {
// this._packetCodec.feed(data)
// })
// return this.handleConnect()
// })
// .catch(err => this._socket!.emit('error', err))
// }
// this._socket!.write(hello)
// this._socket!.on('data', packetHandler)
// } catch (e) {
// this._socket!.emit('error', e)
// }
// }
// }