Socket interface bugfixes
This commit is contained in:
parent
d8d8da47d1
commit
537ff239ba
@ -2,5 +2,5 @@
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$handler = new \danog\MadelineProto\Server(['type' => AF_INET, 'protocol' => 0, 'address' => 'localhost', 'port' => 8002]);
|
||||
$handler = new \danog\MadelineProto\Server(['type' => AF_INET, 'protocol' => 0, 'address' => 'localhost', 'port' => 8005]);
|
||||
$handler->start();
|
@ -114,7 +114,7 @@ class API extends APIFactory
|
||||
return;
|
||||
}
|
||||
$this->serialize();
|
||||
restore_error_handler();
|
||||
//restore_error_handler();
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
|
@ -19,6 +19,10 @@ namespace danog\MadelineProto\Server;
|
||||
class Handler extends \danog\MadelineProto\Connection
|
||||
{
|
||||
use \danog\MadelineProto\TL\TL;
|
||||
use \danog\MadelineProto\TL\Conversion\BotAPI;
|
||||
use \danog\MadelineProto\TL\Conversion\BotAPIFiles;
|
||||
use \danog\MadelineProto\TL\Conversion\Extension;
|
||||
use \danog\MadelineProto\TL\Conversion\TD;
|
||||
use \danog\MadelineProto\Tools;
|
||||
private $madeline;
|
||||
|
||||
@ -26,6 +30,7 @@ class Handler extends \danog\MadelineProto\Connection
|
||||
{
|
||||
$this->sock = $socket;
|
||||
$this->sock->setBlocking(true);
|
||||
$this->must_open = false;
|
||||
$timeout = 2;
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
@ -34,7 +39,7 @@ class Handler extends \danog\MadelineProto\Connection
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Closing socket in fork '.getmypid());
|
||||
echo('Closing socket in fork '.getmypid().PHP_EOL);
|
||||
unset($this->sock);
|
||||
$this->destruct_madeline();
|
||||
exit();
|
||||
@ -58,6 +63,7 @@ class Handler extends \danog\MadelineProto\Connection
|
||||
$buffer = '';
|
||||
|
||||
$first_byte = $this->sock->read(1);
|
||||
|
||||
if ($first_byte === chr(239)) {
|
||||
$this->protocol = 'tcp_abridged';
|
||||
} else {
|
||||
@ -133,11 +139,10 @@ class Handler extends \danog\MadelineProto\Connection
|
||||
$args[1]['updates']['callback'] = [$this, 'update_handler'];
|
||||
}
|
||||
$this->madeline = new \danog\MadelineProto\API(...$args);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($method[0] === '__destruct') {
|
||||
return $this->destruct_madeline();
|
||||
return $this->__destruct();
|
||||
}
|
||||
if ($this->madeline === null) {
|
||||
throw new \danog\MadelineProto\Exception('__construct was not called');
|
||||
@ -199,38 +204,55 @@ class Handler extends \danog\MadelineProto\Connection
|
||||
$tl_frame['function'] = $frame['function'];
|
||||
}
|
||||
if (isset($frame['args'])) {
|
||||
$tl_frame['args'] = json_encode($frame['args']);
|
||||
$args = json_encode($frame['args']);
|
||||
if ($args !== false) $tl_frame['args'] = $args;
|
||||
}
|
||||
$tl = false;
|
||||
}
|
||||
$exception['trace']['frames'][] = $tl_frame;
|
||||
}
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageException'], ['request_id' => $request_id, 'exception' => $exception]));
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], ['_' => 'socketMessageException', 'request_id' => $request_id, 'exception' => $exception], 'exception'));
|
||||
}
|
||||
|
||||
public function send_response($request_id, $response)
|
||||
{
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageResponse'], ['request_id' => $request_id, 'data' => $response]));
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], ['_' => 'socketMessageResponse', 'request_id' => $request_id, 'data' => $response], 'exception'));
|
||||
}
|
||||
|
||||
public function send_data($stream_id, $data)
|
||||
{
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageRawData'], ['stream_id' => $stream_id, 'data' => $data]));
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], ['_' => 'socketMessageRawData', 'stream_id' => $stream_id, 'data' => $data], 'data'));
|
||||
}
|
||||
|
||||
public $logging = false;
|
||||
public function logger($message, $level)
|
||||
{
|
||||
$message = ['_' => 'socketMessageLog', 'data' => $message, 'level' => $level, 'thread' => \danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread()), 'process' => \danog\MadelineProto\Logger::is_fork(), 'file' => basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php')];
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageLog'], $message));
|
||||
}
|
||||
if (!$this->logging) {
|
||||
try {
|
||||
$this->logging = true;
|
||||
|
||||
$message = ['_' => 'socketMessageLog', 'data' => $message, 'level' => $level, 'thread' => \danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread()), 'process' => \danog\MadelineProto\Logger::is_fork(), 'file' => basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['file'], '.php')];
|
||||
|
||||
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], $message, 'log'));
|
||||
} finally {
|
||||
$this->logging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function send_message_safe($message) {
|
||||
if (!isset($this->sock)) return false;
|
||||
try {
|
||||
$this->send_message($message);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->__destruct();
|
||||
}
|
||||
}
|
||||
public function update_handler($update)
|
||||
{
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageUpdate'], ['data' => $update]));
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], ['_' => 'socketMessageUpdate', 'data' => $update], 'update'));
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$this->send_message($this->serialize_object(['type' => 'socketMessageRequest'], ['request_id' => 0, 'method' => $method, 'args' => $args]));
|
||||
$this->send_message_safe($this->serialize_object(['type' => ''], ['_' => 'socketMessageRequest', 'request_id' => 0, 'method' => $method, 'args' => $args], 'method'));
|
||||
}
|
||||
}
|
||||
|
@ -499,7 +499,8 @@ trait TL
|
||||
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['params_missing'], $current_argument['name']);
|
||||
}
|
||||
if ($current_argument['type'] === 'DataJSON') {
|
||||
|
||||
if (in_array($current_argument['type'], ['DataJSON', '%DataJSON'])) {
|
||||
$arguments[$current_argument['name']] = ['_' => 'dataJSON', 'data' => json_encode($arguments[$current_argument['name']])];
|
||||
}
|
||||
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputFile' && $this->settings['upload']['allow_automatic_upload']) {
|
||||
|
@ -1,11 +1,10 @@
|
||||
|
||||
dataJSON#7d748d04 data:string = DataJSON;
|
||||
|
||||
socketMessageRequest request_id:int method:vector<string> args:vector<%DataJSON> = SocketMessage;
|
||||
socketMessageResponse request_id:int data:%DataJSON = SocketMessage;
|
||||
socketMessageException request_id:int exception:SocketException = SocketMessage;
|
||||
socketMessageUpdate data:%DataJSON = SocketMessage;
|
||||
socketMessageLog flags:# thread:flags.0?true process:flags.1?true file:flags.2?string level:int data:string = SocketMessage;
|
||||
socketMessageLog flags:# thread:flags.0?true process:flags.1?true file:string level:int data:%DataJSON = SocketMessage;
|
||||
socketMessageRawData stream_id:int data:bytes = SocketMessage;
|
||||
|
||||
socketException message:string code:int trace:%SocketTLTrace = SocketException;
|
||||
@ -15,4 +14,4 @@ socketDOMException message:string code:int trace:%SocketTLTrace = SocketExceptio
|
||||
|
||||
socketTLTrace frames:vector<%SocketTLFrame> = SocketTLTrace;
|
||||
|
||||
socketTLFrame flags:# file:flags.0?string line:flags.1?string function:flags.2?string args:flags.3?string tl_param:flags.4?string = SocketTLFrame;
|
||||
socketTLFrame flags:# file:flags.0?string line:flags.1?int function:flags.2?string args:flags.3?string tl_param:flags.4?string = SocketTLFrame;
|
||||
|
282
src/danog/MadelineProto/VoIP.php
Normal file
282
src/danog/MadelineProto/VoIP.php
Normal file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2018 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;
|
||||
|
||||
if (!extension_loaded('php-libtgvoip') && false) {
|
||||
class VoIP
|
||||
{
|
||||
use \danog\MadelineProto\VoIP\MessageHandler;
|
||||
|
||||
const PHP_LIBTGVOIP_VERSION = '1.1.2';
|
||||
const STATE_CREATED = 0;
|
||||
const STATE_WAIT_INIT = 1;
|
||||
const STATE_WAIT_INIT_ACK = 2;
|
||||
const STATE_ESTABLISHED = 3;
|
||||
const STATE_FAILED = 4;
|
||||
const STATE_RECONNECTING = 5;
|
||||
|
||||
const TGVOIP_ERROR_UNKNOWN = 0;
|
||||
const TGVOIP_ERROR_INCOMPATIBLE = 1;
|
||||
const TGVOIP_ERROR_TIMEOUT = 2;
|
||||
const TGVOIP_ERROR_AUDIO_IO = 3;
|
||||
|
||||
const NET_TYPE_UNKNOWN = 0;
|
||||
const NET_TYPE_GPRS = 1;
|
||||
const NET_TYPE_EDGE = 2;
|
||||
const NET_TYPE_3G = 3;
|
||||
const NET_TYPE_HSPA = 4;
|
||||
const NET_TYPE_LTE = 5;
|
||||
const NET_TYPE_WIFI = 6;
|
||||
const NET_TYPE_ETHERNET = 7;
|
||||
const NET_TYPE_OTHER_HIGH_SPEED = 8;
|
||||
const NET_TYPE_OTHER_LOW_SPEED = 9;
|
||||
const NET_TYPE_DIALUP = 10;
|
||||
const NET_TYPE_OTHER_MOBILE = 11;
|
||||
|
||||
const DATA_SAVING_NEVER = 0;
|
||||
const DATA_SAVING_MOBILE = 1;
|
||||
const DATA_SAVING_ALWAYS = 2;
|
||||
|
||||
const PROXY_NONE = 0;
|
||||
const PROXY_SOCKS5 = 1;
|
||||
|
||||
const AUDIO_STATE_NONE = -1;
|
||||
const AUDIO_STATE_CREATED = 0;
|
||||
const AUDIO_STATE_CONFIGURED = 1;
|
||||
const AUDIO_STATE_RUNNING = 2;
|
||||
|
||||
const CALL_STATE_NONE = -1;
|
||||
const CALL_STATE_REQUESTED = 0;
|
||||
const CALL_STATE_INCOMING = 1;
|
||||
const CALL_STATE_ACCEPTED = 2;
|
||||
const CALL_STATE_CONFIRMED = 3;
|
||||
const CALL_STATE_READY = 4;
|
||||
const CALL_STATE_ENDED = 5;
|
||||
|
||||
private $MadelineProto;
|
||||
public $configuration = ['endpoints' => [], 'shared_config' => []];
|
||||
public $storage = [];
|
||||
public $internalStorage = [];
|
||||
private $signal = 0;
|
||||
private $callState;
|
||||
private $callID;
|
||||
private $creatorID;
|
||||
private $otherID;
|
||||
private $protocol;
|
||||
private $visualization;
|
||||
private $holdFiles = [];
|
||||
private $inputFiles;
|
||||
private $outputFile;
|
||||
private $isPlaying = false;
|
||||
|
||||
private $connection_settings = [];
|
||||
private $dclist = [];
|
||||
|
||||
private $datacenter;
|
||||
|
||||
public function __construct($creator, $otherID, $callID, $MadelineProto, $callState, $protocol)
|
||||
{
|
||||
$this->creator = $creator;
|
||||
$this->otherID = $otherID;
|
||||
$this->callID = $callID;
|
||||
$this->MadelineProto = $MadelineProto;
|
||||
$this->callState = $callState;
|
||||
$this->protocol = $protocol;
|
||||
}
|
||||
|
||||
public function deInitVoIPController()
|
||||
{
|
||||
}
|
||||
|
||||
public function setVisualization($visualization)
|
||||
{
|
||||
$this->visualization = $visualization;
|
||||
}
|
||||
|
||||
public function getVisualization()
|
||||
{
|
||||
return $this->visualization;
|
||||
}
|
||||
|
||||
public function discard($reason = ['_' => 'phoneCallDiscardReasonDisconnect'], $rating = [], $debug = false)
|
||||
{
|
||||
if ($this->callState === self::CALL_STATE_ENDED || empty($this->configuration)) {
|
||||
return false;
|
||||
}
|
||||
$this->MadelineProto->discard_call($this->callID, $reason, $rating, $debug);
|
||||
$this->deinitVoIPController();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function accept()
|
||||
{
|
||||
if ($this->callState !== self::CALL_STATE_INCOMING) {
|
||||
return false;
|
||||
}
|
||||
$this->callState = self::CALL_STATE_ACCEPTED;
|
||||
if (!$this->MadelineProto->accept_call($this->callID)) {
|
||||
$this->discard_call(['_' => 'phoneCallDiscardReasonDisconnect']);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
$this->deinitVoIPController();
|
||||
}
|
||||
|
||||
public function startTheMagic()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function play($file)
|
||||
{
|
||||
$this->inputFiles[] = $file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function playOnHold($files)
|
||||
{
|
||||
$this->holdFiles = $files;
|
||||
}
|
||||
|
||||
public function setOutputFile($file)
|
||||
{
|
||||
$this->outputFile = $file;
|
||||
}
|
||||
|
||||
public function unsetOutputFile()
|
||||
{
|
||||
$this->outputFile = null;
|
||||
}
|
||||
|
||||
public function setMadeline($MadelineProto)
|
||||
{
|
||||
$this->MadelineProto = $MadelineProto;
|
||||
}
|
||||
|
||||
public function getProtocol()
|
||||
{
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
public function getOtherID()
|
||||
{
|
||||
return $this->otherID;
|
||||
}
|
||||
|
||||
public function getCallID()
|
||||
{
|
||||
return $this->callID;
|
||||
}
|
||||
|
||||
public function isCreator()
|
||||
{
|
||||
return $this->creator;
|
||||
}
|
||||
|
||||
public function whenCreated()
|
||||
{
|
||||
return isset($this->internalStorage['created']) ? $this->internalStorage['created'] : false;
|
||||
}
|
||||
public function parseConfig()
|
||||
{
|
||||
if (count($this->configuration['endpoints'])) {
|
||||
$this->connection_settings['all'] = $this->MadelineProto->settings['connection_settings']['all'];
|
||||
$this->connection_settings['all']['protocol'] = 'obfuscated2';
|
||||
$this->connection_settings['all']['do_not_retry'] = true;
|
||||
|
||||
$test = $this->connection_settings['all']['test_mode'] ? 'test' : 'main';
|
||||
foreach ($this->configuration['endpoints'] as $endpoint) {
|
||||
$this->dclist[$test]['ipv6'][$endpoint['id']] = ['ip_address' => $endpoint['ipv6'], 'port' => $endpoint['port'], 'peer_tag' => $endpoint['peer_tag']];
|
||||
$this->dclist[$test]['ipv4'][$endpoint['id']] = ['ip_address' => $endpoint['ip'], 'port' => $endpoint['port'], 'peer_tag' => $endpoint['peer_tag']];
|
||||
}
|
||||
if (!isset($this->datacenter)) {
|
||||
$this->datacenter = new DataCenter($this->dclist, $this->connection_settings);
|
||||
} else {
|
||||
//$this->datacenter->__construct($this->dclist, $this->connection_settings);
|
||||
}
|
||||
foreach ($this->datacenter->get_dcs() as $new_dc) {
|
||||
$this->datacenter->dc_connect($new_dc);
|
||||
}
|
||||
$this->init_all();
|
||||
foreach ($this->datacenter->get_dcs(false) as $new_dc) {
|
||||
$this->datacenter->dc_connect($new_dc);
|
||||
}
|
||||
$this->init_all();
|
||||
}
|
||||
}
|
||||
|
||||
private function init_all()
|
||||
{
|
||||
$test = $this->connection_settings['all']['test_mode'] ? 'test' : 'main';
|
||||
foreach ($this->datacenter->sockets as $dc_id => $socket) {
|
||||
if ($socket->auth_key === null) {
|
||||
$socket->auth_key = ['id' => $this->configuration['auth_key_id'], 'auth_key' => $this->configuration['auth_key'], 'connection_inited' => self::STATE_CREATED];
|
||||
}
|
||||
if ($socket->type === Connection::API_ENDPOINT) {
|
||||
$socket->type = Connection::VOIP_TCP_REFLECTOR_ENDPOINT;
|
||||
}
|
||||
if ($socket->peer_tag === null) {
|
||||
switch ($socket->type) {
|
||||
case Connection::VOIP_TCP_REFLECTOR_ENDPOINT:
|
||||
case Connection::VOIP_UDP_REFLECTOR_ENDPOINT:
|
||||
$socket->peer_tag = $this->dclist[$test]['ipv4'][$dc_id]['peer_tag'];
|
||||
break;
|
||||
default:
|
||||
$socket->peer_tag = $this->configuration['call_id'];
|
||||
}
|
||||
}
|
||||
if ($socket->auth_key['connection_inited'] === self::STATE_CREATED) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getCallState()
|
||||
{
|
||||
return $this->callState;
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
return 'libponyvoip-1.0';
|
||||
}
|
||||
|
||||
public function getPreferredRelayID()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getLastError()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getDebugLog()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSignalBarsCount()
|
||||
{
|
||||
return $this->signal;
|
||||
}
|
||||
}
|
||||
}
|
152
src/danog/MadelineProto/VoIP/MessageHandler.php
Normal file
152
src/danog/MadelineProto/VoIP/MessageHandler.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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\VoIP;
|
||||
|
||||
/**
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
trait MessageHandler
|
||||
{
|
||||
public function send_message($message, $datacenter)
|
||||
{
|
||||
//$has_ack = false;
|
||||
|
||||
if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) {
|
||||
$messages = [];
|
||||
\danog\MadelineProto\Logger::log("Sending msg_container as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
foreach ($this->datacenter->sockets[$datacenter]->object_queue as $message) {
|
||||
$message['seqno'] = $this->generate_out_seq_no($datacenter, $message['content_related']);
|
||||
$message['bytes'] = strlen($message['body']);
|
||||
//$has_ack = $has_ack || $message['_'] === 'msgs_ack';
|
||||
\danog\MadelineProto\Logger::log("Inside of msg_container, sending {$message['_']} as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message['_'] = 'MTmessage';
|
||||
$messages[] = $message;
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'response' => -1]; //, 'content' => $this->deserialize($message['body'], ['type' => '', 'datacenter' => $datacenter])];
|
||||
}
|
||||
$message_data = $this->serialize_object(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'lol');
|
||||
$message_id = $this->generate_message_id($datacenter);
|
||||
$seq_no = $this->generate_out_seq_no($datacenter, false);
|
||||
} elseif (count($this->datacenter->sockets[$datacenter]->object_queue)) {
|
||||
$message = array_shift($this->datacenter->sockets[$datacenter]->object_queue);
|
||||
\danog\MadelineProto\Logger::log("Sending {$message['_']} as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message_data = $message['body'];
|
||||
$message_id = $message['msg_id'];
|
||||
$seq_no = $this->generate_out_seq_no($datacenter, $message['content_related']);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].$this->datacenter->sockets[$datacenter]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
|
||||
$padding = $this->posmod(-strlen($plaintext), 16);
|
||||
if ($padding < 12) {
|
||||
$padding += 16;
|
||||
}
|
||||
$padding = $this->random($padding);
|
||||
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32).$plaintext.$padding, true), 8, 16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'].$message_key.$this->ige_encrypt($plaintext.$padding, $aes_key, $aes_iv);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['seq_no' => $seq_no, 'response' => -1];
|
||||
$this->datacenter->sockets[$datacenter]->send_message($message);
|
||||
$this->datacenter->sockets[$datacenter]->object_queue = [];
|
||||
|
||||
/*if ($has_ack) {
|
||||
foreach ($this->datacenter->sockets[$datacenter]->ack_queue as $msg_id) {
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['ack'] = true;
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->ack_queue = [];
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading connection and receiving message from server.
|
||||
*/
|
||||
public function recv_message($datacenter)
|
||||
{
|
||||
if ($this->datacenter->sockets[$datacenter]->must_open) {
|
||||
\danog\MadelineProto\Logger::log('Trying to read from closed socket, sending initial ping');
|
||||
if ($this->is_http($datacenter)) {
|
||||
$this->method_call('http_wait', ['max_wait' => 500, 'wait_after' => 150, 'max_delay' => 500], ['datacenter' => $datacenter]);
|
||||
} elseif (isset($this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) && $this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) {
|
||||
$this->method_call('ping', ['ping_id' => 0], ['datacenter' => $datacenter]);
|
||||
} else {
|
||||
throw new \danog\MadelineProto\Exception('Resend query');
|
||||
}
|
||||
}
|
||||
$payload = $this->datacenter->sockets[$datacenter]->read_message();
|
||||
if (strlen($payload) === 4) {
|
||||
$payload = $this->unpack_signed_int($payload);
|
||||
\danog\MadelineProto\Logger::log("Received $payload from DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return $payload;
|
||||
}
|
||||
$auth_key_id = substr($payload, 0, 8);
|
||||
if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
|
||||
$message_id = substr($payload, 8, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
$message_length = unpack('V', substr($payload, 16, 4))[1];
|
||||
$message_data = substr($payload, 20, $message_length);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = [];
|
||||
} elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) {
|
||||
$message_key = substr($payload, 8, 16);
|
||||
$encrypted_data = substr($payload, 24);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], false);
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
/*
|
||||
$server_salt = substr($decrypted_data, 0, 8);
|
||||
if ($server_salt != $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt']) {
|
||||
\danog\MadelineProto\Logger::log('WARNING: Server salt mismatch (my server salt '.$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
*/
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $this->datacenter->sockets[$datacenter]->session_id) {
|
||||
throw new \danog\MadelineProto\Exception('Session id mismatch.');
|
||||
}
|
||||
$message_id = substr($decrypted_data, 16, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
$seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
|
||||
// Dunno how to handle any incorrect sequence numbers
|
||||
$message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length < 12) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too small');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length > 1024) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too big');
|
||||
}
|
||||
if ($message_data_length < 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not positive');
|
||||
}
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
|
||||
}
|
||||
$message_data = substr($decrypted_data, 32, $message_data_length);
|
||||
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32).$decrypted_data, true), 8, 16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
|
||||
} else {
|
||||
$this->close_and_reopen($datacenter);
|
||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $this->deserialize($message_data, ['type' => '', 'datacenter' => $datacenter]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
|
||||
$this->datacenter->sockets[$datacenter]->last_recv = time();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user