mirror of
https://github.com/seriyps/mtproto_proxy.git
synced 2026-08-04 15:19:47 +00:00
mtp_config: parse default DC id from Telegram config; drop get_netloc_safe; ETS protected
- parse_config/1 now returns {DefaultDc, Downstreams} using a single
lists:foldl pass; the 'default X;' line sets the fallback DC id
- update_ids/3 stores {default_dc, DcId} in ETS alongside dc_ids
- get_default_dc/0 reads default_dc from ETS (safe from any process)
- get_downstream_safe/2 fallback uses get_default_dc() instead of
random_choice; errors immediately if default == requested (avoid loop)
- get_netloc_safe/1 removed: dead code since 2018, never called
- ETS table changed from public to protected (only mtp_config writes)
- doc/handler-downstream-flow.md: new sequence diagram + update note
about pool resolution fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
8ce8f2196d
commit
4e0db66fc1
2 changed files with 89 additions and 31 deletions
45
doc/handler-downstream-flow.md
Normal file
45
doc/handler-downstream-flow.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Handler ↔ downstream lookup and handshake
|
||||
|
||||
Shows how `mtp_handler` locates an `mtp_down_conn` for a new client connection
|
||||
and the steady-state data flow that follows.
|
||||
|
||||
**Key actors:**
|
||||
- `mtp_handler` — one process per Telegram client TCP connection
|
||||
- `mtp_dc_pool` — manages a pool of downstream connections for one DC
|
||||
- `mtp_down_conn` — multiplexed TCP connection to a Telegram DC
|
||||
- `Telegram DC` — the upstream Telegram data-centre server
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Telegram client
|
||||
participant Handler as mtp_handler
|
||||
participant Pool as mtp_dc_pool
|
||||
participant Down as mtp_down_conn
|
||||
participant TG as Telegram DC
|
||||
|
||||
Client->>Handler: TCP connect + Hello bytes
|
||||
|
||||
Note over Handler: decode protocol headers<br/>(fake-TLS / obfuscated / secure)<br/>stage: hello → tunnel
|
||||
|
||||
Note over Handler: resolve pool: whereis(dc_to_pool_name(DcId))<br/>(registered name lookup; falls back to default DC from mtp_config if not found)
|
||||
Handler->>Pool: mtp_dc_pool:get(Pool, self(), Opts) [sync]
|
||||
Pool-->>Down: upstream_new(Handler, Opts) [cast]
|
||||
Pool->>Handler: Downstream pid
|
||||
|
||||
Note over Handler: down = Downstream<br/>stage = tunnel
|
||||
|
||||
loop steady-state data exchange
|
||||
Client->>Handler: TCP data
|
||||
Handler->>Down: mtp_down_conn:send(Down, Data) [sync]
|
||||
Down->>TG: TCP data (RPC-framed)
|
||||
TG->>Down: TCP data
|
||||
Down->>Handler: ok
|
||||
Down-->>Handler: {proxy_ans, Down, Data} [cast]
|
||||
Handler->>Client: TCP data
|
||||
Handler-->>Down: mtp_down_conn:ack(Down, Count, Size) [cast]
|
||||
end
|
||||
|
||||
Client->>Handler: TCP close
|
||||
Handler-->>Pool: mtp_dc_pool:return(Pool, self()) [cast]
|
||||
Pool-->>Down: upstream_closed(Down, Handler) [cast]
|
||||
```
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
-export([get_downstream_safe/2,
|
||||
get_downstream_pool/1,
|
||||
get_netloc/1,
|
||||
get_netloc_safe/1,
|
||||
get_secret/0,
|
||||
get_default_dc/0,
|
||||
status/0,
|
||||
update/0]).
|
||||
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
-define(TAB, ?MODULE).
|
||||
-define(IPS_KEY(DcId), {id, DcId}).
|
||||
-define(IDS_KEY, dc_ids).
|
||||
-define(DEFAULT_DC_KEY, default_dc).
|
||||
-define(SECRET_URL, "https://core.telegram.org/getProxySecret").
|
||||
-define(CONFIG_URL, "https://core.telegram.org/getProxyConfig").
|
||||
|
||||
|
|
@ -65,9 +66,11 @@ get_downstream_safe(DcId, Opts) ->
|
|||
error({pool_empty, DcId, Pool})
|
||||
end;
|
||||
not_found ->
|
||||
[{?IDS_KEY, L}] = ets:lookup(?TAB, ?IDS_KEY),
|
||||
NewDcId = random_choice(L),
|
||||
get_downstream_safe(NewDcId, Opts)
|
||||
case get_default_dc() of
|
||||
undefined -> error({no_pool, DcId});
|
||||
DcId -> error({no_pool, DcId}); % default == requested, avoid loop
|
||||
NewDcId -> get_downstream_safe(NewDcId, Opts)
|
||||
end
|
||||
end.
|
||||
|
||||
get_downstream_pool(DcId) ->
|
||||
|
|
@ -78,17 +81,6 @@ get_downstream_pool(DcId) ->
|
|||
not_found
|
||||
end.
|
||||
|
||||
-spec get_netloc_safe(dc_id()) -> {dc_id(), netloc()}.
|
||||
get_netloc_safe(DcId) ->
|
||||
case get_netloc(DcId) of
|
||||
{ok, Addr} -> {DcId, Addr};
|
||||
not_found ->
|
||||
[{?IDS_KEY, L}] = ets:lookup(?TAB, ?IDS_KEY),
|
||||
NewDcId = random_choice(L),
|
||||
%% Get random DC; it might return 0 and recurse aggain
|
||||
get_netloc_safe(NewDcId)
|
||||
end.
|
||||
|
||||
get_netloc(DcId) ->
|
||||
Key = ?IPS_KEY(DcId),
|
||||
case ets:lookup(?TAB, Key) of
|
||||
|
|
@ -107,6 +99,13 @@ get_secret() ->
|
|||
[{_, Key}] = ets:lookup(?TAB, key),
|
||||
Key.
|
||||
|
||||
-spec get_default_dc() -> dc_id() | undefined.
|
||||
get_default_dc() ->
|
||||
case ets:lookup(?TAB, ?DEFAULT_DC_KEY) of
|
||||
[{?DEFAULT_DC_KEY, DcId}] -> DcId;
|
||||
[] -> undefined
|
||||
end.
|
||||
|
||||
-spec status() -> [mtp_dc_pool:status()].
|
||||
status() ->
|
||||
[{?IDS_KEY, L}] = ets:lookup(?TAB, ?IDS_KEY),
|
||||
|
|
@ -130,7 +129,7 @@ init([]) ->
|
|||
#{timeout => {env, ?APP, conf_refresh_interval, 3600},
|
||||
unit => second}),
|
||||
Tab = ets:new(?TAB, [set,
|
||||
public,
|
||||
protected,
|
||||
named_table,
|
||||
{read_concurrency, true}]),
|
||||
State = #state{tab = Tab,
|
||||
|
|
@ -189,17 +188,23 @@ update_key(Tab) ->
|
|||
update_config(Tab) ->
|
||||
Url = application:get_env(mtproto_proxy, proxy_config_url, ?CONFIG_URL),
|
||||
{ok, Body} = http_get(Url),
|
||||
Downstreams = parse_config(Body),
|
||||
{DefaultDc, Downstreams} = parse_config(Body),
|
||||
update_downstreams(Downstreams, Tab),
|
||||
update_ids(Downstreams, Tab).
|
||||
update_ids(Downstreams, DefaultDc, Tab).
|
||||
|
||||
parse_config(Body) ->
|
||||
Lines = string:lexemes(Body, "\n"),
|
||||
ProxyLines = lists:filter(
|
||||
fun("proxy_for " ++ _) -> true;
|
||||
(_) -> false
|
||||
end, Lines),
|
||||
[parse_downstream(Line) || Line <- ProxyLines].
|
||||
{DefaultDc, Downstreams} =
|
||||
lists:foldl(
|
||||
fun("default " ++ _ = Line, {_, Ds}) ->
|
||||
["default", DcIdStr] = string:lexemes(Line, " ;"),
|
||||
{list_to_integer(DcIdStr), Ds};
|
||||
("proxy_for " ++ _ = Line, {Def, Ds}) ->
|
||||
{Def, [parse_downstream(Line) | Ds]};
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, {undefined, []}, Lines),
|
||||
{DefaultDc, lists:reverse(Downstreams)}.
|
||||
|
||||
parse_downstream(Line) ->
|
||||
["proxy_for",
|
||||
|
|
@ -231,9 +236,10 @@ update_downstreams(Downstreams, Tab) ->
|
|||
end,
|
||||
maps:keys(ByDc)).
|
||||
|
||||
update_ids(Downstreams, Tab) ->
|
||||
update_ids(Downstreams, DefaultDc, Tab) ->
|
||||
Ids = lists:usort([DcId || {DcId, _, _} <- Downstreams]),
|
||||
true = ets:insert(Tab, {?IDS_KEY, Ids}).
|
||||
true = ets:insert(Tab, {?IDS_KEY, Ids}),
|
||||
true = ets:insert(Tab, {?DEFAULT_DC_KEY, DefaultDc}).
|
||||
|
||||
update_ip() ->
|
||||
case application:get_env(?APP, ip_lookup_services) of
|
||||
|
|
@ -283,15 +289,22 @@ random_choice(L) ->
|
|||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
parse_test() ->
|
||||
Config = ("# force_probability 1 10
|
||||
proxy_for 1 149.154.175.50:8888;
|
||||
proxy_for -1 149.154.175.50:8888;
|
||||
proxy_for 2 149.154.162.39:80;
|
||||
proxy_for 2 149.154.162.33:80;"),
|
||||
Config = ("# force_probability 1 10\n"
|
||||
"default 2;\n"
|
||||
"proxy_for 1 149.154.175.50:8888;\n"
|
||||
"proxy_for -1 149.154.175.50:8888;\n"
|
||||
"proxy_for 2 149.154.162.39:80;\n"
|
||||
"proxy_for 2 149.154.162.33:80;"),
|
||||
Expect = [{1, {149, 154, 175, 50}, 8888},
|
||||
{-1, {149, 154, 175, 50}, 8888},
|
||||
{2, {149, 154, 162, 39}, 80},
|
||||
{2, {149, 154, 162, 33},80}],
|
||||
?assertEqual(Expect, parse_config(Config)).
|
||||
?assertEqual({2, Expect}, parse_config(Config)).
|
||||
|
||||
parse_no_default_test() ->
|
||||
Config = ("proxy_for 1 149.154.175.50:8888;\n"
|
||||
"proxy_for 2 149.154.162.39:80;"),
|
||||
{DefaultDc, _} = parse_config(Config),
|
||||
?assertEqual(undefined, DefaultDc).
|
||||
|
||||
-endif.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue