11'use strict'
22
3- const net = require ( 'net' )
43const http = require ( 'http' )
54const https = require ( 'https' )
65
76/**
8- * Creates a proxy connection handler for the given proxy configuration
7+ * Creates a proxy-aware connect function based on proxy configuration
98 * @param {Object } proxyConfig - Proxy configuration
109 * @param {string } proxyConfig.type - Proxy type ('socks4', 'socks5', 'http', 'https')
1110 * @param {string } proxyConfig.host - Proxy host
1211 * @param {number } proxyConfig.port - Proxy port
1312 * @param {Object } [proxyConfig.auth] - Authentication credentials
1413 * @param {string } [proxyConfig.auth.username] - Username
1514 * @param {string } [proxyConfig.auth.password] - Password
15+ * @param {string } targetHost - Target Minecraft server host
16+ * @param {number } targetPort - Target Minecraft server port
1617 * @returns {Function } Connection handler function
1718 */
18- function createProxyConnector ( proxyConfig ) {
19+ function createProxyConnect ( proxyConfig , targetHost , targetPort ) {
1920 switch ( proxyConfig . type . toLowerCase ( ) ) {
20- case 'socks4' :
21- return createSocks4Connector ( proxyConfig )
22- case 'socks5' :
23- return createSocks5Connector ( proxyConfig )
2421 case 'http' :
2522 case 'https' :
26- return createHttpConnector ( proxyConfig )
23+ return createHttpConnect ( proxyConfig , targetHost , targetPort )
24+ case 'socks4' :
25+ case 'socks5' :
26+ return createSocksConnect ( proxyConfig , targetHost , targetPort )
2727 default :
2828 throw new Error ( `Unsupported proxy type: ${ proxyConfig . type } ` )
2929 }
3030}
3131
3232/**
33- * Creates a SOCKS4 connection handler
33+ * Creates HTTP CONNECT proxy function (based on existing client_http_proxy example)
3434 */
35- function createSocks4Connector ( proxyConfig ) {
36- return function ( client , targetHost , targetPort ) {
37- const socket = net . createConnection ( proxyConfig . port , proxyConfig . host )
38-
39- socket . on ( 'connect' , ( ) => {
40- // SOCKS4 connect request
41- const userId = proxyConfig . auth ?. username || ''
42- const request = Buffer . alloc ( 9 + userId . length )
43-
44- request [ 0 ] = 0x04 // SOCKS version
45- request [ 1 ] = 0x01 // Connect command
46- request . writeUInt16BE ( targetPort , 2 ) // Port
47-
48- // Convert hostname to IP if needed
49- const targetIP = net . isIP ( targetHost ) ? targetHost : '0.0.0.1' // Use 0.0.0.1 for hostname
50- const ipParts = targetIP . split ( '.' )
51- request [ 4 ] = parseInt ( ipParts [ 0 ] )
52- request [ 5 ] = parseInt ( ipParts [ 1 ] )
53- request [ 6 ] = parseInt ( ipParts [ 2 ] )
54- request [ 7 ] = parseInt ( ipParts [ 3 ] )
55-
56- request . write ( userId , 8 ) // User ID
57- request [ 8 + userId . length ] = 0x00 // Null terminator
58-
59- // Add hostname if using SOCKS4A (when IP is 0.0.0.x)
60- if ( ! net . isIP ( targetHost ) ) {
61- const hostnameBuffer = Buffer . from ( targetHost + '\0' )
62- const fullRequest = Buffer . concat ( [ request , hostnameBuffer ] )
63- socket . write ( fullRequest )
64- } else {
65- socket . write ( request )
66- }
67- } )
68-
69- socket . once ( 'data' , ( data ) => {
70- if ( data . length < 8 ) {
71- socket . destroy ( )
72- client . emit ( 'error' , new Error ( 'Invalid SOCKS4 response' ) )
73- return
74- }
75-
76- if ( data [ 1 ] === 0x5A ) { // Request granted
77- client . setSocket ( socket )
78- client . emit ( 'connect' )
79- } else {
80- socket . destroy ( )
81- client . emit ( 'error' , new Error ( `SOCKS4 connection failed: ${ data [ 1 ] } ` ) )
82- }
83- } )
84-
85- socket . on ( 'error' , ( err ) => {
86- client . emit ( 'error' , new Error ( `SOCKS4 proxy error: ${ err . message } ` ) )
87- } )
88- }
89- }
90-
91- /**
92- * Creates a SOCKS5 connection handler
93- */
94- function createSocks5Connector ( proxyConfig ) {
95- return function ( client , targetHost , targetPort ) {
96- const socket = net . createConnection ( proxyConfig . port , proxyConfig . host )
97- let stage = 'auth'
98-
99- socket . on ( 'connect' , ( ) => {
100- // Authentication negotiation
101- const authMethods = proxyConfig . auth ? [ 0x00 , 0x02 ] : [ 0x00 ] // No auth + Username/Password
102- const authRequest = Buffer . from ( [ 0x05 , authMethods . length , ...authMethods ] )
103- socket . write ( authRequest )
104- } )
105-
106- socket . on ( 'data' , ( data ) => {
107- if ( stage === 'auth' ) {
108- if ( data . length < 2 || data [ 0 ] !== 0x05 ) {
109- socket . destroy ( )
110- client . emit ( 'error' , new Error ( 'Invalid SOCKS5 auth response' ) )
111- return
112- }
113-
114- if ( data [ 1 ] === 0xFF ) {
115- socket . destroy ( )
116- client . emit ( 'error' , new Error ( 'SOCKS5 authentication failed' ) )
117- return
118- }
119-
120- if ( data [ 1 ] === 0x02 && proxyConfig . auth ) {
121- // Username/password authentication
122- const username = proxyConfig . auth . username || ''
123- const password = proxyConfig . auth . password || ''
124- const authData = Buffer . alloc ( 3 + username . length + password . length )
125-
126- authData [ 0 ] = 0x01 // Auth version
127- authData [ 1 ] = username . length
128- authData . write ( username , 2 )
129- authData [ 2 + username . length ] = password . length
130- authData . write ( password , 3 + username . length )
131-
132- socket . write ( authData )
133- stage = 'userpass'
134- } else {
135- // No authentication required
136- sendConnectRequest ( )
137- }
138- } else if ( stage === 'userpass' ) {
139- if ( data . length < 2 || data [ 0 ] !== 0x01 ) {
140- socket . destroy ( )
141- client . emit ( 'error' , new Error ( 'Invalid SOCKS5 userpass response' ) )
142- return
143- }
144-
145- if ( data [ 1 ] !== 0x00 ) {
146- socket . destroy ( )
147- client . emit ( 'error' , new Error ( 'SOCKS5 username/password authentication failed' ) )
148- return
149- }
150-
151- sendConnectRequest ( )
152- } else if ( stage === 'connect' ) {
153- if ( data . length < 10 || data [ 0 ] !== 0x05 ) {
154- socket . destroy ( )
155- client . emit ( 'error' , new Error ( 'Invalid SOCKS5 connect response' ) )
156- return
157- }
158-
159- if ( data [ 1 ] === 0x00 ) { // Success
160- client . setSocket ( socket )
161- client . emit ( 'connect' )
162- } else {
163- socket . destroy ( )
164- client . emit ( 'error' , new Error ( `SOCKS5 connection failed: ${ data [ 1 ] } ` ) )
165- }
166- }
167- } )
168-
169- function sendConnectRequest ( ) {
170- stage = 'connect'
171- const isIP = net . isIP ( targetHost )
172- const hostBuffer = isIP
173- ? Buffer . from ( targetHost . split ( '.' ) . map ( x => parseInt ( x ) ) )
174- : Buffer . concat ( [ Buffer . from ( [ targetHost . length ] ) , Buffer . from ( targetHost ) ] )
175-
176- const request = Buffer . concat ( [
177- Buffer . from ( [ 0x05 , 0x01 , 0x00 ] ) , // Version, Connect, Reserved
178- Buffer . from ( [ isIP ? 0x01 : 0x03 ] ) , // Address type (IPv4 or Domain)
179- hostBuffer ,
180- Buffer . allocUnsafe ( 2 )
181- ] )
182-
183- request . writeUInt16BE ( targetPort , request . length - 2 )
184- socket . write ( request )
185- }
186-
187- socket . on ( 'error' , ( err ) => {
188- client . emit ( 'error' , new Error ( `SOCKS5 proxy error: ${ err . message } ` ) )
189- } )
190- }
191- }
192-
193- /**
194- * Creates an HTTP CONNECT proxy handler
195- */
196- function createHttpConnector ( proxyConfig ) {
197- return function ( client , targetHost , targetPort ) {
198- const isHttps = proxyConfig . type . toLowerCase ( ) === 'https'
35+ function createHttpConnect ( proxyConfig , targetHost , targetPort ) {
36+ return function ( client ) {
19937 const connectOptions = {
20038 host : proxyConfig . host ,
20139 port : proxyConfig . port ,
@@ -211,15 +49,14 @@ function createHttpConnector (proxyConfig) {
21149 }
21250 }
21351
214- const httpModule = isHttps ? https : http
52+ const httpModule = proxyConfig . type . toLowerCase ( ) === 'https' ? https : http
21553 const req = httpModule . request ( connectOptions )
21654
217- req . on ( 'connect' , ( res , socket ) => {
55+ req . on ( 'connect' , ( res , stream ) => {
21856 if ( res . statusCode === 200 ) {
219- client . setSocket ( socket )
57+ client . setSocket ( stream )
22058 client . emit ( 'connect' )
22159 } else {
222- socket . destroy ( )
22360 client . emit ( 'error' , new Error ( `HTTP CONNECT failed: ${ res . statusCode } ${ res . statusMessage } ` ) )
22461 }
22562 } )
@@ -232,35 +69,81 @@ function createHttpConnector (proxyConfig) {
23269 }
23370}
23471
72+ /**
73+ * Creates SOCKS proxy function (based on existing client_socks_proxy example)
74+ */
75+ function createSocksConnect ( proxyConfig , targetHost , targetPort ) {
76+ return function ( client ) {
77+ let socks
78+ try {
79+ socks = require ( 'socks' ) . SocksClient
80+ } catch ( err ) {
81+ client . emit ( 'error' , new Error ( 'SOCKS proxy requires "socks" package: npm install socks' ) )
82+ return
83+ }
84+
85+ const socksOptions = {
86+ proxy : {
87+ host : proxyConfig . host ,
88+ port : proxyConfig . port ,
89+ type : proxyConfig . type === 'socks4' ? 4 : 5
90+ } ,
91+ command : 'connect' ,
92+ destination : {
93+ host : targetHost ,
94+ port : targetPort
95+ }
96+ }
97+
98+ // Add authentication if provided (SOCKS5 only)
99+ if ( proxyConfig . auth && proxyConfig . type === 'socks5' ) {
100+ socksOptions . proxy . userId = proxyConfig . auth . username
101+ socksOptions . proxy . password = proxyConfig . auth . password
102+ }
103+
104+ socks . createConnection ( socksOptions , ( err , info ) => {
105+ if ( err ) {
106+ client . emit ( 'error' , new Error ( `SOCKS proxy error: ${ err . message } ` ) )
107+ return
108+ }
109+ client . setSocket ( info . socket )
110+ client . emit ( 'connect' )
111+ } )
112+ }
113+ }
114+
235115/**
236116 * Creates a proxy-aware agent for HTTP requests (used for authentication)
237117 */
238118function createProxyAgent ( proxyConfig ) {
239- const agentOptions = {
240- host : proxyConfig . host ,
241- port : proxyConfig . port
242- }
119+ try {
120+ const ProxyAgent = require ( 'proxy-agent' )
121+ const protocol = proxyConfig . type . toLowerCase ( ) === 'https'
122+ ? 'https:'
123+ : proxyConfig . type . toLowerCase ( ) === 'http'
124+ ? 'http:'
125+ : proxyConfig . type . toLowerCase ( ) === 'socks5'
126+ ? 'socks5:'
127+ : proxyConfig . type . toLowerCase ( ) === 'socks4' ? 'socks4:' : 'http:'
128+
129+ const agentOptions = {
130+ protocol,
131+ host : proxyConfig . host ,
132+ port : proxyConfig . port
133+ }
243134
244- if ( proxyConfig . auth ) {
245- agentOptions . auth = `${ proxyConfig . auth . username } :${ proxyConfig . auth . password } `
246- }
135+ if ( proxyConfig . auth ) {
136+ agentOptions . auth = `${ proxyConfig . auth . username } :${ proxyConfig . auth . password } `
137+ }
247138
248- switch ( proxyConfig . type . toLowerCase ( ) ) {
249- case 'http' :
250- return new http . Agent ( agentOptions )
251- case 'https' :
252- return new https . Agent ( agentOptions )
253- case 'socks4' :
254- case 'socks5' :
255- // For SOCKS proxies, we'll use a simple HTTP agent for now
256- // In production, you might want to use a proper SOCKS agent
257- return new http . Agent ( )
258- default :
259- return undefined
139+ return new ProxyAgent ( agentOptions )
140+ } catch ( err ) {
141+ // Fallback to basic agent if proxy-agent not available
142+ return new http . Agent ( )
260143 }
261144}
262145
263146module . exports = {
264- createProxyConnector ,
147+ createProxyConnect ,
265148 createProxyAgent
266149}
0 commit comments