ZaStoGram_desktop/Telegram/SourceFiles/mtproto/runtime/runtime_environment.h
loop-uh 958d05339b Record that a server told us the time, not that it moved the clock
The clock reference was inferred from the correction being nonzero, which
inverts the warning. base::unixtime::update() returns without applying a
shift smaller than kIgnoreTimeDifference, three seconds - so a machine whose
clock is right keeps a zero shift and looks exactly like one that never heard
from a server. The HTTP correction does not cover for it either: the Date
fetch runs only off a fake TLS failure path, so a client that works never
performs one.

Put together, clock_ref=none would have been logged on every hello of every
healthy user, and stayed silent for the users already off by the three
seconds a relay refuses. A warning everyone sees stops being read, and the
first thing to stop being read is the one case it exists for.

So the flag now records the event it was always meant to record: a server
time arrived. It is raised at all four places one can arrive - three in
receive.cpp, one in the key creator - next to the update call rather than
inside it, because the update is exactly what may decide to do nothing. The
flag lives in the runtime layer as process-wide atomic state; base::unixtime
would be the natural home, but it is a submodule, and its own ValueUpdated
has the same hole on the ignore path.

The guard test now pins both halves: that the reference comes from
ServerTimeReceived(), and that every update call site has a matching mark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:08:52 +03:00

