@@ -172,14 +172,23 @@ public function handleSocks4(ConnectionInterface $stream, $protocolVersion, Stre
172172 // suppliying hostnames is only allowed for SOCKS4a (or automatically detected version)
173173 $ supportsHostname = ($ protocolVersion === null || $ protocolVersion === '4a ' );
174174
175+ $ remote = $ stream ->getRemoteAddress ();
176+ if ($ remote !== null ) {
177+ // remove transport scheme and prefix socks4:// instead
178+ if (($ pos = strpos ($ remote , ':// ' )) !== false ) {
179+ $ remote = substr ($ remote , $ pos + 3 );
180+ }
181+ $ remote = 'socks4:// ' . $ remote ;
182+ }
183+
175184 $ that = $ this ;
176185 return $ reader ->readByteAssert (0x01 )->then (function () use ($ reader ) {
177186 return $ reader ->readBinary (array (
178187 'port ' => 'n ' ,
179188 'ipLong ' => 'N ' ,
180189 'null ' => 'C '
181190 ));
182- })->then (function ($ data ) use ($ reader , $ supportsHostname ) {
191+ })->then (function ($ data ) use ($ reader , $ supportsHostname, $ remote ) {
183192 if ($ data ['null ' ] !== 0x00 ) {
184193 throw new Exception ('Not a null byte ' );
185194 }
@@ -191,12 +200,12 @@ public function handleSocks4(ConnectionInterface $stream, $protocolVersion, Stre
191200 }
192201 if ($ data ['ipLong ' ] < 256 && $ supportsHostname ) {
193202 // invalid IP => probably a SOCKS4a request which appends the hostname
194- return $ reader ->readStringNull ()->then (function ($ string ) use ($ data ){
195- return array ($ string , $ data ['port ' ]);
203+ return $ reader ->readStringNull ()->then (function ($ string ) use ($ data, $ remote ){
204+ return array ($ string , $ data ['port ' ], $ remote );
196205 });
197206 } else {
198207 $ ip = long2ip ($ data ['ipLong ' ]);
199- return array ($ ip , $ data ['port ' ]);
208+ return array ($ ip , $ data ['port ' ], $ remote );
200209 }
201210 })->then (function ($ target ) use ($ stream , $ that ) {
202211 return $ that ->connectTarget ($ stream , $ target )->then (function (ConnectionInterface $ remote ) use ($ stream ){
@@ -215,14 +224,24 @@ public function handleSocks4(ConnectionInterface $stream, $protocolVersion, Stre
215224
216225 public function handleSocks5 (ConnectionInterface $ stream , $ auth =null , StreamReader $ reader )
217226 {
227+ $ remote = $ stream ->getRemoteAddress ();
228+ if ($ remote !== null ) {
229+ // remove transport scheme and prefix socks5:// instead
230+ if (($ pos = strpos ($ remote , ':// ' )) !== false ) {
231+ $ remote = substr ($ remote , $ pos + 3 );
232+ }
233+ $ remote = 'socks5:// ' . $ remote ;
234+ }
235+
218236 $ that = $ this ;
219237 return $ reader ->readByte ()->then (function ($ num ) use ($ reader ) {
220238 // $num different authentication mechanisms offered
221239 return $ reader ->readLength ($ num );
222- })->then (function ($ methods ) use ($ reader , $ stream , $ auth ) {
240+ })->then (function ($ methods ) use ($ reader , $ stream , $ auth, & $ remote ) {
223241 if ($ auth === null && strpos ($ methods ,"\x00" ) !== false ) {
224242 // accept "no authentication"
225243 $ stream ->write (pack ('C2 ' , 0x05 , 0x00 ));
244+
226245 return 0x00 ;
227246 } else if ($ auth !== null && strpos ($ methods ,"\x02" ) !== false ) {
228247 // username/password authentication (RFC 1929) sub negotiation
@@ -231,18 +250,15 @@ public function handleSocks5(ConnectionInterface $stream, $auth=null, StreamRead
231250 return $ reader ->readByte ();
232251 })->then (function ($ length ) use ($ reader ) {
233252 return $ reader ->readLength ($ length );
234- })->then (function ($ username ) use ($ reader , $ auth , $ stream ) {
253+ })->then (function ($ username ) use ($ reader , $ auth , $ stream, & $ remote ) {
235254 return $ reader ->readByte ()->then (function ($ length ) use ($ reader ) {
236255 return $ reader ->readLength ($ length );
237- })->then (function ($ password ) use ($ username , $ auth , $ stream ) {
256+ })->then (function ($ password ) use ($ username , $ auth , $ stream, & $ remote ) {
238257 // username and password given => authenticate
239- $ remote = $ stream ->getRemoteAddress ();
258+
259+ // prefix username/password to remote URI
240260 if ($ remote !== null ) {
241- // remove transport scheme and prefix socks5:// instead
242- if (($ pos = strpos ($ remote , ':// ' )) !== false ) {
243- $ remote = substr ($ remote , $ pos + 3 );
244- }
245- $ remote = 'socks5:// ' . rawurlencode ($ username ) . ': ' . rawurlencode ($ password ) . '@ ' . $ remote ;
261+ $ remote = str_replace (':// ' , ':// ' . rawurlencode ($ username ) . ': ' . rawurlencode ($ password ) . '@ ' , $ remote );
246262 }
247263
248264 return $ auth ($ username , $ password , $ remote )->then (function () use ($ stream , $ username ) {
@@ -296,9 +312,9 @@ public function handleSocks5(ConnectionInterface $stream, $auth=null, StreamRead
296312 } else {
297313 throw new UnexpectedValueException ('Invalid target type ' );
298314 }
299- })->then (function ($ host ) use ($ reader ) {
300- return $ reader ->readBinary (array ('port ' =>'n ' ))->then (function ($ data ) use ($ host ) {
301- return array ($ host , $ data ['port ' ]);
315+ })->then (function ($ host ) use ($ reader, & $ remote ) {
316+ return $ reader ->readBinary (array ('port ' =>'n ' ))->then (function ($ data ) use ($ host, & $ remote ) {
317+ return array ($ host , $ data ['port ' ], $ remote );
302318 });
303319 })->then (function ($ target ) use ($ that , $ stream ) {
304320 return $ that ->connectTarget ($ stream , $ target );
@@ -322,14 +338,18 @@ public function connectTarget(ConnectionInterface $stream, array $target)
322338 if (strpos ($ uri , ': ' ) !== false ) {
323339 $ uri = '[ ' . $ uri . '] ' ;
324340 }
325- $ uri = $ uri . ': ' . $ target [1 ];
341+ $ uri .= ': ' . $ target [1 ];
326342
327343 // validate URI so a string hostname can not pass excessive URI parts
328344 $ parts = parse_url ('tcp:// ' . $ uri );
329345 if (!$ parts || !isset ($ parts ['scheme ' ], $ parts ['host ' ], $ parts ['port ' ]) || count ($ parts ) !== 3 ) {
330346 return Promise \reject (new InvalidArgumentException ('Invalid target URI given ' ));
331347 }
332348
349+ if (isset ($ target [2 ])) {
350+ $ uri .= '?source= ' . rawurlencode ($ target [2 ]);
351+ }
352+
333353 $ stream ->emit ('target ' , $ target );
334354 $ that = $ this ;
335355 $ connecting = $ this ->connector ->connect ($ uri );
0 commit comments