@@ -160,9 +160,9 @@ public function connect($uri)
160
160
// start TCP/IP connection to SOCKS server
161
161
$ connecting = $ this ->connector ->connect ($ socksUri );
162
162
163
- $ deferred = new Deferred (function ($ _ , $ reject ) use ($ connecting ) {
163
+ $ deferred = new Deferred (function ($ _ , $ reject ) use ($ uri , $ connecting ) {
164
164
$ reject (new RuntimeException (
165
- 'Connection cancelled while waiting for proxy (ECONNABORTED) ' ,
165
+ 'Connection to ' . $ uri . ' cancelled while waiting for proxy (ECONNABORTED) ' ,
166
166
defined ('SOCKET_ECONNABORTED ' ) ? SOCKET_ECONNABORTED : 103
167
167
));
168
168
@@ -177,12 +177,12 @@ public function connect($uri)
177
177
// resolve plain connection once SOCKS protocol is completed
178
178
$ that = $ this ;
179
179
$ connecting ->then (
180
- function (ConnectionInterface $ stream ) use ($ that , $ host , $ port , $ deferred ) {
181
- $ that ->handleConnectedSocks ($ stream , $ host , $ port , $ deferred );
180
+ function (ConnectionInterface $ stream ) use ($ that , $ host , $ port , $ deferred, $ uri ) {
181
+ $ that ->handleConnectedSocks ($ stream , $ host , $ port , $ deferred, $ uri );
182
182
},
183
- function (Exception $ e ) use ($ deferred ) {
183
+ function (Exception $ e ) use ($ uri , $ deferred ) {
184
184
$ deferred ->reject ($ e = new RuntimeException (
185
- 'Connection failed because connection to proxy failed (ECONNREFUSED) ' ,
185
+ 'Connection to ' . $ uri . ' failed because connection to proxy failed (ECONNREFUSED) ' ,
186
186
defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111 ,
187
187
$ e
188
188
));
@@ -213,26 +213,33 @@ function (Exception $e) use ($deferred) {
213
213
* @param string $host
214
214
* @param int $port
215
215
* @param Deferred $deferred
216
+ * @param string $uri
216
217
* @return void
217
218
* @internal
218
219
*/
219
- public function handleConnectedSocks (ConnectionInterface $ stream , $ host , $ port , Deferred $ deferred )
220
+ public function handleConnectedSocks (ConnectionInterface $ stream , $ host , $ port , Deferred $ deferred, $ uri )
220
221
{
221
222
$ reader = new StreamReader ();
222
223
$ stream ->on ('data ' , array ($ reader , 'write ' ));
223
224
224
- $ stream ->on ('error ' , $ onError = function (Exception $ e ) use ($ deferred ) {
225
- $ deferred ->reject (new RuntimeException ('Stream error while waiting for response from proxy (EIO) ' , defined ('SOCKET_EIO ' ) ? SOCKET_EIO : 5 , $ e ));
225
+ $ stream ->on ('error ' , $ onError = function (Exception $ e ) use ($ deferred , $ uri ) {
226
+ $ deferred ->reject (new RuntimeException (
227
+ 'Connection to ' . $ uri . ' failed because connection to proxy caused a stream error (EIO) ' ,
228
+ defined ('SOCKET_EIO ' ) ? SOCKET_EIO : 5 , $ e )
229
+ );
226
230
});
227
231
228
- $ stream ->on ('close ' , $ onClose = function () use ($ deferred ) {
229
- $ deferred ->reject (new RuntimeException ('Connection to proxy lost while waiting for response (ECONNRESET) ' , defined ('SOCKET_ECONNRESET ' ) ? SOCKET_ECONNRESET : 104 ));
232
+ $ stream ->on ('close ' , $ onClose = function () use ($ deferred , $ uri ) {
233
+ $ deferred ->reject (new RuntimeException (
234
+ 'Connection to ' . $ uri . ' failed because connection to proxy was lost while waiting for response from proxy (ECONNRESET) ' ,
235
+ defined ('SOCKET_ECONNRESET ' ) ? SOCKET_ECONNRESET : 104 )
236
+ );
230
237
});
231
238
232
239
if ($ this ->protocolVersion === 5 ) {
233
- $ promise = $ this ->handleSocks5 ($ stream , $ host , $ port , $ reader );
240
+ $ promise = $ this ->handleSocks5 ($ stream , $ host , $ port , $ reader, $ uri );
234
241
} else {
235
- $ promise = $ this ->handleSocks4 ($ stream , $ host , $ port , $ reader );
242
+ $ promise = $ this ->handleSocks4 ($ stream , $ host , $ port , $ reader, $ uri );
236
243
}
237
244
238
245
$ promise ->then (function () use ($ deferred , $ stream , $ reader , $ onError , $ onClose ) {
@@ -241,18 +248,22 @@ public function handleConnectedSocks(ConnectionInterface $stream, $host, $port,
241
248
$ stream ->removeListener ('close ' , $ onClose );
242
249
243
250
$ deferred ->resolve ($ stream );
244
- }, function (Exception $ error ) use ($ deferred , $ stream ) {
251
+ }, function (Exception $ error ) use ($ deferred , $ stream, $ uri ) {
245
252
// pass custom RuntimeException through as-is, otherwise wrap in protocol error
246
253
if (!$ error instanceof RuntimeException) {
247
- $ error = new RuntimeException ('Invalid response received from proxy (EBADMSG) ' , defined ('SOCKET_EBADMSG ' ) ? SOCKET_EBADMSG : 71 , $ error );
254
+ $ error = new RuntimeException (
255
+ 'Connection to ' . $ uri . ' failed because proxy returned invalid response (EBADMSG) ' ,
256
+ defined ('SOCKET_EBADMSG ' ) ? SOCKET_EBADMSG : 71 ,
257
+ $ error
258
+ );
248
259
}
249
260
250
261
$ deferred ->reject ($ error );
251
262
$ stream ->close ();
252
263
});
253
264
}
254
265
255
- private function handleSocks4 (ConnectionInterface $ stream , $ host , $ port , StreamReader $ reader )
266
+ private function handleSocks4 (ConnectionInterface $ stream , $ host , $ port , StreamReader $ reader, $ uri )
256
267
{
257
268
// do not resolve hostname. only try to convert to IP
258
269
$ ip = ip2long ($ host );
@@ -272,17 +283,20 @@ private function handleSocks4(ConnectionInterface $stream, $host, $port, StreamR
272
283
'status ' => 'C ' ,
273
284
'port ' => 'n ' ,
274
285
'ip ' => 'N '
275
- ))->then (function ($ data ) {
286
+ ))->then (function ($ data ) use ( $ uri ) {
276
287
if ($ data ['null ' ] !== 0x00 ) {
277
288
throw new Exception ('Invalid SOCKS response ' );
278
289
}
279
290
if ($ data ['status ' ] !== 0x5a ) {
280
- throw new RuntimeException ('Proxy refused connection with SOCKS error code ' . sprintf ('0x%02X ' , $ data ['status ' ]) . ' (ECONNREFUSED) ' , defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111 );
291
+ throw new RuntimeException (
292
+ 'Connection to ' . $ uri . ' failed because proxy refused connection with error code ' . sprintf ('0x%02X ' , $ data ['status ' ]) . ' (ECONNREFUSED) ' ,
293
+ defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111
294
+ );
281
295
}
282
296
});
283
297
}
284
298
285
- private function handleSocks5 (ConnectionInterface $ stream , $ host , $ port , StreamReader $ reader )
299
+ private function handleSocks5 (ConnectionInterface $ stream , $ host , $ port , StreamReader $ reader, $ uri )
286
300
{
287
301
// protocol version 5
288
302
$ data = pack ('C ' , 0x05 );
@@ -302,7 +316,7 @@ private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamR
302
316
return $ reader ->readBinary (array (
303
317
'version ' => 'C ' ,
304
318
'method ' => 'C '
305
- ))->then (function ($ data ) use ($ auth , $ stream , $ reader ) {
319
+ ))->then (function ($ data ) use ($ auth , $ stream , $ reader, $ uri ) {
306
320
if ($ data ['version ' ] !== 0x05 ) {
307
321
throw new Exception ('Version/Protocol mismatch ' );
308
322
}
@@ -314,14 +328,20 @@ private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamR
314
328
return $ reader ->readBinary (array (
315
329
'version ' => 'C ' ,
316
330
'status ' => 'C '
317
- ))->then (function ($ data ) {
331
+ ))->then (function ($ data ) use ( $ uri ) {
318
332
if ($ data ['version ' ] !== 0x01 || $ data ['status ' ] !== 0x00 ) {
319
- throw new RuntimeException ('Username/Password authentication failed (EACCES) ' , defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13 );
333
+ throw new RuntimeException (
334
+ 'Connection to ' . $ uri . ' failed because proxy denied access with given authentication details (EACCES) ' ,
335
+ defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13
336
+ );
320
337
}
321
338
});
322
339
} else if ($ data ['method ' ] !== 0x00 ) {
323
340
// any other method than "no authentication"
324
- throw new RuntimeException ('No acceptable authentication method found (EACCES) ' , defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13 );
341
+ throw new RuntimeException (
342
+ 'Connection to ' . $ uri . ' failed because proxy denied access due to unsupported authentication method (EACCES) ' ,
343
+ defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13
344
+ );
325
345
}
326
346
})->then (function () use ($ stream , $ reader , $ host , $ port ) {
327
347
// do not resolve hostname. only try to convert to (binary/packed) IP
@@ -345,32 +365,59 @@ private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamR
345
365
'null ' => 'C ' ,
346
366
'type ' => 'C '
347
367
));
348
- })->then (function ($ data ) use ($ reader ) {
368
+ })->then (function ($ data ) use ($ reader, $ uri ) {
349
369
if ($ data ['version ' ] !== 0x05 || $ data ['null ' ] !== 0x00 ) {
350
370
throw new Exception ('Invalid SOCKS response ' );
351
371
}
352
372
if ($ data ['status ' ] !== 0x00 ) {
353
373
// map limited list of SOCKS error codes to common socket error conditions
354
374
// @link https://tools.ietf.org/html/rfc1928#section-6
355
375
if ($ data ['status ' ] === Server::ERROR_GENERAL ) {
356
- throw new RuntimeException ('SOCKS server reported a general server failure (ECONNREFUSED) ' , defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111 );
376
+ throw new RuntimeException (
377
+ 'Connection to ' . $ uri . ' failed because proxy refused connection with general server failure (ECONNREFUSED) ' ,
378
+ defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111
379
+ );
357
380
} elseif ($ data ['status ' ] === Server::ERROR_NOT_ALLOWED_BY_RULESET ) {
358
- throw new RuntimeException ('SOCKS server reported connection is not allowed by ruleset (EACCES) ' , defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13 );
381
+ throw new RuntimeException (
382
+ 'Connection to ' . $ uri . ' failed because proxy denied access due to ruleset (EACCES) ' ,
383
+ defined ('SOCKET_EACCES ' ) ? SOCKET_EACCES : 13
384
+ );
359
385
} elseif ($ data ['status ' ] === Server::ERROR_NETWORK_UNREACHABLE ) {
360
- throw new RuntimeException ('SOCKS server reported network unreachable (ENETUNREACH) ' , defined ('SOCKET_ENETUNREACH ' ) ? SOCKET_ENETUNREACH : 101 );
386
+ throw new RuntimeException (
387
+ 'Connection to ' . $ uri . ' failed because proxy reported network unreachable (ENETUNREACH) ' ,
388
+ defined ('SOCKET_ENETUNREACH ' ) ? SOCKET_ENETUNREACH : 101
389
+ );
361
390
} elseif ($ data ['status ' ] === Server::ERROR_HOST_UNREACHABLE ) {
362
- throw new RuntimeException ('SOCKS server reported host unreachable (EHOSTUNREACH) ' , defined ('SOCKET_EHOSTUNREACH ' ) ? SOCKET_EHOSTUNREACH : 113 );
391
+ throw new RuntimeException (
392
+ 'Connection to ' . $ uri . ' failed because proxy reported host unreachable (EHOSTUNREACH) ' ,
393
+ defined ('SOCKET_EHOSTUNREACH ' ) ? SOCKET_EHOSTUNREACH : 113
394
+ );
363
395
} elseif ($ data ['status ' ] === Server::ERROR_CONNECTION_REFUSED ) {
364
- throw new RuntimeException ('SOCKS server reported connection refused (ECONNREFUSED) ' , defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111 );
396
+ throw new RuntimeException (
397
+ 'Connection to ' . $ uri . ' failed because proxy reported connection refused (ECONNREFUSED) ' ,
398
+ defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111
399
+ );
365
400
} elseif ($ data ['status ' ] === Server::ERROR_TTL ) {
366
- throw new RuntimeException ('SOCKS server reported TTL/timeout expired (ETIMEDOUT) ' , defined ('SOCKET_ETIMEDOUT ' ) ? SOCKET_ETIMEDOUT : 110 );
401
+ throw new RuntimeException (
402
+ 'Connection to ' . $ uri . ' failed because proxy reported TTL/timeout expired (ETIMEDOUT) ' ,
403
+ defined ('SOCKET_ETIMEDOUT ' ) ? SOCKET_ETIMEDOUT : 110
404
+ );
367
405
} elseif ($ data ['status ' ] === Server::ERROR_COMMAND_UNSUPPORTED ) {
368
- throw new RuntimeException ('SOCKS server does not support the CONNECT command (EPROTO) ' , defined ('SOCKET_EPROTO ' ) ? SOCKET_EPROTO : 71 );
406
+ throw new RuntimeException (
407
+ 'Connection to ' . $ uri . ' failed because proxy does not support the CONNECT command (EPROTO) ' ,
408
+ defined ('SOCKET_EPROTO ' ) ? SOCKET_EPROTO : 71
409
+ );
369
410
} elseif ($ data ['status ' ] === Server::ERROR_ADDRESS_UNSUPPORTED ) {
370
- throw new RuntimeException ('SOCKS server does not support this address type (EPROTO) ' , defined ('SOCKET_EPROTO ' ) ? SOCKET_EPROTO : 71 );
411
+ throw new RuntimeException (
412
+ 'Connection to ' . $ uri . ' failed because proxy does not support this address type (EPROTO) ' ,
413
+ defined ('SOCKET_EPROTO ' ) ? SOCKET_EPROTO : 71
414
+ );
371
415
}
372
416
373
- throw new RuntimeException ('SOCKS server reported an unassigned error code ' . sprintf ('0x%02X ' , $ data ['status ' ]) . ' (ECONNREFUSED) ' , defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111 );
417
+ throw new RuntimeException (
418
+ 'Connection to ' . $ uri . ' failed because proxy server refused connection with unknown error code ' . sprintf ('0x%02X ' , $ data ['status ' ]) . ' (ECONNREFUSED) ' ,
419
+ defined ('SOCKET_ECONNREFUSED ' ) ? SOCKET_ECONNREFUSED : 111
420
+ );
374
421
}
375
422
if ($ data ['type ' ] === 0x01 ) {
376
423
// IPv4 address => skip IP and port
0 commit comments