Fix possible crash in calls.

Fixes #5732.
This commit is contained in:
John Preston 2019-03-12 15:16:58 +04:00
parent 13a9b967e9
commit 383b29dbd8
4 changed files with 123 additions and 85 deletions

View File

@ -40,27 +40,27 @@ constexpr auto kMinLayer = 65;
constexpr auto kHangupTimeoutMs = 5000; constexpr auto kHangupTimeoutMs = 5000;
constexpr auto kSha256Size = 32; constexpr auto kSha256Size = 32;
using tgvoip::Endpoint; void AppendEndpoint(
std::vector<tgvoip::Endpoint> &list,
void ConvertEndpoint( const MTPPhoneConnection &connection) {
std::vector<tgvoip::Endpoint> &ep, connection.match([&](const MTPDphoneConnection &data) {
const MTPDphoneConnection &mtc) { if (data.vpeer_tag.v.length() != 16) {
if (mtc.vpeer_tag.v.length() != 16) { return;
return; }
} auto ipv4 = tgvoip::IPv4Address(std::string(
auto ipv4 = tgvoip::IPv4Address(std::string( data.vip.v.constData(),
mtc.vip.v.constData(), data.vip.v.size()));
mtc.vip.v.size())); auto ipv6 = tgvoip::IPv6Address(std::string(
auto ipv6 = tgvoip::IPv6Address(std::string( data.vipv6.v.constData(),
mtc.vipv6.v.constData(), data.vipv6.v.size()));
mtc.vipv6.v.size())); list.emplace_back(
ep.push_back(Endpoint( (int64_t)data.vid.v,
(int64_t)mtc.vid.v, (uint16_t)data.vport.v,
(uint16_t)mtc.vport.v, ipv4,
ipv4, ipv6,
ipv6, tgvoip::Endpoint::Type::UDP_RELAY,
tgvoip::Endpoint::Type::UDP_RELAY, (unsigned char*)data.vpeer_tag.v.data());
(unsigned char*)mtc.vpeer_tag.v.data())); });
} }
constexpr auto kFingerprintDataSize = 256; constexpr auto kFingerprintDataSize = 256;
@ -252,7 +252,8 @@ void Call::actuallyAnswer() {
Expects(_type == Type::Incoming); Expects(_type == Type::Incoming);
if (_state != State::Starting && _state != State::WaitingIncoming) { if (_state != State::Starting && _state != State::WaitingIncoming) {
if (_state != State::ExchangingKeys || !_answerAfterDhConfigReceived) { if (_state != State::ExchangingKeys
|| !_answerAfterDhConfigReceived) {
return; return;
} }
} }
@ -271,18 +272,19 @@ void Call::actuallyAnswer() {
| MTPDphoneCallProtocol::Flag::f_udp_reflector), | MTPDphoneCallProtocol::Flag::f_udp_reflector),
MTP_int(kMinLayer), MTP_int(kMinLayer),
MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer())) MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()))
)).done([this](const MTPphone_PhoneCall &result) { )).done([=](const MTPphone_PhoneCall &result) {
Expects(result.type() == mtpc_phone_phoneCall); Expects(result.type() == mtpc_phone_phoneCall);
auto &call = result.c_phone_phoneCall(); auto &call = result.c_phone_phoneCall();
Auth().data().processUsers(call.vusers); Auth().data().processUsers(call.vusers);
if (call.vphone_call.type() != mtpc_phoneCallWaiting) { if (call.vphone_call.type() != mtpc_phoneCallWaiting) {
LOG(("Call Error: Expected phoneCallWaiting in response to phone.acceptCall()")); LOG(("Call Error: "
"Not phoneCallWaiting in response to phone.acceptCall."));
finish(FinishType::Failed); finish(FinishType::Failed);
return; return;
} }
handleUpdate(call.vphone_call); handleUpdate(call.vphone_call);
}).fail([this](const RPCError &error) { }).fail([=](const RPCError &error) {
handleRequestError(error); handleRequestError(error);
}).send(); }).send();
} }
@ -371,7 +373,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
Unexpected("phoneCallRequested call inside an existing call handleUpdate()"); Unexpected("phoneCallRequested call inside an existing call handleUpdate()");
} }
if (Auth().userId() != data.vparticipant_id.v) { if (Auth().userId() != data.vparticipant_id.v) {
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(data.vparticipant_id.v).arg(Auth().userId())); LOG(("Call Error: Wrong call participant_id %1, expected %2."
).arg(data.vparticipant_id.v
).arg(Auth().userId()));
finish(FinishType::Failed); finish(FinishType::Failed);
return true; return true;
} }
@ -379,7 +383,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
_accessHash = data.vaccess_hash.v; _accessHash = data.vaccess_hash.v;
auto gaHashBytes = bytes::make_span(data.vg_a_hash.v); auto gaHashBytes = bytes::make_span(data.vg_a_hash.v);
if (gaHashBytes.size() != kSha256Size) { if (gaHashBytes.size() != kSha256Size) {
LOG(("Call Error: Wrong g_a_hash size %1, expected %2.").arg(gaHashBytes.size()).arg(kSha256Size)); LOG(("Call Error: Wrong g_a_hash size %1, expected %2."
).arg(gaHashBytes.size()
).arg(kSha256Size));
finish(FinishType::Failed); finish(FinishType::Failed);
return true; return true;
} }
@ -400,7 +406,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
if (data.vid.v != _id) { if (data.vid.v != _id) {
return false; return false;
} }
if (_type == Type::Outgoing && _state == State::Waiting && data.vreceive_date.v != 0) { if (_type == Type::Outgoing
&& _state == State::Waiting
&& data.vreceive_date.v != 0) {
_discardByTimeoutTimer.callOnce(Global::CallRingTimeoutMs()); _discardByTimeoutTimer.callOnce(Global::CallRingTimeoutMs());
setState(State::Ringing); setState(State::Ringing);
startWaitingTrack(); startWaitingTrack();
@ -427,16 +435,23 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
if (data.is_need_debug()) { if (data.is_need_debug()) {
auto debugLog = _controller ? _controller->GetDebugLog() : std::string(); auto debugLog = _controller ? _controller->GetDebugLog() : std::string();
if (!debugLog.empty()) { if (!debugLog.empty()) {
MTP::send(MTPphone_SaveCallDebug(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_dataJSON(MTP_string(debugLog)))); MTP::send(
MTPphone_SaveCallDebug(
MTP_inputPhoneCall(
MTP_long(_id),
MTP_long(_accessHash)),
MTP_dataJSON(MTP_string(debugLog))));
} }
} }
if (data.is_need_rating() && _id && _accessHash) { if (data.is_need_rating() && _id && _accessHash) {
Ui::show(Box<RateCallBox>(_id, _accessHash)); Ui::show(Box<RateCallBox>(_id, _accessHash));
} }
if (data.has_reason() && data.vreason.type() == mtpc_phoneCallDiscardReasonDisconnect) { if (data.has_reason()
&& data.vreason.type() == mtpc_phoneCallDiscardReasonDisconnect) {
LOG(("Call Info: Discarded with DISCONNECT reason.")); LOG(("Call Info: Discarded with DISCONNECT reason."));
} }
if (data.has_reason() && data.vreason.type() == mtpc_phoneCallDiscardReasonBusy) { if (data.has_reason()
&& data.vreason.type() == mtpc_phoneCallDiscardReasonBusy) {
setState(State::Busy); setState(State::Busy);
} else if (_type == Type::Outgoing || _state == State::HangingUp) { } else if (_type == Type::Outgoing || _state == State::HangingUp) {
setState(State::Ended); setState(State::Ended);
@ -451,7 +466,8 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
return false; return false;
} }
if (_type != Type::Outgoing) { if (_type != Type::Outgoing) {
LOG(("Call Error: Unexpected phoneCallAccepted for an incoming call.")); LOG(("Call Error: "
"Unexpected phoneCallAccepted for an incoming call."));
finish(FinishType::Failed); finish(FinishType::Failed);
} else if (checkCallFields(data)) { } else if (checkCallFields(data)) {
confirmAcceptedCall(data); confirmAcceptedCall(data);
@ -465,8 +481,17 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
Expects(_type == Type::Outgoing); Expects(_type == Type::Outgoing);
auto firstBytes = bytes::make_span(call.vg_b.v); if (_state == State::ExchangingKeys
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p); || _controller) {
LOG(("Call Warning: Unexpected confirmAcceptedCall."));
return;
}
const auto firstBytes = bytes::make_span(call.vg_b.v);
const auto computedAuthKey = MTP::CreateAuthKey(
firstBytes,
_randomPower,
_dhConfig.p);
if (computedAuthKey.empty()) { if (computedAuthKey.empty()) {
LOG(("Call Error: Could not compute mod-exp final.")); LOG(("Call Error: Could not compute mod-exp final."));
finish(FinishType::Failed); finish(FinishType::Failed);
@ -561,10 +586,10 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
} }
const auto &protocol = call.vprotocol.c_phoneCallProtocol(); const auto &protocol = call.vprotocol.c_phoneCallProtocol();
auto endpoints = std::vector<Endpoint>(); auto endpoints = std::vector<tgvoip::Endpoint>();
ConvertEndpoint(endpoints, call.vconnection.c_phoneConnection()); AppendEndpoint(endpoints, call.vconnection);
for (int i = 0; i < call.valternative_connections.v.length(); i++) { for (const auto &connection : call.valternative_connections.v) {
ConvertEndpoint(endpoints, call.valternative_connections.v[i].c_phoneConnection()); AppendEndpoint(endpoints, connection);
} }
auto callbacks = tgvoip::VoIPController::Callbacks(); auto callbacks = tgvoip::VoIPController::Callbacks();
@ -602,7 +627,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
_controller->SetCallbacks(callbacks); _controller->SetCallbacks(callbacks);
if (Global::UseProxyForCalls() if (Global::UseProxyForCalls()
&& (Global::ProxySettings() == ProxyData::Settings::Enabled)) { && (Global::ProxySettings() == ProxyData::Settings::Enabled)) {
const auto proxy = Global::SelectedProxy(); const auto &proxy = Global::SelectedProxy();
if (proxy.supportsCalls()) { if (proxy.supportsCalls()) {
Assert(proxy.type == ProxyData::Type::Socks5); Assert(proxy.type == ProxyData::Type::Socks5);
_controller->SetProxy( _controller->SetProxy(

View File

@ -121,7 +121,7 @@ public:
bytes::vector getKeyShaForFingerprint() const; bytes::vector getKeyShaForFingerprint() const;
QString getDebugLog() const; QString getDebugLog() const;
void setCurrentAudioDevice(bool input, std::string deviceID); void setCurrentAudioDevice(bool input, std::string deviceID);
void setAudioVolume(bool input, float level); void setAudioVolume(bool input, float level);
void setAudioDuckingEnabled(bool enabled); void setAudioDuckingEnabled(bool enabled);

View File

@ -106,10 +106,13 @@ void Instance::destroyCall(not_null<Call*> call) {
} }
void Instance::destroyCurrentPanel() { void Instance::destroyCurrentPanel() {
_pendingPanels.erase(std::remove_if(_pendingPanels.begin(), _pendingPanels.end(), [](auto &&panel) { _pendingPanels.erase(
return !panel; std::remove_if(
}), _pendingPanels.end()); _pendingPanels.begin(),
_pendingPanels.push_back(_currentCallPanel.release()); _pendingPanels.end(),
[](auto &&panel) { return !panel; }),
_pendingPanels.end());
_pendingPanels.emplace_back(_currentCallPanel.release());
_pendingPanels.back()->hideAndDestroy(); // Always queues the destruction. _pendingPanels.back()->hideAndDestroy(); // Always queues the destruction.
} }
@ -130,56 +133,65 @@ void Instance::createCall(not_null<UserData*> user, Call::Type type) {
void Instance::refreshDhConfig() { void Instance::refreshDhConfig() {
Expects(_currentCall != nullptr); Expects(_currentCall != nullptr);
const auto weak = base::make_weak(_currentCall);
request(MTPmessages_GetDhConfig( request(MTPmessages_GetDhConfig(
MTP_int(_dhConfig.version), MTP_int(_dhConfig.version),
MTP_int(MTP::ModExpFirst::kRandomPowerSize) MTP_int(MTP::ModExpFirst::kRandomPowerSize)
)).done([this, call = base::make_weak(_currentCall)]( )).done([=](const MTPmessages_DhConfig &result) {
const MTPmessages_DhConfig &result) { const auto call = weak.get();
auto random = bytes::const_span(); const auto random = updateDhConfig(result);
switch (result.type()) {
case mtpc_messages_dhConfig: {
auto &config = result.c_messages_dhConfig();
if (!MTP::IsPrimeAndGood(bytes::make_span(config.vp.v), config.vg.v)) {
LOG(("API Error: bad p/g received in dhConfig."));
callFailed(call.get());
return;
}
_dhConfig.g = config.vg.v;
_dhConfig.p = bytes::make_vector(config.vp.v);
random = bytes::make_span(config.vrandom.v);
} break;
case mtpc_messages_dhConfigNotModified: {
auto &config = result.c_messages_dhConfigNotModified();
random = bytes::make_span(config.vrandom.v);
if (!_dhConfig.g || _dhConfig.p.empty()) {
LOG(("API Error: dhConfigNotModified on zero version."));
callFailed(call.get());
return;
}
} break;
default: Unexpected("Type in messages.getDhConfig");
}
if (random.size() != MTP::ModExpFirst::kRandomPowerSize) {
LOG(("API Error: dhConfig random bytes wrong size: %1").arg(random.size()));
callFailed(call.get());
return;
}
if (call) {
call->start(random);
}
}).fail([this, call = base::make_weak(_currentCall)](
const RPCError &error) {
if (!call) { if (!call) {
DEBUG_LOG(("API Warning: call was destroyed before got dhConfig."));
return; return;
} }
callFailed(call.get()); if (!random.empty()) {
Assert(random.size() == MTP::ModExpFirst::kRandomPowerSize);
call->start(random);
} else {
callFailed(call);
}
}).fail([=](const RPCError &error) {
const auto call = weak.get();
if (!call) {
return;
}
callFailed(call);
}).send(); }).send();
} }
bytes::const_span Instance::updateDhConfig(
const MTPmessages_DhConfig &data) {
const auto validRandom = [](const QByteArray & random) {
if (random.size() != MTP::ModExpFirst::kRandomPowerSize) {
return false;
}
return true;
};
return data.match([&](const MTPDmessages_dhConfig &data)
-> bytes::const_span {
auto primeBytes = bytes::make_vector(data.vp.v);
if (!MTP::IsPrimeAndGood(primeBytes, data.vg.v)) {
LOG(("API Error: bad p/g received in dhConfig."));
return {};
} else if (!validRandom(data.vrandom.v)) {
return {};
}
_dhConfig.g = data.vg.v;
_dhConfig.p = std::move(primeBytes);
_dhConfig.version = data.vversion.v;
return bytes::make_span(data.vrandom.v);
}, [&](const MTPDmessages_dhConfigNotModified &data)
-> bytes::const_span {
if (!_dhConfig.g || _dhConfig.p.empty()) {
LOG(("API Error: dhConfigNotModified on zero version."));
return {};
} else if (!validRandom(data.vrandom.v)) {
return {};
}
return bytes::make_span(data.vrandom.v);
});
}
void Instance::refreshServerConfig() { void Instance::refreshServerConfig() {
if (_serverConfigRequestId) { if (_serverConfigRequestId) {
return; return;

View File

@ -60,6 +60,7 @@ private:
void refreshDhConfig(); void refreshDhConfig();
void refreshServerConfig(); void refreshServerConfig();
bytes::const_span updateDhConfig(const MTPmessages_DhConfig &data);
bool alreadyInCall(); bool alreadyInCall();
void handleCallUpdate(const MTPPhoneCall &call); void handleCallUpdate(const MTPPhoneCall &call);