SOCKS5.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. /**
  3. * SOCKS5 proxy connection class
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE
  8. *
  9. * This source file is subject to BSD 3-Clause License that is bundled
  10. * with this package in the file LICENSE and available at the URL
  11. * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
  12. *
  13. * @category HTTP
  14. * @package HTTP_Request2
  15. * @author Alexey Borzov <avb@php.net>
  16. * @copyright 2008-2014 Alexey Borzov <avb@php.net>
  17. * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
  18. * @link http://pear.php.net/package/HTTP_Request2
  19. */
  20. /** Socket wrapper class used by Socket Adapter */
  21. require_once 'HTTP/Request2/SocketWrapper.php';
  22. /**
  23. * SOCKS5 proxy connection class (used by Socket Adapter)
  24. *
  25. * @category HTTP
  26. * @package HTTP_Request2
  27. * @author Alexey Borzov <avb@php.net>
  28. * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
  29. * @version Release: 2.2.1
  30. * @link http://pear.php.net/package/HTTP_Request2
  31. * @link http://pear.php.net/bugs/bug.php?id=19332
  32. * @link http://tools.ietf.org/html/rfc1928
  33. */
  34. class HTTP_Request2_SOCKS5 extends HTTP_Request2_SocketWrapper
  35. {
  36. /**
  37. * Constructor, tries to connect and authenticate to a SOCKS5 proxy
  38. *
  39. * @param string $address Proxy address, e.g. 'tcp://localhost:1080'
  40. * @param int $timeout Connection timeout (seconds)
  41. * @param array $contextOptions Stream context options
  42. * @param string $username Proxy user name
  43. * @param string $password Proxy password
  44. *
  45. * @throws HTTP_Request2_LogicException
  46. * @throws HTTP_Request2_ConnectionException
  47. * @throws HTTP_Request2_MessageException
  48. */
  49. public function __construct(
  50. $address, $timeout = 10, array $contextOptions = array(),
  51. $username = null, $password = null
  52. ) {
  53. parent::__construct($address, $timeout, $contextOptions);
  54. if (strlen($username)) {
  55. $request = pack('C4', 5, 2, 0, 2);
  56. } else {
  57. $request = pack('C3', 5, 1, 0);
  58. }
  59. $this->write($request);
  60. $response = unpack('Cversion/Cmethod', $this->read(3));
  61. if (5 != $response['version']) {
  62. throw new HTTP_Request2_MessageException(
  63. 'Invalid version received from SOCKS5 proxy: ' . $response['version'],
  64. HTTP_Request2_Exception::MALFORMED_RESPONSE
  65. );
  66. }
  67. switch ($response['method']) {
  68. case 2:
  69. $this->performAuthentication($username, $password);
  70. case 0:
  71. break;
  72. default:
  73. throw new HTTP_Request2_ConnectionException(
  74. "Connection rejected by proxy due to unsupported auth method"
  75. );
  76. }
  77. }
  78. /**
  79. * Performs username/password authentication for SOCKS5
  80. *
  81. * @param string $username Proxy user name
  82. * @param string $password Proxy password
  83. *
  84. * @throws HTTP_Request2_ConnectionException
  85. * @throws HTTP_Request2_MessageException
  86. * @link http://tools.ietf.org/html/rfc1929
  87. */
  88. protected function performAuthentication($username, $password)
  89. {
  90. $request = pack('C2', 1, strlen($username)) . $username
  91. . pack('C', strlen($password)) . $password;
  92. $this->write($request);
  93. $response = unpack('Cvn/Cstatus', $this->read(3));
  94. if (1 != $response['vn'] || 0 != $response['status']) {
  95. throw new HTTP_Request2_ConnectionException(
  96. 'Connection rejected by proxy due to invalid username and/or password'
  97. );
  98. }
  99. }
  100. /**
  101. * Connects to a remote host via proxy
  102. *
  103. * @param string $remoteHost Remote host
  104. * @param int $remotePort Remote port
  105. *
  106. * @throws HTTP_Request2_ConnectionException
  107. * @throws HTTP_Request2_MessageException
  108. */
  109. public function connect($remoteHost, $remotePort)
  110. {
  111. $request = pack('C5', 0x05, 0x01, 0x00, 0x03, strlen($remoteHost))
  112. . $remoteHost . pack('n', $remotePort);
  113. $this->write($request);
  114. $response = unpack('Cversion/Creply/Creserved', $this->read(1024));
  115. if (5 != $response['version'] || 0 != $response['reserved']) {
  116. throw new HTTP_Request2_MessageException(
  117. 'Invalid response received from SOCKS5 proxy',
  118. HTTP_Request2_Exception::MALFORMED_RESPONSE
  119. );
  120. } elseif (0 != $response['reply']) {
  121. throw new HTTP_Request2_ConnectionException(
  122. "Unable to connect to {$remoteHost}:{$remotePort} through SOCKS5 proxy",
  123. 0, $response['reply']
  124. );
  125. }
  126. }
  127. }
  128. ?>