mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 08:03:45 +08:00
Support new secret types for path-ed WEB proxies.
This commit is contained in:
@@ -134,7 +134,9 @@ using ProxyData = MTP::ProxyData;
|
||||
? "&pass=" + qthelp::url_encode(proxy.password) : "")
|
||||
+ (((proxy.type == Type::Mtproto || proxy.type == Type::Web)
|
||||
&& !proxy.password.isEmpty())
|
||||
? "&secret=" + proxy.password : "");
|
||||
? "&secret=" + ((proxy.type == Type::Web)
|
||||
? MTP::EncodeWebProxyLinkSecret(proxy)
|
||||
: proxy.password) : "");
|
||||
}
|
||||
|
||||
[[nodiscard]] QString ProxyDataToLocalLink(const ProxyData &proxy) {
|
||||
@@ -347,6 +349,11 @@ void ShareProxy(
|
||||
} else if (type == ProxyData::Type::Mtproto || web) {
|
||||
proxy.password = fields.value(u"secret"_q);
|
||||
proxy.password.replace('+', '-').replace('/', '_');
|
||||
if (web) {
|
||||
proxy.password = MTP::DecodeWebProxyLinkSecret(
|
||||
proxy.password,
|
||||
!proxy.webBasePath().isEmpty());
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
};
|
||||
|
||||
@@ -193,6 +193,15 @@ namespace {
|
||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals));
|
||||
}
|
||||
|
||||
// A base path needs a client that understands one, so a link that carries a
|
||||
// path encodes its secret as base64url of this marker byte followed by the real
|
||||
// secret. A client without path support decodes 17 bytes whose first byte is not
|
||||
// the 0xDD of a padded secret, so it reports the link as an unsupported proxy
|
||||
// type and asks the user to update, instead of accepting a pathless entry.
|
||||
// 0xDD is the one byte that must never be used here: the older parser reads a
|
||||
// 17-byte secret starting with it as an ordinary valid one.
|
||||
constexpr auto kWebProxyLinkSecretMarker = uchar(0x70);
|
||||
|
||||
// The address field accepts a pasted URL, so the scheme is removed before the
|
||||
// address is parsed and only the canonical form is stored. `http://` is left in
|
||||
// place, and therefore rejected: HTTPS and port 443 are fixed for a WEB proxy.
|
||||
@@ -333,6 +342,12 @@ QString WebProxyBridgeCapability(const ProxyData &proxy) {
|
||||
== u"Proxy.Example.COM/App/"_q);
|
||||
Assert(StripWebProxyScheme(u"http://proxy.example.com"_q)
|
||||
== u"http://proxy.example.com"_q);
|
||||
const auto plainSecret = u"8561944064fc730cbfa4473562d8ec59"_q;
|
||||
const auto markedSecret = u"cIVhlEBk_HMMv6RHNWLY7Fk"_q;
|
||||
Assert(DecodeWebProxyLinkSecret(markedSecret, true) == plainSecret);
|
||||
Assert(DecodeWebProxyLinkSecret(markedSecret, false) == plainSecret);
|
||||
Assert(DecodeWebProxyLinkSecret(plainSecret, false) == plainSecret);
|
||||
Assert(DecodeWebProxyLinkSecret(plainSecret, true).isEmpty());
|
||||
Assert(NormalizeWebProxyBasePath(u"per%20cent"_q).isEmpty());
|
||||
Assert(NormalizeWebProxyBasePath(u"dot.ted"_q).isEmpty());
|
||||
Assert(NormalizeWebProxyBasePath(u".."_q).isEmpty());
|
||||
@@ -377,6 +392,43 @@ QString WebProxyBridgeCapability(const ProxyData &proxy) {
|
||||
key);
|
||||
}
|
||||
|
||||
QString EncodeWebProxyLinkSecret(const ProxyData &proxy) {
|
||||
Expects(proxy.type == ProxyData::Type::Web);
|
||||
|
||||
const auto secret = proxy.secretFromMtprotoPassword();
|
||||
if (proxy.webBasePath().isEmpty() || secret.empty()) {
|
||||
return proxy.password;
|
||||
}
|
||||
auto marked = QByteArray(1, char(kWebProxyLinkSecretMarker));
|
||||
marked.append(
|
||||
reinterpret_cast<const char*>(secret.data()),
|
||||
int(secret.size()));
|
||||
return QString::fromLatin1(marked.toBase64(
|
||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals));
|
||||
}
|
||||
|
||||
QString DecodeWebProxyLinkSecret(const QString &value, bool hasBasePath) {
|
||||
const auto decoded = QByteArray::fromBase64(
|
||||
value.toLatin1(),
|
||||
QByteArray::Base64UrlEncoding
|
||||
| QByteArray::AbortOnBase64DecodingErrors);
|
||||
|
||||
// A canonical secret is 16 bytes, 17 starting with 0xDD, or 21+ starting
|
||||
// with 0xEE, so a longer value behind this marker is never ambiguous.
|
||||
const auto marked = (decoded.size() >= 17)
|
||||
&& (uchar(decoded[0]) == kWebProxyLinkSecretMarker);
|
||||
if (marked) {
|
||||
return QString::fromLatin1(decoded.mid(1).toHex());
|
||||
}
|
||||
|
||||
// An unmarked secret on a link that carries a base path is rejected rather
|
||||
// than accepted: that is exactly the link a client without path support
|
||||
// would take for a pathless proxy on an empty host, so the marked form is
|
||||
// required once a path is present. A root link keeps the plain secret and
|
||||
// still works in those clients.
|
||||
return hasBasePath ? QString() : value;
|
||||
}
|
||||
|
||||
QString WebProxyBridgePath(const ProxyData &proxy) {
|
||||
Expects(proxy.type == ProxyData::Type::Web);
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@ struct ProxyData {
|
||||
|
||||
[[nodiscard]] QString NormalizeWebProxyHost(const QString &value);
|
||||
[[nodiscard]] QString WebProxyBridgeCapability(const ProxyData &proxy);
|
||||
[[nodiscard]] QString EncodeWebProxyLinkSecret(const ProxyData &proxy);
|
||||
[[nodiscard]] QString DecodeWebProxyLinkSecret(
|
||||
const QString &value,
|
||||
bool hasBasePath);
|
||||
[[nodiscard]] QString WebProxyBridgePath(const ProxyData &proxy);
|
||||
[[nodiscard]] QString WebProxyBridgeUrl(const ProxyData &proxy);
|
||||
[[nodiscard]] ProxyData ToDirectIpProxy(
|
||||
|
||||
@@ -552,10 +552,14 @@ tg://webproxy?server=<address>&secret=<secret>
|
||||
|
||||
`<address>` is the percent-encoded `host` or `host/path`, so a base path travels in
|
||||
the same parameter as `proxy.example.com%2Fdobry-cola-super-app`. There is no
|
||||
separate path parameter. Following either link shows the address and secret with
|
||||
one connect action. It does not check status or enable the proxy until that action
|
||||
is invoked. Saved WEB entries can be shared as a public link or a direct-scheme QR
|
||||
link.
|
||||
separate path parameter. A link that carries a path also encodes its secret as
|
||||
unpadded base64url of `0x70` plus the real secret, so a client without path
|
||||
support reports an unsupported proxy type and asks the user to update, instead of
|
||||
normalizing the address to an empty host and offering to connect to it. A
|
||||
root link keeps the plain secret and still works in those clients. Following
|
||||
either link shows the address and secret with one connect action. It does not
|
||||
check status or enable the proxy until that action is invoked. Saved WEB entries
|
||||
can be shared as a public link or a direct-scheme QR link.
|
||||
|
||||
Application proxy changes configure/deconfigure the web transport before MTP
|
||||
sessions restart. WEB follows the MTProxy path in `Session`, `SessionPrivate`, and
|
||||
|
||||
Reference in New Issue
Block a user