mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
407 lines
12 KiB
C++
407 lines
12 KiB
C++
#include <mutex>
|
|
|
|
#include "TgVoip.h"
|
|
|
|
#include "Controller.h"
|
|
#include "Layer92.h"
|
|
#include "Message.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <iostream>
|
|
|
|
#ifndef TGVOIP_USE_CUSTOM_CRYPTO
|
|
extern "C" {
|
|
#include <openssl/sha.h>
|
|
#include <openssl/aes.h>
|
|
#include <openssl/modes.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/crypto.h>
|
|
}
|
|
|
|
void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
|
|
AES_KEY akey;
|
|
AES_set_encrypt_key(key, 32*8, &akey);
|
|
AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
|
|
}
|
|
|
|
void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
|
|
AES_KEY akey;
|
|
AES_set_decrypt_key(key, 32*8, &akey);
|
|
AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
|
|
}
|
|
|
|
void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){
|
|
RAND_bytes(buffer, len);
|
|
}
|
|
|
|
void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){
|
|
SHA1(msg, len, output);
|
|
}
|
|
|
|
void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){
|
|
SHA256(msg, len, output);
|
|
}
|
|
|
|
void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){
|
|
AES_KEY akey;
|
|
AES_set_encrypt_key(key, 32*8, &akey);
|
|
CRYPTO_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num, (block128_f) AES_encrypt);
|
|
}
|
|
|
|
void tgvoip_openssl_aes_cbc_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
|
|
AES_KEY akey;
|
|
AES_set_encrypt_key(key, 256, &akey);
|
|
AES_cbc_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
|
|
}
|
|
|
|
void tgvoip_openssl_aes_cbc_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
|
|
AES_KEY akey;
|
|
AES_set_decrypt_key(key, 256, &akey);
|
|
AES_cbc_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
|
|
}
|
|
|
|
const char * openssl_version() {
|
|
return SSLeay_version(SSLEAY_VERSION);
|
|
}
|
|
|
|
CryptoFunctions Layer92::crypto={
|
|
tgvoip_openssl_rand_bytes,
|
|
tgvoip_openssl_sha1,
|
|
tgvoip_openssl_sha256,
|
|
tgvoip_openssl_aes_ige_encrypt,
|
|
tgvoip_openssl_aes_ige_decrypt,
|
|
tgvoip_openssl_aes_ctr_encrypt,
|
|
tgvoip_openssl_aes_cbc_encrypt,
|
|
tgvoip_openssl_aes_cbc_decrypt
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef TGVOIP_NAMESPACE
|
|
namespace TGVOIP_NAMESPACE {
|
|
#endif
|
|
|
|
class TgVoipImpl : public TgVoip, public sigslot::has_slots<> {
|
|
public:
|
|
TgVoipImpl(
|
|
std::vector<TgVoipEndpoint> const &endpoints,
|
|
TgVoipPersistentState const &persistentState,
|
|
std::unique_ptr<TgVoipProxy> const &proxy,
|
|
TgVoipConfig const &config,
|
|
TgVoipEncryptionKey const &encryptionKey,
|
|
TgVoipNetworkType initialNetworkType
|
|
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
|
|
,
|
|
TgVoipCrypto const &crypto
|
|
#endif
|
|
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
|
|
,
|
|
TgVoipAudioDataCallbacks const &audioDataCallbacks
|
|
#endif
|
|
) {
|
|
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
|
|
tgvoip::VoIPController::crypto.sha1 = crypto.sha1;
|
|
tgvoip::VoIPController::crypto.sha256 = crypto.sha256;
|
|
tgvoip::VoIPController::crypto.rand_bytes = crypto.rand_bytes;
|
|
tgvoip::VoIPController::crypto.aes_ige_encrypt = crypto.aes_ige_encrypt;
|
|
tgvoip::VoIPController::crypto.aes_ige_decrypt = crypto.aes_ige_decrypt;
|
|
tgvoip::VoIPController::crypto.aes_ctr_encrypt = crypto.aes_ctr_encrypt;
|
|
#endif
|
|
|
|
// std::cerr << "OpenSSL version: " << openssl_version() << std::endl; // to verify because of WebRTC BoringSSL
|
|
|
|
EncryptionKey encryptionKeyValue;
|
|
memcpy(encryptionKeyValue, encryptionKey.value.data(), 256);
|
|
controller_ = new Controller(encryptionKey.isOutgoing, encryptionKeyValue, 5, 3);
|
|
|
|
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
|
|
audioCallbacks = audioDataCallbacks;
|
|
controller_->SignalRecord.connect(this, &TgVoipImpl::record);
|
|
controller_->SignalPlay.connect(this, &TgVoipImpl::play);
|
|
#ifdef TGVOIP_PREPROCESSED_OUTPUT
|
|
controller_->SignalPreprocessed.connect(this, &TgVoipImpl::preprocessed);
|
|
#endif
|
|
#endif
|
|
|
|
if (proxy != nullptr) {
|
|
controller_->SetProxy(rtc::ProxyType::PROXY_SOCKS5, rtc::SocketAddress(proxy->host, proxy->port),
|
|
proxy->login, proxy->password);
|
|
}
|
|
|
|
controller_->SignalNewState.connect(this, &TgVoipImpl::controllerStateCallback);
|
|
controller_->Start();
|
|
|
|
for (const auto &endpoint : endpoints) {
|
|
rtc::SocketAddress addr(endpoint.host.ipv4, endpoint.port);
|
|
Controller::EndpointType type;
|
|
switch (endpoint.type) {
|
|
case TgVoipEndpointType::UdpRelay:
|
|
type = Controller::EndpointType::UDP;
|
|
break;
|
|
case TgVoipEndpointType::Lan:
|
|
case TgVoipEndpointType::Inet:
|
|
type = Controller::EndpointType::P2P;
|
|
break;
|
|
case TgVoipEndpointType::TcpRelay:
|
|
type = Controller::EndpointType::TCP;
|
|
break;
|
|
default:
|
|
type = Controller::EndpointType::UDP;
|
|
break;
|
|
}
|
|
controller_->AddEndpoint(addr, endpoint.peerTag, type);
|
|
}
|
|
|
|
setNetworkType(initialNetworkType);
|
|
|
|
switch (config.dataSaving) {
|
|
case TgVoipDataSaving::Mobile:
|
|
controller_->SetDataSaving(true);
|
|
break;
|
|
case TgVoipDataSaving::Always:
|
|
controller_->SetDataSaving(true);
|
|
break;
|
|
default:
|
|
controller_->SetDataSaving(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
~TgVoipImpl() override {
|
|
stop();
|
|
}
|
|
|
|
void setOnStateUpdated(std::function<void(TgVoipState)> onStateUpdated) override {
|
|
std::lock_guard<std::mutex> lock(m_onStateUpdated);
|
|
onStateUpdated_ = onStateUpdated;
|
|
}
|
|
|
|
void setOnSignalBarsUpdated(std::function<void(int)> onSignalBarsUpdated) override {
|
|
std::lock_guard<std::mutex> lock(m_onSignalBarsUpdated);
|
|
onSignalBarsUpdated_ = onSignalBarsUpdated;
|
|
}
|
|
|
|
void setNetworkType(TgVoipNetworkType networkType) override {
|
|
message::NetworkType mappedType;
|
|
|
|
switch (networkType) {
|
|
case TgVoipNetworkType::Unknown:
|
|
mappedType = message::NetworkType::nUnknown;
|
|
break;
|
|
case TgVoipNetworkType::Gprs:
|
|
mappedType = message::NetworkType::nGprs;
|
|
break;
|
|
case TgVoipNetworkType::Edge:
|
|
mappedType = message::NetworkType::nEdge;
|
|
break;
|
|
case TgVoipNetworkType::ThirdGeneration:
|
|
mappedType = message::NetworkType::n3gOrAbove;
|
|
break;
|
|
case TgVoipNetworkType::Hspa:
|
|
mappedType = message::NetworkType::n3gOrAbove;
|
|
break;
|
|
case TgVoipNetworkType::Lte:
|
|
mappedType = message::NetworkType::n3gOrAbove;
|
|
break;
|
|
case TgVoipNetworkType::WiFi:
|
|
mappedType = message::NetworkType::nHighSpeed;
|
|
break;
|
|
case TgVoipNetworkType::Ethernet:
|
|
mappedType = message::NetworkType::nHighSpeed;
|
|
break;
|
|
case TgVoipNetworkType::OtherHighSpeed:
|
|
mappedType = message::NetworkType::nHighSpeed;
|
|
break;
|
|
case TgVoipNetworkType::OtherLowSpeed:
|
|
mappedType = message::NetworkType::nEdge;
|
|
break;
|
|
case TgVoipNetworkType::OtherMobile:
|
|
mappedType = message::NetworkType::n3gOrAbove;
|
|
break;
|
|
case TgVoipNetworkType::Dialup:
|
|
mappedType = message::NetworkType::nGprs;
|
|
break;
|
|
default:
|
|
mappedType = message::NetworkType::nUnknown;
|
|
break;
|
|
}
|
|
|
|
controller_->SetNetworkType(mappedType);
|
|
}
|
|
|
|
void setMuteMicrophone(bool muteMicrophone) override {
|
|
controller_->SetMute(muteMicrophone);
|
|
}
|
|
|
|
void setAudioOutputGainControlEnabled(bool enabled) override {
|
|
}
|
|
|
|
void setEchoCancellationStrength(int strength) override {
|
|
}
|
|
|
|
std::string getLastError() override {
|
|
return ""; // TODO: not implemented
|
|
}
|
|
|
|
std::string getDebugInfo() override {
|
|
return ""; // TODO: not implemented
|
|
}
|
|
|
|
int64_t getPreferredRelayId() override {
|
|
return 0; // we don't have endpoint ids
|
|
}
|
|
|
|
TgVoipTrafficStats getTrafficStats() override {
|
|
return TgVoipTrafficStats{}; // TODO: not implemented
|
|
}
|
|
|
|
TgVoipPersistentState getPersistentState() override {
|
|
return TgVoipPersistentState{}; // we dont't have such information
|
|
}
|
|
|
|
TgVoipFinalState stop() override {
|
|
TgVoipFinalState finalState = {
|
|
};
|
|
|
|
delete controller_;
|
|
controller_ = nullptr;
|
|
|
|
return finalState;
|
|
}
|
|
|
|
void controllerStateCallback(Controller::State state) {
|
|
if (onStateUpdated_) {
|
|
TgVoipState mappedState;
|
|
switch (state) {
|
|
case Controller::State::WaitInit:
|
|
mappedState = TgVoipState::WaitInit;
|
|
break;
|
|
case Controller::State::WaitInitAck:
|
|
mappedState = TgVoipState::WaitInitAck;
|
|
break;
|
|
case Controller::State::Established:
|
|
mappedState = TgVoipState::Estabilished;
|
|
break;
|
|
case Controller::State::Failed:
|
|
mappedState = TgVoipState::Failed;
|
|
break;
|
|
case Controller::State::Reconnecting:
|
|
mappedState = TgVoipState::Reconnecting;
|
|
break;
|
|
default:
|
|
mappedState = TgVoipState::Estabilished;
|
|
break;
|
|
}
|
|
|
|
onStateUpdated_(mappedState);
|
|
}
|
|
}
|
|
|
|
private:
|
|
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
|
|
TgVoipAudioDataCallbacks audioCallbacks;
|
|
|
|
void play(const int16_t *data, size_t size) {
|
|
if (!audioCallbacks.output)
|
|
return;
|
|
int16_t buf[size];
|
|
memcpy(buf, data, size * 2);
|
|
audioCallbacks.output(buf, size);
|
|
}
|
|
|
|
void record(int16_t *data, size_t size) {
|
|
if (audioCallbacks.input)
|
|
audioCallbacks.input(data, size);
|
|
}
|
|
|
|
void preprocessed(const int16_t *data, size_t size) {
|
|
if (!audioCallbacks.preprocessed)
|
|
return;
|
|
int16_t buf[size];
|
|
memcpy(buf, data, size * 2);
|
|
audioCallbacks.preprocessed(buf, size);
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
Controller *controller_;
|
|
std::function<void(TgVoipState)> onStateUpdated_;
|
|
std::function<void(int)> onSignalBarsUpdated_;
|
|
std::mutex m_onStateUpdated, m_onSignalBarsUpdated;
|
|
};
|
|
|
|
std::function<void(std::string const &)> globalLoggingFunction;
|
|
|
|
void __tgvoip_call_tglog(const char *format, ...){
|
|
va_list vaArgs;
|
|
va_start(vaArgs, format);
|
|
|
|
va_list vaCopy;
|
|
va_copy(vaCopy, vaArgs);
|
|
const int length = std::vsnprintf(nullptr, 0, format, vaCopy);
|
|
va_end(vaCopy);
|
|
|
|
std::vector<char> zc(length + 1);
|
|
std::vsnprintf(zc.data(), zc.size(), format, vaArgs);
|
|
va_end(vaArgs);
|
|
|
|
if (globalLoggingFunction != nullptr) {
|
|
globalLoggingFunction(std::string(zc.data(), zc.size()));
|
|
}
|
|
}
|
|
|
|
void TgVoip::setLoggingFunction(std::function<void(std::string const &)> loggingFunction) {
|
|
globalLoggingFunction = loggingFunction;
|
|
}
|
|
|
|
void TgVoip::setGlobalServerConfig(const std::string &serverConfig) {
|
|
}
|
|
|
|
int TgVoip::getConnectionMaxLayer() {
|
|
return 92; // TODO: retrieve from LayerBase
|
|
}
|
|
|
|
std::string TgVoip::getVersion() {
|
|
return ""; // TODO: version not known while not released
|
|
}
|
|
|
|
TgVoip *TgVoip::makeInstance(
|
|
TgVoipConfig const &config,
|
|
TgVoipPersistentState const &persistentState,
|
|
std::vector<TgVoipEndpoint> const &endpoints,
|
|
std::unique_ptr<TgVoipProxy> const &proxy,
|
|
TgVoipNetworkType initialNetworkType,
|
|
TgVoipEncryptionKey const &encryptionKey
|
|
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
|
|
,
|
|
TgVoipCrypto const &crypto
|
|
#endif
|
|
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
|
|
,
|
|
TgVoipAudioDataCallbacks const &audioDataCallbacks
|
|
#endif
|
|
) {
|
|
return new TgVoipImpl(
|
|
endpoints,
|
|
persistentState,
|
|
proxy,
|
|
config,
|
|
encryptionKey,
|
|
initialNetworkType
|
|
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
|
|
,
|
|
crypto
|
|
#endif
|
|
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
|
|
,
|
|
audioDataCallbacks
|
|
#endif
|
|
);
|
|
}
|
|
|
|
TgVoip::~TgVoip() = default;
|
|
|
|
#ifdef TGVOIP_NAMESPACE
|
|
}
|
|
#endif
|