diff options
Diffstat (limited to 'erts/preloaded/src/net.erl')
-rw-r--r-- | erts/preloaded/src/net.erl | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl new file mode 100644 index 0000000000..39c907eca3 --- /dev/null +++ b/erts/preloaded/src/net.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(net). + +%% Administrative and "global" utility functions +-export([ + on_load/0, on_load/1, on_load/2, + info/0 + ]). + +-export([ + getnameinfo/1, getnameinfo/2, + getaddrinfo/2, + + if_name2index/1, + if_index2name/1, + if_names/0 + ]). + +-export_type([ + ip_address/0, + ip4_address/0, + ip6_address/0, + in_sockaddr/0, + in4_sockaddr/0, + in6_sockaddr/0, + port_number/0, + + address_info/0, + name_info/0, + + name_info_flags/0, + name_info_flag/0, + name_info_flag_ext/0, + + network_interface_name/0, + network_interface_index/0 + ]). + +-record(name_info, {flags, host, service}). +-record(address_info, {flags, family, socket_type, protocol, addr}). + +%% Many of these should be moved to the socket module. +-type ip_address() :: ip4_address() | ip6_address(). +-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. +-type ip6_address() :: + {0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535, + 0..65535}. +-type uint20() :: 0..16#FFFFF. +-type uint32() :: 0..16#FFFFFFFF. +-type in6_flow_info() :: uint20(). +-type in6_scope_id() :: uint32(). +-record(in4_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip4_address()}). +-type in4_sockaddr() :: #in4_sockaddr{}. +-record(in6_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip6_address(), + flowinfo = 0 :: in6_flow_info(), + scope_id = 0 :: in6_scope_id()}). +-type in6_sockaddr() :: #in6_sockaddr{}. + +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + +-type port_number() :: 0..65535. + +-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()]. +-type name_info_flag() :: namereqd | + dgram | + nofqdn | + numerichost | + nomericserv. +-type name_info_flag_ext() :: idn | + idna_allow_unassigned | + idna_use_std3_ascii_rules. +-type name_info() :: #name_info{}. +-type address_info() :: #address_info{}. +-type network_interface_name() :: string(). +-type network_interface_index() :: non_neg_integer(). + + +-define(NET_NAME_INFO_NAMEREQD, 0). +-define(NET_NAME_INFO_DGRAM, 1). +-define(NET_NAME_INFO_NOFQDN, 2). +-define(NET_NAME_INFO_NUMERICHOST, 3). +-define(NET_NAME_INFO_NUMERICSERV, 4). +-define(NET_NAME_INFO_IDN, 5). +-define(NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED, 6). +-define(NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES, 7). + + +%% =========================================================================== +%% +%% Administrative and utility API +%% +%% =========================================================================== + +-spec on_load() -> ok. + +%% Should we require that the Extra arg is a map? +on_load() -> + on_load(#{}). + +-spec on_load(Extra) -> ok when + Extra :: maps:map(). + +on_load(Extra) when is_map(Extra) -> + on_load(atom_to_list(?MODULE), Extra). + +-spec on_load(Path, Extra) -> ok when + Path :: string(), + Extra :: maps:map(). + +on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> + on_load(nif_is_loaded(), Path, Extra). + +on_load(true, _Path, _Extra) -> + ok; +on_load(false, Path, Extra) -> + ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + + + +-spec info() -> list(). + +info() -> + nif_info(). + + + +%% =========================================================================== +%% +%% The proper net API +%% +%% =========================================================================== + +%% =========================================================================== +%% +%% getnameinfo - Address-to-name translation in protocol-independent manner. +%% +%% + +-spec getnameinfo(SockAddr) -> {ok, Info} | {error, Reason} when + SockAddr :: in_sockaddr(), + Info :: name_info(), + Reason :: term(). + +getnameinfo(SockAddr) + when is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr) -> + getnameinfo(SockAddr, []). + +-spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when + SockAddr :: in_sockaddr(), + Flags :: name_info_flags(), + Info :: name_info(), + Reason :: term(). + +getnameinfo(SockAddr, Flags) + when (is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr)) andalso + is_list(Flags) -> + try + begin + EFlags = enc_name_info_flags(Flags), + nif_getnameinfo(SockAddr, EFlags) + end + catch + throw:T -> + T; + error:Reason -> + {error, Reason} + end. + + +enc_name_info_flags([]) -> + 0; +enc_name_info_flags(Flags) -> + EFlags = [{namereqd, ?NET_NAME_INFO_NAMEREQD}, + {dgram, ?NET_NAME_INFO_DGRAM}, + {nofqdn, ?NET_NAME_INFO_NOFQDN}, + {numerichost, ?NET_NAME_INFO_NUMERICHOST}, + {numericserv, ?NET_NAME_INFO_NUMERICSERV}, + + %% The below flags was introduce with glibc 2.3.4. + {idn, ?NET_NAME_INFO_IDN}, + {idna_allow_unassigned, ?NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED}, + {idna_use_std3_ascii_rules,?NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES}], + enc_flags(Flags, EFlags). + + +enc_flags([], _) -> + 0; +enc_flags(Flags, EFlags) -> + F = fun(Flag, Acc) -> + case lists:keysearch(Flag, 1, EFlags) of + {value, {Flag, EFlag}} -> + Acc bor (1 bsl EFlag); + false -> + throw({error, {unknown_flag, Flag}}) + end + end, + lists:foldl(F, 0, Flags). + + +%% =========================================================================== +%% +%% getaddrinfo - Network address and service translation +%% +%% There is also a "hint" argument that we "at some point" should implement. + +-spec getaddrinfo(Host, Service) -> {ok, Info} | {error, Reason} when + Host :: string(), + Service :: string(), + Info :: [address_info()], + Reason :: term(). + +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + nif_getaddrinfo(Host, Service, undefined). + + + +%% =========================================================================== +%% +%% if_name2index - Mappings between network interface names and indexes: +%% name -> idx +%% +%% + +-spec if_name2index(Name) -> {ok, Idx} | {error, Reason} when + Name :: string(), + Idx :: non_neg_integer(), + Reason :: term(). + +if_name2index(If) when is_list(If) -> + nif_if_name2index(If). + + + +%% =========================================================================== +%% +%% if_index2name - Mappings between network interface names and indexes: +%% idx -> name +%% +%% + +-spec if_index2name(Idx) -> {ok, Name} | {error, Reason} when + Idx :: non_neg_integer(), + Name :: string(), + Reason :: term(). + +if_index2name(Idx) when is_integer(Idx) -> + nif_if_index2name(Idx). + + + +%% =========================================================================== +%% +%% if_names - Get network interface names and indexes +%% +%% + +-spec if_names() -> Names | {error, Reason} when + Names :: [{Idx, If}], + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +if_names() -> + nif_if_names(). + + + +%% =========================================================================== +%% +%% Misc utility functions +%% +%% =========================================================================== + +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp(Now) -> + N2T = fun(N) -> calendar:now_to_local_time(N) end, + format_timestamp(Now, N2T, true). + +format_timestamp({_N1, _N2, N3} = N, N2T, true) -> + FormatExtra = ".~.2.0w", + ArgsExtra = [N3 div 10000], + format_timestamp(N, N2T, FormatExtra, ArgsExtra); +format_timestamp({_N1, _N2, _N3} = N, N2T, false) -> + FormatExtra = "", + ArgsExtra = [], + format_timestamp(N, N2T, FormatExtra, ArgsExtra). + +format_timestamp(N, N2T, FormatExtra, ArgsExtra) -> + {Date, Time} = N2T(N), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra, + [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra), + lists:flatten(FormatDate). + + +%% =========================================================================== +%% +%% The actual NIF-functions. +%% +%% =========================================================================== + +nif_is_loaded() -> + false. + +nif_info() -> + erlang:error(badarg). + +nif_getnameinfo(_Addr, _Flags) -> + erlang:error(badarg). + +nif_getaddrinfo(_Host, _Service, _Hints) -> + erlang:error(badarg). + +nif_if_name2index(_Name) -> + erlang:error(badarg). + +nif_if_index2name(_Id) -> + erlang:error(badarg). + +nif_if_names() -> + erlang:error(badarg). |