summaryrefslogtreecommitdiff
path: root/lib/kernel/src/gen_sctp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/src/gen_sctp.erl')
-rw-r--r--lib/kernel/src/gen_sctp.erl246
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{},