Location: PHPKode > scripts > Socksed > socksed.php
<?
/**
 * Socksed
 * This class can be used to connect to a SOCKS server.
 * It supports SOCKS version 4, 4A and 5 with SOCKS authentication.
   It also support IPv6 connection.
 *
 * @package socksed
 * @access public
 * @author WindyLea (hide@address.com, 2011)
 * @version 0.2.1
 */ 

class socksed {
	var $hostname;
	var $port;
	var $version = 5;
	var $timeout;
	var $username;
	var $password;
	var $context;
	var $error;

	var $_socket;
	var $_socks4_error;
	var $_socks5_error;

	/**
	* Some initializing before connecting, this function exists just to make the class compatible with PHP 4
	*
	* @access public
	* @return bool *false* on error
	*/
	function initialize() 
	{
		if(!is_array($this->_socks4_error)) 
		{
			$this->_socks4_error = array(
				91 => "request rejected or failed", 
				92 => "request failed because client is not running identd (or not reachable from the server)", 
				93 => "request failed because client's identd could not confirm the user ID string in the request"
			);
		}

		if(!is_array($this->_socks5_error)) 
		{
			$this->_socks5_error = array(
				1 => "general failure", 
				2 => "connection not allowed by ruleset", 
				3 => "network unreachable", 
				4 => "host unreachable", 
				5 => "connection refused by destination host", 
				6 => "TTL expired", 
				7 => "command not supported / protocol error", 
				8 => "address type not supported"
			);
		}

		if($this->port < 0 || $this->port > 65535) 
			return $this->error("Invalid SOCKS port specified");

		if($this->version != 5 && $this->version != 4 && $this->version != 4.5) 
			return $this->error("Invalid SOCKS version specified");

		if($this->timeout < 0 || !ctype_digit($this->timeout)) 
			$this->timeout = 30;

		if(!$this->is_ipv6($this->hostname) && $this->version != 5) 
			return $this->error("SOCKS4 does not support IPv6 connection");

		if(isset($this->context)) 
		{
			if (version_compare(PHP_VERSION, "5.0.0", "<") || !function_exists("stream_socket_client")) 
			{
				$this->error("Using socket context requires PHP 5 or higher");
				unset($this->context);
			} else 
			{
				if(!is_resource($this->context)) 
					$this->context = stream_context_create($this->context);
			}
		}

		return true;
	}

	/**
	* Check whether an input string is a IPv4 or not
	*
	* @access public
	* @param string $string The string to be checked
	* @return bool *false* is not a IPv4
	*/

	function is_ipv4($string) 
	{
		$parts = explode(".", $string);
		if(count($parts) != 4) 
			return false;

		foreach($parts as $part) 
		{
			if(is_numeric($part) && ($part >= 0) && ($part < 256)) 
				$i++;
		}

		if($parts[0] = 0) 
			return false;

		if($i != 4) 
			return false;

		return true;
	}

	/**
	* Check whether an input string is a IPv6 or not
	  This function is derived from crisp. For reference, visit this link : 
	  http://crisp.tweakblogs.net/blog/3049/ipv6-validation-more-caveats.html
	*
	* @access public
	* @param string $string The string to be checked
	* @return bool *false* is not a IPv6
	*/

	function is_ipv6($string) 
	{
		if (strlen($string) < 3) 
			return $string == "::";

		if (strpos($string, ".")) 
		{
			$lastcolon = strrpos($string, ":");

			if (!($lastcolon && socksed::is_ipv4(substr($string, $lastcolon + 1)))) 
				return false;

			$string = substr($string, 0, $lastcolon) . ":0:0";
		}

		if (strpos($string, "::") === false) 
			return preg_match("/\A(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}\z/i", $string);

		$colonCount = substr_count($string, ":");
		if ($colonCount < 8) 
			return preg_match("/\A(?::|(?:[a-f0-9]{1,4}:)+):(?:(?:[a-f0-9]{1,4}:)*[a-f0-9]{1,4})?\z/i", $string);

		if ($colonCount == 8) 
			return preg_match("/\A(?:::)?(?:[a-f0-9]{1,4}:){6}[a-f0-9]{1,4}(?:::)?\z/i", $string);

		return false;
	}

