network-infra-utility 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +70 -0
  5. data/Rakefile +1 -1
  6. data/bin/dns-query +834 -0
  7. data/bin/geo-doc +135 -0
  8. data/bin/geo-get +1 -1
  9. data/document/ASNum/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +242 -0
  10. data/document/DNSQuery/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +248 -0
  11. data/document/Geo/345/221/275/344/273/244/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +441 -0
  12. data/document/IP/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +297 -0
  13. data/document/MAC/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +296 -0
  14. data/document/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/344/275/277/347/224/250/346/226/271/346/263/225.md +764 -0
  15. data/network-infra-utility.gemspec +4 -2
  16. data/network.rb +3 -1
  17. data/service/geodb/geodb.rb +278 -1
  18. data/service/ssh/README.md +942 -0
  19. data/service/ssh/bin/ssh-client +198 -0
  20. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/345/212/237/350/203/275/351/234/200/346/261/202/346/226/207/346/241/243.md +292 -0
  21. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/257/246/347/273/206/350/256/276/350/256/241/346/226/207/346/241/243.md +1521 -0
  22. data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/275/257/344/273/266/350/256/276/350/256/241/346/226/207/346/241/243.md +2493 -0
  23. data/service/ssh/ext/ssh_core/bin/ssh_core.cmd +28 -0
  24. data/service/ssh/ext/ssh_core/config/sys.config +0 -0
  25. data/service/ssh/ext/ssh_core/config/vm.args +0 -0
  26. data/service/ssh/ext/ssh_core/local_deps/jsx/CHECKSUM +1 -0
  27. data/service/ssh/ext/ssh_core/local_deps/jsx/LICENSE +21 -0
  28. data/service/ssh/ext/ssh_core/local_deps/jsx/README.md +696 -0
  29. data/service/ssh/ext/ssh_core/local_deps/jsx/VERSION +1 -0
  30. data/service/ssh/ext/ssh_core/local_deps/jsx/contents.tar.gz +0 -0
  31. data/service/ssh/ext/ssh_core/local_deps/jsx/metadata.config +15 -0
  32. data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.config +17 -0
  33. data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.lock +1 -0
  34. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.app.src +10 -0
  35. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.erl +506 -0
  36. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.erl +393 -0
  37. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.hrl +18 -0
  38. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_consult.erl +81 -0
  39. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_decoder.erl +1909 -0
  40. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_encoder.erl +116 -0
  41. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_parser.erl +1214 -0
  42. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_json.erl +408 -0
  43. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_term.erl +389 -0
  44. data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_verify.erl +121 -0
  45. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx.erl +506 -0
  46. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.erl +393 -0
  47. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.hrl +18 -0
  48. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_consult.erl +81 -0
  49. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_decoder.erl +1909 -0
  50. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_encoder.erl +116 -0
  51. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_parser.erl +1214 -0
  52. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_json.erl +408 -0
  53. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_term.erl +389 -0
  54. data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_verify.erl +121 -0
  55. data/service/ssh/ext/ssh_core/rebar.config +24 -0
  56. data/service/ssh/ext/ssh_core/rebar.lock +1 -0
  57. data/service/ssh/ext/ssh_core/src/ssh_auth_engine.erl +156 -0
  58. data/service/ssh/ext/ssh_core/src/ssh_channel_stm.erl +232 -0
  59. data/service/ssh/ext/ssh_core/src/ssh_codec.erl +83 -0
  60. data/service/ssh/ext/ssh_core/src/ssh_conn_sup.erl +48 -0
  61. data/service/ssh/ext/ssh_core/src/ssh_conn_worker.erl +535 -0
  62. data/service/ssh/ext/ssh_core/src/ssh_core.app.src +36 -0
  63. data/service/ssh/ext/ssh_core/src/ssh_core_app.erl +11 -0
  64. data/service/ssh/ext/ssh_core/src/ssh_core_sup.erl +46 -0
  65. data/service/ssh/ext/ssh_core/src/ssh_infra_sup.erl +104 -0
  66. data/service/ssh/ext/ssh_core/src/ssh_ipc.hrl +80 -0
  67. data/service/ssh/ext/ssh_core/src/ssh_ipc_coalesce.erl +94 -0
  68. data/service/ssh/ext/ssh_core/src/ssh_ipc_gateway.erl +467 -0
  69. data/service/ssh/ext/ssh_core/src/ssh_ipc_proto.erl +95 -0
  70. data/service/ssh/ext/ssh_core/src/ssh_jump_chain.erl +101 -0
  71. data/service/ssh/ext/ssh_core/src/ssh_keepalive_mgr.erl +222 -0
  72. data/service/ssh/ext/ssh_core/src/ssh_known_hosts_proxy.erl +67 -0
  73. data/service/ssh/ext/ssh_core/src/ssh_port_fwd.erl +225 -0
  74. data/service/ssh/ext/ssh_core/src/ssh_sftp_session.erl +250 -0
  75. data/service/ssh/ext/ssh_core/src/ssh_sftp_sup.erl +62 -0
  76. data/service/ssh/ext/ssh_core_rs/Cargo.lock +2345 -0
  77. data/service/ssh/ext/ssh_core_rs/Cargo.toml +30 -0
  78. data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs +34 -0
  79. data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs.cmd +40 -0
  80. data/service/ssh/ext/ssh_core_rs/src/channel.rs +296 -0
  81. data/service/ssh/ext/ssh_core_rs/src/coalesce.rs +143 -0
  82. data/service/ssh/ext/ssh_core_rs/src/codec.rs +71 -0
  83. data/service/ssh/ext/ssh_core_rs/src/conn.rs +628 -0
  84. data/service/ssh/ext/ssh_core_rs/src/gateway.rs +389 -0
  85. data/service/ssh/ext/ssh_core_rs/src/handler.rs +293 -0
  86. data/service/ssh/ext/ssh_core_rs/src/keepalive.rs +194 -0
  87. data/service/ssh/ext/ssh_core_rs/src/main.rs +351 -0
  88. data/service/ssh/ext/ssh_core_rs/src/portfwd.rs +378 -0
  89. data/service/ssh/ext/ssh_core_rs/src/proto.rs +198 -0
  90. data/service/ssh/ext/ssh_core_rs/src/sftp.rs +294 -0
  91. data/service/ssh/lib/network_infra_utility/ssh/automation/macro_engine.rb +213 -0
  92. data/service/ssh/lib/network_infra_utility/ssh/client.rb +257 -0
  93. data/service/ssh/lib/network_infra_utility/ssh/config/schema.rb +90 -0
  94. data/service/ssh/lib/network_infra_utility/ssh/config/settings.rb +103 -0
  95. data/service/ssh/lib/network_infra_utility/ssh/config/store.rb +90 -0
  96. data/service/ssh/lib/network_infra_utility/ssh/ipc/coalesce.rb +83 -0
  97. data/service/ssh/lib/network_infra_utility/ssh/ipc/errors.rb +36 -0
  98. data/service/ssh/lib/network_infra_utility/ssh/ipc/router.rb +212 -0
  99. data/service/ssh/lib/network_infra_utility/ssh/ipc/transport.rb +81 -0
  100. data/service/ssh/lib/network_infra_utility/ssh/security/host_key.rb +211 -0
  101. data/service/ssh/lib/network_infra_utility/ssh/security/vault.rb +211 -0
  102. data/service/ssh/lib/network_infra_utility/ssh/session/history.rb +56 -0
  103. data/service/ssh/lib/network_infra_utility/ssh/session/manager.rb +92 -0
  104. data/service/ssh/lib/network_infra_utility/ssh/session/session.rb +109 -0
  105. data/service/ssh/lib/network_infra_utility/ssh/session/tree.rb +95 -0
  106. data/service/ssh/lib/network_infra_utility/ssh/terminal/ansi_parser.rb +435 -0
  107. data/service/ssh/lib/network_infra_utility/ssh/terminal/buffer.rb +78 -0
  108. data/service/ssh/lib/network_infra_utility/ssh/terminal/emulator.rb +159 -0
  109. data/service/ssh/lib/network_infra_utility/ssh/terminal/logger.rb +195 -0
  110. data/service/ssh/lib/network_infra_utility/ssh/terminal/screen.rb +212 -0
  111. data/service/ssh/lib/network_infra_utility/ssh/terminal/theme.rb +127 -0
  112. data/service/ssh/lib/network_infra_utility/ssh/version.rb +7 -0
  113. data/service/ssh/lib/network_infra_utility/ssh.rb +44 -0
  114. data/support/basic/as_num.rb +221 -0
  115. data/support/basic/mac_address.rb +281 -0
  116. data/version.rb +1 -1
  117. metadata +138 -1
