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>
201 lines
5.8 KiB
C++
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
|