Location: PHPKode > scripts > socks5 > socks5/class.socks5.php
<?php
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <hide@address.com> wrote this file. As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return Günter Grodotzki
 * ----------------------------------------------------------------------------
 */

/**
 * a socks5 client in php (original by sergey krivoy, ported to php5
 * + bugfixes/improvements
 * @author Günter Grodotzki <hide@address.com>
 * @version 20081224
 */
class socks5
{
	private $socket;
	private $debug;
	private $socks_server;
	private $socks_port;
	private $socks_auth;
	private $interface;
	private $timeout = 30;
	private $dnstunnel = true;

	
	/**
	 * specify socks5-proxy to use here
	 * @param $server
	 * @param $port
	 * @param $username
	 * @param $password
	 */
	public function __construct($server, $port, $username = null, $password = null)
	{
		$this->socks_server = $server;
		$this->socks_port = $port;

		if($username !== null && $password !== null)
		{
			$this->socks_auth['username'] = $username;
			$this->socks_auth['password'] = $password;
		}

	}
	
	public function __destruct()
	{
		if(is_resource($this->socket))
		{
			socket_close($this->socket);
		}
	}
	
	
	/**
	 * set interface IP for outgoing connection to the socks proxy
	 * @param $ip
	 */
	public function set_interface($ip)
	{
		$this->interface = $ip;
	}
	
	
	/**
	 * set timeouts for connection and receiving data
	 * @param $seconds
	 */
	public function set_timeout($seconds)
	{
		if(is_int($seconds))
		{
			$this->timeout = $seconds;
		}		
	}
	
	
	/**
	 * if true, dns lookups will be performed by the socks-proxy 
	 * @param $bool
	 */
	public function set_dnstunnel($bool)
	{
		if(is_bool($bool))
		{
			$this->dnstunnel = $bool;
		}
	}
	

	/**
	 * connect to given host through previously given socks5-proxy
	 * @param $host
	 * @param $port
	 * @return bool
	 */
	public function connect($host, $port)
	{		
		if($this->bind_socks() === true)
		{
			if(socket_write($this->socket, $this->get_connection_request($host, $port)) !== false)
			{
				if(@socket_recv($this->socket, $buffer, 1024, 0) !== false)
				{
					$response = unpack("Cversion/Cresult/Creg/Ctype/Lip/Sport", $buffer);
					if(isset($response['version']) && isset($response['result']) && $response['version'] == 0x05 && $response['result'] == 0x00)
					{
						return true;			
					}
				}			
			}			
		}
		return false;
	}
	
	
	/**
	 * send request and return response
	 * @param $request
	 * @return string
	 */
	public function request($request)
	{
		$buffer = "";
		
		if($this->socket !== false && socket_write($this->socket, $request) !== false)
		{
			while(socket_recv($this->socket, $recv, 1024, 0) !== false)
			{
				if($recv === null)
				{
					break;
				}
				$buffer .= $recv;
			}		
			
			return $buffer;
		}
	}
	
	
	/**
	 * connect to given socks-proxy and bind
	 * @return unknown_type
	 */
	private function bind_socks()
	{
		// check dependency
		if(function_exists("socket_create"))
		{
			$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
			if($this->socket !== false)
			{
				socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => $this->timeout, "usec" => 0));	
				socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => $this->timeout, "usec" => 0));		
				
				// bind to local interface
				$this->bind_interface();
				
				/* timeout http://de2.php.net/manual/en/function.socket-connect.php#36223 */
				socket_set_nonblock($this->socket);
				
				$attempts = 0;
				// socket_connect + socket_set_nonblock throws out error-warning
				while(!($connected = @socket_connect($this->socket, $this->socks_server, $this->socks_port)) && $attempts++ < $this->timeout)
				{
					$err = socket_last_error($this->socket);
					if($err != SOCKET_EINPROGRESS && $err != SOCKET_EALREADY)
					{
						socket_close($this->socket);
						trigger_error("connection error", E_USER_WARNING);
						return false;
					}
					sleep(1);
				}
				
				if(!$connected)
				{					
					trigger_error("connection timed out", E_USER_WARNING);
					socket_close($this->socket);
					return false;
				}
				
				socket_set_block($this->socket);

				if(isset($this->socks_auth['username']) && isset($this->socks_auth['password']))
				{
					$method = 0x02;
				}
				else
				{
					$method = 0x00;
				}

				if(socket_write($this->socket, pack("C3", 0x05, 0x01, $method)) !== false)
				{
					socket_recv($this->socket, $buffer, 1024, 0);
					$response = unpack("Cversion/Cmethod", $buffer);
					
					if(isset($response['version']) && isset($response['method']) && $response['version'] == 0x05 && $response['method'] == $method)
					{						
						switch($method)
						{
							case 0x02:
								return $this->auth_userpass();
								break;
								
							default:
								return true;
								break;
						}
					}
				}
				
				socket_close($this->socket);
				return false;
			}
			else
			{
				trigger_error(socket_strerror(socket_last_error($this->socket)), E_USER_WARNING);
				return false;
			}
		}
		else
		{
			trigger_error("module 'sockets' not found", E_USER_ERROR);
			return false;			
		}
		/*
		if($response['method'] == 0xFF)
		{
			echo "not supported";
		}
		*/
	}

	
	/**
	 * bind to local interface
	 */
	private function bind_interface()
	{
		if($this->interface !== null)
		{
			return socket_bind($this->socket, $this->interface);
		}		
	}
	
	
	/**
	 * auth via the second method, e.g. plaintext username/password
	 * @see http://tools.ietf.org/html/rfc1929
	 * @return bool
	 */
	private function auth_userpass()
	{
		// (thx2dinesh)
		if(socket_write($this->socket, pack("CC", 0x01, strlen($this->socks_auth['username'])) . $this->socks_auth['username'] . pack("C", strlen($this->socks_auth['password'])) . $this->socks_auth['password']))
		{						
			socket_recv($this->socket, $buffer, 1024, 0);
			$response = unpack("Cversion/Cstatus", $buffer);	
			if(isset($response['status']) && $response['status'] == 0x00)
			{
				return true;
			}
			else
			{
				socket_close($this->socket);
				return false;
			}
		}
	}
	
	
	/**
	 * generate socks5 connection request
	 * @param $host
	 * @param $port
	 * @return binary string
	 */
	private function get_connection_request($host, $port)
	{
		switch($this->dnstunnel)
		{
			case true:
				return pack("C5", 0x05, 0x01, 0x00, 0x03, strlen($host)).$host.pack("n", $port);
				break;
				
			case false:
				return pack("C4Nn", 0x05, 0x01, 0x00, 0x01, ip2long(gethostbyname($host)), $port);
				break;
		}
	}
}
?>
Return current item: socks5