aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/inet.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/inet.erl')
-rw-r--r--lib/kernel/src/inet.erl1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
new file mode 100644
index 0000000000..b86aa1839e
--- /dev/null
+++ b/lib/kernel/src/inet.erl
@@ -0,0 +1,1342 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(inet).
+
+-include("inet.hrl").
+-include("inet_int.hrl").
+-include("inet_sctp.hrl").
+
+%% socket
+-export([peername/1, sockname/1, port/1, send/2,
+ setopts/2, getopts/2,
+ getif/1, getif/0, getiflist/0, getiflist/1,
+ ifget/3, ifget/2, ifset/3, ifset/2,
+ getstat/1, getstat/2,
+ ip/1, stats/0, options/0,
+ pushf/3, popf/1, close/1, gethostname/0, gethostname/1]).
+
+-export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]).
+
+-export([i/0, i/1, i/2]).
+
+-export([getll/1, getfd/1, open/7, fdopen/5]).
+
+-export([tcp_controlling_process/2, udp_controlling_process/2,
+ tcp_close/1, udp_close/1]).
+%% used by socks5
+-export([setsockname/2, setpeername/2]).
+
+%% resolve
+-export([gethostbyname/1, gethostbyname/2, gethostbyname/3,
+ gethostbyname_tm/3]).
+-export([gethostbyaddr/1, gethostbyaddr/2,
+ gethostbyaddr_tm/2]).
+
+-export([getservbyname/2, getservbyport/2]).
+-export([getaddrs/2, getaddrs/3, getaddrs_tm/3,
+ getaddr/2, getaddr/3, getaddr_tm/3]).
+-export([translate_ip/2]).
+
+-export([get_rc/0]).
+
+%% format error
+-export([format_error/1]).
+
+%% timer interface
+-export([start_timer/1, timeout/1, timeout/2, stop_timer/1]).
+
+%% imports
+-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
+
+%% Record Signature
+-define(RS(Record),
+ {Record, record_info(size, Record)}).
+%% Record Signature Check (guard)
+-define(RSC(Record, RS),
+ element(1, Record) =:= element(1, RS),
+ tuple_size(Record) =:= element(2, RS)).
+
+%%% ---------------------------------
+%%% Contract type definitions
+
+-type socket() :: port().
+-type posix() :: atom().
+
+-type socket_setopt() ::
+ {'raw', non_neg_integer(), non_neg_integer(), binary()} |
+ %% TCP/UDP options
+ {'reuseaddr', boolean()} |
+ {'keepalive', boolean()} |
+ {'dontroute', boolean()} |
+ {'linger', {boolean(), non_neg_integer()}} |
+ {'broadcast', boolean()} |
+ {'sndbuf', non_neg_integer()} |
+ {'recbuf', non_neg_integer()} |
+ {'priority', non_neg_integer()} |
+ {'tos', non_neg_integer()} |
+ {'nodelay', boolean()} |
+ {'multicast_ttl', non_neg_integer()} |
+ {'multicast_loop', boolean()} |
+ {'multicast_if', ip_address()} |
+ {'add_membership', {ip_address(), ip_address()}} |
+ {'drop_membership', {ip_address(), ip_address()}} |
+ {'header', non_neg_integer()} |
+ {'buffer', non_neg_integer()} |
+ {'active', boolean() | 'once'} |
+ {'packet',
+ 0 | 1 | 2 | 4 | 'raw' | 'sunrm' | 'asn1' |
+ 'cdr' | 'fcgi' | 'line' | 'tpkt' | 'http' | 'httph' | 'http_bin' | 'httph_bin' } |
+ {'mode', list() | binary()} |
+ {'port', 'port', 'term'} |
+ {'exit_on_close', boolean()} |
+ {'low_watermark', non_neg_integer()} |
+ {'high_watermark', non_neg_integer()} |
+ {'bit8', 'clear' | 'set' | 'on' | 'off'} |
+ {'send_timeout', non_neg_integer() | 'infinity'} |
+ {'send_timeout_close', boolean()} |
+ {'delay_send', boolean()} |
+ {'packet_size', non_neg_integer()} |
+ {'read_packets', non_neg_integer()} |
+ %% SCTP options
+ {'sctp_rtoinfo', #sctp_rtoinfo{}} |
+ {'sctp_associnfo', #sctp_assocparams{}} |
+ {'sctp_initmsg', #sctp_initmsg{}} |
+ {'sctp_nodelay', boolean()} |
+ {'sctp_autoclose', non_neg_integer()} |
+ {'sctp_disable_fragments', boolean()} |
+ {'sctp_i_want_mapped_v4_addr', boolean()} |
+ {'sctp_maxseg', non_neg_integer()} |
+ {'sctp_primary_addr', #sctp_prim{}} |
+ {'sctp_set_peer_primary_addr', #sctp_setpeerprim{}} |
+ {'sctp_adaptation_layer', #sctp_setadaptation{}} |
+ {'sctp_peer_addr_params', #sctp_paddrparams{}} |
+ {'sctp_default_send_param', #sctp_sndrcvinfo{}} |
+ {'sctp_events', #sctp_event_subscribe{}} |
+ {'sctp_delayed_ack_time', #sctp_assoc_value{}}.
+
+-type socket_getopt() ::
+ {'raw',
+ non_neg_integer(), non_neg_integer(), binary()|non_neg_integer()} |
+ %% TCP/UDP options
+ 'reuseaddr' | 'keepalive' | 'dontroute' | 'linger' |
+ 'broadcast' | 'sndbuf' | 'recbuf' | 'priority' | 'tos' | 'nodelay' |
+ 'multicast_ttl' | 'multicast_loop' | 'multicast_if' |
+ 'add_membership' | 'drop_membership' |
+ 'header' | 'buffer' | 'active' | 'packet' | 'mode' | 'port' |
+ 'exit_on_close' | 'low_watermark' | 'high_watermark' | 'bit8' |
+ 'send_timeout' | 'send_timeout_close' |
+ 'delay_send' | 'packet_size' | 'read_packets' |
+ %% SCTP options
+ {'sctp_status', #sctp_status{}} |
+ 'sctp_get_peer_addr_info' |
+ {'sctp_get_peer_addr_info', #sctp_status{}} |
+ 'sctp_rtoinfo' |
+ {'sctp_rtoinfo', #sctp_rtoinfo{}} |
+ 'sctp_associnfo' |
+ {'sctp_associnfo', #sctp_assocparams{}} |
+ 'sctp_initmsg' |
+ {'sctp_initmsg', #sctp_initmsg{}} |
+ 'sctp_nodelay' | 'sctp_autoclose' | 'sctp_disable_fragments' |
+ 'sctp_i_want_mapped_v4_addr' | 'sctp_maxseg' |
+ {'sctp_primary_addr', #sctp_prim{}} |
+ {'sctp_set_peer_primary_addr', #sctp_setpeerprim{}} |
+ 'sctp_adaptation_layer' |
+ {'sctp_adaptation_layer', #sctp_setadaptation{}} |
+ {'sctp_peer_addr_params', #sctp_paddrparams{}} |
+ 'sctp_default_send_param' |
+ {'sctp_default_send_param', #sctp_sndrcvinfo{}} |
+ 'sctp_events' |
+ {'sctp_events', #sctp_event_subscribe{}} |
+ 'sctp_delayed_ack_time' |
+ {'sctp_delayed_ack_time', #sctp_assoc_value{}}.
+
+-type ether_address() :: [0..255].
+
+-type if_setopt() ::
+ {'addr', ip_address()} |
+ {'broadaddr', ip_address()} |
+ {'dstaddr', ip_address()} |
+ {'mtu', non_neg_integer()} |
+ {'netmask', ip_address()} |
+ {'flags', ['up' | 'down' | 'broadcast' | 'no_broadcast' |
+ 'pointtopoint' | 'no_pointtopoint' |
+ 'running' | 'multicast']} |
+ {'hwaddr', ether_address()}.
+
+-type if_getopt() ::
+ 'addr' | 'broadaddr' | 'dstaddr' |
+ 'mtu' | 'netmask' | 'flags' |'hwaddr'.
+
+-type family_option() :: 'inet' | 'inet6'.
+-type protocol_option() :: 'tcp' | 'udp' | 'sctp'.
+-type stat_option() ::
+ 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
+ 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'.
+
+%%% ---------------------------------
+
+-spec get_rc() -> [{any(),any()}].
+
+get_rc() ->
+ inet_db:get_rc().
+
+-spec close(Socket :: socket()) -> 'ok'.
+
+close(Socket) ->
+ prim_inet:close(Socket),
+ receive
+ {Closed, Socket} when Closed =:= tcp_closed; Closed =:= udp_closed ->
+ ok
+ after 0 ->
+ ok
+ end.
+
+-spec peername(Socket :: socket()) ->
+ {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}.
+
+peername(Socket) ->
+ prim_inet:peername(Socket).
+
+-spec setpeername(Socket :: socket(), Address :: {ip_address(), ip_port()}) ->
+ 'ok' | {'error', any()}.
+
+setpeername(Socket, {IP,Port}) ->
+ prim_inet:setpeername(Socket, {IP,Port});
+setpeername(Socket, undefined) ->
+ prim_inet:setpeername(Socket, undefined).
+
+
+-spec sockname(Socket :: socket()) ->
+ {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}.
+
+sockname(Socket) ->
+ prim_inet:sockname(Socket).
+
+-spec setsockname(Socket :: socket(), Address :: {ip_address(), ip_port()}) ->
+ 'ok' | {'error', any()}.
+
+setsockname(Socket, {IP,Port}) ->
+ prim_inet:setsockname(Socket, {IP,Port});
+setsockname(Socket, undefined) ->
+ prim_inet:setsockname(Socket, undefined).
+
+-spec port(Socket :: socket()) -> {'ok', ip_port()} | {'error', any()}.
+
+port(Socket) ->
+ case prim_inet:sockname(Socket) of
+ {ok, {_,Port}} -> {ok, Port};
+ Error -> Error
+ end.
+
+-spec send(Socket :: socket(), Packet :: iolist()) -> % iolist()?
+ 'ok' | {'error', posix()}.
+
+send(Socket, Packet) ->
+ prim_inet:send(Socket, Packet).
+
+-spec setopts(Socket :: socket(), Opts :: [socket_setopt()]) ->
+ 'ok' | {'error', posix()}.
+
+setopts(Socket, Opts) ->
+ prim_inet:setopts(Socket, Opts).
+
+-spec getopts(Socket :: socket(), Opts :: [socket_getopt()]) ->
+ {'ok', [socket_setopt()]} | {'error', posix()}.
+
+getopts(Socket, Opts) ->
+ prim_inet:getopts(Socket, Opts).
+
+-spec getiflist(Socket :: socket()) ->
+ {'ok', [string()]} | {'error', posix()}.
+
+getiflist(Socket) ->
+ prim_inet:getiflist(Socket).
+
+-spec getiflist() -> {'ok', [string()]} | {'error', posix()}.
+
+getiflist() ->
+ withsocket(fun(S) -> prim_inet:getiflist(S) end).
+
+-spec ifget(Socket :: socket(),
+ Name :: string() | atom(),
+ Opts :: [if_getopt()]) ->
+ {'ok', [if_setopt()]} | {'error', posix()}.
+
+ifget(Socket, Name, Opts) ->
+ prim_inet:ifget(Socket, Name, Opts).
+
+-spec ifget(Name :: string() | atom(), Opts :: [if_getopt()]) ->
+ {'ok', [if_setopt()]} | {'error', posix()}.
+
+ifget(Name, Opts) ->
+ withsocket(fun(S) -> prim_inet:ifget(S, Name, Opts) end).
+
+-spec ifset(Socket :: socket(),
+ Name :: string() | atom(),
+ Opts :: [if_setopt()]) ->
+ 'ok' | {'error', posix()}.
+
+ifset(Socket, Name, Opts) ->
+ prim_inet:ifset(Socket, Name, Opts).
+
+-spec ifset(Name :: string() | atom(), Opts :: [if_setopt()]) ->
+ 'ok' | {'error', posix()}.
+
+ifset(Name, Opts) ->
+ withsocket(fun(S) -> prim_inet:ifset(S, Name, Opts) end).
+
+-spec getif() ->
+ {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} |
+ {'error', posix()}.
+
+getif() ->
+ withsocket(fun(S) -> getif(S) end).
+
+%% backwards compatible getif
+-spec getif(Socket :: socket()) ->
+ {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} |
+ {'error', posix()}.
+
+getif(Socket) ->
+ case prim_inet:getiflist(Socket) of
+ {ok, IfList} ->
+ {ok, lists:foldl(
+ fun(Name,Acc) ->
+ case prim_inet:ifget(Socket,Name,
+ [addr,broadaddr,netmask]) of
+ {ok,[{addr,A},{broadaddr,B},{netmask,M}]} ->
+ [{A,B,M}|Acc];
+ %% Some interfaces does not have a b-addr
+ {ok,[{addr,A},{netmask,M}]} ->
+ [{A,undefined,M}|Acc];
+ _ ->
+ Acc
+ end
+ end, [], IfList)};
+ Error -> Error
+ end.
+
+withsocket(Fun) ->
+ case inet_udp:open(0,[]) of
+ {ok,Socket} ->
+ Res = Fun(Socket),
+ inet_udp:close(Socket),
+ Res;
+ Error ->
+ Error
+ end.
+
+pushf(_Socket, Fun, _State) when is_function(Fun) ->
+ {error, einval}.
+
+popf(_Socket) ->
+ {error, einval}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% the hostname is not cached any more because this
+% could cause troubles on at least windows with plug-and-play
+% and network-cards inserted and removed in conjunction with
+% use of the DHCP-protocol
+% should never fail
+
+-spec gethostname() -> {'ok', string()}.
+
+gethostname() ->
+ case inet_udp:open(0,[]) of
+ {ok,U} ->
+ {ok,Res} = gethostname(U),
+ inet_udp:close(U),
+ {Res2,_} = lists:splitwith(fun($.)->false;(_)->true end,Res),
+ {ok, Res2};
+ _ ->
+ {ok, "nohost.nodomain"}
+ end.
+
+-spec gethostname(Socket :: socket()) ->
+ {'ok', string()} | {'error', posix()}.
+
+gethostname(Socket) ->
+ prim_inet:gethostname(Socket).
+
+-spec getstat(Socket :: socket()) ->
+ {'ok', [{stat_option(), integer()}]} | {'error', posix()}.
+
+getstat(Socket) ->
+ prim_inet:getstat(Socket, stats()).
+
+-spec getstat(Socket :: socket(), Statoptions :: [stat_option()]) ->
+ {'ok', [{stat_option(), integer()}]} | {'error', posix()}.
+
+getstat(Socket,What) ->
+ prim_inet:getstat(Socket, What).
+
+-spec gethostbyname(Name :: string() | atom()) ->
+ {'ok', #hostent{}} | {'error', posix()}.
+
+gethostbyname(Name) ->
+ gethostbyname_tm(Name, inet, false).
+
+-spec gethostbyname(Name :: string() | atom(), Family :: family_option()) ->
+ {'ok', #hostent{}} | {'error', posix()}.
+
+gethostbyname(Name,Family) ->
+ gethostbyname_tm(Name, Family, false).
+
+-spec gethostbyname(Name :: string() | atom(),
+ Family :: family_option(),
+ Timeout :: non_neg_integer() | 'infinity') ->
+ {'ok', #hostent{}} | {'error', posix()}.
+
+gethostbyname(Name,Family,Timeout) ->
+ Timer = start_timer(Timeout),
+ Res = gethostbyname_tm(Name,Family,Timer),
+ stop_timer(Timer),
+ Res.
+
+gethostbyname_tm(Name,Family,Timer) ->
+ gethostbyname_tm(Name,Family,Timer,inet_db:res_option(lookup)).
+
+
+-spec gethostbyaddr(Address :: string() | ip_address()) ->
+ {'ok', #hostent{}} | {'error', posix()}.
+
+gethostbyaddr(Address) ->
+ gethostbyaddr_tm(Address, false).
+
+-spec gethostbyaddr(Address :: string() | ip_address(),
+ Timeout :: non_neg_integer() | 'infinity') ->
+ {'ok', #hostent{}} | {'error', posix()}.
+
+gethostbyaddr(Address,Timeout) ->
+ Timer = start_timer(Timeout),
+ Res = gethostbyaddr_tm(Address, Timer),
+ stop_timer(Timer),
+ Res.
+
+gethostbyaddr_tm(Address,Timer) ->
+ gethostbyaddr_tm(Address, Timer, inet_db:res_option(lookup)).
+
+-spec ip(Ip :: ip_address() | string() | atom()) ->
+ {'ok', ip_address()} | {'error', posix()}.
+
+ip({A,B,C,D}) when ?ip(A,B,C,D) ->
+ {ok, {A,B,C,D}};
+ip(Name) ->
+ case gethostbyname(Name) of
+ {ok, Ent} ->
+ {ok, hd(Ent#hostent.h_addr_list)};
+ Error -> Error
+ end.
+
+%% This function returns the erlang port used (with inet_drv)
+
+-spec getll(Socket :: socket()) -> {'ok', socket()}.
+
+getll(Socket) when is_port(Socket) ->
+ {ok, Socket}.
+
+%%
+%% Return the internal file descriptor number
+%%
+
+-spec getfd(Socket :: socket()) ->
+ {'ok', non_neg_integer()} | {'error', posix()}.
+
+getfd(Socket) ->
+ prim_inet:getfd(Socket).
+
+%%
+%% Lookup an ip address
+%%
+
+-spec getaddr(Host :: ip_address() | string() | atom(),
+ Family :: family_option()) ->
+ {'ok', ip_address()} | {'error', posix()}.
+
+getaddr(Address, Family) ->
+ getaddr(Address, Family, infinity).
+
+-spec getaddr(Host :: ip_address() | string() | atom(),
+ Family :: family_option(),
+ Timeout :: non_neg_integer() | 'infinity') ->
+ {'ok', ip_address()} | {'error', posix()}.
+
+getaddr(Address, Family, Timeout) ->
+ Timer = start_timer(Timeout),
+ Res = getaddr_tm(Address, Family, Timer),
+ stop_timer(Timer),
+ Res.
+
+getaddr_tm(Address, Family, Timer) ->
+ case getaddrs_tm(Address, Family, Timer) of
+ {ok, [IP|_]} -> {ok, IP};
+ Error -> Error
+ end.
+
+-spec getaddrs(Host :: ip_address() | string() | atom(),
+ Family :: family_option()) ->
+ {'ok', [ip_address()]} | {'error', posix()}.
+
+getaddrs(Address, Family) ->
+ getaddrs(Address, Family, infinity).
+
+-spec getaddrs(Host :: ip_address() | string() | atom(),
+ Family :: family_option(),
+ Timeout :: non_neg_integer() | 'infinity') ->
+ {'ok', [ip_address()]} | {'error', posix()}.
+
+getaddrs(Address, Family, Timeout) ->
+ Timer = start_timer(Timeout),
+ Res = getaddrs_tm(Address, Family, Timer),
+ stop_timer(Timer),
+ Res.
+
+-spec getservbyport(Port :: ip_port(), Protocol :: atom() | string()) ->
+ {'ok', string()} | {'error', posix()}.
+
+getservbyport(Port, Proto) ->
+ case inet_udp:open(0, []) of
+ {ok,U} ->
+ Res = prim_inet:getservbyport(U, Port, Proto),
+ inet_udp:close(U),
+ Res;
+ Error -> Error
+ end.
+
+-spec getservbyname(Name :: atom() | string(),
+ Protocol :: atom() | string()) ->
+ {'ok', ip_port()} | {'error', posix()}.
+
+getservbyname(Name, Protocol) when is_atom(Name) ->
+ case inet_udp:open(0, []) of
+ {ok,U} ->
+ Res = prim_inet:getservbyname(U, Name, Protocol),
+ inet_udp:close(U),
+ Res;
+ Error -> Error
+ end.
+
+%% Return a list of available options
+options() ->
+ [
+ tos, priority, reuseaddr, keepalive, dontroute, linger,
+ broadcast, sndbuf, recbuf, nodelay,
+ buffer, header, active, packet, deliver, mode,
+ multicast_if, multicast_ttl, multicast_loop,
+ exit_on_close, high_watermark, low_watermark,
+ bit8, send_timeout, send_timeout_close
+ ].
+
+%% Return a list of statistics options
+
+-spec stats() -> [stat_option(),...].
+
+stats() ->
+ [recv_oct, recv_cnt, recv_max, recv_avg, recv_dvi,
+ send_oct, send_cnt, send_max, send_avg, send_pend].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Available options for tcp:connect
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+connect_options() ->
+ [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ header, active, packet, packet_size, buffer, mode, deliver,
+ exit_on_close, high_watermark, low_watermark, bit8, send_timeout,
+ send_timeout_close, delay_send,raw].
+
+connect_options(Opts, Family) ->
+ BaseOpts =
+ case application:get_env(kernel, inet_default_connect_options) of
+ {ok,List} when is_list(List) ->
+ NList = [{active, true} | lists:keydelete(active,1,List)],
+ #connect_opts{ opts = NList};
+ {ok,{active,_Bool}} ->
+ #connect_opts{ opts = [{active,true}]};
+ {ok,Option} ->
+ #connect_opts{ opts = [{active,true}, Option]};
+ _ ->
+ #connect_opts{ opts = [{active,true}]}
+ end,
+ case con_opt(Opts, BaseOpts, connect_options()) of
+ {ok, R} ->
+ {ok, R#connect_opts {
+ ifaddr = translate_ip(R#connect_opts.ifaddr, Family)
+ }};
+ Error -> Error
+ end.
+
+con_opt([{raw,A,B,C}|Opts],R,As) ->
+ con_opt([{raw,{A,B,C}}|Opts],R,As);
+con_opt([Opt | Opts], R, As) ->
+ case Opt of
+ {ip,IP} -> con_opt(Opts, R#connect_opts { ifaddr = IP }, As);
+ {ifaddr,IP} -> con_opt(Opts, R#connect_opts { ifaddr = IP }, As);
+ {port,P} -> con_opt(Opts, R#connect_opts { port = P }, As);
+ {fd,Fd} -> con_opt(Opts, R#connect_opts { fd = Fd }, As);
+ binary -> con_add(mode, binary, R, Opts, As);
+ list -> con_add(mode, list, R, Opts, As);
+ {tcp_module,_} -> con_opt(Opts, R, As);
+ inet -> con_opt(Opts, R, As);
+ inet6 -> con_opt(Opts, R, As);
+ {Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As);
+ _ -> {error, badarg}
+ end;
+con_opt([], R, _) ->
+ {ok, R}.
+
+con_add(Name, Val, R, Opts, AllOpts) ->
+ case add_opt(Name, Val, R#connect_opts.opts, AllOpts) of
+ {ok, SOpts} ->
+ con_opt(Opts, R#connect_opts { opts = SOpts }, AllOpts);
+ Error -> Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Available options for tcp:listen
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+listen_options() ->
+ [tos, priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
+ header, active, packet, buffer, mode, deliver, backlog,
+ exit_on_close, high_watermark, low_watermark, bit8, send_timeout,
+ send_timeout_close, delay_send, packet_size,raw].
+
+listen_options(Opts, Family) ->
+ BaseOpts =
+ case application:get_env(kernel, inet_default_listen_options) of
+ {ok,List} when is_list(List) ->
+ NList = [{active, true} | lists:keydelete(active,1,List)],
+ #listen_opts{ opts = NList};
+ {ok,{active,_Bool}} ->
+ #listen_opts{ opts = [{active,true}]};
+ {ok,Option} ->
+ #listen_opts{ opts = [{active,true}, Option]};
+ _ ->
+ #listen_opts{ opts = [{active,true}]}
+ end,
+ case list_opt(Opts, BaseOpts, listen_options()) of
+ {ok, R} ->
+ {ok, R#listen_opts {
+ ifaddr = translate_ip(R#listen_opts.ifaddr, Family)
+ }};
+ Error -> Error
+ end.
+
+list_opt([{raw,A,B,C}|Opts], R, As) ->
+ list_opt([{raw,{A,B,C}}|Opts], R, As);
+list_opt([Opt | Opts], R, As) ->
+ case Opt of
+ {ip,IP} -> list_opt(Opts, R#listen_opts { ifaddr = IP }, As);
+ {ifaddr,IP} -> list_opt(Opts, R#listen_opts { ifaddr = IP }, As);
+ {port,P} -> list_opt(Opts, R#listen_opts { port = P }, As);
+ {fd,Fd} -> list_opt(Opts, R#listen_opts { fd = Fd }, As);
+ {backlog,BL} -> list_opt(Opts, R#listen_opts { backlog = BL }, As);
+ binary -> list_add(mode, binary, R, Opts, As);
+ list -> list_add(mode, list, R, Opts, As);
+ {tcp_module,_} -> list_opt(Opts, R, As);
+ inet -> list_opt(Opts, R, As);
+ inet6 -> list_opt(Opts, R, As);
+ {Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As);
+ _ -> {error, badarg}
+ end;
+list_opt([], R, _SockOpts) ->
+ {ok, R}.
+
+list_add(Name, Val, R, Opts, As) ->
+ case add_opt(Name, Val, R#listen_opts.opts, As) of
+ {ok, SOpts} ->
+ list_opt(Opts, R#listen_opts { opts = SOpts }, As);
+ Error -> Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Available options for udp:open
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+udp_options() ->
+ [tos, priority, reuseaddr, sndbuf, recbuf, header, active, buffer, mode,
+ deliver,
+ broadcast, dontroute, multicast_if, multicast_ttl, multicast_loop,
+ add_membership, drop_membership, read_packets,raw].
+
+
+udp_options(Opts, Family) ->
+ case udp_opt(Opts, #udp_opts { }, udp_options()) of
+ {ok, R} ->
+ {ok, R#udp_opts {
+ ifaddr = translate_ip(R#udp_opts.ifaddr, Family)
+ }};
+ Error -> Error
+ end.
+
+udp_opt([{raw,A,B,C}|Opts], R, As) ->
+ udp_opt([{raw,{A,B,C}}|Opts], R, As);
+udp_opt([Opt | Opts], R, As) ->
+ case Opt of
+ {ip,IP} -> udp_opt(Opts, R#udp_opts { ifaddr = IP }, As);
+ {ifaddr,IP} -> udp_opt(Opts, R#udp_opts { ifaddr = IP }, As);
+ {port,P} -> udp_opt(Opts, R#udp_opts { port = P }, As);
+ {fd,Fd} -> udp_opt(Opts, R#udp_opts { fd = Fd }, As);
+ binary -> udp_add(mode, binary, R, Opts, As);
+ list -> udp_add(mode, list, R, Opts, As);
+ {udp_module,_} -> udp_opt(Opts, R, As);
+ inet -> udp_opt(Opts, R, As);
+ inet6 -> udp_opt(Opts, R, As);
+ {Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As);
+ _ -> {error, badarg}
+ end;
+udp_opt([], R, _SockOpts) ->
+ {ok, R}.
+
+udp_add(Name, Val, R, Opts, As) ->
+ case add_opt(Name, Val, R#udp_opts.opts, As) of
+ {ok, SOpts} ->
+ udp_opt(Opts, R#udp_opts { opts = SOpts }, As);
+ Error -> Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Available options for sctp:open
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Currently supported options include:
+% (*) {mode, list|binary} or just list|binary
+% (*) {active, true|false|once}
+% (*) {sctp_module, inet_sctp|inet6_sctp} or just inet|inet6
+% (*) options set via setsockopt.
+% The full list is below in sctp_options/0 .
+% All other options are currently NOT supported. In particular:
+% (*) multicast on SCTP is not (yet) supported, as it may be incompatible
+% with automatic associations;
+% (*) passing of open FDs ("fdopen") is not supported.
+sctp_options() ->
+[ % The following are generic inet options supported for SCTP sockets:
+ mode, active, buffer, tos, priority, dontroute, reuseaddr, linger, sndbuf,
+ recbuf,
+
+ % Other options are SCTP-specific (though they may be similar to their
+ % TCP and UDP counter-parts):
+ sctp_rtoinfo, sctp_associnfo, sctp_initmsg,
+ sctp_autoclose, sctp_nodelay, sctp_disable_fragments,
+ sctp_i_want_mapped_v4_addr, sctp_maxseg, sctp_primary_addr,
+ sctp_set_peer_primary_addr, sctp_adaptation_layer, sctp_peer_addr_params,
+ sctp_default_send_param, sctp_events, sctp_delayed_ack_time,
+ sctp_status, sctp_get_peer_addr_info
+].
+
+sctp_options(Opts, Mod) ->
+ case sctp_opt(Opts, Mod, #sctp_opts{}, sctp_options()) of
+ {ok,#sctp_opts{ifaddr=undefined}=SO} ->
+ {ok,SO#sctp_opts{ifaddr=Mod:translate_ip(?SCTP_DEF_IFADDR)}};
+ {ok,_}=OK ->
+ OK;
+ Error -> Error
+ end.
+
+sctp_opt([Opt|Opts], Mod, R, As) ->
+ case Opt of
+ {ip,IP} ->
+ sctp_opt_ifaddr(Opts, Mod, R, As, IP);
+ {ifaddr,IP} ->
+ sctp_opt_ifaddr(Opts, Mod, R, As, IP);
+ {port,Port} ->
+ case Mod:getserv(Port) of
+ {ok,P} ->
+ sctp_opt(Opts, Mod, R#sctp_opts{port=P}, As);
+ Error -> Error
+ end;
+ binary -> sctp_opt (Opts, Mod, R, As, mode, binary);
+ list -> sctp_opt (Opts, Mod, R, As, mode, list);
+ {sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with
+ inet -> sctp_opt (Opts, Mod, R, As); % Done with
+ inet6 -> sctp_opt (Opts, Mod, R, As); % Done with
+ {Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val);
+ _ -> {error,badarg}
+ end;
+sctp_opt([], _Mod, R, _SockOpts) ->
+ {ok, R}.
+
+sctp_opt(Opts, Mod, R, As, Name, Val) ->
+ case add_opt(Name, Val, R#sctp_opts.opts, As) of
+ {ok,SocketOpts} ->
+ sctp_opt(Opts, Mod, R#sctp_opts{opts=SocketOpts}, As);
+ Error -> Error
+ end.
+
+sctp_opt_ifaddr(Opts, Mod, #sctp_opts{ifaddr=IfAddr}=R, As, Addr) ->
+ IP = Mod:translate_ip(Addr),
+ sctp_opt(Opts, Mod,
+ R#sctp_opts{
+ ifaddr=case IfAddr of
+ undefined -> IP;
+ _ when is_list(IfAddr) -> [IP|IfAddr];
+ _ -> [IP,IfAddr]
+ end}, As).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Util to check and insert option in option list
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_opt(Name, Val, Opts, As) ->
+ case lists:member(Name, As) of
+ true ->
+ case prim_inet:is_sockopt_val(Name, Val) of
+ true ->
+ Opts1 = lists:keydelete(Name, 1, Opts),
+ {ok, [{Name,Val} | Opts1]};
+ false -> {error,badarg}
+ end;
+ false -> {error,badarg}
+ end.
+
+
+translate_ip(any, inet) -> {0,0,0,0};
+translate_ip(loopback, inet) -> {127,0,0,1};
+translate_ip(any, inet6) -> {0,0,0,0,0,0,0,0};
+translate_ip(loopback, inet6) -> {0,0,0,0,0,0,0,1};
+translate_ip(IP, _) -> IP.
+
+
+getaddrs_tm({A,B,C,D} = IP, Fam, _) ->
+ %% Only "syntactic" validation and check of family.
+ if
+ ?ip(A,B,C,D) ->
+ if
+ Fam =:= inet -> {ok,[IP]};
+ true -> {error,eafnosupport}
+ end;
+ true -> {error,einval}
+ end;
+getaddrs_tm({A,B,C,D,E,F,G,H} = IP, Fam, _) ->
+ %% Only "syntactic" validation; we assume that the address was
+ %% "semantically" validated when it was converted to a tuple.
+ if
+ ?ip6(A,B,C,D,E,F,G,H) ->
+ if
+ Fam =:= inet6 -> {ok,[IP]};
+ true -> {error,eafnosupport}
+ end;
+ true -> {error,einval}
+ end;
+getaddrs_tm(Address, Family, Timer) when is_atom(Address) ->
+ getaddrs_tm(atom_to_list(Address), Family, Timer);
+getaddrs_tm(Address, Family, Timer) ->
+ case inet_parse:visible_string(Address) of
+ false ->
+ {error,einval};
+ true ->
+ %% Address is a host name or a valid IP address,
+ %% either way check it with the resolver.
+ case gethostbyname_tm(Address, Family, Timer) of
+ {ok,Ent} -> {ok,Ent#hostent.h_addr_list};
+ Error -> Error
+ end
+ end.
+
+%%
+%% gethostbyname with option search
+%%
+gethostbyname_tm(Name, Type, Timer, [dns | Opts]) ->
+ Res = inet_res:gethostbyname_tm(Name, Type, Timer),
+ case Res of
+ {ok,_} -> Res;
+ {error,timeout} -> Res;
+ {error,formerr} -> {error,einval};
+ {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts)
+ end;
+gethostbyname_tm(Name, Type, Timer, [file | Opts]) ->
+ case inet_hosts:gethostbyname(Name, Type) of
+ {error,formerr} -> {error,einval};
+ {error,_} -> gethostbyname_tm(Name,Type,Timer,Opts);
+ Result -> Result
+ end;
+gethostbyname_tm(Name, Type, Timer, [yp | Opts]) ->
+ gethostbyname_tm_native(Name, Type, Timer, Opts);
+gethostbyname_tm(Name, Type, Timer, [nis | Opts]) ->
+ gethostbyname_tm_native(Name, Type, Timer, Opts);
+gethostbyname_tm(Name, Type, Timer, [nisplus | Opts]) ->
+ gethostbyname_tm_native(Name, Type, Timer, Opts);
+gethostbyname_tm(Name, Type, Timer, [wins | Opts]) ->
+ gethostbyname_tm_native(Name, Type, Timer, Opts);
+gethostbyname_tm(Name, Type, Timer, [native | Opts]) ->
+ gethostbyname_tm_native(Name, Type, Timer, Opts);
+gethostbyname_tm(_, _, _, [no_default|_]) ->
+ %% If the native resolver has failed, we should not bother
+ %% to try to be smarter and parse the IP address here.
+ {error,nxdomain};
+gethostbyname_tm(Name, Type, Timer, [_ | Opts]) ->
+ gethostbyname_tm(Name, Type, Timer, Opts);
+%% Last resort - parse the hostname as address
+gethostbyname_tm(Name, inet, _Timer, []) ->
+ case inet_parse:ipv4_address(Name) of
+ {ok,IP4} ->
+ {ok,make_hostent(Name, [IP4], [], inet)};
+ _ ->
+ gethostbyname_self(Name)
+ end;
+gethostbyname_tm(Name, inet6, _Timer, []) ->
+ case inet_parse:ipv6_address(Name) of
+ {ok,IP6} ->
+ {ok,make_hostent(Name, [IP6], [], inet6)};
+ _ ->
+ %% Even if Name is a valid IPv4 address, we can't
+ %% assume it's correct to return it on a IPv6
+ %% format ( {0,0,0,0,0,16#ffff,?u16(A,B),?u16(C,D)} ).
+ %% This host might not support IPv6.
+ gethostbyname_self(Name)
+ end.
+
+gethostbyname_tm_native(Name, Type, Timer, Opts) ->
+ %% Fixme: add (global) timeout to gethost_native
+ case inet_gethost_native:gethostbyname(Name, Type) of
+ {error,formerr} -> {error,einval};
+ {error,timeout} -> {error,timeout};
+ {error,_} -> gethostbyname_tm(Name, Type, Timer, Opts++[no_default]);
+ Result -> Result
+ end.
+
+%% Make sure we always can look up our own hostname.
+gethostbyname_self(Name) ->
+ Type = case inet_db:res_option(inet6) of
+ true -> inet6;
+ false -> inet
+ end,
+ case inet_db:gethostname() of
+ Name ->
+ {ok,make_hostent(Name, [translate_ip(loopback, Type)],
+ [], Type)};
+ Self ->
+ case inet_db:res_option(domain) of
+ "" -> {error,nxdomain};
+ Domain ->
+ case lists:append([Self,".",Domain]) of
+ Name ->
+ {ok,make_hostent(Name,
+ [translate_ip(loopback, Type)],
+ [], Type)};
+ _ -> {error,nxdomain}
+ end
+ end
+ end.
+
+make_hostent(Name, Addrs, Aliases, Type) ->
+ #hostent{h_name = Name,
+ h_aliases = Aliases,
+ h_addrtype = Type,
+ h_length = case Type of inet -> 4; inet6 -> 16 end,
+ h_addr_list = Addrs}.
+
+%%
+%% gethostbyaddr with option search
+%%
+gethostbyaddr_tm(Addr, Timer, [dns | Opts]) ->
+ Res = inet_res:gethostbyaddr_tm(Addr,Timer),
+ case Res of
+ {ok,_} -> Res;
+ {error,timeout} -> Res;
+ {error,formerr} -> {error, einval};
+ {error,_} -> gethostbyaddr_tm(Addr,Timer,Opts)
+ end;
+gethostbyaddr_tm(Addr, Timer, [file | Opts]) ->
+ case inet_hosts:gethostbyaddr(Addr) of
+ {error,formerr} -> {error, einval};
+ {error,_} -> gethostbyaddr_tm(Addr,Timer,Opts);
+ Result -> Result
+ end;
+gethostbyaddr_tm(Addr, Timer, [yp | Opts]) ->
+ gethostbyaddr_tm_native(Addr, Timer, Opts);
+gethostbyaddr_tm(Addr, Timer, [nis | Opts]) ->
+ gethostbyaddr_tm_native(Addr, Timer, Opts);
+gethostbyaddr_tm(Addr, Timer, [nisplus | Opts]) ->
+ gethostbyaddr_tm_native(Addr, Timer, Opts);
+gethostbyaddr_tm(Addr, Timer, [wins | Opts]) ->
+ gethostbyaddr_tm_native(Addr, Timer, Opts);
+gethostbyaddr_tm(Addr, Timer, [native | Opts]) ->
+ gethostbyaddr_tm_native(Addr, Timer, Opts);
+gethostbyaddr_tm(Addr, Timer, [_ | Opts]) ->
+ gethostbyaddr_tm(Addr, Timer, Opts);
+gethostbyaddr_tm({127,0,0,1}=IP, _Timer, []) ->
+ gethostbyaddr_self(IP, inet);
+gethostbyaddr_tm({0,0,0,0,0,0,0,1}=IP, _Timer, []) ->
+ gethostbyaddr_self(IP, inet6);
+gethostbyaddr_tm(_Addr, _Timer, []) ->
+ {error, nxdomain}.
+
+gethostbyaddr_self(IP, Type) ->
+ Name = inet_db:gethostname(),
+ case inet_db:res_option(domain) of
+ "" ->
+ {ok,make_hostent(Name, [IP], [], Type)};
+ Domain ->
+ {ok,make_hostent(Name++"."++Domain, [IP], [Name], Type)}
+ end.
+
+gethostbyaddr_tm_native(Addr, Timer, Opts) ->
+ %% Fixme: user timer for timeoutvalue
+ case inet_gethost_native:gethostbyaddr(Addr) of
+ {error,formerr} -> {error, einval};
+ {error,_} -> gethostbyaddr_tm(Addr,Timer,Opts);
+ Result -> Result
+ end.
+
+-spec open(Fd :: integer(),
+ Addr :: ip_address(),
+ Port :: ip_port(),
+ Opts :: [socket_setopt()],
+ Protocol :: protocol_option(),
+ Family :: 'inet' | 'inet6',
+ Module :: atom()) ->
+ {'ok', socket()} | {'error', posix()}.
+
+open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
+ case prim_inet:open(Protocol, Family) of
+ {ok,S} ->
+ case prim_inet:setopts(S, Opts) of
+ ok ->
+ case if is_list(Addr) ->
+ prim_inet:bind(S, add,
+ [case A of
+ {_,_} -> A;
+ _ -> {A,Port}
+ end || A <- Addr]);
+ true ->
+ prim_inet:bind(S, Addr, Port)
+ end of
+ {ok, _} ->
+ inet_db:register_socket(S, Module),
+ {ok,S};
+ Error ->
+ prim_inet:close(S),
+ Error
+ end;
+ Error ->
+ prim_inet:close(S),
+ Error
+ end;
+ Error ->
+ Error
+ end;
+open(Fd, _Addr, _Port, Opts, Protocol, Family, Module) ->
+ fdopen(Fd, Opts, Protocol, Family, Module).
+
+-spec fdopen(Fd :: non_neg_integer(),
+ Opts :: [socket_setopt()],
+ Protocol :: protocol_option(),
+ Family :: family_option(),
+ Module :: atom()) ->
+ {'ok', socket()} | {'error', posix()}.
+
+fdopen(Fd, Opts, Protocol, Family, Module) ->
+ case prim_inet:fdopen(Protocol, Fd, Family) of
+ {ok, S} ->
+ case prim_inet:setopts(S, Opts) of
+ ok ->
+ inet_db:register_socket(S, Module),
+ {ok, S};
+ Error ->
+ prim_inet:close(S), Error
+ end;
+ Error -> Error
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% socket stat
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+i() -> i(tcp), i(udp).
+
+i(Proto) -> i(Proto, [port, module, recv, sent, owner,
+ local_address, foreign_address, state]).
+
+i(tcp, Fs) ->
+ ii(tcp_sockets(), Fs, tcp);
+i(udp, Fs) ->
+ ii(udp_sockets(), Fs, udp).
+
+ii(Ss, Fs, Proto) ->
+ LLs = [h_line(Fs) | info_lines(Ss, Fs, Proto)],
+ Maxs = foldl(
+ fun(Line,Max0) -> smax(Max0,Line) end,
+ duplicate(length(Fs),0),LLs),
+ Fmt = append(["~-" ++ integer_to_list(N) ++ "s " || N <- Maxs]) ++ "\n",
+ lists:foreach(fun(Line) -> io:format(Fmt, Line) end, LLs).
+
+smax([Max|Ms], [Str|Strs]) ->
+ N = length(Str),
+ [if N > Max -> N; true -> Max end | smax(Ms, Strs)];
+smax([], []) -> [].
+
+info_lines(Ss, Fs, Proto) -> [i_line(S, Fs,Proto) || S <- Ss].
+i_line(S, Fs, Proto) -> [info(S, F, Proto) || F <- Fs].
+
+h_line(Fs) -> [h_field(atom_to_list(F)) || F <- Fs].
+
+h_field([C|Cs]) -> [upper(C) | hh_field(Cs)].
+
+hh_field([$_,C|Cs]) -> [$\s,upper(C) | hh_field(Cs)];
+hh_field([C|Cs]) -> [C|hh_field(Cs)];
+hh_field([]) -> [].
+
+upper(C) when C >= $a, C =< $z -> (C-$a) + $A;
+upper(C) -> C.
+
+
+info(S, F, Proto) ->
+ case F of
+ owner ->
+ case erlang:port_info(S, connected) of
+ {connected, Owner} -> pid_to_list(Owner);
+ _ -> " "
+ end;
+ port ->
+ case erlang:port_info(S,id) of
+ {id, Id} -> integer_to_list(Id);
+ undefined -> " "
+ end;
+ sent ->
+ case prim_inet:getstat(S, [send_oct]) of
+ {ok,[{send_oct,N}]} -> integer_to_list(N);
+ _ -> " "
+ end;
+ recv ->
+ case prim_inet:getstat(S, [recv_oct]) of
+ {ok,[{recv_oct,N}]} -> integer_to_list(N);
+ _ -> " "
+ end;
+ local_address ->
+ fmt_addr(prim_inet:sockname(S), Proto);
+ foreign_address ->
+ fmt_addr(prim_inet:peername(S), Proto);
+ state ->
+ case prim_inet:getstatus(S) of
+ {ok,Status} -> fmt_status(Status);
+ _ -> " "
+ end;
+ packet ->
+ case prim_inet:getopt(S, packet) of
+ {ok,Type} when is_atom(Type) -> atom_to_list(Type);
+ {ok,Type} when is_integer(Type) -> integer_to_list(Type);
+ _ -> " "
+ end;
+ type ->
+ case prim_inet:gettype(S) of
+ {ok,{_,stream}} -> "STREAM";
+ {ok,{_,dgram}} -> "DGRAM";
+ _ -> " "
+ end;
+ fd ->
+ case prim_inet:getfd(S) of
+ {ok, Fd} -> integer_to_list(Fd);
+ _ -> " "
+ end;
+ module ->
+ case inet_db:lookup_socket(S) of
+ {ok,Mod} -> atom_to_list(Mod);
+ _ -> "prim_inet"
+ end
+ end.
+%% Possible flags: (sorted)
+%% [accepting,bound,busy,connected,connecting,listen,listening,open]
+%%
+fmt_status(Flags) ->
+ case lists:sort(Flags) of
+ [accepting | _] -> "ACCEPTING";
+ [bound,busy,connected|_] -> "CONNECTED*";
+ [bound,connected|_] -> "CONNECTED";
+ [bound,listen,listening | _] -> "LISTENING";
+ [bound,listen | _] -> "LISTEN";
+ [bound,connecting | _] -> "CONNECTING";
+ [bound,open] -> "BOUND";
+ [open] -> "IDLE";
+ [] -> "CLOSED";
+ _ -> "????"
+ end.
+
+fmt_addr({error,enotconn}, _) -> "*:*";
+fmt_addr({error,_}, _) -> " ";
+fmt_addr({ok,Addr}, Proto) ->
+ case Addr of
+ %%Dialyzer {0,0} -> "*:*";
+ {{0,0,0,0},Port} -> "*:" ++ fmt_port(Port, Proto);
+ {{0,0,0,0,0,0,0,0},Port} -> "*:" ++ fmt_port(Port, Proto);
+ {{127,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto);
+ {{0,0,0,0,0,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto);
+ {IP,Port} -> inet_parse:ntoa(IP) ++ ":" ++ fmt_port(Port, Proto)
+ end.
+
+fmt_port(N, Proto) ->
+ case inet:getservbyport(N, Proto) of
+ {ok, Name} -> Name;
+ _ -> integer_to_list(N)
+ end.
+
+%% Return a list of all tcp sockets
+tcp_sockets() -> port_list("tcp_inet").
+udp_sockets() -> port_list("udp_inet").
+
+%% Return all ports having the name 'Name'
+port_list(Name) ->
+ filter(
+ fun(Port) ->
+ case erlang:port_info(Port, name) of
+ {name, Name} -> true;
+ _ -> false
+ end
+ end, erlang:ports()).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% utils
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-spec format_error(posix()) -> string().
+
+format_error(exbadport) -> "invalid port state";
+format_error(exbadseq) -> "bad command sequence";
+format_error(Tag) ->
+ erl_posix_msg:message(Tag).
+
+%% Close a TCP socket.
+tcp_close(S) when is_port(S) ->
+ %% if exit_on_close is set we must force a close even if remotely closed!!!
+ prim_inet:close(S),
+ receive {tcp_closed, S} -> ok after 0 -> ok end.
+
+%% Close a UDP socket.
+udp_close(S) when is_port(S) ->
+ receive
+ {udp_closed, S} -> ok
+ after 0 ->
+ prim_inet:close(S),
+ receive {udp_closed, S} -> ok after 0 -> ok end
+ end.
+
+%% Set controlling process for TCP socket.
+tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
+ case erlang:port_info(S, connected) of
+ {connected, Pid} when Pid =/= self() ->
+ {error, not_owner};
+ undefined ->
+ {error, einval};
+ _ ->
+ case prim_inet:getopt(S, active) of
+ {ok, A0} ->
+ prim_inet:setopt(S, active, false),
+ case tcp_sync_input(S, NewOwner, false) of
+ true -> %% socket already closed,
+ ok;
+ false ->
+ try erlang:port_connect(S, NewOwner) of
+ true ->
+ unlink(S), %% unlink from port
+ prim_inet:setopt(S, active, A0),
+ ok
+ catch
+ error:Reason ->
+ {error, Reason}
+ end
+ end;
+ Error ->
+ Error
+ end
+ end.
+
+tcp_sync_input(S, Owner, Flag) ->
+ receive
+ {tcp, S, Data} ->
+ Owner ! {tcp, S, Data},
+ tcp_sync_input(S, Owner, Flag);
+ {tcp_closed, S} ->
+ Owner ! {tcp_closed, S},
+ tcp_sync_input(S, Owner, true);
+ {S, {data, Data}} ->
+ Owner ! {S, {data, Data}},
+ tcp_sync_input(S, Owner, Flag);
+ {inet_async, S, Ref, Status} ->
+ Owner ! {inet_async, S, Ref, Status},
+ tcp_sync_input(S, Owner, Flag);
+ {inet_reply, S, Status} ->
+ Owner ! {inet_reply, S, Status},
+ tcp_sync_input(S, Owner, Flag)
+ after 0 ->
+ Flag
+ end.
+
+%% Set controlling process for UDP or SCTP socket.
+udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) ->
+ case erlang:port_info(S, connected) of
+ {connected, Pid} when Pid =/= self() ->
+ {error, not_owner};
+ _ ->
+ {ok, A0} = prim_inet:getopt(S, active),
+ prim_inet:setopt(S, active, false),
+ udp_sync_input(S, NewOwner),
+ try erlang:port_connect(S, NewOwner) of
+ true ->
+ unlink(S),
+ prim_inet:setopt(S, active, A0),
+ ok
+ catch
+ error:Reason ->
+ {error, Reason}
+ end
+ end.
+
+udp_sync_input(S, Owner) ->
+ receive
+ {sctp, S, _, _, _}=Msg -> udp_sync_input(S, Owner, Msg);
+ {udp, S, _, _, _}=Msg -> udp_sync_input(S, Owner, Msg);
+ {udp_closed, S}=Msg -> udp_sync_input(S, Owner, Msg);
+ {S, {data,_}}=Msg -> udp_sync_input(S, Owner, Msg);
+ {inet_async, S, _, _}=Msg -> udp_sync_input(S, Owner, Msg);
+ {inet_reply, S, _}=Msg -> udp_sync_input(S, Owner, Msg)
+ after 0 ->
+ ok
+ end.
+
+udp_sync_input(S, Owner, Msg) ->
+ Owner ! Msg,
+ udp_sync_input(S, Owner).
+
+start_timer(infinity) -> false;
+start_timer(Timeout) ->
+ erlang:start_timer(Timeout, self(), inet).
+
+timeout(false) -> infinity;
+timeout(Timer) ->
+ case erlang:read_timer(Timer) of
+ false -> 0;
+ Time -> Time
+ end.
+
+timeout(Time, false) -> Time;
+timeout(Time, Timer) ->
+ TimerTime = timeout(Timer),
+ if TimerTime < Time -> TimerTime;
+ true -> Time
+ end.
+
+stop_timer(false) -> false;
+stop_timer(Timer) ->
+ case erlang:cancel_timer(Timer) of
+ false ->
+ receive
+ {timeout,Timer,_} -> false
+ after 0 ->
+ false
+ end;
+ T -> T
+ end.