	/**
	* Converts a string containing an (IPv6) Internet Protocol dotted address into a proper address

	  This function is derived from johniskew[at]yahoo[dot]com. For reference, visit this URL : 
	  http://www.php.net/manual/en/function.ip2long.php#82013
	*
	* @access public
	* @param string $ip The IP to be converted
	* @return bool *false* on error
	*/

	function ipv62long($ip) {
		if(!socksed::is_ipv4($ip) && !socksed::is_ipv6($ip)) 
			return false;

		$parts = explode(':', $ip);

		# If this is mixed IPv6/IPv4, convert end to IPv6 value
		if(socksed::is_ipv4($parts[count($parts) - 1])) 
		{
			$partsV4 = explode(".", $parts[count($parts) - 1]);

			for($i = 0; $i < 4; $i++) 
			{
				$partsV4[$i] = str_pad(dechex($partsV4[$i]), 2, "0", STR_PAD_LEFT);
			}

			$parts[count($parts) - 1] = $partsV4[0].$partsV4[1];
			$parts[] = $partsV4[2].$partsV4[3];
		}

		$numMissing = 8 - count($parts);
		$expandedParts = array();
		$expansionDone = false;

		foreach($parts as $part) 
		{
			if(!$expansionDone && $part == "") 
			{
				for($i = 0; $i <= $numMissing; $i++) 
				{
					$expandedParts[] = "0000";
				}
				$expansionDone = true;
			}
			else 
			{
				$expandedParts[] = $part;
			}
		}

		foreach($expandedParts as &$part) 
		{
			$part = str_pad($part, 4, "0", STR_PAD_LEFT);
		}

		$ip = implode(":", $expandedParts);
		$hex = implode("", $expandedParts);

		# Validate the final IP
		if(!socksed::is_ipv4($ip) && !socksed::is_ipv6($ip)) 
			return false;

		return strtolower(str_pad($hex, 32, "0", STR_PAD_LEFT));
}

	/**
	* Print an error message
	*
	* @access public
	* @param string $error Error message
	* @return bool
	*/

	function error($error) 
	{
		$this->disconnect();
		$this->error = $error;
		return false;
	}

	/**
	* Connect to an Internet address through the SOCKS server and send a request
	*
	* @access public
	* @param string $hostname The destination address
	* @param string $port The port ( default is 80 )
	* @param string $request The client request for the destination address $hostname, default is null
	* @return string|bool On succress return the SOCKS server response if $request is not empty or "false" if error occurred
	*/

