Класс сокета с поддержкой socks4/5-прокси фичи: - полная поддержка сокс4/5-прокси - автоопределение типа прокси вспомогательный класс (socket.class.php) от этого класса идет наследование класса с поддержкой соксов socket.class.php PHP: /** * Socket Class * @author hodik <icq://664679> * @package Classes * @subpackage network * @version 1.0 * @filesource */ /** * CSocket * @package Classes * @subpackage network */ class CSocket{ /** * @desc * @access private */ var $_persistent = false; /** * @desc * @access private */ var $_connected = false; /** * @desc * @access private */ var $_conn = null; /** * Конструктор класса * @param boolean постоянное соединение или нет * @return void */ function CSocket($persistent = false) { $this->_persistent = $persistent; } /** * Проверяет, установлено ли соединение * @return boolean */ function _valid() { return is_resource($this->_conn) && ($this->_connected == true); } /** * Устанавливает соединение * @param string * @param int * @return bool */ function connect($url, $timeout = 10) { $host = parse_url($url, PHP_URL_HOST); if(!$port = parse_url($url, PHP_URL_PORT)) $port = 80; $sockFunc = ($this->_persistent) ? "pfsockopen" : "fsockopen"; $this->_conn = $sockFunc($host, $port, $errno, $errstr, $timeout); if(!$this->_conn) { trigger_error("Couldn't connect to $host:$port", E_USER_WARNING); return false; } $this->_connected = true; return true; } /** * Посылает данные (binary-safe) * @param какие-то данные * @return boolean */ function send($data) { if(!$this->_valid()) { trigger_error("Error on sending data. Reason: Not connected", E_USER_WARNING); return false; } if(fputs($this->_conn, $data) === FALSE) { trigger_error("Error on sending data to the server", E_USER_WARNING); return false; } return true; } /** * Читает из входного потока определенное количество символов * @param int * @return string */ function read($len) { if(!$this->_valid()) { trigger_error("Error on reading data. Reason: Not connected", E_USER_WARNING); return false; } if(($buf = fgets($this->_conn, $len)) === FALSE) { trigger_error("Error on reading data. Reason: Connection error", E_USER_WARNING); return false; } return $buf; } /** * Читает строку из входного потока * @return string */ function readString() { if(!$this->_valid()) { trigger_error("Error on reading string. Reason: Not connected", E_USER_WARNING); return false; } $str = null; while(!feof($this->_conn)) { if(($buf = fgets($this->_conn, 128)) === FALSE) { trigger_error("Error on reading string. Reason: Connection error", E_USER_WARNING); return false; } $str .= $buf; if(strlen($str) >= 1 && (substr($str, -2) == "\r\n" || substr($str, -1) == "\n")) { return rtrim($str); } } if($str) return $str; else return false; } /** * Читает все данные из входного потока * @return string */ function readAll() { if(!$this->_valid()) { trigger_error("Error on reading data. Reason: Not connected", E_USER_WARNING); return false; } $str = ""; while(!feof($this->_conn)) { if(($buf = fgets($this->_conn, 128)) === FALSE) { trigger_error("Error on reading data. Reason: Connection error", E_USER_WARNING); return false; } $str .= $buf; } return $str; } /** * Проверяет, активно ли соединение * @return boolean */ function isActive() { return $this->_valid(); } /** * Разрывает соединение * @return boolean */ function disconnect() { if(!$this->_valid()) { trigger_error("Error on disconnecting. Reason: Not connected", E_USER_WARNING); return false; } $this->_connected = false; fclose($this->_conn); return true; } } ?> собсно сам класс для соксов (socks.class.php) PHP: <? /** * Socket Class Through SOCKS4/5 proxy * @author hodik <icq://664679> * @package Classes * @subpackage network * @version 1.0 * @filesource */ /** * CSocksSocket * @package Classes * @subpackage network */ require_once("socket.class.php"); define("SOCKS4_TYPE", 1); define("SOCKS5_TYPE", 2); define("AUTOMATIC_TYPE", 3); class CSocksSocket extends CSocket{ /** * @desc * @access private */ var $_proxyHost; /** * @desc * @access private */ var $_proxyPort; /** * @desc * @access private */ var $_proxyType; /** * @desc * @access private */ var $_proxyUser; /** * @desc * @access private */ var $_proxyPwd; /** * @desc * @access private */ var $_host; /** * @desc * @access private */ var $_port; /** * @desc * @access private */ var $_timeout; /** * Устанавливает прокси и его тип * @param string * @return void */ function setProxy($proxyURL) { $this->_proxyHost = parse_url($proxyURL, PHP_URL_HOST); $this->_proxyPort = parse_url($proxyURL, PHP_URL_PORT); $this->_proxyUser = parse_url($proxyURL, PHP_URL_USER); $this->_proxyPwd = parse_url($proxyURL, PHP_URL_PASS); $this->_proxyType = strtolower(parse_url($proxyURL, PHP_URL_SCHEME)); } /** * Преобразует имя хоста или его адрес в число (4 байта) * @param string * @return int * @access private */ function _host2int($host) { $ip = gethostbyname($host); if(preg_match("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $ip, $matches)) { $retVal = pack("C4", $matches[1], $matches[2], $matches[3], $matches[4]); } return $retVal; } /** * Соединяется через socks4 * @access private * @return boolean */ function _connectThroughSocks4() { $query = pack("C2", 0x04, 0x01); //VN, CD $query .= pack("n", $this->_port); //DSTPORT $query .= $this->_host2int($this->_host); //DSTIP $query .= ($this->_proxyUser != "")? $this->_proxyUser : "0"; //IDENTD $query .= pack("C", 0); //NULL if(!$this->send($query)) return false; if(($response = $this->read(9)) === false) return false; $answer = unpack("Cvn/Ccd", substr($response, 0, 2)); //по протоколу vn всегда должен быть 0 if($answer["vn"] != 0) { trigger_error("The VN field of proxy server response is not zero. It should be 0", E_USER_WARNING); return false; } switch($answer["cd"]) { case 90: return true; break; case 91: case 92: case 93: trigger_error("Request to proxy have been rejected or failed", E_USER_WARNING); return false; break; default: trigger_error("Unknow response from proxy", E_USER_WARNING); return false; break; } return false; } /** * Выполняет аутентификацию логин/пароль по протоколу SOCKS5 * @access private * @return boolean */ function _doSOCKS5Auth() { $request = pack("C", 1); //VN $request .= pack("C", strlen($this->_proxyUser)); $request .= $this->_proxyUser; $request .= pack("C", strlen($this->_proxyPwd)); $request .= $this->_proxyPwd; if(!$this->send($request)) return false; if(($response = $this->read(3)) === false) return false; $answer = unpack("Cvn/Cresult", $response); if($answer["vn"] != 1 && $answer["result"] != 0) { trigger_error("Proxy have rejected the connection. Reason: Invalid username or/and password", E_USER_WARNING); return false; } return true; } /** * Соединяется через socks5 * @access private * @return boolean */ function _connectThroughSocks5() { //выбор тип аутентификации if($this->_proxyUser) { $request = pack("C4", 5, //версия сокс-протокола 2, //длина 0, //без аутентификации 2);//логин и пароль } else { $request = pack("C3", 5, 1, 0); //без аутентификации } if(!$this->send($request)) return false; if(($response = $this->read(3)) === false) return false; $answer = unpack("Cver/Cmethod", $response); //аутентификация: логин и пароль if($answer["method"] == 2 && !$this->_doSOCKS5Auth()) return false; //без аутентификации elseif($answer["method"] == 0) { /* ничего не делаем*/ } else { trigger_error("Proxy have rejected the connection. Reason: All the auth methods not supported", E_USER_WARNING); return false; } //запрос CONNECT $request = pack("C4", 0x05, 0x01, 0x00, 0x01); //VN=5,Method=1,Reserved=0,ATYP=ipv4 $request .= $this->_host2int($this->_host); //DSTIP $request .= pack("n", $this->_port); //DSTPORT if(!$this->send($request)) return false; if(($response = $this->read(11)) === false) return false; //читаем ответ сервера $answer = unpack("Cver/CREP", substr($response, 0, 2)); switch($answer["REP"]) { case 0: return true; default: trigger_error("Proxy have rejected the connection. Reason: code error={$answer['REP']}", E_USER_WARNING); return false; } } /** * Определяет тип прокси и соединяется * @return boolean */ function _connectAutoType() { //попробуем как через сокс4, а вдруг получится if($this->_connectThroughSocks4()) { $this->_proxyType = "socks4"; return true; } //остался последний вариант: сокс5 (!!!) else { //на всякий случай разорвать соединение, а то сервер подумает, что над ним издеваются =) $this->disconnect(); if(!parent::connect("{$this->_proxyHost}:{$this->_proxyPort}", $this->_timeout)) return false; if($this->_connectThroughSocks5()) { $this->_proxyType = "socks5"; return true; } } //наверно это оказался SOCKS6, поддержку потом добавлю =) return false; } /** * Устанавливает соединение * @param string * @param int * @return boolean */ function connect($url, $timeout = 10) { $this->_host = parse_url($url, PHP_URL_HOST); if(!$this->_port = parse_url($url, PHP_URL_PORT)) $this->_port = 80; $this->_timeout = $timeout; if(!parent::connect("{$this->_proxyHost}:{$this->_proxyPort}", $this->_timeout)) return false; switch($this->_proxyType) { case "socks4"; return $this->_connectThroughSocks4(); break; case "socks5": return $this->_connectThroughSocks5(); break; case "socks": return $this->_connectAutoType(); break; default: return $this->_connectAutoType(); break; } } } ?> пример использования: PHP: <? include("socks.class.php"); $sock = new CSocksSocket(); $sock->setProxy("socks://127.0.0.1:1080");//автоопределение $sock->setProxy("socks4://127.0.0.1:1080"); //явно задан вид - сокс4 $sock->setProxy("socks5://127.0.0.1:1080"); //явно задан вид - сокс5 $sock->connect("yandex.ru:80", 10); //10-это таймаут $sock->send("GET / HTTP/1.0\r\nConnection: close\r\n\r\n"); print $sock->readAll(); ?>