201 lines
5.8 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include "mtproto/config/mtproto_dc_options.h"
#include "mtproto/dc_id.h"
#include "mtproto/runtime/connection_status_types.h"
#include "rpl/lifetime.h"
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <memory>
namespace MTP {
class ConnectionStatus;
class ProxyEndpointContext;
class ProxyServices;
struct ProxyDiagnosticsEvent;
struct ProxyEventReport;
struct RuntimeProxySettings final {
Fn<bool()> enabled;
Fn<ProxyData()> selected;
Fn<ProxyData::Settings()> settings;
Fn<bool()> tryIPv6;
Fn<bool()> fastProxyWarmup;
Fn<ProxyStealthOptions()> stealthOptions;
Fn<void(Fn<void()>, rpl::lifetime&)> watchConnectionTypeChanges;
Fn<bool(const QString&, const QStringList&, crl::time)> applyDomainIps;
Fn<bool(const QString&, const QString&)> promoteDomainIp;
};
struct RuntimeDeviceSettings final {
Fn<QString()> model;
Fn<void(Fn<void(QString)>, rpl::lifetime&)> watchModelChanges;
};
struct RuntimeLanguageGateway final {
Fn<QString()> systemCode;
Fn<QString()> cloudCode;
Fn<QString()> packName;
Fn<void(const QString&)> setSuggested;
Fn<void(int, int)> setCurrentVersions;
Fn<void()> resetToDefault;
};
struct RuntimeStorageGateway final {
Fn<void()> writeSettings;
Fn<void(const QString&)> writeAutoupdatePrefix;
};
struct RuntimeAppGateway final {
Fn<void()> refreshGlobalProxy;
Fn<void()> badMtprotoConfigurationError;
};
struct RuntimeDiagnosticsGateway final {
Fn<void(ProxyDiagnosticsEvent)> writeProxyDiagnosticsLine;
Fn<void(ProxyEventReport)> reportProxyEvent;
};
struct RuntimeProxyResolver final {
Fn<void(QString)> resolveDomain;
Fn<void(QString, QString)> setGoodDomain;
Fn<void(QString, QStringList, qint64)> domainResolved;
};
struct RuntimeInstanceServices final {
ConnectionStatus *connectionStatus = nullptr;
Fn<DcId()> mainDcId;
Fn<DcOptions::Variants(DcId, DcType, bool)> dcOptionsLookup;
RuntimeProxyResolver proxyResolver;
Fn<void()> syncHttpUnixtime;
};
struct RuntimeProxyCapabilities final {
Fn<QString()> path;
};
class RuntimeTimer final {
public:
RuntimeTimer() = default;
RuntimeTimer(
Fn<void(crl::time)> callOnce,
Fn<void(crl::time)> callEach,
Fn<void()> cancel,
Fn<bool()> isActive)
: _callOnce(std::move(callOnce))
, _callEach(std::move(callEach))
, _cancel(std::move(cancel))
, _isActive(std::move(isActive)) {
}
void callOnce(crl::time delay) {
if (_callOnce) {
_callOnce(delay);
}
}
void callEach(crl::time delay) {
if (_callEach) {
_callEach(delay);
}
}
void cancel() {
if (_cancel) {
_cancel();
}
}
[[nodiscard]] bool isActive() const {
return _isActive ? _isActive() : false;
}
private:
Fn<void(crl::time)> _callOnce;
Fn<void(crl::time)> _callEach;
Fn<void()> _cancel;
Fn<bool()> _isActive;
};
struct RuntimeAsyncGateway final {
Fn<crl::time()> now;
Fn<int(int)> randomIndex;
Fn<void(crl::time, QObject*, Fn<void()>)> singleShot;
Fn<RuntimeTimer(not_null<QThread*>, Fn<void()>)> makeTimer;
};
struct RuntimeEnvironmentDescriptor final {
RuntimeProxySettings proxy;
RuntimeDeviceSettings device;
RuntimeLanguageGateway language;
RuntimeStorageGateway storage;
RuntimeAppGateway app;
RuntimeDiagnosticsGateway diagnostics;
RuntimeProxyCapabilities proxyCapabilities;
RuntimeAsyncGateway async;
};
class RuntimeEnvironment final : public QObject {
public:
RuntimeEnvironment(
RuntimeEnvironmentDescriptor descriptor,
std::shared_ptr<ProxyEndpointContext> endpointContext = nullptr);
~RuntimeEnvironment();
void bindInstance(RuntimeInstanceServices services);
void unbindInstance(ConnectionStatus *status);
[[nodiscard]] const RuntimeProxySettings &proxy() const;
[[nodiscard]] const RuntimeDeviceSettings &device() const;
[[nodiscard]] const RuntimeLanguageGateway &language() const;
[[nodiscard]] const RuntimeStorageGateway &storage() const;
[[nodiscard]] const RuntimeAppGateway &app() const;
[[nodiscard]] const RuntimeDiagnosticsGateway &diagnostics() const;
[[nodiscard]] const RuntimeInstanceServices &instance() const;
[[nodiscard]] const RuntimeProxyResolver &proxyResolver() const;
[[nodiscard]] const RuntimeProxyCapabilities &proxyCapabilities() const;
[[nodiscard]] const RuntimeAsyncGateway &async() const;
[[nodiscard]] ProxyServices &proxyServices() const;
[[nodiscard]] ProxyRuntimeId proxyRuntimeId() const;
[[nodiscard]] ProxyEndpointContext &proxyEndpointContext() const;
[[nodiscard]] auto proxyEndpointContextShared() const
-> std::shared_ptr<ProxyEndpointContext>;
private:
std::shared_ptr<ProxyEndpointContext> _proxyEndpointContext;
ProxyRuntimeId _proxyRuntimeId = 0;
RuntimeEnvironmentDescriptor _descriptor;
std::unique_ptr<ProxyServices> _proxyServices;
RuntimeInstanceServices _instance;
};
[[nodiscard]] std::shared_ptr<RuntimeEnvironment> CreateRuntimeEnvironment();
[[nodiscard]] std::shared_ptr<RuntimeEnvironment> CreateRuntimeEnvironment(
std::shared_ptr<ProxyEndpointContext> endpointContext);
[[nodiscard]] not_null<RuntimeEnvironment*> DefaultRuntimeEnvironment();
// Whether a server has ever told this process the time. A relay reads the
// time out of the fake TLS digest and refuses a hello it does not like, so
// the client needs to know whether any reference stood behind the value it
// sent - and the correction cannot answer that. base::unixtime::update()
// returns without applying a shift smaller than three seconds, so a machine
// whose clock is right keeps a zero shift and is indistinguishable from one
// that never heard from a server. This records the hearing, not the shift.
void NoteServerTimeReceived();
[[nodiscard]] bool ServerTimeReceived();
} // namespace MTP