Skip to content

Commit e52edfd

Browse files
FrayxRulezlevlam
authored andcommitted
Support web proxy links
1 parent 7d9313f commit e52edfd

5 files changed

Lines changed: 122 additions & 1 deletion

File tree

‎td/telegram/LinkManager.cpp‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,10 @@ class LinkManager::InternalLinkProxy final : public InternalLink {
12261226
auto type_mtproto = static_cast<const td_api::proxyTypeMtproto *>(type);
12271227
return td_api::make_object<td_api::proxyTypeMtproto>(type_mtproto->secret_);
12281228
}
1229+
case td_api::proxyTypeWeb::ID: {
1230+
auto type_web = static_cast<const td_api::proxyTypeWeb *>(type);
1231+
return td_api::make_object<td_api::proxyTypeWeb>(type_web->path_, type_web->secret_);
1232+
}
12291233
default:
12301234
UNREACHABLE();
12311235
return nullptr;
@@ -2265,7 +2269,7 @@ LinkManager::LinkInfo LinkManager::get_link_info(Slice link) {
22652269
static const FlatHashSet<Slice, SliceHash> disallowed_subdomains(
22662270
{"addemoji", "addlist", "addstickers", "addstyle", "addtheme", "auction", "auth", "boost", "call",
22672271
"confirmphone", "contact", "giftcode", "invoice", "joinchat", "login", "m", "nft", "proxy",
2268-
"setlanguage", "share", "socks", "web", "a", "k", "z"});
2272+
"setlanguage", "share", "socks", "web", "webproxy", "a", "k", "z"});
22692273
if (is_valid_username(subdomain) && disallowed_subdomains.count(subdomain) == 0) {
22702274
result.type_ = LinkType::TMe;
22712275
result.query_ = PSTRING() << '/' << subdomain << http_url.query_;
@@ -2744,6 +2748,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_tg_link_query(Slice que
27442748
} else {
27452749
return td::make_unique<InternalLinkProxy>(string(), 0, nullptr);
27462750
}
2751+
} else if (path.size() == 1 && path[0] == "webproxy") {
2752+
// webproxy?server=<host>[%2F<path>]&secret=<secret>
2753+
return get_internal_link_web_proxy(url_query.get_arg("server"), url_query.get_arg("secret"));
27472754
} else if (path.size() == 1 && path[0] == "privatepost") {
27482755
// privatepost?channel=123456789&post=12345&single&thread=<thread_id>&comment=<message_id>&t=<media_timestamp>
27492756
if (has_arg("channel") && has_arg("post")) {
@@ -3011,6 +3018,9 @@ unique_ptr<LinkManager::InternalLink> LinkManager::parse_t_me_link_query(Slice q
30113018
} else {
30123019
return td::make_unique<InternalLinkProxy>(string(), 0, nullptr);
30133020
}
3021+
} else if (path[0] == "webproxy") {
3022+
// /webproxy?server=<host>[%2F<path>]&secret=<secret>
3023+
return get_internal_link_web_proxy(url_query.get_arg("server"), url_query.get_arg("secret"));
30143024
} else if (path[0] == "bg") {
30153025
if (path.size() >= 2 && !path[1].empty()) {
30163026
// /bg/<hex_color>
@@ -3247,6 +3257,22 @@ unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_message_dra
32473257
return td::make_unique<InternalLinkMessageDraft>(std::move(full_text), contains_url);
32483258
}
32493259

3260+
unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_web_proxy(Slice address, Slice secret) {
3261+
// a port parameter is ignored, because web proxies always use 443
3262+
auto r_address = Proxy::parse_web_proxy_address(address);
3263+
if (r_address.is_ok()) {
3264+
auto host = std::move(r_address.ok_ref().first);
3265+
auto path = std::move(r_address.ok_ref().second);
3266+
auto r_secret = Proxy::get_web_proxy_link_secret(secret, !path.empty());
3267+
if (r_secret.is_ok()) {
3268+
return td::make_unique<InternalLinkProxy>(
3269+
std::move(host), Proxy::WEB_PROXY_PORT,
3270+
td_api::make_object<td_api::proxyTypeWeb>(std::move(path), r_secret.ok().get_encoded_secret()));
3271+
}
3272+
}
3273+
return td::make_unique<InternalLinkProxy>(string(), 0, nullptr);
3274+
}
3275+
32503276
unique_ptr<LinkManager::InternalLink> LinkManager::get_internal_link_ton_connect(Slice query, bool check_path) {
32513277
const auto url_query = parse_url_query(query);
32523278
if (check_path && !url_query.path_.empty()) {

‎td/telegram/LinkManager.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ class LinkManager final : public Actor {
222222

223223
static unique_ptr<InternalLink> get_internal_link_ton_connect(Slice query, bool check_path);
224224

225+
static unique_ptr<InternalLink> get_internal_link_web_proxy(Slice address, Slice secret);
226+
225227
static Result<string> get_internal_link_impl(const td_api::InternalLinkType *type_ptr, bool is_internal);
226228

227229
static Result<string> check_link_impl(Slice link, bool http_only, bool https_only);

‎td/telegram/net/Proxy.cpp‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ bool Proxy::is_valid_web_proxy_path(Slice path) {
9999
return path.empty() || !is_segment_start;
100100
}
101101

102+
Result<std::pair<string, string>> Proxy::parse_web_proxy_address(Slice address) {
103+
address = trim(address);
104+
Slice scheme("https://");
105+
if (address.size() >= scheme.size() && to_lower(address.substr(0, scheme.size())) == scheme) {
106+
address.remove_prefix(scheme.size());
107+
}
108+
Slice host = address;
109+
Slice path;
110+
auto slash_pos = address.find('/');
111+
if (slash_pos != Slice::npos) {
112+
host = address.substr(0, slash_pos);
113+
path = address.substr(slash_pos + 1);
114+
}
115+
TRY_RESULT(normalized_host, normalize_web_proxy_host(host));
116+
if (!is_valid_web_proxy_path(path)) {
117+
return Status::Error(400, "Invalid web proxy path");
118+
}
119+
return std::make_pair(std::move(normalized_host), path.str());
120+
}
121+
102122
Result<mtproto::ProxySecret> Proxy::get_web_proxy_secret(Slice encoded_secret) {
103123
TRY_RESULT(secret, mtproto::ProxySecret::from_link(encoded_secret));
104124
if (secret.emulate_tls()) {
@@ -108,6 +128,22 @@ Result<mtproto::ProxySecret> Proxy::get_web_proxy_secret(Slice encoded_secret) {
108128
return std::move(secret);
109129
}
110130

131+
Result<mtproto::ProxySecret> Proxy::get_web_proxy_link_secret(Slice link_secret, bool has_path) {
132+
auto r_marked = base64url_decode(link_secret);
133+
if (r_marked.is_ok() && r_marked.ok().size() >= 17 &&
134+
static_cast<unsigned char>(r_marked.ok()[0]) == WEB_PROXY_LINK_SECRET_MARKER &&
135+
Slice(base64url_encode(r_marked.ok())) == link_secret) {
136+
auto r_secret = mtproto::ProxySecret::from_binary(Slice(r_marked.ok()).substr(1));
137+
if (r_secret.is_ok() && !r_secret.ok().emulate_tls()) {
138+
return r_secret.move_as_ok();
139+
}
140+
}
141+
if (has_path) {
142+
return Status::Error(400, "Wrong proxy secret");
143+
}
144+
return get_web_proxy_secret(link_secret);
145+
}
146+
111147
string Proxy::get_web_proxy_address() const {
112148
CHECK(type_ == Type::Web);
113149
if (path_.empty()) {

‎td/telegram/net/Proxy.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,14 @@ class Proxy {
8686
return proxy;
8787
}
8888

89+
// returns the canonical host and path of a web proxy address "host[/path]"
90+
static Result<std::pair<string, string>> parse_web_proxy_address(Slice address);
91+
8992
static Result<mtproto::ProxySecret> get_web_proxy_secret(Slice encoded_secret);
9093

94+
// a link secret of a web proxy with a path is marked, so that clients without path support reject the link
95+
static Result<mtproto::ProxySecret> get_web_proxy_link_secret(Slice link_secret, bool has_path);
96+
9197
CSlice server() const {
9298
return server_;
9399
}

‎test/link.cpp‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,11 @@ static auto proxy_socks(const td::string &server, td::int32 port, const td::stri
470470
server, port, td::td_api::make_object<td::td_api::proxyTypeSocks5>(username, password)));
471471
}
472472

473+
static auto proxy_web(const td::string &server, const td::string &path, const td::string &secret) {
474+
return td::td_api::make_object<td::td_api::internalLinkTypeProxy>(td::td_api::make_object<td::td_api::proxy>(
475+
server, 443, td::td_api::make_object<td::td_api::proxyTypeWeb>(path, secret)));
476+
}
477+
473478
static auto public_chat(const td::string &chat_username, const td::string &draft_text = td::string(),
474479
bool open_profile = false, const td::string &unpaid_messages_token = td::string()) {
475480
return td::td_api::make_object<td::td_api::internalLinkTypePublicChat>(chat_username, draft_text, open_profile,
@@ -1470,6 +1475,52 @@ TEST(Link, parse_internal_link_part3) {
14701475
parse_internal_link("tg:proxy?server=&port=80&secret=1234567890abcdef1234567890ABCDEF", unsupported_proxy());
14711476
parse_internal_link("tg:proxy?server=%FF&port=80&secret=1234567890abcdef1234567890ABCDEF", unsupported_proxy());
14721477

1478+
parse_internal_link("t.me/webproxy?server=proxy.example.com&secret=8561944064fc730cbfa4473562d8ec59",
1479+
proxy_web("proxy.example.com", "", "8561944064fc730cbfa4473562d8ec59"));
1480+
parse_internal_link("https://t.me/webproxy?server=proxy.example.com&port=80&secret=8561944064FC730CBFA4473562D8EC59",
1481+
proxy_web("proxy.example.com", "", "8561944064fc730cbfa4473562d8ec59"));
1482+
parse_internal_link("t.me/webproxy?server=HTTPS%3A%2F%2FProxy.Example.COM.&secret=dd8561944064fc730cbfa4473562d8ec59",
1483+
proxy_web("proxy.example.com", "", "dd8561944064fc730cbfa4473562d8ec59"));
1484+
parse_internal_link(
1485+
"t.me/webproxy?server=%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%80%D1%84&secret="
1486+
"8561944064fc730cbfa4473562d8ec59",
1487+
proxy_web("xn--e1afmkfd.xn--p1ai", "", "8561944064fc730cbfa4473562d8ec59"));
1488+
parse_internal_link(
1489+
"t.me/webproxy?server=%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D0%A0%D0%A4&secret="
1490+
"8561944064fc730cbfa4473562d8ec59",
1491+
proxy_web("xn--e1afmkfd.xn--p1ai", "", "8561944064fc730cbfa4473562d8ec59"));
1492+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2Fdobry-cola-super-app&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1493+
proxy_web("proxy.example.com", "dobry-cola-super-app", "8561944064fc730cbfa4473562d8ec59"));
1494+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2Fa%2Fb_c&secret=cN2FYZRAZPxzDL-kRzVi2OxZ",
1495+
proxy_web("proxy.example.com", "a/b_c", "dd8561944064fc730cbfa4473562d8ec59"));
1496+
parse_internal_link("t.me/webproxy?server=proxy.example.com&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1497+
proxy_web("proxy.example.com", "", "8561944064fc730cbfa4473562d8ec59"));
1498+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2Fapp&secret=8561944064fc730cbfa4473562d8ec59",
1499+
unsupported_proxy());
1500+
parse_internal_link("t.me/webproxy?server=proxy.example.com&secret=7tAAAAAAAAAAAAAAAAAAAAAAAAcuZ29vZ2xlLmNvbQ",
1501+
unsupported_proxy());
1502+
parse_internal_link("t.me/webproxy?server=proxy.example.com&secret=de8561944064fc730cbfa4473562d8ec59",
1503+
unsupported_proxy());
1504+
parse_internal_link("t.me/webproxy?server=1.2.3.4&secret=8561944064fc730cbfa4473562d8ec59", unsupported_proxy());
1505+
parse_internal_link("t.me/webproxy?server=proxy.0x7f&secret=8561944064fc730cbfa4473562d8ec59", unsupported_proxy());
1506+
parse_internal_link("t.me/webproxy?server=localhost&secret=8561944064fc730cbfa4473562d8ec59", unsupported_proxy());
1507+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2F-app&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1508+
unsupported_proxy());
1509+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2Fa%2F%2Fb&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1510+
unsupported_proxy());
1511+
parse_internal_link("t.me/webproxy?server=proxy.example.com%2Fa.b&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1512+
unsupported_proxy());
1513+
parse_internal_link("t.me/webproxy?server=proxy.example.com", unsupported_proxy());
1514+
parse_internal_link("t.me/webproxy", unsupported_proxy());
1515+
1516+
parse_internal_link("tg:webproxy?server=proxy.example.com&secret=8561944064fc730cbfa4473562d8ec59",
1517+
proxy_web("proxy.example.com", "", "8561944064fc730cbfa4473562d8ec59"));
1518+
parse_internal_link("tg://webproxy?server=proxy.example.com%2Fdobry-cola-super-app&secret=cIVhlEBk_HMMv6RHNWLY7Fk",
1519+
proxy_web("proxy.example.com", "dobry-cola-super-app", "8561944064fc730cbfa4473562d8ec59"));
1520+
parse_internal_link("tg:webproxy?server=proxy.example.com%2Fapp&secret=8561944064fc730cbfa4473562d8ec59",
1521+
unsupported_proxy());
1522+
parse_internal_link("tg:webproxy", unsupported_proxy());
1523+
14731524
parse_internal_link(
14741525
"t.me/"
14751526
"sendgrams?startapp=tonconnect-v__2-id__c745acd825f83bf7ef88d671ea69c90fd1c7e71f78b1079ec8ad3c231fed7a65-trace--"

0 commit comments

Comments
 (0)