Форум АНТИЧАТ (http://forum.antichat.ru/index.php)
-   PHP, PERL, MySQL, JavaScript (http://forum.antichat.ru/forumdisplay.php?f=37)
-   -   Ходим в php через socks4/5 (http://forum.antichat.ru/showthread.php?t=57030)

h0d 31.12.2007 20:09

Ходим в php через socks4/5
 
Класс сокета с поддержкой 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($urlPHP_URL_HOST);
        if(!
$port parse_url($urlPHP_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->_conn128)) === FALSE)
            {
                
trigger_error("Error on reading string. Reason: Connection error"E_USER_WARNING);
                return 
false;
            }
            
            
$str .= $buf;
            if(
strlen($str) >= && (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->_conn128)) === 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();  
?>


Московское время: 00:44.

Powered by: vBulletin Version 3.0.x
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.