You can now proxy MadelineProto

This commit is contained in:
Daniil Gentili 2017-06-03 16:40:14 +02:00
parent 08c6e8d838
commit 86067bbf6b
7 changed files with 213 additions and 23 deletions

View File

@ -1,6 +1,8 @@
# Changelog
MadelineProto can now be proxied.
Added `$no_updates` parameter to the deserialize method of `\danog\MadelineProto\Serialization`.

View File

@ -117,7 +117,7 @@ $MadelineProto = new \danog\MadelineProto\API();
### Settings
The constructor accepts an optional parameter, which is the settings array. This array contains some other arrays, which are the settings for a specific MadelineProto function.
See [here](https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L232) for the default values for the settings arrays and explanations for every setting.
See [here](https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L405) for the default values for the settings arrays and explanations for every setting.
You can provide part of any subsetting array, that way the remaining arrays will be automagically set to default and undefined values of specified subsetting arrays will be set to the default values.
Example:
@ -270,6 +270,97 @@ array(3) {
To specify a custom callback change the correct value in the settings. The specified callable must accept one parameter for the update.
### Using a proxy
You can use a proxy with MadelineProto.
To do that, simply create a class that implements the `\danog\MadelineProto\Proxy` interface, and enter its name in the settings.
Your proxy class MUST use the `\Socket` class for all TCP/UDP communications.
Your proxy class can also have a setExtra method that accepts an array as the first parameter, to pass the values provided in the proxy_extra setting.
The `\Socket` class has the following methods (all of the following methods must also be implemented by your proxy class):
```public function __construct(int $domain, int $type, int $protocol);```
Works exactly like the [socket_connect](http://php.net/manual/en/function.socket-connect.php) function.
```public function setOption(int $level, int $name, $value);```
Works exactly like the [socket_set_option](http://php.net/manual/en/function.socket-set-option.php) function.
```public function getOption(int $name, $value);```
Works exactly like the [socket_get_option](http://php.net/manual/en/function.socket-get-option.php) function.
```public function setBlocking(bool $blocking);```
Works like the [socket_block](http://php.net/manual/en/function.socket-set-block.php) or [socket_nonblock](http://php.net/manual/en/function.socket-set-nonblock.php) functions.
```public function bind(string $address, [ int $port = 0 ]);```
Works exactly like the [socket_bind](http://php.net/manual/en/function.socket-bind.php) function.
```public function listen([ int $backlog = 0 ]);```
Works exactly like the [socket_listen](http://php.net/manual/en/function.socket-listen.php) function.
```public function accept();```
Works exactly like the [socket_accept](http://php.net/manual/en/function.socket-accept.php) function.
```public function connect(string $address, [ int $port = 0 ]);```
Works exactly like the [socket_accept](http://php.net/manual/en/function.socket-connect.php) function.
```public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0);```
Works exactly like the [socket_select](http://php.net/manual/en/function.socket-select.php) function.
```public function read(int $length, [ int $flags = 0 ]);```
Works exactly like the [socket_read](http://php.net/manual/en/function.socket-read.php) function.
```public function write(string $buffer, [ int $length ]);```
Works exactly like the [socket_read](http://php.net/manual/en/function.socket-write.php) function.
```public function send(string $data, int $length, int $flags);```
Works exactly like the [socket_send](http://php.net/manual/en/function.socket-send.php) function.
```public function close();```
Works exactly like the [socket_close](http://php.net/manual/en/function.socket-close.php) function.
### Uploading and downloading files
MadelineProto provides wrapper methods to upload and download files that support bot API file ids.

View File

@ -13,20 +13,12 @@ If not, see <http://www.gnu.org/licenses/>.
if (!extension_loaded('pthreads')) {
class Socket
{
private $sock;
public function __construct(int $domain, int $type, int $protocol)
{
$this->sock = socket_create($domain, $type, $protocol);
}
public function setBlocking(bool $blocking)
{
if ($blocking) {
return socket_set_block($this->sock);
}
return socket_set_nonblock($this->sock);
}
public function setOption(int $level, int $name, $value)
{
if (in_array($name, [\SO_RCVTIMEO, \SO_SNDTIMEO])) {
@ -41,12 +33,59 @@ if (!extension_loaded('pthreads')) {
return socket_get_option($this->sock, $level, $name);
}
public function __call($name, $args)
public function setBlocking(bool $blocking)
{
$name = 'socket_'.$name;
array_unshift($args, $this->sock);
if ($blocking) {
return socket_set_block($this->sock);
}
return $name(...$args);
return socket_set_nonblock($this->sock);
}
public function bind(string $address, int $port = 0) {
return socket_bind($this->sock, $address, $port);
}
public function listen(int $backlog = 0) {
return socket_listen($this->sock, $backlog);
}
public function accept() {
return socket_accept($this->sock);
}
public function connect(string $address, int $port = 0) {
return socket_connect($this->sock, $address, $port);
}
public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0) {
return socket_select($read, $write, $except, $tv_sec, $tv_usec);
}
public function read(int $length, int $flags = 0) {
return socket_read($this->sock, $length, $flags);
}
public function write(string $buffer, int $length = -1) {
return $length === -1 ? socket_write($this->sock, $buffer) : socket_write($this->sock, $buffer, $Length);
}
public function send(string $data, int $length, int $flags) {
return socket_send($data, $length, $flags);
}
public function close() {
return socket_close($this->sock);
}
public function getPeerName(bool $port = true) {
$address = '';
$port = 0;
$port ? socket_getpeername($this->sock, $address, $ip) : socket_getpeername($this->sock, $address);
return $port ? ['host' => $address, 'port' => $port] : ['host' => $address];
}
public function getSockName(bool $port = true) {
$address = '';
$port = 0;
$port ? socket_getsockname($this->sock, $address, $ip) : socket_getsockname($this->sock, $address);
return $port ? ['host' => $address, 'port' => $port] : ['host' => $address];
}
}
}

View File

@ -39,6 +39,8 @@ class Connection extends \Volatile
public $new_outgoing = [];
public $max_incoming_id;
public $max_outgoing_id;
public $proxy = '\Socket';
public $extra = [];
public $i = [];
/* public function __get($name) {
@ -49,7 +51,7 @@ var_dump(is_null($this->{$name}));
return $this->{$name};
}*/
public function ___construct($ip, $port, $protocol, $timeout, $ipv6)
public function ___construct($proxy, $extra, $ip, $port, $protocol, $timeout, $ipv6)
{
// Can use:
@ -66,9 +68,18 @@ var_dump(is_null($this->{$name}));
$this->ipv6 = $ipv6;
$this->ip = $ip;
$this->port = $port;
$this->proxy = $proxy;
$this->extra = $extra;
if (($has_proxy = $proxy !== '\Socket') && !isset(class_implements($proxy)['\danog\MadelineProto\Proxy'])) {
throw new \danog\MadelineProto\Exception('Invalid proxy class provided!');
}
switch ($this->protocol) {
case 'tcp_abridged':
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if ($has_proxy && $this->extra !== []) {
$this->sock->setExtra($this->extra);
}
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
@ -80,7 +91,10 @@ var_dump(is_null($this->{$name}));
$this->write(chr(239));
break;
case 'tcp_intermediate':
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if ($has_proxy && $this->extra !== []) {
$this->sock->setExtra($this->extra);
}
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock->connect($ip, $port)) {
@ -95,7 +109,10 @@ var_dump(is_null($this->{$name}));
break;
case 'tcp_full':
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if ($has_proxy && $this->extra !== []) {
$this->sock->setExtra($this->extra);
}
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
@ -111,7 +128,10 @@ var_dump(is_null($this->{$name}));
case 'http':
case 'https':
$this->parsed = parse_url($ip);
$this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
if ($has_proxy && $this->extra !== []) {
$this->sock->setExtra($this->extra);
}
if (!$this->sock->connect($this->parsed['host'], $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
@ -156,12 +176,12 @@ var_dump(is_null($this->{$name}));
public function close_and_reopen()
{
$this->__destruct();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function __sleep()
{
return ['protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id'];
return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'sock'];
}
public function __wakeup()

View File

@ -88,7 +88,7 @@ var_dump(is_null($this->{$name}));
}
\danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$this->settings[$dc_config_number]['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE);
$this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
$this->sockets[$dc_number] = new Connection($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
return true;
}

View File

@ -463,6 +463,8 @@ class MTProto extends \Volatile
'test_mode' => false, // decides whether to connect to the main telegram servers or to the testing servers (deep telegram)
'ipv6' => $this->ipv6, // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean
'timeout' => 2, // timeout for sockets
'proxy' => '\Socket', // The proxy class to use
'proxy_extra' => [] // Extra parameters to pass to the proxy class using setExtra
],
],
'app_info' => [ // obtained in https://my.telegram.org
@ -717,7 +719,7 @@ class MTProto extends \Volatile
public function getV()
{
return 38;
return 41;
}
public function get_self()

View File

@ -0,0 +1,36 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
interface Proxy
{
public function __construct(int $domain, int $type, int $protocol);
public function setOption(int $level, int $name, $value);
public function getOption(int $level, int $name);
public function setBlocking(bool $blocking);
public function bind(string $address, int $port = 0);
public function listen(int $backlog = 0);
public function accept();
public function connect(string $address, int $port = 0);
public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0);
public function read(int $length, int $flags = 0);
public function write(string $buffer, int $length = -1);
public function send(string $data, int $length, int $flags);
public function close();
public function getPeerName(bool $port = true);
public function getSockName(bool $port = true);
}