From 8164279427c564a47c34c9978a9df140a5864eae Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 27 Oct 2020 20:53:13 +0100 Subject: [PATCH] Update --- src/danog/MadelineProto/Stream/Ogg/Ogg.php | 4 + src/danog/MadelineProto/VoIP.php | 96 +++++++++++-------- .../MadelineProto/VoIP/AuthKeyHandler.php | 3 +- .../MadelineProto/VoIP/MessageHandler.php | 66 +++++++++---- src/danog/MadelineProto/VoIPServerConfig.php | 9 +- 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/src/danog/MadelineProto/Stream/Ogg/Ogg.php b/src/danog/MadelineProto/Stream/Ogg/Ogg.php index 344e566e..2e8e3642 100644 --- a/src/danog/MadelineProto/Stream/Ogg/Ogg.php +++ b/src/danog/MadelineProto/Stream/Ogg/Ogg.php @@ -187,6 +187,9 @@ class Ogg for ($x = 0; $x < $count; $x++) { $sizes[]= $this->readLen($content, $offset); } + if (!$selfDelimited) { + $sizes []= ($len - ($offset + $padding)); + } } else { // CBR $size = $selfDelimited ? $this->readLen($content, $offset) @@ -241,6 +244,7 @@ class Ogg while (true) { $init = yield $this->stream->bufferRead(4+23); if (empty($init)) { + $this->emitter->complete(); return false; // EOF } if (\substr($init, 0, 4) !== self::CAPTURE_PATTERN) { diff --git a/src/danog/MadelineProto/VoIP.php b/src/danog/MadelineProto/VoIP.php index 40a045af..27b230ed 100644 --- a/src/danog/MadelineProto/VoIP.php +++ b/src/danog/MadelineProto/VoIP.php @@ -19,6 +19,7 @@ use danog\MadelineProto\Stream\ConnectionContext; use danog\MadelineProto\Stream\Ogg\Ogg; use danog\MadelineProto\VoIP\Endpoint; +use function Amp\delay; use function Amp\File\open; if (\extension_loaded('php-libtgvoip')) { @@ -30,7 +31,7 @@ class VoIP use \danog\MadelineProto\VoIP\MessageHandler; use \danog\MadelineProto\VoIP\AckHandler; - const PHP_LIBTGVOIP_VERSION = '1.1.2'; + const PHP_LIBTGVOIP_VERSION = '1.5.0'; const STATE_CREATED = 0; const STATE_WAIT_INIT = 1; const STATE_WAIT_INIT_ACK = 2; @@ -99,13 +100,13 @@ class VoIP const PROTO_ID = 'GrVP'; - const PROTOCOL_VERSION = 3; - const MIN_PROTOCOL_VERSION = 3; + const PROTOCOL_VERSION = 9; + const MIN_PROTOCOL_VERSION = 9; const STREAM_TYPE_AUDIO = 1; const STREAM_TYPE_VIDEO = 2; - const CODEC_OPUS = 1; + const CODEC_OPUS = 'SUPO'; private $TLID_DECRYPTED_AUDIO_BLOCK; @@ -137,7 +138,7 @@ class VoIP private bool $creator; private PermAuthKey $authKey; - private int $peerVersion; + private int $peerVersion = 0; /** * @var Endpoint[] @@ -149,24 +150,39 @@ class VoIP private $datacenter; - public function __construct(bool $creator, int $otherID, $callID, MTProto $MadelineProto, $callState, $protocol) + public function __construct(bool $creator, int $otherID, MTProto $MadelineProto, $callState) { $this->creator = $creator; $this->otherID = $otherID; - $this->callID = $callID; - $this->MadelineProto = $MadelineProto; + //$this->callID = $callID; + $this->madeline = $this->MadelineProto = $MadelineProto; $this->callState = $callState; - $this->protocol = $protocol; + //$this->protocol = $protocol; $this->TLID_REFLECTOR_SELF_INFO = \strrev(\hex2bin(self::TLID_REFLECTOR_SELF_INFO_HEX)); $this->TLID_REFLECTOR_PEER_INFO = \strrev(\hex2bin(self::TLID_REFLECTOR_PEER_INFO_HEX)); $this->TLID_DECRYPTED_AUDIO_BLOCK = \strrev(\hex2bin(self::TLID_DECRYPTED_AUDIO_BLOCK_HEX)); $this->TLID_SIMPLE_AUDIO_BLOCK = \strrev(\hex2bin(self::TLID_SIMPLE_AUDIO_BLOCK_HEX)); } + public static function getConnectionMaxLayer(): int + { + return 92; + } + public function deInitVoIPController() { } + public function getDebugString(): string + { + return ''; + } + + public function setCall($callID) + { + $this->callID = $callID; + } + public function setVisualization($visualization) { $this->visualization = $visualization; @@ -210,24 +226,34 @@ class VoIP public function startTheMagic() { - foreach ($this->sockets as $socket) { - Tools::callFork(function () use ($socket) { - while ($payload = $this->recv_message($socket)) { + Tools::callFork((function () { + $this->authKey = new PermAuthKey(); + $this->authKey->setAuthKey($this->configuration['auth_key']); + + foreach ($this->configuration['endpoints'] as $endpoint) { + $this->sockets['v6 '.$endpoint['id']] = new Endpoint('['.$endpoint['ipv6'].']', $endpoint['port'], $endpoint['peer_tag'], true, $this); + $this->sockets['v4 '.$endpoint['id']] = new Endpoint($endpoint['ip'], $endpoint['port'], $endpoint['peer_tag'], true, $this); + } + foreach ($this->sockets as $socket) { + yield from $socket->connect(); + } + $this->init_all(); + Tools::callFork((function () use ($socket) { + while ($payload = yield from $this->recv_message($socket)) { Tools::callFork($this->handlePacket($socket, $payload)); } - }); - } + })()); + })()); return $this; } public function handlePacket($datacenter, $packet) { - \var_dump($packet); + //\var_dump($packet); switch ($packet['_']) { case self::PKT_INIT: - $this->voip_state = self::STATE_WAIT_INIT_ACK; + //$this->voip_state = self::STATE_WAIT_INIT_ACK; $this->send_message(['_' => self::PKT_INIT_ACK, 'protocol' => self::PROTOCOL_VERSION, 'min_protocol' => self::MIN_PROTOCOL_VERSION, 'all_streams' => [['id' => 0, 'type' => self::STREAM_TYPE_AUDIO, 'codec' => self::CODEC_OPUS, 'frame_duration' => 60, 'enabled' => 1]]], $datacenter); - break; - case self::PKT_INIT_ACK: + if ($this->voip_state !== self::STATE_ESTABLISHED) { $this->voip_state = self::STATE_ESTABLISHED; @@ -237,21 +263,25 @@ class VoIP $ogg = yield from Ogg::init($stream, 60000); $it = $ogg->getEmitter()->iterate(); Tools::callFork($ogg->read()); - Tools::callFork(function () use ($it) { + Tools::callFork((function () use ($it, $datacenter) { $timestamp = 0; - $t = microtime(true); + $frames = []; while (yield $it->advance()) { - $elapsed = microtime(true) - $t; - $t = microtime(true); + $frames []= $it->getCurrent(); + } + foreach ($frames as $frame) { + $t = (microtime(true) / 1000) + 60; + yield $this->send_message(['_' => self::PKT_STREAM_DATA, 'stream_id' => 0, 'data' => $frame, 'timestamp' => $timestamp], $datacenter); + + yield new Delayed((int) ($t - (microtime(true) / 1000))); - yield new Delayed((int) (60000 - $elapsed / 1000)); - - yield $this->send_message(['_' => self::PKT_STREAM_DATA, 'stream_id' => 0, 'data' => $it->getCurrent(), 'timestamp' => $this->timestamp], $datacenter); $timestamp += 60; } - }); + })()); } break; + case self::PKT_INIT_ACK: + break; } } public $timestamp = 0; @@ -321,23 +351,11 @@ class VoIP public function parseConfig() { - $this->authKey = new PermAuthKey(); - $this->authKey->setAuthKey($this->configuration['auth_key']); - if (\count($this->configuration['endpoints'])) { - foreach ($this->configuration['endpoints'] as $endpoint) { - $this->sockets['v6 '.$endpoint['id']] = new Endpoint($endpoint['ipv6'], $endpoint['port'], $endpoint['peer_tag'], true, $this); - $this->sockets['v4 '.$endpoint['id']] = new Endpoint($endpoint['ip'], $endpoint['port'], $endpoint['peer_tag'], true, $this); - } - foreach ($this->sockets as $socket) { - yield from $socket->connect(); - } - } } private function init_all() { - $test = $this->connection_settings['all']['test_mode'] ? 'test' : 'main'; - foreach ($this->datacenter->sockets as $dc_id => $socket) { + foreach ($this->sockets as $socket) { $this->send_message(['_' => self::PKT_INIT, 'protocol' => self::PROTOCOL_VERSION, 'min_protocol' => self::MIN_PROTOCOL_VERSION, 'audio_streams' => [self::CODEC_OPUS], 'video_streams' => []], $socket); $this->voip_state = self::STATE_WAIT_INIT; } diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php index ffb8887f..8b608794 100644 --- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php +++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php @@ -137,7 +137,7 @@ trait AuthKeyHandler $g_b = $dh_config['g']->powMod($b, $dh_config['p']); Crypt::checkG($g_b, $dh_config['p']); try { - $res = yield from $this->methodCallAsyncRead('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]]); + $res = yield from $this->methodCallAsyncRead('phone.acceptCall', ['peer' => ['id' => $call['id'], 'access_hash' => $call['access_hash'], '_' => 'inputPhoneCall'], 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]]); } catch (\danog\MadelineProto\RPCErrorException $e) { if ($e->rpc === 'CALL_ALREADY_ACCEPTED') { $this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])); @@ -322,6 +322,7 @@ trait AuthKeyHandler { \array_walk($this->calls, function ($controller, $id) { if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) { + $this->logger("Discarding ended call..."); $controller->discard(); } }); diff --git a/src/danog/MadelineProto/VoIP/MessageHandler.php b/src/danog/MadelineProto/VoIP/MessageHandler.php index 72e44fc6..a8e40322 100644 --- a/src/danog/MadelineProto/VoIP/MessageHandler.php +++ b/src/danog/MadelineProto/VoIP/MessageHandler.php @@ -28,12 +28,12 @@ trait MessageHandler if ($l <= 253) { $concat .= \chr($l); $concat .= $object; - $concat .= \pack('@'.$this->posmod(-$l - 1, 4)); + $concat .= \pack('@'.Tools::posmod(-$l - 1, 4)); } else { $concat .= \chr(254); $concat .= \substr(Tools::packSignedInt($l), 0, 3); $concat .= $object; - $concat .= \pack('@'.$this->posmod(-$l, 4)); + $concat .= \pack('@'.Tools::posmod(-$l, 4)); } return $concat; @@ -47,13 +47,13 @@ trait MessageHandler if ($l === 254) { $long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1]; $x = \stream_get_contents($stream, $long_len); - $resto = $this->posmod(-$long_len, 4); + $resto = Tools::posmod(-$long_len, 4); if ($resto > 0) { \stream_get_contents($stream, $resto); } } else { $x = \stream_get_contents($stream, $l); - $resto = $this->posmod(-($l + 1), 4); + $resto = Tools::posmod(-($l + 1), 4); if ($resto > 0) { \stream_get_contents($stream, $resto); } @@ -78,8 +78,9 @@ trait MessageHandler $message .= Tools::packUnsignedInt($flags); $message .= \chr(\count($args['audio_streams'])); foreach ($args['audio_streams'] as $codec) { - $message .= \chr($codec); + $message .= $codec; } + $message .= chr(0); $message .= \chr(\count($args['video_streams'])); foreach ($args['video_streams'] as $codec) { $message .= \chr($codec); @@ -95,7 +96,7 @@ trait MessageHandler foreach ($args['all_streams'] as $stream) { $message .= \chr($stream['id']); $message .= \chr($stream['type']); - $message .= \chr($stream['codec']); + $message .= $stream['codec']; $message .= \pack('v', $stream['frame_duration']); $message .= \chr($stream['enabled']); } @@ -184,11 +185,18 @@ trait MessageHandler } } - if (\in_array($this->voip_state, [\danog\MadelineProto\VoIP::STATE_WAIT_INIT, \danog\MadelineProto\VoIP::STATE_WAIT_INIT_ACK])) { + if ($this->peerVersion >= 8 || (!$this->peerVersion && true)) { + $payload = \chr($args['_']); + $payload .= Tools::packUnsignedInt($this->session_in_seq_no); + $payload .= Tools::packUnsignedInt($this->session_out_seq_no); + $payload .= Tools::packUnsignedInt($ack_mask); + $payload .= \chr(0); + $payload .= $message; + } elseif (\in_array($this->voip_state, [\danog\MadelineProto\VoIP::STATE_WAIT_INIT, \danog\MadelineProto\VoIP::STATE_WAIT_INIT_ACK])) { $payload = $this->TLID_DECRYPTED_AUDIO_BLOCK; - $payload .= $this->random(8); + $payload .= Tools::random(8); $payload .= \chr(7); - $payload .= $this->random(7); + $payload .= Tools::random(7); $flags = 0; $flags = $flags | 4; // call_id $flags = $flags | 16; // seqno @@ -211,9 +219,9 @@ trait MessageHandler } } else { $payload = $this->TLID_SIMPLE_AUDIO_BLOCK; - $payload .= $this->random(8); + $payload .= Tools::random(8); $payload .= \chr(7); - $payload .= $this->random(7); + $payload .= Tools::random(7); $message = \chr($args['_']).Tools::packUnsignedInt($this->session_in_seq_no).Tools::packUnsignedInt($this->session_out_seq_no).Tools::packUnsignedInt($ack_mask).$message; $payload .= $this->pack_string($message); @@ -295,8 +303,29 @@ trait MessageHandler $result['peer_port'] = Tools::unpackSignedInt(\stream_get_contents($payload, 4)); return $result; default: - \danog\MadelineProto\Logger::log('Unknown packet received: '.\bin2hex($crc), \danog\MadelineProto\Logger::ERROR); - return false; + if ($this->peerVersion >= 8 || (!$this->peerVersion && true)) { + \fseek($payload, 0); + $result['_'] = \ord(\stream_get_contents($payload, 1)); + $in_seq_no = \unpack('V', \stream_get_contents($payload, 4))[1]; + $out_seq_no = \unpack('V', \stream_get_contents($payload, 4))[1]; + $ack_mask = \unpack('V', \stream_get_contents($payload, 4))[1]; + $flags = \ord(\stream_get_contents($payload, 1)); + if ($flags & 1) { + $result['extra'] = []; + $count = \ord(\stream_get_contents($payload, 1)); + for ($x = 0; $x < $count; $x++) { + $len = \ord(\stream_get_contents($payload, 1)); + $result['extra'][]= \stream_get_contents($payload, $len); + } + } + $message = \fopen('php://memory', 'rw+b'); + + \fwrite($message, \stream_get_contents($payload)); + \fseek($message, 0); + } else { + \danog\MadelineProto\Logger::log('Unknown packet received: '.\bin2hex($crc), \danog\MadelineProto\Logger::ERROR); + return false; + } } if (!$this->received_packet($in_seq_no, $out_seq_no, $ack_mask)) { return false; @@ -306,19 +335,14 @@ trait MessageHandler // // packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector video_streams:byteVector = Packet; case \danog\MadelineProto\VoIP::PKT_INIT: - $result['protocol'] = Tools::unpackSignedInt(\stream_get_contents($message, 4)); + $result['protocol'] = $this->peerVersion = Tools::unpackSignedInt(\stream_get_contents($message, 4)); $result['min_protocol'] = Tools::unpackSignedInt(\stream_get_contents($message, 4)); $flags = \unpack('V', \stream_get_contents($message, 4))[1]; $result['data_saving_enabled'] = (bool) ($flags & 1); $result['audio_streams'] = []; $length = \ord(\stream_get_contents($message, 1)); for ($x = 0; $x < $length; $x++) { - $result['audio_streams'][$x] = \ord(\stream_get_contents($message, 1)); - } - $result['video_streams'] = []; - $length = \ord(\stream_get_contents($message, 1)); - for ($x = 0; $x < $length; $x++) { - $result['video_streams'][$x] = \ord(\stream_get_contents($message, 1)); + $result['audio_streams'][$x] = \stream_get_contents($message, 4); } break; // streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType; @@ -331,7 +355,7 @@ trait MessageHandler $length = \ord(\stream_get_contents($message, 1)); for ($x = 0; $x < $length; $x++) { $result['all_streams'][$x]['id'] = \ord(\stream_get_contents($message, 1)); - $result['all_streams'][$x]['type'] = \ord(\stream_get_contents($message, 1)); + $result['all_streams'][$x]['type'] = \stream_get_contents($message, 4); $result['all_streams'][$x]['codec'] = \ord(\stream_get_contents($message, 1)); $result['all_streams'][$x]['frame_duration'] = \unpack('v', \stream_get_contents($message, 2))[1]; $result['all_streams'][$x]['enabled'] = \ord(\stream_get_contents($message, 1)); diff --git a/src/danog/MadelineProto/VoIPServerConfig.php b/src/danog/MadelineProto/VoIPServerConfig.php index ff87cf2d..6b886d53 100644 --- a/src/danog/MadelineProto/VoIPServerConfig.php +++ b/src/danog/MadelineProto/VoIPServerConfig.php @@ -19,11 +19,13 @@ namespace danog\MadelineProto; -if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) { +if (\class_exists(VoIPServerConfig::class)) { + return; +} /** * Manages storage of VoIP server config. */ - class VoIPServerConfig extends VoIPServerConfigInternal + class VoIPServerConfig { /** * The configuration. @@ -47,7 +49,6 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) { public static function update(array $config) { self::$_config = $config; - self::updateInternal(self::getFinal()); } /** * Get shared call settings. @@ -68,7 +69,6 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) { public static function updateDefault(array $configDefault) { self::$_configDefault = $configDefault; - self::updateInternal(self::getFinal()); } /** * Get default shared call settings. @@ -89,4 +89,3 @@ if (\class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal')) { return \array_merge(self::$_configDefault, self::$_config); } } -}