예제 #1
0
 public function handleSocks5(Stream $stream, $auth = null)
 {
     $reader = new StreamReader($stream);
     $that = $this;
     return $reader->readByte()->then(function ($num) use($reader) {
         // $num different authentication mechanisms offered
         return $reader->readLength($num);
     })->then(function ($methods) use($reader, $stream, $auth) {
         if ($auth === null && strpos($methods, "") !== false) {
             // accept "no authentication"
             $stream->write(pack('C2', 0x5, 0x0));
             return 0x0;
         } else {
             if ($auth !== null && strpos($methods, "") !== false) {
                 // username/password authentication (RFC 1929) sub negotiation
                 $stream->write(pack('C2', 0x5, 0x2));
                 return $reader->readByteAssert(0x1)->then(function () use($reader) {
                     return $reader->readByte();
                 })->then(function ($length) use($reader) {
                     return $reader->readLength($length);
                 })->then(function ($username) use($reader, $auth, $stream) {
                     return $reader->readByte()->then(function ($length) use($reader) {
                         return $reader->readLength($length);
                     })->then(function ($password) use($username, $auth, $stream) {
                         // username and password known => authenticate
                         // echo 'auth: ' . $username.' : ' . $password . PHP_EOL;
                         return $auth($username, $password)->then(function () use($stream, $username) {
                             // accept
                             $stream->emit('auth', array($username));
                             $stream->write(pack('C2', 0x1, 0x0));
                         }, function () use($stream) {
                             // reject => send any code but 0x00
                             $stream->end(pack('C2', 0x1, 0xff));
                             throw new UnexpectedValueException('Unable to authenticate');
                         });
                     });
                 });
             } else {
                 // reject all offered authentication methods
                 $stream->end(pack('C2', 0x5, 0xff));
                 throw new UnexpectedValueException('No acceptable authentication mechanism found');
             }
         }
     })->then(function ($method) use($reader, $stream) {
         return $reader->readBinary(array('version' => 'C', 'command' => 'C', 'null' => 'C', 'type' => 'C'));
     })->then(function ($data) use($reader) {
         if ($data['version'] !== 0x5) {
             throw new UnexpectedValueException('Invalid SOCKS version');
         }
         if ($data['command'] !== 0x1) {
             throw new UnexpectedValueException('Only CONNECT requests supported');
         }
         //             if ($data['null'] !== 0x00) {
         //                 throw new UnexpectedValueException('Reserved byte has to be NULL');
         //             }
         if ($data['type'] === 0x3) {
             // target hostname string
             return $reader->readByte()->then(function ($len) use($reader) {
                 return $reader->readLength($len);
             });
         } else {
             if ($data['type'] === 0x1) {
                 // target IPv4
                 return $reader->readLength(4)->then(function ($addr) {
                     return inet_ntop($addr);
                 });
             } else {
                 if ($data['type'] === 0x4) {
                     // target IPv6
                     return $reader->readLength(16)->then(function ($addr) {
                         return inet_ntop($addr);
                     });
                 } else {
                     throw new UnexpectedValueException('Invalid target type');
                 }
             }
         }
     })->then(function ($host) use($reader) {
         return $reader->readBinary(array('port' => 'n'))->then(function ($data) use($host) {
             return array($host, $data['port']);
         });
     })->then(function ($target) use($that, $stream) {
         return $that->connectTarget($stream, $target);
     }, function ($error) use($stream) {
         throw new UnexpectedValueException('SOCKS5 protocol error', 0, $error);
     })->then(function (Stream $remote) use($stream) {
         $stream->write(pack('C4Nn', 0x5, 0x0, 0x0, 0x1, 0, 0));
         return $remote;
     }, function (Exception $error) use($stream) {
         $code = 0x1;
         $stream->end(pack('C4Nn', 0x5, $code, 0x0, 0x1, 0, 0));
         throw $error;
     });
 }
예제 #2
0
 protected function handleSocks5(Stream $stream, $host, $port, $auth = null)
 {
     // protocol version 5
     $data = pack('C', 0x5);
     if ($auth === null) {
         // one method, no authentication
         $data .= pack('C2', 0x1, 0x0);
     } else {
         // two methods, username/password and no authentication
         $data .= pack('C3', 0x2, 0x2, 0x0);
     }
     $stream->write($data);
     $that = $this;
     $reader = new StreamReader($stream);
     return $reader->readBinary(array('version' => 'C', 'method' => 'C'))->then(function ($data) use($auth, $stream, $reader) {
         if ($data['version'] !== 0x5) {
             throw new Exception('Version/Protocol mismatch');
         }
         if ($data['method'] === 0x2 && $auth !== null) {
             // username/password authentication requested and provided
             $stream->write($auth);
             return $reader->readBinary(array('version' => 'C', 'status' => 'C'))->then(function ($data) {
                 if ($data['version'] !== 0x1 || $data['status'] !== 0x0) {
                     throw new Exception('Username/Password authentication failed');
                 }
             });
         } else {
             if ($data['method'] !== 0x0) {
                 // any other method than "no authentication"
                 throw new Exception('Unacceptable authentication method requested');
             }
         }
     })->then(function () use($stream, $reader, $host, $port) {
         // do not resolve hostname. only try to convert to (binary/packed) IP
         $ip = @inet_pton($host);
         $data = pack('C3', 0x5, 0x1, 0x0);
         if ($ip === false) {
             // not an IP, send as hostname
             $data .= pack('C2', 0x3, strlen($host)) . $host;
         } else {
             // send as IPv4 / IPv6
             $data .= pack('C', strpos($host, ':') === false ? 0x1 : 0x4) . $ip;
         }
         $data .= pack('n', $port);
         $stream->write($data);
         return $reader->readBinary(array('version' => 'C', 'status' => 'C', 'null' => 'C', 'type' => 'C'));
     })->then(function ($data) use($reader) {
         if ($data['version'] !== 0x5 || $data['status'] !== 0x0 || $data['null'] !== 0x0) {
             throw new Exception('Invalid SOCKS response');
         }
         if ($data['type'] === 0x1) {
             // IPv4 address => skip IP and port
             return $reader->readLength(6);
         } else {
             if ($data['type'] === 0x3) {
                 // domain name => read domain name length
                 return $reader->readBinary(array('length' => 'C'))->then(function ($data) use($that) {
                     // skip domain name and port
                     return $that->readLength($data['length'] + 2);
                 });
             } else {
                 if ($data['type'] === 0x4) {
                     // IPv6 address => skip IP and port
                     return $reader->readLength(18);
                 } else {
                     throw new Exception('Invalid SOCKS reponse: Invalid address type');
                 }
             }
         }
     });
 }