	function connect($hostname, $port = 80, $request = null) 
	{
		$this->disconnect();

		if(!$this->initialize()) 
			return false;

		if($this->is_ipv6($this->hostname)) 
		{
			if(isset($this->context)) 
				$this->_socket = @stream_socket_client("tcp://[".$this->hostname."]:".$this->port, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $this->context);
			else
				$this->_socket = @fsockopen("tcp://[".$this->hostname."]", $this->port, $errno, $errstr, $this->timeout);
		} else {
			if(isset($this->context))
				$this->_socket = @stream_socket_client("tcp://".$this->hostname.":".$this->port, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $this->context);
			else
				$this->_socket = @fsockopen("tcp://".$this->hostname, $this->port, $errno, $errstr, $this->timeout);
		}

		if (!$this->_socket) 
			return $this->error($errstr);

		if($this->version == 4 || $this->version == 4.5) 
		{

			if($this->version == 4) 
			{
				if(!$this->is_ipv4($hostname)) $hostname = gethostbyname($hostname);
				$packet = "\x04\x01".pack("n", $port).pack("H*", dechex(ip2long($hostname)))."\x43\x6C\x69\x65\x6E\x74\x00"; 
			} else 
			{
				$packet = "\x04\x01".pack("n", $port)."\x00\x00\x00\x7B\x43\x6C\x69\x65\x6E\x74\x00".$hostname."\x00";
			}

			fwrite($this->_socket, $packet, strlen($packet));
			$response = fread($this->_socket, 9);

			if(strlen($response) == 8 && (ord($response[0]) == 0 || ord($response[0]) == 4)) 
			{
				$status = ord($response[1]);
				if($status != 90) 
					return $this->error("Error from SOCKS4 server : ".$this->_socks4_error[$status]);
			} else 
			{
				return $this->error("The SOCKS server returned an invalid response");
			}

		} elseif($this->version == 5) 
		{
			$authentication = null;
			if(strlen($this->username) <= 255 && strlen($this->username) > 0 && strlen($this->password) <= 255 && strlen($this->password) > 0) 
				$authentication = true;
	
			$packet = "\x05\x01\x00";
			if($authentication) 
				$packet = "\x05\x02\x00\x02";
		
			fwrite($this->_socket, $packet, strlen($packet));
			$response = fread($this->_socket, 3);

			if(strlen($response) == 2 && ord($response[0]) == 5) 
			{
				$method = ord($response[1]);

				switch($method) 
				{
					case 0:
						break;
					case 2:
						if(!$authentication) 
							return $this->error("The SOCKS server requires authentication");

						$packet = "\x01".chr(strlen($this->username)).$this->username.chr(strlen($this->password)).$this->password;
						fwrite($this->_socket, $packet, strlen($packet));
						$response = fread($this->_socket, 3);

						if(strlen($response) == 2 && ord($response[0]) == 1 && ord($response[1]) == 0) 
							break;
						else 
							return $this->error("Authentication with the SOCKS server failed (wrong username/password?)");
					default:
						return $this->error("No negotiation method was accepted by the SOCKS server");
				}

				$packet = "\x05\x01\x00";

				if($this->is_ipv4($hostname)) 
				{
					$packet.= "\x01".pack("H*", dechex(ip2long($hostname)));
				} elseif($this->is_ipv6($hostname)) 
				{
					$hostname = socksed::ipv62long($hostname);

					if($hostname === false) 
						return $this->error("Unable to convert the specified IPv6 address");

					$packet.= "\x04".pack("H*", dechex(ip2long($hostname)));
				} else 
				{
					$packet.= "\x03".chr(strlen($hostname)).$hostname;
				}

				$packet.= pack("n", $port);
				fwrite($this->_socket, $packet, strlen($packet));
				$response = fread($this->_socket, 1024);

				if(ord($response[0]) == 5) 
				{
					$status = ord($response[1]);
					if($status != 0) 
						return $this->error("Error from SOCKS5 server : ".$this->_socks5_error[$status]);
				} else 
				{
					return $this->error("The SOCKS server returned an invalid response");
				}

			} else 
			{
				return $this->error("The SOCKS server actively refused the connection");
			}

		}

		if(empty($request)) 
			return true;

		return($this->send($request));
	}

	/**
	* Disconnect from the SOCKS server
	*
	* @access public
	* @return bool
	*/

	function disconnect() 
	{
		if($this->_socket) 
		{
			fclose($this->_socket);
			unset($this->_socket);
		}

		return true;
	}

	/**
	* Send a packet to the SOCKS server
	*
	* @access public
	* @param string $packet The packet
	* @return bool|string On success return the response message from the SOCKS server or "false" if not connected
	*/

	function send($packet) 
	{
		$response = null;

		if($this->_socket) 
		{
			fwrite($this->_socket, $packet, strlen($packet));

			while (!feof($this->_socket)) 
			{
				$response.= fread($this->_socket, 1024);
			}

			return $response;
		}
		return false;
	}
}
?>
Return current item: Socksed