| для тех, кому интересна эта тема.
socket.class.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
<?
/**
* 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;
}
}
}
?>
|
Пример использования:
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();
|
класс взят с адреса
http://www.xaker.name/forvb/showthread.php?t=6366 | |