// todo: move to fuman // import { connect } from 'node:net' // // @ts-expect-error no typings // import { normalize } from 'ip6' // import type { tl } from '@mtcute/node' // import { BaseTcpTransport, IntermediatePacketCodec, MtArgumentError, NodePlatform, TransportState, assertNever } from '@mtcute/node' // import { dataViewFromBuffer } from '@mtcute/node/utils.js' // const p = new NodePlatform() // /** // * An error has occurred while connecting to an SOCKS proxy // */ // export class SocksProxyConnectionError extends Error { // readonly proxy: SocksProxySettings // constructor(proxy: SocksProxySettings, message: string) { // super(`Error while connecting to ${proxy.host}:${proxy.port}: ${message}`) // this.proxy = proxy // } // } // /** // * Settings for a SOCKS4/5 proxy // */ // export interface SocksProxySettings { // /** // * 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 // /** // * Proxy authorization username, if needed // */ // user?: string // /** // * Proxy authorization password, if needed // */ // password?: string // /** // * Version of the SOCKS proxy (4 or 5) // * // * @default `5` // */ // version?: 4 | 5 // } // function writeIpv4(ip: string, buf: Uint8Array, offset: number): void { // const parts = ip.split('.') // if (parts.length !== 4) { // throw new MtArgumentError('Invalid IPv4 address') // } // for (let i = 0; i < 4; i++) { // const n = Number.parseInt(parts[i]) // if (Number.isNaN(n) || n < 0 || n > 255) { // throw new MtArgumentError('Invalid IPv4 address') // } // buf[offset + i] = n // } // } // function buildSocks4ConnectRequest(ip: string, port: number, username = ''): Uint8Array { // const userId = p.utf8Encode(username) // const buf = new Uint8Array(9 + userId.length) // buf[0] = 0x04 // VER // buf[1] = 0x01 // CMD = establish a TCP/IP stream connection // dataViewFromBuffer(buf).setUint16(2, port, false) // writeIpv4(ip, buf, 4) // DSTIP // buf.set(userId, 8) // buf[8 + userId.length] = 0x00 // ID (null-termination) // return buf // } // function buildSocks5Greeting(authAvailable: boolean): Uint8Array { // const buf = new Uint8Array(authAvailable ? 4 : 3) // buf[0] = 0x05 // VER // if (authAvailable) { // buf[1] = 0x02 // NAUTH // buf[2] = 0x00 // AUTH[0] = No authentication // buf[3] = 0x02 // AUTH[1] = Username/password // } else { // buf[1] = 0x01 // NAUTH // buf[2] = 0x00 // AUTH[0] = No authentication // } // return buf // } // function buildSocks5Auth(username: string, password: string) { // const usernameBuf = p.utf8Encode(username) // const passwordBuf = p.utf8Encode(password) // if (usernameBuf.length > 255) { // throw new MtArgumentError(`Too long username (${usernameBuf.length} > 255)`) // } // if (passwordBuf.length > 255) { // throw new MtArgumentError(`Too long password (${passwordBuf.length} > 255)`) // } // const buf = new Uint8Array(3 + usernameBuf.length + passwordBuf.length) // buf[0] = 0x01 // VER of auth // buf[1] = usernameBuf.length // buf.set(usernameBuf, 2) // buf[2 + usernameBuf.length] = passwordBuf.length // buf.set(passwordBuf, 3 + usernameBuf.length) // return buf // } // function writeIpv6(ip: string, buf: Uint8Array, offset: number): void { // // eslint-disable-next-line ts/no-unsafe-call // ip = normalize(ip) as string // const parts = ip.split(':') // if (parts.length !== 8) { // throw new MtArgumentError('Invalid IPv6 address') // } // const dv = dataViewFromBuffer(buf) // for (let i = 0, j = offset; i < 8; i++, j += 2) { // const n = Number.parseInt(parts[i]) // if (Number.isNaN(n) || n < 0 || n > 0xFFFF) { // throw new MtArgumentError('Invalid IPv6 address') // } // dv.setUint16(j, n, false) // } // } // function buildSocks5Connect(ip: string, port: number, ipv6 = false): Uint8Array { // const buf = new Uint8Array(ipv6 ? 22 : 10) // const dv = dataViewFromBuffer(buf) // buf[0] = 0x05 // VER // buf[1] = 0x01 // CMD = establish a TCP/IP stream connection // buf[2] = 0x00 // RSV // if (ipv6) { // buf[3] = 0x04 // TYPE = IPv6 // writeIpv6(ip, buf, 4) // ADDR // dv.setUint16(20, port, false) // } else { // buf[3] = 0x01 // TYPE = IPv4 // writeIpv4(ip, buf, 4) // ADDR // dv.setUint16(8, port, false) // } // return buf // } // const SOCKS4_ERRORS: Record = { // 91: 'Request rejected or failed', // 92: 'Request failed because client is not running identd', // 93: "Request failed because client's identd could not confirm the user ID in the request", // } // const SOCKS5_ERRORS: Record = { // 1: 'General failure', // 2: 'Connection not allowed by ruleset', // 3: 'Network unreachable', // 4: 'Host unreachable', // 5: 'Connection refused by destination host', // 6: 'TTL expired', // 7: 'Command not supported / protocol error', // 8: 'Address type not supported', // } // /** // * TCP transport that connects via a SOCKS4/5 proxy. // */ // export abstract class BaseSocksTcpTransport extends BaseTcpTransport { // readonly _proxy: SocksProxySettings // constructor(proxy: SocksProxySettings) { // super() // if (proxy.version != null && proxy.version !== 4 && proxy.version !== 5) { // throw new SocksProxyConnectionError( // proxy, // `Invalid SOCKS version: ${proxy.version}`, // ) // } // this._proxy = proxy // } // connect(dc: tl.RawDcOption): void { // if (this._state !== TransportState.Idle) { // throw new MtArgumentError('Transport is not IDLE') // } // if (!this.packetCodecInitialized) { // this._packetCodec.on('error', err => this.emit('error', err)) // this._packetCodec.on('packet', buf => this.emit('message', buf)) // this.packetCodecInitialized = true // } // this._state = TransportState.Connecting // this._currentDc = dc // this._socket = connect(this._proxy.port, this._proxy.host, this._onProxyConnected.bind(this)) // this._socket.on('error', this.handleError.bind(this)) // this._socket.on('close', this.close.bind(this)) // } // private _onProxyConnected() { // let packetHandler: (msg: Uint8Array) => void // if (this._proxy.version === 4) { // packetHandler = (msg) => { // if (msg[0] !== 0x04) { // // VER, must be 4 // this._socket!.emit( // 'error', // new SocksProxyConnectionError(this._proxy, `Server returned version ${msg[0]}`), // ) // return // } // const code = msg[1] // this.log.debug('[%s:%d] CONNECT returned code %d', this._proxy.host, this._proxy.port, code) // if (code === 0x5A) { // this._socket!.off('data', packetHandler) // this._socket!.on('data', data => this._packetCodec.feed(data)) // this.handleConnect() // } else { // const msg // = code in SOCKS4_ERRORS ? SOCKS4_ERRORS[code] : `Unknown error code: 0x${code.toString(16)}` // this._socket!.emit('error', new SocksProxyConnectionError(this._proxy, msg)) // } // } // this.log.debug('[%s:%d] connected to proxy, sending CONNECT', this._proxy.host, this._proxy.port) // try { // this._socket!.write( // buildSocks4ConnectRequest(this._currentDc!.ipAddress, this._currentDc!.port, this._proxy.user), // ) // } catch (e) { // this._socket!.emit('error', e) // } // } else { // let state: 'greeting' | 'auth' | 'connect' = 'greeting' // const sendConnect = () => { // this.log.debug('[%s:%d] sending CONNECT', this._proxy.host, this._proxy.port) // try { // this._socket!.write( // buildSocks5Connect(this._currentDc!.ipAddress, this._currentDc!.port, this._currentDc!.ipv6), // ) // state = 'connect' // } catch (e) { // this._socket!.emit('error', e) // } // } // packetHandler = (msg) => { // switch (state) { // case 'greeting': { // if (msg[0] !== 0x05) { // // VER, must be 5 // this._socket!.emit( // 'error', // new SocksProxyConnectionError(this._proxy, `Server returned version ${msg[0]}`), // ) // return // } // const chosen = msg[1] // this.log.debug( // '[%s:%d] GREETING returned auth method %d', // this._proxy.host, // this._proxy.port, // chosen, // ) // switch (chosen) { // case 0x00: // // "No authentication" // sendConnect() // break // case 0x02: // // Username/password // if (!this._proxy.user || !this._proxy.password) { // // should not happen // this._socket!.emit( // 'error', // new SocksProxyConnectionError( // this._proxy, // 'Authentication is required, but not provided', // ), // ) // break // } // try { // this._socket!.write(buildSocks5Auth(this._proxy.user, this._proxy.password)) // state = 'auth' // } catch (e) { // this._socket!.emit('error', e) // } // break // case 0xFF: // default: // // "no acceptable methods were offered" // this._socket!.emit( // 'error', // new SocksProxyConnectionError( // this._proxy, // 'Authentication is required, but not provided/supported', // ), // ) // break // } // break // } // case 'auth': // if (msg[0] !== 0x01) { // // VER of auth, must be 1 // this._socket!.emit( // 'error', // new SocksProxyConnectionError(this._proxy, `Server returned version ${msg[0]}`), // ) // return // } // this.log.debug('[%s:%d] AUTH returned code %d', this._proxy.host, this._proxy.port, msg[1]) // if (msg[1] === 0x00) { // // success // sendConnect() // } else { // this._socket!.emit( // 'error', // new SocksProxyConnectionError(this._proxy, 'Authentication failure'), // ) // } // break // case 'connect': { // if (msg[0] !== 0x05) { // // VER, must be 5 // this._socket!.emit( // 'error', // new SocksProxyConnectionError(this._proxy, `Server returned version ${msg[0]}`), // ) // return // } // const code = msg[1] // this.log.debug('[%s:%d] CONNECT returned code %d', this._proxy.host, this._proxy.port, code) // if (code === 0x00) { // // Request granted // this._socket!.off('data', packetHandler) // this._socket!.on('data', data => this._packetCodec.feed(data)) // this.handleConnect() // } else { // const msg // = code in SOCKS5_ERRORS // ? SOCKS5_ERRORS[code] // : `Unknown error code: 0x${code.toString(16)}` // this._socket!.emit('error', new SocksProxyConnectionError(this._proxy, msg)) // } // break // } // default: // assertNever(state) // } // } // this.log.debug('[%s:%d] connected to proxy, sending GREETING', this._proxy.host, this._proxy.port) // try { // this._socket!.write(buildSocks5Greeting(Boolean(this._proxy.user && this._proxy.password))) // } catch (e) { // this._socket!.emit('error', e) // } // } // this._socket!.on('data', packetHandler) // } // } // /** // * Socks TCP transport using an intermediate packet codec. // * // * Should be the one passed as `transport` to `TelegramClient` constructor // * (unless you want to use a custom codec). // */ // export class SocksTcpTransport extends BaseSocksTcpTransport { // _packetCodec: IntermediatePacketCodec = new IntermediatePacketCodec() // }