/home/runner/work/eturnal/eturnal/_build/test/cover/aggregate/eturnal_misc.html

1 %%% eturnal STUN/TURN server.
2 %%%
3 %%% Copyright (c) 2020-2022 Holger Weiss <holger@zedat.fu-berlin.de>.
4 %%% Copyright (c) 2020-2022 ProcessOne, SARL.
5 %%% All rights reserved.
6 %%%
7 %%% Licensed under the Apache License, Version 2.0 (the "License");
8 %%% you may not use this file except in compliance with the License.
9 %%% You may obtain a copy of the License at
10 %%%
11 %%% http://www.apache.org/licenses/LICENSE-2.0
12 %%%
13 %%% Unless required by applicable law or agreed to in writing, software
14 %%% distributed under the License is distributed on an "AS IS" BASIS,
15 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 %%% See the License for the specific language governing permissions and
17 %%% limitations under the License.
18
19 -module(eturnal_misc).
20 -export([my_ipv4_addr/0,
21 my_ipv6_addr/0,
22 addr_to_str/1,
23 addr_to_str/2,
24 version/0,
25 info/0]).
26
27 -ifdef(EUNIT).
28 -include_lib("eunit/include/eunit.hrl").
29 -endif.
30 -include_lib("kernel/include/logger.hrl").
31 -include("eturnal.hrl").
32
33 %% API.
34
35 -spec my_ipv4_addr() -> inet:ip4_address() | undefined.
36 my_ipv4_addr() ->
37 3 case my_host_addr(inet) of
38 {_, _, _, _} = Addr ->
39
:-(
Addr;
40 undefined ->
41 3 my_if_addr(inet)
42 end.
43
44 -spec my_ipv6_addr() -> inet:ip6_address() | undefined.
45 my_ipv6_addr() ->
46 3 case my_host_addr(inet6) of
47 {_, _, _, _, _, _, _, _} = Addr ->
48
:-(
Addr;
49 undefined ->
50 3 my_if_addr(inet6)
51 end.
52
53 -spec addr_to_str(inet:ip_address(), inet:port_number()) -> iolist().
54 addr_to_str(Addr, Port) ->
55 12 addr_to_str({Addr, Port}).
56
57 -spec addr_to_str({inet:ip_address(), inet:port_number()} | inet:ip_address())
58 -> iolist().
59 addr_to_str({Addr, Port}) when is_tuple(Addr) ->
60 12 [addr_to_str(Addr), [$: | integer_to_list(Port)]];
61 addr_to_str({0, 0, 0, 0, 0, 16#FFFF, D7, D8}) ->
62
:-(
addr_to_str({D7 bsr 8, D7 band 255, D8 bsr 8, D8 band 255});
63 addr_to_str({_, _, _, _, _, _, _, _} = Addr) ->
64
:-(
[$[, inet:ntoa(Addr), $]];
65 addr_to_str(Addr) ->
66 12 inet:ntoa(Addr).
67
68 -spec version() -> binary().
69 version() ->
70 4 {ok, Version} = application:get_key(vsn),
71 4 unicode:characters_to_binary(Version).
72
73 -spec info() -> eturnal_node_info().
74 info() ->
75 1 #eturnal_node_info{
76 eturnal_vsn = version(),
77 otp_vsn = {erlang:system_info(otp_release), erlang:system_info(version)},
78 uptime = element(1, erlang:statistics(wall_clock)),
79 num_sessions = length(supervisor:which_children(turn_tmp_sup)),
80 num_processes = erlang:system_info(process_count),
81 num_reductions = element(1, erlang:statistics(reductions)),
82 total_queue_len = erlang:statistics(total_run_queue_lengths),
83 total_memory = erlang:memory(total)}.
84
85 %% Internal functions.
86
87 -spec my_host_addr(inet) -> inet:ip4_address() | undefined;
88 (inet6) -> inet:ip6_address() | undefined.
89 my_host_addr(Family) ->
90 6 {ok, Name} = inet:gethostname(),
91 6 case inet:getaddr(Name, Family) of
92 {ok, Addr} ->
93 6 case is_public_addr(Addr) of
94 true ->
95
:-(
Addr;
96 false ->
97 6 undefined
98 end;
99 {error, _} ->
100
:-(
undefined
101 end.
102
103 -spec my_if_addr(inet) -> inet:ip4_address() | undefined;
104 (inet6) -> inet:ip6_address() | undefined.
105 my_if_addr(Family) ->
106 %
107 % net:getifaddrs/1 is more convenient, but depends on Erlang/OTP 22.3+ with
108 % 'socket' support (and therefore wouldn't work on Windows, for example):
109 %
110 % net:getifaddrs(#{family => Family, flags => [up]}).
111 %
112 8 {ok, Interfaces} = inet:getifaddrs(),
113 8 Addrs = lists:filtermap(fun({_Name, Opts}) ->
114 24 filter_interface(Family, Opts)
115 end, Interfaces),
116 8 case Addrs of
117 [Addr | _] ->
118
:-(
Addr;
119 [] ->
120 8 undefined
121 end.
122
123 -spec filter_interface(inet, [{atom(), tuple()}])
124 -> {true, inet:ip4_address()} | false;
125 (inet6, [{atom(), tuple()}])
126 -> {true, inet:ip6_address()} | false.
127 filter_interface(Family, Opts) ->
128 24 Flags = lists:flatten([Fs || {flags, Fs} <- Opts]),
129 24 case lists:member(up, Flags) of
130 true ->
131 24 Addrs = [Addr || {addr, Addr} <- Opts,
132 40 family_matches(Addr, Family),
133 20 is_public_addr(Addr)],
134 24 case Addrs of
135 [Addr | _] ->
136
:-(
{true, Addr};
137 [] ->
138 24 false
139 end;
140 false ->
141
:-(
false
142 end.
143
144 -spec is_public_addr(inet:ip_address()) -> boolean().
145 is_public_addr({0, 0, 0, 0}) ->
146
:-(
false;
147 is_public_addr({127, _, _, _}) ->
148 4 false;
149 is_public_addr({169, 254, _, _}) ->
150
:-(
false;
151 is_public_addr({10, _, _, _}) ->
152 7 false;
153 is_public_addr({172, D2, _, _}) when D2 >= 16, D2 =< 31 ->
154 5 false;
155 is_public_addr({192, 168, _, _}) ->
156
:-(
false;
157 is_public_addr({0, 0, 0, 0, 0, 0, 0, 0}) ->
158
:-(
false;
159 is_public_addr({0, 0, 0, 0, 0, 0, 0, 1}) ->
160 7 false;
161 is_public_addr({65152, _, _, _, _, _, _, _}) ->
162 4 false;
163 is_public_addr({D1, _, _, _, _, _, _, _}) when D1 >= 64512, D1 =< 65023 ->
164 1 false;
165 is_public_addr({_, _, _, _}) ->
166 1 true;
167 is_public_addr({_, _, _, _, _, _, _, _}) ->
168 1 true.
169
170 -spec family_matches(inet:ip_address(), inet | inet6) -> boolean().
171 family_matches({_, _, _, _}, inet) ->
172 13 true;
173 family_matches({_, _, _, _}, inet6) ->
174 13 false;
175 family_matches({_, _, _, _, _, _, _, _}, inet6) ->
176 9 true;
177 family_matches({_, _, _, _, _, _, _, _}, inet) ->
178 9 false.
179
180 %% EUnit tests.
181
182 -ifdef(EUNIT).
183 -define(PRIVATE_IPV4ADDR, {172, 20, 0, 2}).
184 -define(PUBLIC_IPV4ADDR, {203, 0, 113, 2}).
185 -define(PRIVATE_IPV6ADDR, {65000, 0, 0, 0, 0, 0, 0, 2}).
186 -define(PUBLIC_IPV6ADDR, {8193, 3512, 0, 0, 0, 0, 0, 2}).
187
188 addr_test_() ->
189 2 [?_assert(is_public_addr(?PUBLIC_IPV4ADDR)),
190 1 ?_assert(is_public_addr(?PUBLIC_IPV6ADDR)),
191 1 ?_assert(family_matches(?PUBLIC_IPV4ADDR, inet)),
192 1 ?_assert(family_matches(?PUBLIC_IPV6ADDR, inet6)),
193 1 ?_assertNot(is_public_addr(?PRIVATE_IPV4ADDR)),
194 1 ?_assertNot(is_public_addr(?PRIVATE_IPV6ADDR)),
195 1 ?_assertNot(family_matches(?PRIVATE_IPV4ADDR, inet6)),
196 1 ?_assertNot(family_matches(?PRIVATE_IPV6ADDR, inet)),
197 1 ?_test(my_if_addr(inet)),
198 1 ?_test(my_if_addr(inet6))].
199 -endif.
Line Hits Source