aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded/src/net.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded/src/net.erl')
-rw-r--r--erts/preloaded/src/net.erl357
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).