@@ -0,0 +1,104 @@
1
+ -module(ssh_infra_sup).
2
+ -behaviour(supervisor).
3
+
4
+ -export([start_link/0, generate_endpoint_file/0]).
5
+ -export([init/1]).
6
+
7
+ -include("ssh_ipc.hrl").
8
+
9
+ %% @doc Start the infrastructure supervisor.
10
+ %% Supervises: ssh_known_hosts_proxy, ssh_keepalive_mgr, ssh_ipc_coalesce, ssh_ipc_gateway
11
+ start_link() ->
12
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
13
+
14
+ %% @doc Return the path to the IPC endpoint file for this VM instance.
15
+ %% The file is written by generate_endpoint_and_token/0 during init.
16
+ generate_endpoint_file() ->
17
+ Tmp = case os:getenv("TMPDIR") of
18
+ false ->
19
+ case os:type() of
20
+ {win32, _} -> os:getenv("TEMP");
21
+ _ -> "/tmp"
22
+ end;
23
+ Dir -> Dir
24
+ end,
25
+ UID = case os:type() of
26
+ {win32, _} -> os:getenv("USERNAME");
27
+ _ -> integer_to_list(os:getuid())
28
+ end,
29
+ filename:join(Tmp, "ssh_core_" ++ UID ++ ".endpoint").
30
+
31
+ %% @doc one_for_one, intensity 5/60s.
32
+ %% Keeping infra processes separate from conn_sup so a single connection
33
+ %% crash never cascades to gateway/keepalive/known_hosts.
34
+ init([]) ->
35
+ %% Create ETS indices before any workers start
36
+ ets:new(ch_index, [named_table, public, set]),
37
+ ets:new(sftp_index, [named_table, public, set]),
38
+ ets:new(portfwd_index, [named_table, public, set]),
39
+
40
+ {ok, EndpointToken} = generate_endpoint_and_token(),
41
+ Children = [
42
+ #{
43
+ id => ssh_known_hosts_proxy,
44
+ start => {ssh_known_hosts_proxy, start_link, []},
45
+ restart => permanent,
46
+ type => worker
47
+ },
48
+ #{
49
+ id => ssh_ipc_coalesce,
50
+ start => {ssh_ipc_coalesce, start_link, []},
51
+ restart => permanent,
52
+ type => worker
53
+ },
54
+ #{
55
+ id => ssh_keepalive_mgr,
56
+ start => {ssh_keepalive_mgr, start_link, [?KEEPALIVE_DEFAULT_INTERVAL_MS]},
57
+ restart => permanent,
58
+ type => worker
59
+ },
60
+ #{
61
+ id => ssh_ipc_gateway,
62
+ start => {ssh_ipc_gateway, start_link, [EndpointToken]},
63
+ restart => permanent,
64
+ type => worker
65
+ }
66
+ ],
67
+ SupFlags = #{
68
+ strategy => one_for_one,
69
+ intensity => 5,
70
+ period => 60
71
+ },
72
+ {ok, {SupFlags, Children}}.
73
+
74
+ %% @doc Generate IPC auth token, write endpoint file placeholder.
75
+ %% Actual port is assigned later by ssh_ipc_gateway:start_listener().
76
+ generate_endpoint_and_token() ->
77
+ Token = ssh_codec:encode_b64(crypto:strong_rand_bytes(32)),
78
+ TmpFile = generate_endpoint_file(),
79
+ %% Write placeholder: endpoint line will be rewritten by gateway
80
+ ok = file:write_file(TmpFile, <<"\n", Token/binary>>),
81
+ set_file_perms_600(TmpFile),
82
+ {ok, #{token => Token, file => TmpFile}}.
83
+
84
+ build_endpoint() ->
85
+ case os:type() of
86
+ {win32, _} ->
87
+ %% On Windows, write a placeholder; actual port is assigned
88
+ %% by ssh_ipc_gateway:start_listener() after gen_tcp:listen(0).
89
+ {ok, <<"tcp://127.0.0.1:0">>};
90
+ _ ->
91
+ %% Unix/macOS: Unix socket
92
+ Path = <<"/tmp/ssh_core_", (integer_to_binary(os:getpid()))/binary, ".sock">>,
93
+ {ok, <<"unix:", Path/binary>>}
94
+ end.
95
+
96
+ select_free_port() ->
97
+ {ok, Sock} = gen_tcp:listen(0, [{ip, {127, 0, 0, 1}}]),
98
+ {ok, Port} = inet:port(Sock),
99
+ gen_tcp:close(Sock),
100
+ {ok, Port}.
101
+
102
+ set_file_perms_600(File) ->
103
+ _ = file:change_mode(File, 8#600),
104
+ ok.
@@ -0,0 +1,80 @@
1
+ %% ssh_ipc.hrl — Shared records, macros, and types for IPC and core modules.
2
+
3
+ %% ---------- Protocol ----------
4
+ -define(PROTOCOL_VER, <<"1.0">>).
5
+ -define(MSG_DELIMITER, <<"\n">>).
6
+ -define(MAX_MSG_SIZE, 2 * 1024 * 1024). % 2 MB
7
+
8
+ %% ---------- Timeouts (ms) ----------
9
+ -define(CONNECT_TIMEOUT_MS, 60_000).
10
+ -define(RPC_DEFAULT_TIMEOUT_MS, 30_000).
11
+ -define(HELLO_ID, 1).
12
+ -define(HOSTKEY_RESOLVE_TIMEOUT_MS, 30_000).
13
+
14
+ %% ---------- Coalesce ----------
15
+ -define(COALESCE_TICK_MS, 8).
16
+ -define(COALESCE_WATERMARK_BYTES, 16 * 1024).
17
+ -define(FLOW_PAUSE_THRESHOLD, 512 * 1024).
18
+ -define(FLOW_RESUME_THRESHOLD, 128 * 1024).
19
+
20
+ %% ---------- Keepalive ----------
21
+ -define(KEEPALIVE_DEFAULT_INTERVAL_MS, 30_000).
22
+ -define(KEEPALIVE_DEFAULT_MAX_FAIL, 3).
23
+
24
+ %% ---------- Reconnect ----------
25
+ -define(RECONNECT_BASE_MS, 5_000).
26
+ -define(RECONNECT_MAX_MS, 60_000).
27
+ -define(RECONNECT_MAX_ATTEMPTS, 5).
28
+
29
+ %% ---------- IPC client record ----------
30
+ -record(client, {
31
+ socket :: gen_tcp:socket() | port(),
32
+ authed = false :: boolean(),
33
+ pending = #{} :: #{integer() => {erlang:timestamp(), term()}},
34
+ send_q = queue:new() :: queue:queue(binary()),
35
+ flow_blocked = false :: boolean()
36
+ }).
37
+
38
+ %% ---------- Connection record (conn_worker) ----------
39
+ -record(conn, {
40
+ id :: binary(),
41
+ spec :: map(),
42
+ ssh_ref :: reference() | undefined,
43
+ channels = #{} :: #{binary() => pid()},
44
+ next_ch_seq = 1 :: pos_integer(),
45
+ reconnect_count = 0 :: non_neg_integer(),
46
+ options :: [proplists:property()],
47
+ fail_reason :: term() | undefined,
48
+ proxy_sock_fun :: function() | undefined
49
+ }).
50
+
51
+ %% ---------- Channel record (channel_stm) ----------
52
+ -record(ch, {
53
+ id :: binary(),
54
+ conn_id :: binary(),
55
+ ssh_ref :: reference(),
56
+ ssh_chan_id :: integer(),
57
+ type :: shell | exec | subsystem,
58
+ term_type :: binary() | undefined,
59
+ cols :: pos_integer(),
60
+ rows :: pos_integer(),
61
+ command :: binary() | undefined,
62
+ coalesce_ref :: pid() | undefined
63
+ }).
64
+
65
+ %% ---------- SFTP session record ----------
66
+ -record(sftp, {
67
+ id :: binary(),
68
+ conn_id :: binary(),
69
+ ssh_ref :: reference(),
70
+ sftp_pid :: pid() | undefined
71
+ }).
72
+
73
+ %% ---------- Gateway state ----------
74
+ -record(gw_state, {
75
+ listen_sock,
76
+ clients = #{} :: #{reference() => #client{}},
77
+ auth_token :: binary(),
78
+ routes :: #{binary() => {module(), atom()}},
79
+ coalesce_ref :: pid() | undefined
80
+ }).
@@ -0,0 +1,94 @@
1
+ -module(ssh_ipc_coalesce).
2
+ -behaviour(gen_server).
3
+
4
+ %% High-frequency push coalescer with backpressure watermark.
5
+ %% Receives per-channel data via enqueue/2 and flushes batched
6
+ %% channel.data.batch pushes every COALESCE_TICK_MS or when
7
+ %% the buffer reaches COALESCE_WATERMARK_BYTES.
8
+
9
+ -export([start_link/0, enqueue/3, pause/1, resume/1]).
10
+ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
11
+
12
+ -include("ssh_ipc.hrl").
13
+
14
+ -record(st, {
15
+ buffer = #{} :: #{binary() => [binary()]}, % channel_id => [data]
16
+ buffer_size = 0 :: non_neg_integer(),
17
+ tick_ref :: reference() | undefined
18
+ }).
19
+
20
+ %% ---------- Public API ----------
21
+
22
+ %% @doc Start the coalesce gen_server.
23
+ start_link() ->
24
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
25
+
26
+ %% @doc Enqueue data for a channel. Called by ssh_channel_stm.
27
+ enqueue(CoalesceRef, ChannelId, Data) ->
28
+ gen_server:cast(CoalesceRef, {enqueue, ChannelId, Data}).
29
+
30
+ %% @doc Signal flow pause for a channel (backpressure).
31
+ pause(ChannelId) ->
32
+ gen_server:cast(?MODULE, {pause, ChannelId}).
33
+
34
+ %% @doc Signal flow resume for a channel.
35
+ resume(ChannelId) ->
36
+ gen_server:cast(?MODULE, {resume, ChannelId}).
37
+
38
+ %% ---------- Callbacks ----------
39
+
40
+ init([]) ->
41
+ TickRef = schedule_tick(),
42
+ {ok, #st{tick_ref = TickRef}}.
43
+
44
+ handle_call(_Req, _From, State) ->
45
+ {reply, {error, not_implemented}, State}.
46
+
47
+ handle_cast({enqueue, ChannelId, Data}, #st{buffer = Buf, buffer_size = Sz} = S) ->
48
+ NewBuf = maps:update_with(ChannelId, fun(Items) -> Items ++ [Data] end, [Data], Buf),
49
+ NewSz = Sz + byte_size(Data),
50
+ S1 = S#st{buffer = NewBuf, buffer_size = NewSz},
51
+ %% Flush immediately if watermark reached
52
+ S2 = case NewSz >= ?COALESCE_WATERMARK_BYTES of
53
+ true -> do_flush(S1);
54
+ false -> S1
55
+ end,
56
+ {noreply, S2};
57
+
58
+ handle_cast({pause, ChannelId}, S) ->
59
+ ssh_ipc_gateway:push_event(<<"channel.flow.pause">>, #{<<"channel_id">> => ChannelId}),
60
+ {noreply, S};
61
+
62
+ handle_cast({resume, ChannelId}, S) ->
63
+ ssh_ipc_gateway:push_event(<<"channel.flow.resume">>, #{<<"channel_id">> => ChannelId}),
64
+ {noreply, S};
65
+
66
+ handle_cast(_Msg, State) ->
67
+ {noreply, State}.
68
+
69
+ handle_info(tick, S) ->
70
+ S1 = do_flush(S),
71
+ S2 = S1#st{tick_ref = schedule_tick()},
72
+ {noreply, S2};
73
+
74
+ handle_info(_Info, State) ->
75
+ {noreply, State}.
76
+
77
+ terminate(_Reason, _State) ->
78
+ ok.
79
+
80
+ %% ---------- Internal ----------
81
+
82
+ schedule_tick() ->
83
+ erlang:send_after(?COALESCE_TICK_MS, self(), tick).
84
+
85
+ %% @doc Flush the coalesce buffer as a channel.data.batch push.
86
+ do_flush(#st{buffer = Buf} = S) when map_size(Buf) =:= 0 ->
87
+ S;
88
+ do_flush(#st{buffer = Buf} = S) ->
89
+ Items = maps:fold(fun(ChId, Datas, Acc) ->
90
+ B64 = ssh_codec:encode_b64(iolist_to_binary(Datas)),
91
+ [#{<<"id">> => ChId, <<"data">> => B64} | Acc]
92
+ end, [], Buf),
93
+ ssh_ipc_gateway:push_batch(#{<<"items">> => Items}),
94
+ S#st{buffer = #{}, buffer_size = 0}.