public function proxify(\Erebot\URIInterface $proxyURI, \Erebot\URIInterface $nextURI) { $credentials = $proxyURI->getUserInfo(); $host = $nextURI->getHost(); $port = $nextURI->getPort(); $scheme = $nextURI->getScheme(); if ($port === null) { $port = getservbyname($scheme, 'tcp'); } if (!is_int($port) || $port <= 0 || $port > 65535) { throw new \Erebot\InvalidValueException('Invalid port'); } $request = ""; $request .= sprintf("CONNECT %s:%d HTTP/1.0\r\n", $host, $port); $request .= sprintf("Host: %s:%d\r\n", $host, $port); $request .= "User-Agent: Erebot/dev-master\r\n"; if ($credentials !== null) { $request .= sprintf("Proxy-Authorization: basic %s\r\n", base64_encode($credentials)); } $request .= "\r\n"; for ($written = 0, $len = strlen($request); $written < $len; $written += $fwrite) { $fwrite = fwrite($this->_socket, substr($request, $written)); if ($fwrite === false) { throw new \Erebot\Exception('Connection closed by proxy'); } } $line = stream_get_line($this->_socket, 4096, "\r\n"); if ($line === false) { throw new \Erebot\InvalidValueException('Invalid response from proxy'); } $this->_logger->debug('%(line)s', array('line' => addcslashes($line, ".."))); $contents = array_filter(explode(" ", $line)); switch ((int) $contents[1]) { case 200: break; case 407: throw new \Erebot\Exception('Proxy authentication required'); default: throw new \Erebot\Exception('Connection rejected by proxy'); } // Avoid an endless loop by limiting the number of headers. // No HTTP server is likely to send more than 2^10 headers anyway. $max = 1 << 10; for ($i = 0; $i < $max; $i++) { $line = stream_get_line($this->_socket, 4096, "\r\n"); if ($line === false) { throw new \Erebot\InvalidValueException('Invalid response from proxy'); } if ($line == "") { break; } $this->_logger->debug('%(line)s', array('line' => addcslashes($line, ".."))); } if ($i === $max) { throw new \Erebot\InvalidValueException('Endless loop detected in proxy response'); } }
public function proxify(\Erebot\URIInterface $proxyURI, \Erebot\URIInterface $nextURI) { $port = $nextURI->getPort(); $scheme = $nextURI->getScheme(); if ($port === null) { $port = getservbyname($scheme, 'tcp'); } if (!is_int($port) || $port <= 0 || $port > 65535) { throw new \Erebot\InvalidValueException('Invalid port'); } // No authentication or username/password-based authentication. $this->write(""); $line = $this->read(2); if ($line[0] != "") { throw new \Erebot\InvalidValueException('Bad SOCKS version'); } switch (ord($line[1])) { case 0: // No authentication break; case 2: // Username/password-based authentication $this->userpass($proxyURI); break; default: throw new \Erebot\InvalidValueException('No acceptable method'); } // CONNECT. $host = $nextURI->getHost(); $this->write("" . pack("Ca*n", strlen($host), $host, $port)); $line = $this->read(4); if ($line[0] != "") { throw new \Erebot\InvalidValueException('Bad SOCKS version'); } $error = ord($line[1]); if ($error) { // Taken fromt eh RFC. $errors = array(1 => 'General SOCKS server failure', 'Connection not allowed by ruleset', 'Network unreachable', 'Host unreachable', 'Connection refused', 'TTL expired', 'Command not supported', 'Address type not supported'); if (!isset($errors[$error])) { throw new \Erebot\InvalidValueException('Unknown error'); } throw new \Erebot\InvalidValueException($errors[$error]); } switch (ord($line[3])) { case 1: // IPv4. $this->read(4); break; case 3: // Domain name. $len = ord($this->read(1)); $this->read($len); break; case 4: // IPv6. $this->read(16); break; default: throw new \Erebot\InvalidValueException('Address type not supported'); } // Consume the port. $this->read(2); }