diff options
Diffstat (limited to 'lib/kernel/src/gen_sctp.erl')
-rw-r--r-- | lib/kernel/src/gen_sctp.erl | 246 |
1 files changed, 215 insertions, 31 deletions
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d3582f89e6..f53a9dca3e 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2021. All Rights Reserved. +%% Copyright Ericsson AB 2007-2022. 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. @@ -30,16 +30,33 @@ -export([open/0, open/1, open/2, close/1]). -export([listen/2, peeloff/2]). -export([connect/3, connect/4, connect/5, - connect_init/3, connect_init/4, connect_init/5]). + connect_init/3, connect_init/4, connect_init/5, + connectx_init/3, connectx_init/4,connectx_init/5]). -export([eof/2, abort/2]). -export([send/3, send/4, recv/1, recv/2]). -export([error_string/1]). -export([controlling_process/2]). -type assoc_id() :: term(). + -type option() :: + elementary_option() | + record_option(). + +-type option_name() :: + elementary_option_name() | + record_option() | + ro_option(). + +-type option_value() :: + elementary_option() | + record_option() | + ro_option(). + +-type elementary_option() :: {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | + {debug, boolean()} | {dontroute, boolean()} | {high_msgq_watermark, pos_integer()} | {linger, {boolean(), non_neg_integer()}} | @@ -49,33 +66,23 @@ {recbuf, non_neg_integer()} | {reuseaddr, boolean()} | {ipv6_v6only, boolean()} | - {sctp_adaptation_layer, #sctp_setadaptation{}} | - {sctp_associnfo, #sctp_assocparams{}} | + {sndbuf, non_neg_integer()} | {sctp_autoclose, non_neg_integer()} | - {sctp_default_send_param, #sctp_sndrcvinfo{}} | - {sctp_delayed_ack_time, #sctp_assoc_value{}} | {sctp_disable_fragments, boolean()} | - {sctp_events, #sctp_event_subscribe{}} | - {sctp_get_peer_addr_info, #sctp_paddrinfo{}} | {sctp_i_want_mapped_v4_addr, boolean()} | - {sctp_initmsg, #sctp_initmsg{}} | {sctp_maxseg, non_neg_integer()} | {sctp_nodelay, boolean()} | - {sctp_peer_addr_params, #sctp_paddrparams{}} | - {sctp_primary_addr, #sctp_prim{}} | - {sctp_rtoinfo, #sctp_rtoinfo{}} | - {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} | - {sctp_status, #sctp_status{}} | - {sndbuf, non_neg_integer()} | {tos, non_neg_integer()} | {tclass, non_neg_integer()} | {ttl, non_neg_integer()} | {recvtos, boolean()} | {recvtclass, boolean()} | {recvttl, boolean()}. --type option_name() :: + +-type elementary_option_name() :: active | buffer | + debug | dontroute | high_msgq_watermark | linger | @@ -85,23 +92,11 @@ recbuf | reuseaddr | ipv6_v6only | - sctp_adaptation_layer | - sctp_associnfo | sctp_autoclose | - sctp_default_send_param | - sctp_delayed_ack_time | sctp_disable_fragments | - sctp_events | - sctp_get_peer_addr_info | sctp_i_want_mapped_v4_addr | - sctp_initmsg | sctp_maxseg | sctp_nodelay | - sctp_peer_addr_params | - sctp_primary_addr | - sctp_rtoinfo | - sctp_set_peer_primary_addr | - sctp_status | sndbuf | tos | tclass | @@ -109,9 +104,27 @@ recvtos | recvtclass | recvttl. + +-type record_option() :: + {sctp_adaptation_layer, #sctp_setadaptation{}} | + {sctp_associnfo, #sctp_assocparams{}} | + {sctp_default_send_param, #sctp_sndrcvinfo{}} | + {sctp_delayed_ack_time, #sctp_assoc_value{}} | + {sctp_events, #sctp_event_subscribe{}} | + {sctp_initmsg, #sctp_initmsg{}} | + {sctp_peer_addr_params, #sctp_paddrparams{}} | + {sctp_primary_addr, #sctp_prim{}} | + {sctp_rtoinfo, #sctp_rtoinfo{}} | + {sctp_set_peer_primary_addr, #sctp_setpeerprim{}}. + +-type ro_option() :: + {sctp_get_peer_addr_info, #sctp_paddrinfo{}} | + {sctp_status, #sctp_status{}}. + -type sctp_socket() :: port(). --export_type([assoc_id/0, option/0, option_name/0, sctp_socket/0]). +-export_type( + [assoc_id/0, option/0, option_name/0, option_value/0, sctp_socket/0]). -spec open() -> {ok, Socket} | {error, inet:posix()} when Socket :: sctp_socket(). @@ -357,11 +370,11 @@ do_connect(_S, _SockAddr, _Opts, _Timeout, _ConnWait) -> badarg. -do_connect(S, Addr, Port, Opts, Timeout, ConnWait) +do_connect(S, Addr, Service, Opts, Timeout, ConnWait) when is_port(S) andalso is_list(Opts) -> case inet_db:lookup_socket(S) of {ok,Mod} -> - case Mod:getserv(Port) of + case Mod:getserv(Service) of {ok,Port} -> try inet:start_timer(Timeout) of Timer -> @@ -389,6 +402,177 @@ do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) -> badarg. + +-spec connectx_init(Socket, SockAddrs, Opts) -> + {ok, assoc_id()} | {error, inet:posix()} when + Socket :: sctp_socket(), + SockAddrs:: [{inet:ip_address(), inet:port_number()} | + inet:family_address() | + socket:sockaddr_in() | socket:sockaddr_in6()], + Opts :: [option()]. +%% +connectx_init(S, SockAddrs, Opts) -> + case do_connectx(S, SockAddrs, Opts) of + badarg -> + erlang:error(badarg, [S, SockAddrs, Opts]); + Result -> + Result + end. + +-spec connectx_init(Socket, Addrs, Port, Opts) -> + {ok, assoc_id()} | {error, inet:posix()} when + Socket :: sctp_socket(), + Addrs :: [inet:ip_address() | inet:hostname()], + Port :: inet:port_number() | atom(), + Opts :: [option()]. +%% +connectx_init(S, Addrs, Port, Opts) -> + connectx_init(S, Addrs, Port, Opts, infinity). + +-spec connectx_init(Socket, Addrs, Port, Opts, Timeout) -> + {ok, assoc_id()} | {error, inet:posix()} when + Socket :: sctp_socket(), + Addrs :: [inet:ip_address() | inet:hostname()], + Port :: inet:port_number() | atom(), + Opts :: [option()], + Timeout :: timeout(). +%% +connectx_init(S, Addrs, Port, Opts, Timeout) -> + case do_connectx(S, Addrs, Port, Opts, Timeout) of + badarg -> + erlang:error(badarg, [S, Addrs, Port, Opts, Timeout]); + Result -> + Result + end. + + +do_connectx(S, SockAddrs, Opts) + when is_port(S), is_list(SockAddrs), is_list(Opts) -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + case ensure_sockaddrs(SockAddrs) of + {SockAddrs_1, Port} -> + SockAddrs_2 = set_port(SockAddrs_1, Port), + Mod:connectx(S, SockAddrs_2, Opts); + Error1 -> + Error1 + end; + {error, _} = Error2-> + Error2 + end; +do_connectx(_S, _SockAddrs, _Opts) -> + badarg. + +do_connectx(S, Addrs, Service, Opts, Timeout) + when is_port(S), is_list(Addrs), is_list(Opts) -> + case inet_db:lookup_socket(S) of + {ok,Mod} -> + case Mod:getserv(Service) of + {ok,Port} -> + try inet:start_timer(Timeout) of + Timer -> + try + case getaddrs(Mod, Addrs, Timer) of + IPs when is_list(IPs) -> + Mod:connectx(S, IPs, Port, Opts); + Error1 -> + Error1 + end + after + _ = inet:stop_timer(Timer) + end + catch + error:badarg -> + badarg + end; + {error, _} = Error2 -> + Error2 + end; + {error, _} = Error3 -> + Error3 + end; +do_connectx(_S, _Addrs, _Port, _Opts, _Timeout) -> + badarg. + +ensure_sockaddrs(SockAddrs) -> + ensure_sockaddrs(SockAddrs, 0, []). +%% +ensure_sockaddrs([SockAddr | SockAddrs], Port, Acc) -> + case SockAddr of + {IP, P} when is_tuple(IP) -> + ensure_sockaddrs(SockAddrs, Port, [SockAddr | Acc], P); + {Family, {_, P}} + when Family =:= inet; + Family =:= inet6 -> + ensure_sockaddrs(SockAddrs, Port, [SockAddr | Acc], P); + #{family := Family} + when Family =:= inet; + Family =:= inet6 -> + SockAddr_1 = inet:ensure_sockaddr(SockAddr), + ensure_sockaddrs( + SockAddrs, Port, [SockAddr_1 | Acc], + maps:get(port, SockAddr_1, 0)); + _ -> badarg + end; +ensure_sockaddrs([], 0, _) -> + badarg; +ensure_sockaddrs([], Port, Acc) -> + {lists:reverse(Acc), Port}. +%% +ensure_sockaddrs(SockAddrs, Port, Acc, P) -> + if + is_integer(P) -> + if + 0 < P -> + ensure_sockaddrs(SockAddrs, P, Acc); + P < 0 -> + badarg; + true -> + ensure_sockaddrs(SockAddrs, Port, Acc) + end; + true -> + badarg + end. + +set_port([SockAddr | SockAddrs], Port) -> + case SockAddr of + {IP, P} when is_tuple(IP) -> + set_port( + SockAddrs, Port, SockAddr, P, + fun () -> {IP, Port} end); + {Family, {Addr, P}} -> + set_port( + SockAddrs, Port, SockAddr, P, + fun () -> {Family, {Addr, Port}} end); + #{port := P} -> + set_port( + SockAddrs, Port, SockAddr, P, + fun () -> SockAddr#{port := Port} end) + end; +set_port([], _Port) -> + []. +%% +set_port(SockAddrs, Port, SockAddr, P, NewSockAddrFun) -> + [case P of + Port -> SockAddr; + _ -> NewSockAddrFun() + end | set_port(SockAddrs, Port)]. + +getaddrs(Mod, Addrs, Timer) -> + getaddrs(Mod, Addrs, Timer, []). +%% +getaddrs(Mod, [Addr | Addrs], Timer, Acc) -> + case Mod:getaddr(Addr, Timer) of + {ok, IP} -> + getaddrs(Mod, Addrs, Timer, [IP | Acc]); + {error, _} -> + badarg + end; +getaddrs(_Mod, [], _Timer, Acc) -> + lists:reverse(Acc). + + + -spec eof(Socket, Assoc) -> ok | {error, Reason} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{}, |