mirror of https://github.com/procxx/kepka.git
Support base64 encoding of the secret.
This commit is contained in:
parent
2f0331b2e0
commit
8135f4b427
|
@ -145,6 +145,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
|
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
|
||||||
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
|
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
|
||||||
"lng_sure_enable" = "Enable";
|
"lng_sure_enable" = "Enable";
|
||||||
|
"lng_proxy_invalid" = "The proxy link is invalid.";
|
||||||
|
"lng_proxy_unsupported" = "Your Telegram Desktop version doesn't support this proxy type or the proxy link is invalid. Please update Telegram Desktop to the latest version.";
|
||||||
|
|
||||||
"lng_edit_deleted" = "This message was deleted";
|
"lng_edit_deleted" = "This message was deleted";
|
||||||
"lng_edit_too_long" = "Your message text is too long";
|
"lng_edit_too_long" = "Your message text is too long";
|
||||||
|
|
|
@ -31,6 +31,57 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||||
|
|
||||||
|
class Base64UrlInput : public Ui::MaskedInputField {
|
||||||
|
public:
|
||||||
|
Base64UrlInput(
|
||||||
|
QWidget *parent,
|
||||||
|
const style::InputField &st,
|
||||||
|
rpl::producer<QString> placeholder,
|
||||||
|
const QString &val);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void correctValue(
|
||||||
|
const QString &was,
|
||||||
|
int wasCursor,
|
||||||
|
QString &now,
|
||||||
|
int &nowCursor) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Base64UrlInput::Base64UrlInput(
|
||||||
|
QWidget *parent,
|
||||||
|
const style::InputField &st,
|
||||||
|
rpl::producer<QString> placeholder,
|
||||||
|
const QString &val)
|
||||||
|
: MaskedInputField(parent, st, std::move(placeholder), val) {
|
||||||
|
if (!QRegularExpression("^[a-zA-Z0-9_\\-]+$").match(val).hasMatch()) {
|
||||||
|
setText(QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Base64UrlInput::correctValue(
|
||||||
|
const QString &was,
|
||||||
|
int wasCursor,
|
||||||
|
QString &now,
|
||||||
|
int &nowCursor) {
|
||||||
|
QString newText;
|
||||||
|
newText.reserve(now.size());
|
||||||
|
auto newPos = nowCursor;
|
||||||
|
for (auto i = 0, l = now.size(); i < l; ++i) {
|
||||||
|
const auto ch = now[i];
|
||||||
|
if ((ch >= '0' && ch <= '9')
|
||||||
|
|| (ch >= 'a' && ch <= 'z')
|
||||||
|
|| (ch >= 'A' && ch <= 'Z')
|
||||||
|
|| (ch == '-')
|
||||||
|
|| (ch == '_')) {
|
||||||
|
newText.append(ch);
|
||||||
|
} else if (i < nowCursor) {
|
||||||
|
--newPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCorrectedText(now, nowCursor, newText, newPos);
|
||||||
|
}
|
||||||
|
|
||||||
class ProxyRow : public Ui::RippleButton {
|
class ProxyRow : public Ui::RippleButton {
|
||||||
public:
|
public:
|
||||||
using View = ProxiesBoxController::ItemView;
|
using View = ProxiesBoxController::ItemView;
|
||||||
|
@ -150,7 +201,7 @@ private:
|
||||||
QPointer<Ui::PortInput> _port;
|
QPointer<Ui::PortInput> _port;
|
||||||
QPointer<Ui::InputField> _user;
|
QPointer<Ui::InputField> _user;
|
||||||
QPointer<Ui::PasswordInput> _password;
|
QPointer<Ui::PasswordInput> _password;
|
||||||
QPointer<Ui::HexInput> _secret;
|
QPointer<Base64UrlInput> _secret;
|
||||||
|
|
||||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _credentials;
|
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _credentials;
|
||||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _mtprotoCredentials;
|
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _mtprotoCredentials;
|
||||||
|
@ -852,12 +903,11 @@ void ProxyBox::setupMtprotoCredentials(const ProxyData &data) {
|
||||||
addLabel(mtproto, tr::lng_proxy_credentials(tr::now));
|
addLabel(mtproto, tr::lng_proxy_credentials(tr::now));
|
||||||
|
|
||||||
auto secretWrap = object_ptr<Ui::RpWidget>(mtproto);
|
auto secretWrap = object_ptr<Ui::RpWidget>(mtproto);
|
||||||
_secret = Ui::CreateChild<Ui::HexInput>(
|
_secret = Ui::CreateChild<Base64UrlInput>(
|
||||||
secretWrap.data(),
|
secretWrap.data(),
|
||||||
st::connectionUserInputField,
|
st::connectionUserInputField,
|
||||||
tr::lng_connection_proxy_secret_ph(),
|
tr::lng_connection_proxy_secret_ph(),
|
||||||
(data.type == Type::Mtproto) ? data.password : QString());
|
(data.type == Type::Mtproto) ? data.password : QString());
|
||||||
_secret->setMaxLength(ProxyData::MaxMtprotoPasswordLength());
|
|
||||||
_secret->move(0, 0);
|
_secret->move(0, 0);
|
||||||
_secret->heightValue(
|
_secret->heightValue(
|
||||||
) | rpl::start_with_next([=, wrap = secretWrap.data()](int height) {
|
) | rpl::start_with_next([=, wrap = secretWrap.data()](int height) {
|
||||||
|
@ -975,6 +1025,11 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||||
strong->closeBox();
|
strong->closeBox();
|
||||||
}
|
}
|
||||||
}), LayerOption::KeepOther);
|
}), LayerOption::KeepOther);
|
||||||
|
} else {
|
||||||
|
Ui::show(Box<InformBox>(
|
||||||
|
(proxy.status() == ProxyData::Status::Unsupported
|
||||||
|
? tr::lng_proxy_unsupported(tr::now)
|
||||||
|
: tr::lng_proxy_invalid(tr::now))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,30 +62,127 @@ namespace {
|
||||||
|
|
||||||
std::atomic<int> GlobalAtomicRequestId = 0;
|
std::atomic<int> GlobalAtomicRequestId = 0;
|
||||||
|
|
||||||
QReadWriteLock unixtimeLock;
|
QReadWriteLock unixtimeLock;
|
||||||
volatile int32 unixtimeDelta = 0;
|
volatile int32 unixtimeDelta = 0;
|
||||||
volatile bool unixtimeWasSet = false;
|
volatile bool unixtimeWasSet = false;
|
||||||
volatile uint64 _msgIdStart, _msgIdLocal = 0, _msgIdMsStart;
|
volatile uint64 _msgIdStart, _msgIdLocal = 0, _msgIdMsStart;
|
||||||
|
|
||||||
void _initMsgIdConstants() {
|
void _initMsgIdConstants() {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
LARGE_INTEGER li;
|
LARGE_INTEGER li;
|
||||||
QueryPerformanceCounter(&li);
|
QueryPerformanceCounter(&li);
|
||||||
_msgIdMsStart = li.QuadPart;
|
_msgIdMsStart = li.QuadPart;
|
||||||
#elif defined Q_OS_MAC
|
#elif defined Q_OS_MAC
|
||||||
_msgIdMsStart = mach_absolute_time();
|
_msgIdMsStart = mach_absolute_time();
|
||||||
#else
|
#else
|
||||||
timespec ts;
|
timespec ts;
|
||||||
clock_gettime(CLOCK_REALTIME, &ts);
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
_msgIdMsStart = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
_msgIdMsStart = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32 msgIdRand;
|
uint32 msgIdRand;
|
||||||
memset_rand(&msgIdRand, sizeof(uint32));
|
memset_rand(&msgIdRand, sizeof(uint32));
|
||||||
_msgIdStart = (((uint64)((uint32)unixtime()) << 32) | (uint64)msgIdRand);
|
_msgIdStart = (((uint64)((uint32)unixtime()) << 32) | (uint64)msgIdRand);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsHexMtprotoPassword(const QString &password) {
|
||||||
|
const auto bad = [](QChar ch) {
|
||||||
|
const auto code = ch.unicode();
|
||||||
|
return (code < 'a' || code > 'f')
|
||||||
|
&& (code < 'A' || code > 'F')
|
||||||
|
&& (code < '0' || code > '9');
|
||||||
|
};
|
||||||
|
return (password.size() % 2 == 0)
|
||||||
|
&& (std::find_if(password.begin(), password.end(), bad)
|
||||||
|
== password.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ProxyData::Status HexMtprotoPasswordStatus(
|
||||||
|
const QString &password) {
|
||||||
|
const auto size = password.size() / 2;
|
||||||
|
const auto valid = (size == 16)
|
||||||
|
|| (size == 17 && (password[0] == 'd') && (password[1] == 'd'))
|
||||||
|
|| (size >= 21 && (password[0] == 'e') && (password[1] == 'e'));
|
||||||
|
if (valid) {
|
||||||
|
return ProxyData::Status::Valid;
|
||||||
|
} else if (size < 16) {
|
||||||
|
return ProxyData::Status::Invalid;
|
||||||
|
}
|
||||||
|
return ProxyData::Status::Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bytes::vector SecretFromHexMtprotoPassword(
|
||||||
|
const QString &password) {
|
||||||
|
Expects(password.size() % 2 == 0);
|
||||||
|
|
||||||
|
const auto size = password.size() / 2;
|
||||||
|
const auto fromHex = [](QChar ch) -> int {
|
||||||
|
const auto code = int(ch.unicode());
|
||||||
|
if (code >= '0' && code <= '9') {
|
||||||
|
return (code - '0');
|
||||||
|
} else if (code >= 'A' && code <= 'F') {
|
||||||
|
return 10 + (code - 'A');
|
||||||
|
} else if (ch >= 'a' && ch <= 'f') {
|
||||||
|
return 10 + (code - 'a');
|
||||||
|
}
|
||||||
|
Unexpected("Code in ProxyData fromHex.");
|
||||||
|
};
|
||||||
|
auto result = bytes::vector(size);
|
||||||
|
for (auto i = 0; i != size; ++i) {
|
||||||
|
const auto high = fromHex(password[2 * i]);
|
||||||
|
const auto low = fromHex(password[2 * i + 1]);
|
||||||
|
if (high < 0 || low < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
result[i] = static_cast<bytes::type>(high * 16 + low);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsBase64UrlMtprotoPassword(const QString &password) {
|
||||||
|
const auto bad = [](QChar ch) {
|
||||||
|
const auto code = ch.unicode();
|
||||||
|
return (code < 'a' || code > 'z')
|
||||||
|
&& (code < 'A' || code > 'Z')
|
||||||
|
&& (code < '0' || code > '9')
|
||||||
|
&& (code != '_')
|
||||||
|
&& (code != '-');
|
||||||
|
};
|
||||||
|
return (password.size() % 4 != 1)
|
||||||
|
&& (std::find_if(password.begin(), password.end(), bad)
|
||||||
|
== password.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ProxyData::Status Base64UrlMtprotoPasswordStatus(
|
||||||
|
const QString &password) {
|
||||||
|
const auto size = (password.size() * 3) / 4;
|
||||||
|
const auto valid = (size == 16)
|
||||||
|
|| (size == 17
|
||||||
|
&& (password[0] == '3')
|
||||||
|
&& ((password[1] >= 'Q') && (password[1] <= 'Z')
|
||||||
|
|| (password[1] >= 'a') && (password[1] <= 'f')))
|
||||||
|
|| (size >= 21
|
||||||
|
&& (password[0] == '7')
|
||||||
|
&& (password[1] >= 'g')
|
||||||
|
&& (password[1] <= 'v'));
|
||||||
|
if (size < 16) {
|
||||||
|
return ProxyData::Status::Invalid;
|
||||||
|
} else if (valid) {
|
||||||
|
return ProxyData::Status::Valid;
|
||||||
|
}
|
||||||
|
return ProxyData::Status::Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bytes::vector SecretFromBase64UrlMtprotoPassword(
|
||||||
|
const QString &password) {
|
||||||
|
const auto result = QByteArray::fromBase64(
|
||||||
|
password.toLatin1(),
|
||||||
|
QByteArray::Base64UrlEncoding);
|
||||||
|
return bytes::make_vector(bytes::make_span(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TimeId LocalUnixtime() {
|
TimeId LocalUnixtime() {
|
||||||
return (TimeId)time(nullptr);
|
return (TimeId)time(nullptr);
|
||||||
}
|
}
|
||||||
|
@ -233,16 +330,16 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxyData::valid() const {
|
bool ProxyData::valid() const {
|
||||||
if (type == Type::None || host.isEmpty() || !port) {
|
return status() == Status::Valid;
|
||||||
return false;
|
|
||||||
} else if (type == Type::Mtproto && !ValidMtprotoPassword(password)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ProxyData::MaxMtprotoPasswordLength() {
|
ProxyData::Status ProxyData::status() const {
|
||||||
return 34;
|
if (type == Type::None || host.isEmpty() || !port) {
|
||||||
|
return Status::Invalid;
|
||||||
|
} else if (type == Type::Mtproto) {
|
||||||
|
return MtprotoPasswordStatus(password);
|
||||||
|
}
|
||||||
|
return Status::Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxyData::supportsCalls() const {
|
bool ProxyData::supportsCalls() const {
|
||||||
|
@ -259,30 +356,13 @@ bool ProxyData::tryCustomResolve() const {
|
||||||
|
|
||||||
bytes::vector ProxyData::secretFromMtprotoPassword() const {
|
bytes::vector ProxyData::secretFromMtprotoPassword() const {
|
||||||
Expects(type == Type::Mtproto);
|
Expects(type == Type::Mtproto);
|
||||||
Expects(password.size() % 2 == 0);
|
|
||||||
|
|
||||||
const auto length = password.size() / 2;
|
if (IsHexMtprotoPassword(password)) {
|
||||||
const auto fromHex = [](QChar ch) -> int {
|
return SecretFromHexMtprotoPassword(password);
|
||||||
const auto code = int(ch.unicode());
|
} else if (IsBase64UrlMtprotoPassword(password)) {
|
||||||
if (code >= '0' && code <= '9') {
|
return SecretFromBase64UrlMtprotoPassword(password);
|
||||||
return (code - '0');
|
|
||||||
} else if (code >= 'A' && code <= 'F') {
|
|
||||||
return 10 + (code - 'A');
|
|
||||||
} else if (ch >= 'a' && ch <= 'f') {
|
|
||||||
return 10 + (code - 'a');
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
auto result = bytes::vector(length);
|
|
||||||
for (auto i = 0; i != length; ++i) {
|
|
||||||
const auto high = fromHex(password[2 * i]);
|
|
||||||
const auto low = fromHex(password[2 * i + 1]);
|
|
||||||
if (high < 0 || low < 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
result[i] = static_cast<gsl::byte>(high * 16 + low);
|
|
||||||
}
|
}
|
||||||
return result;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyData::operator bool() const {
|
ProxyData::operator bool() const {
|
||||||
|
@ -304,16 +384,17 @@ bool ProxyData::operator!=(const ProxyData &other) const {
|
||||||
return !(*this == other);
|
return !(*this == other);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxyData::ValidMtprotoPassword(const QString &secret) {
|
bool ProxyData::ValidMtprotoPassword(const QString &password) {
|
||||||
using Expression = QRegularExpression;
|
return MtprotoPasswordStatus(password) == Status::Valid;
|
||||||
if (secret.size() == 32) {
|
}
|
||||||
static const auto check = Expression("^[a-fA-F0-9]{32}$");
|
|
||||||
return check.match(secret).hasMatch();
|
ProxyData::Status ProxyData::MtprotoPasswordStatus(const QString &password) {
|
||||||
} else if (secret.size() == 34) {
|
if (IsHexMtprotoPassword(password)) {
|
||||||
static const auto check = Expression("^(dd|ee)[a-fA-F0-9]{32}$");
|
return HexMtprotoPasswordStatus(password);
|
||||||
return check.match(secret).hasMatch();
|
} else if (IsBase64UrlMtprotoPassword(password)) {
|
||||||
|
return Base64UrlMtprotoPasswordStatus(password);
|
||||||
}
|
}
|
||||||
return false;
|
return Status::Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex) {
|
ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex) {
|
||||||
|
|
|
@ -270,6 +270,11 @@ struct ProxyData {
|
||||||
Http,
|
Http,
|
||||||
Mtproto,
|
Mtproto,
|
||||||
};
|
};
|
||||||
|
enum class Status {
|
||||||
|
Valid,
|
||||||
|
Unsupported,
|
||||||
|
Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
Type type = Type::None;
|
Type type = Type::None;
|
||||||
QString host;
|
QString host;
|
||||||
|
@ -279,16 +284,18 @@ struct ProxyData {
|
||||||
std::vector<QString> resolvedIPs;
|
std::vector<QString> resolvedIPs;
|
||||||
crl::time resolvedExpireAt = 0;
|
crl::time resolvedExpireAt = 0;
|
||||||
|
|
||||||
bool valid() const;
|
[[nodiscard]] bool valid() const;
|
||||||
bool supportsCalls() const;
|
[[nodiscard]] Status status() const;
|
||||||
bool tryCustomResolve() const;
|
[[nodiscard]] bool supportsCalls() const;
|
||||||
bytes::vector secretFromMtprotoPassword() const;
|
[[nodiscard]] bool tryCustomResolve() const;
|
||||||
explicit operator bool() const;
|
[[nodiscard]] bytes::vector secretFromMtprotoPassword() const;
|
||||||
bool operator==(const ProxyData &other) const;
|
[[nodiscard]] explicit operator bool() const;
|
||||||
bool operator!=(const ProxyData &other) const;
|
[[nodiscard]] bool operator==(const ProxyData &other) const;
|
||||||
|
[[nodiscard]] bool operator!=(const ProxyData &other) const;
|
||||||
|
|
||||||
static bool ValidMtprotoPassword(const QString &secret);
|
[[nodiscard]] static bool ValidMtprotoPassword(const QString &password);
|
||||||
static int MaxMtprotoPasswordLength();
|
[[nodiscard]] static Status MtprotoPasswordStatus(
|
||||||
|
const QString &password);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ constexpr auto kConnectionStartPrefixSize = 64;
|
||||||
|
|
||||||
class TcpConnection::Protocol {
|
class TcpConnection::Protocol {
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<Protocol> Create(bytes::vector &&secret);
|
static std::unique_ptr<Protocol> Create(bytes::const_span secret);
|
||||||
|
|
||||||
virtual uint32 id() const = 0;
|
virtual uint32 id() const = 0;
|
||||||
virtual bool supportsArbitraryLength() const = 0;
|
virtual bool supportsArbitraryLength() const = 0;
|
||||||
|
@ -223,15 +223,14 @@ bytes::const_span TcpConnection::Protocol::VersionD::readPacket(
|
||||||
return bytes.subspan(sizeLength, size - sizeLength);
|
return bytes.subspan(sizeLength, size - sizeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto TcpConnection::Protocol::Create(bytes::vector &&secret)
|
auto TcpConnection::Protocol::Create(bytes::const_span secret)
|
||||||
-> std::unique_ptr<Protocol> {
|
-> std::unique_ptr<Protocol> {
|
||||||
if (secret.size() == 17
|
if ((secret.size() >= 21 && secret[0] == bytes::type(0xEE))
|
||||||
&& (static_cast<uchar>(secret[0]) == 0xDD
|
|| (secret.size() == 17 && secret[0] == bytes::type(0xDD))) {
|
||||||
|| static_cast<uchar>(secret[0]) == 0xEE)) {
|
|
||||||
return std::make_unique<VersionD>(
|
return std::make_unique<VersionD>(
|
||||||
bytes::make_vector(bytes::make_span(secret).subspan(1)));
|
bytes::make_vector(secret.subspan(1, 16)));
|
||||||
} else if (secret.size() == 16) {
|
} else if (secret.size() == 16) {
|
||||||
return std::make_unique<Version1>(std::move(secret));
|
return std::make_unique<Version1>(bytes::make_vector(secret));
|
||||||
} else if (secret.empty()) {
|
} else if (secret.empty()) {
|
||||||
return std::make_unique<Version0>();
|
return std::make_unique<Version0>();
|
||||||
}
|
}
|
||||||
|
@ -522,10 +521,13 @@ void TcpConnection::connectToServer(
|
||||||
Expects(_protocol == nullptr);
|
Expects(_protocol == nullptr);
|
||||||
Expects(_protocolDcId == 0);
|
Expects(_protocolDcId == 0);
|
||||||
|
|
||||||
|
const auto secret = (_proxy.type == ProxyData::Type::Mtproto)
|
||||||
|
? _proxy.secretFromMtprotoPassword()
|
||||||
|
: protocolSecret;
|
||||||
if (_proxy.type == ProxyData::Type::Mtproto) {
|
if (_proxy.type == ProxyData::Type::Mtproto) {
|
||||||
_address = _proxy.host;
|
_address = _proxy.host;
|
||||||
_port = _proxy.port;
|
_port = _proxy.port;
|
||||||
_protocol = Protocol::Create(_proxy.secretFromMtprotoPassword());
|
_protocol = Protocol::Create(secret);
|
||||||
|
|
||||||
DEBUG_LOG(("TCP Info: "
|
DEBUG_LOG(("TCP Info: "
|
||||||
"dc:%1 - Connecting to proxy '%2'"
|
"dc:%1 - Connecting to proxy '%2'"
|
||||||
|
@ -534,14 +536,17 @@ void TcpConnection::connectToServer(
|
||||||
} else {
|
} else {
|
||||||
_address = address;
|
_address = address;
|
||||||
_port = port;
|
_port = port;
|
||||||
_protocol = Protocol::Create(base::duplicate(protocolSecret));
|
_protocol = Protocol::Create(secret);
|
||||||
|
|
||||||
DEBUG_LOG(("TCP Info: "
|
DEBUG_LOG(("TCP Info: "
|
||||||
"dc:%1 - Connecting to '%2'"
|
"dc:%1 - Connecting to '%2'"
|
||||||
).arg(protocolDcId
|
).arg(protocolDcId
|
||||||
).arg(_address + ':' + QString::number(_port)));
|
).arg(_address + ':' + QString::number(_port)));
|
||||||
}
|
}
|
||||||
_socket = AbstractSocket::Create(thread(), protocolSecret, _proxy);
|
_socket = AbstractSocket::Create(
|
||||||
|
thread(),
|
||||||
|
secret,
|
||||||
|
ToNetworkProxy(_proxy));
|
||||||
_protocolDcId = protocolDcId;
|
_protocolDcId = protocolDcId;
|
||||||
|
|
||||||
_socket->connected(
|
_socket->connected(
|
||||||
|
|
|
@ -257,7 +257,7 @@ bool DcOptions::ApplyOneOption(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i->second.push_back(Endpoint(dcId, flags, ip, port, secret));
|
i->second.emplace_back(dcId, flags, ip, port, secret);
|
||||||
} else {
|
} else {
|
||||||
data.emplace(dcId, std::vector<Endpoint>(
|
data.emplace(dcId, std::vector<Endpoint>(
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -16,12 +16,8 @@ namespace internal {
|
||||||
std::unique_ptr<AbstractSocket> AbstractSocket::Create(
|
std::unique_ptr<AbstractSocket> AbstractSocket::Create(
|
||||||
not_null<QThread*> thread,
|
not_null<QThread*> thread,
|
||||||
const bytes::vector &secret,
|
const bytes::vector &secret,
|
||||||
const ProxyData &proxy) {
|
const QNetworkProxy &proxy) {
|
||||||
const auto proxySecret = (proxy.type == ProxyData::Type::Mtproto)
|
if (secret.size() >= 21 && secret[0] == bytes::type(0xEE)) {
|
||||||
? proxy.secretFromMtprotoPassword()
|
|
||||||
: bytes::vector();
|
|
||||||
const auto &usingSecret = proxySecret.empty() ? secret : proxySecret;
|
|
||||||
if (!usingSecret.empty() && usingSecret[0] == bytes::type(0xEE)) {
|
|
||||||
return std::make_unique<TlsSocket>(thread, secret, proxy);
|
return std::make_unique<TlsSocket>(thread, secret, proxy);
|
||||||
} else {
|
} else {
|
||||||
return std::make_unique<TcpSocket>(thread, proxy);
|
return std::make_unique<TcpSocket>(thread, proxy);
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
static std::unique_ptr<AbstractSocket> Create(
|
static std::unique_ptr<AbstractSocket> Create(
|
||||||
not_null<QThread*> thread,
|
not_null<QThread*> thread,
|
||||||
const bytes::vector &secret,
|
const bytes::vector &secret,
|
||||||
const ProxyData &proxy);
|
const QNetworkProxy &proxy);
|
||||||
|
|
||||||
explicit AbstractSocket(not_null<QThread*> thread) {
|
explicit AbstractSocket(not_null<QThread*> thread) {
|
||||||
moveToThread(thread);
|
moveToThread(thread);
|
||||||
|
|
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
TcpSocket::TcpSocket(not_null<QThread*> thread, const ProxyData &proxy)
|
TcpSocket::TcpSocket(not_null<QThread*> thread, const QNetworkProxy &proxy)
|
||||||
: AbstractSocket(thread) {
|
: AbstractSocket(thread) {
|
||||||
_socket.moveToThread(thread);
|
_socket.moveToThread(thread);
|
||||||
_socket.setProxy(ToNetworkProxy(proxy));
|
_socket.setProxy(proxy);
|
||||||
const auto wrap = [&](auto handler) {
|
const auto wrap = [&](auto handler) {
|
||||||
return [=](auto &&...args) {
|
return [=](auto &&...args) {
|
||||||
InvokeQueued(this, [=] { handler(args...); });
|
InvokeQueued(this, [=] { handler(args...); });
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace internal {
|
||||||
|
|
||||||
class TcpSocket final : public AbstractSocket {
|
class TcpSocket final : public AbstractSocket {
|
||||||
public:
|
public:
|
||||||
TcpSocket(not_null<QThread*> thread, const ProxyData &proxy);
|
TcpSocket(not_null<QThread*> thread, const QNetworkProxy &proxy);
|
||||||
|
|
||||||
void connectToHost(const QString &address, int port) override;
|
void connectToHost(const QString &address, int port) override;
|
||||||
bool isConnected() override;
|
bool isConnected() override;
|
||||||
|
|
|
@ -131,8 +131,8 @@ class ClientHelloGenerator {
|
||||||
public:
|
public:
|
||||||
ClientHelloGenerator(
|
ClientHelloGenerator(
|
||||||
const MTPTlsClientHello &rules,
|
const MTPTlsClientHello &rules,
|
||||||
const QByteArray &domain,
|
bytes::const_span domain,
|
||||||
const bytes::vector &key);
|
bytes::const_span key);
|
||||||
[[nodiscard]] ClientHello result();
|
[[nodiscard]] ClientHello result();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -149,8 +149,8 @@ private:
|
||||||
void writeDigest();
|
void writeDigest();
|
||||||
void writeTimestamp();
|
void writeTimestamp();
|
||||||
|
|
||||||
const QByteArray &_domain;
|
bytes::const_span _domain;
|
||||||
const bytes::vector &_key;
|
bytes::const_span _key;
|
||||||
bytes::vector _greases;
|
bytes::vector _greases;
|
||||||
std::vector<int> _scopeStack;
|
std::vector<int> _scopeStack;
|
||||||
QByteArray _result;
|
QByteArray _result;
|
||||||
|
@ -162,8 +162,8 @@ private:
|
||||||
|
|
||||||
ClientHelloGenerator::ClientHelloGenerator(
|
ClientHelloGenerator::ClientHelloGenerator(
|
||||||
const MTPTlsClientHello &rules,
|
const MTPTlsClientHello &rules,
|
||||||
const QByteArray &domain,
|
bytes::const_span domain,
|
||||||
const bytes::vector &key)
|
bytes::const_span key)
|
||||||
: _domain(domain)
|
: _domain(domain)
|
||||||
, _key(key)
|
, _key(key)
|
||||||
, _greases(PrepareGreases()) {
|
, _greases(PrepareGreases()) {
|
||||||
|
@ -256,7 +256,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockDomain &data) {
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bytes::copy(storage, bytes::make_span(_domain));
|
bytes::copy(storage, _domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockScope &data) {
|
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockScope &data) {
|
||||||
|
@ -309,22 +309,11 @@ void ClientHelloGenerator::writeTimestamp() {
|
||||||
|
|
||||||
[[nodiscard]] ClientHello PrepareClientHello(
|
[[nodiscard]] ClientHello PrepareClientHello(
|
||||||
const MTPTlsClientHello &rules,
|
const MTPTlsClientHello &rules,
|
||||||
const QByteArray &domain,
|
bytes::const_span domain,
|
||||||
const bytes::vector &key) {
|
bytes::const_span key) {
|
||||||
return ClientHelloGenerator(rules, domain, key).result();
|
return ClientHelloGenerator(rules, domain, key).result();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bytes::vector ExtractKey(
|
|
||||||
const bytes::vector &secret,
|
|
||||||
const ProxyData &proxy) {
|
|
||||||
const auto proxySecret = proxy.secretFromMtprotoPassword();
|
|
||||||
const auto &useSecret = proxySecret.empty() ? secret : proxySecret;
|
|
||||||
if (useSecret.size() != 17 || useSecret[0] != bytes::type(0xEE)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return bytes::make_vector(bytes::make_span(useSecret).subspan(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) {
|
[[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) {
|
||||||
if (data.size() < check.size()) {
|
if (data.size() < check.size()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -345,11 +334,13 @@ void ClientHelloGenerator::writeTimestamp() {
|
||||||
TlsSocket::TlsSocket(
|
TlsSocket::TlsSocket(
|
||||||
not_null<QThread*> thread,
|
not_null<QThread*> thread,
|
||||||
const bytes::vector &secret,
|
const bytes::vector &secret,
|
||||||
const ProxyData &proxy)
|
const QNetworkProxy &proxy)
|
||||||
: AbstractSocket(thread)
|
: AbstractSocket(thread)
|
||||||
, _key(ExtractKey(secret, proxy)) {
|
, _secret(secret) {
|
||||||
|
Expects(_secret.size() >= 21 && _secret[0] == bytes::type(0xEE));
|
||||||
|
|
||||||
_socket.moveToThread(thread);
|
_socket.moveToThread(thread);
|
||||||
_socket.setProxy(ToNetworkProxy(proxy));
|
_socket.setProxy(proxy);
|
||||||
const auto wrap = [&](auto handler) {
|
const auto wrap = [&](auto handler) {
|
||||||
return [=](auto &&...args) {
|
return [=](auto &&...args) {
|
||||||
InvokeQueued(this, [=] { handler(args...); });
|
InvokeQueued(this, [=] { handler(args...); });
|
||||||
|
@ -377,6 +368,14 @@ TlsSocket::TlsSocket(
|
||||||
wrap([=](Error e) { handleError(e); }));
|
wrap([=](Error e) { handleError(e); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes::const_span TlsSocket::domainFromSecret() const {
|
||||||
|
return bytes::make_span(_secret).subspan(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes::const_span TlsSocket::keyFromSecret() const {
|
||||||
|
return bytes::make_span(_secret).subspan(1, 16);
|
||||||
|
}
|
||||||
|
|
||||||
void TlsSocket::plainConnected() {
|
void TlsSocket::plainConnected() {
|
||||||
if (_state != State::Connecting) {
|
if (_state != State::Connecting) {
|
||||||
return;
|
return;
|
||||||
|
@ -385,8 +384,8 @@ void TlsSocket::plainConnected() {
|
||||||
static const auto kClientHelloRules = PrepareClientHelloRules();
|
static const auto kClientHelloRules = PrepareClientHelloRules();
|
||||||
const auto hello = PrepareClientHello(
|
const auto hello = PrepareClientHello(
|
||||||
kClientHelloRules,
|
kClientHelloRules,
|
||||||
"www.google.com",
|
domainFromSecret(),
|
||||||
_key);
|
keyFromSecret());
|
||||||
if (hello.data.isEmpty()) {
|
if (hello.data.isEmpty()) {
|
||||||
LOG(("TLS Error: Could not generate Client Hello!"));
|
LOG(("TLS Error: Could not generate Client Hello!"));
|
||||||
_state = State::Error;
|
_state = State::Error;
|
||||||
|
@ -492,7 +491,7 @@ void TlsSocket::checkHelloDigest() {
|
||||||
kHelloDigestLength);
|
kHelloDigestLength);
|
||||||
const auto digestCopy = bytes::make_vector(digest);
|
const auto digestCopy = bytes::make_vector(digest);
|
||||||
bytes::set_with_const(digest, bytes::type(0));
|
bytes::set_with_const(digest, bytes::type(0));
|
||||||
const auto check = openssl::HmacSha256(_key, fulldata);
|
const auto check = openssl::HmacSha256(keyFromSecret(), fulldata);
|
||||||
if (bytes::compare(digestCopy, check) != 0) {
|
if (bytes::compare(digestCopy, check) != 0) {
|
||||||
LOG(("TLS Error: Bad Server Hello digest."));
|
LOG(("TLS Error: Bad Server Hello digest."));
|
||||||
handleError();
|
handleError();
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
TlsSocket(
|
TlsSocket(
|
||||||
not_null<QThread*> thread,
|
not_null<QThread*> thread,
|
||||||
const bytes::vector &secret,
|
const bytes::vector &secret,
|
||||||
const ProxyData &proxy);
|
const QNetworkProxy &proxy);
|
||||||
|
|
||||||
void connectToHost(const QString &address, int port) override;
|
void connectToHost(const QString &address, int port) override;
|
||||||
bool isConnected() override;
|
bool isConnected() override;
|
||||||
|
@ -36,6 +36,9 @@ private:
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] bytes::const_span domainFromSecret() const;
|
||||||
|
[[nodiscard]] bytes::const_span keyFromSecret() const;
|
||||||
|
|
||||||
void plainConnected();
|
void plainConnected();
|
||||||
void plainDisconnected();
|
void plainDisconnected();
|
||||||
void plainReadyRead();
|
void plainReadyRead();
|
||||||
|
@ -49,8 +52,8 @@ private:
|
||||||
[[nodiscard]] bool checkNextPacket();
|
[[nodiscard]] bool checkNextPacket();
|
||||||
void shiftIncomingBy(int amount);
|
void shiftIncomingBy(int amount);
|
||||||
|
|
||||||
|
const bytes::vector _secret;
|
||||||
QTcpSocket _socket;
|
QTcpSocket _socket;
|
||||||
bytes::vector _key;
|
|
||||||
State _state = State::NotConnected;
|
State _state = State::NotConnected;
|
||||||
QByteArray _incoming;
|
QByteArray _incoming;
|
||||||
int _incomingGoodDataOffset = 0;
|
int _incomingGoodDataOffset = 0;
|
||||||
|
|
|
@ -1332,9 +1332,13 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
|
||||||
|
|
||||||
const auto unchecked = static_cast<ProxyData::Settings>(settings);
|
const auto unchecked = static_cast<ProxyData::Settings>(settings);
|
||||||
switch (unchecked) {
|
switch (unchecked) {
|
||||||
|
case ProxyData::Settings::Enabled:
|
||||||
|
Global::SetProxySettings(Global::SelectedProxy()
|
||||||
|
? ProxyData::Settings::Enabled
|
||||||
|
: ProxyData::Settings::System);
|
||||||
|
break;
|
||||||
case ProxyData::Settings::Disabled:
|
case ProxyData::Settings::Disabled:
|
||||||
case ProxyData::Settings::System:
|
case ProxyData::Settings::System:
|
||||||
case ProxyData::Settings::Enabled:
|
|
||||||
Global::SetProxySettings(unchecked);
|
Global::SetProxySettings(unchecked);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue