1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(gen_sctp).
22
23%% This module provides functions for communicating with
24%% sockets using the SCTP protocol.  The implementation assumes that
25%% the OS kernel supports SCTP providing user-level SCTP Socket API:
26%%     http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13
27
28-include("inet_sctp.hrl").
29
30-export([open/0,open/1,open/2,close/1]).
31-export([listen/2,peeloff/2]).
32-export([connect/4,connect/5,connect_init/4,connect_init/5]).
33-export([eof/2,abort/2]).
34-export([send/3,send/4,recv/1,recv/2]).
35-export([error_string/1]).
36-export([controlling_process/2]).
37
38-type assoc_id() :: term().
39-type option() ::
40        {active, true | false | once | -32768..32767} |
41        {buffer, non_neg_integer()} |
42        {dontroute, boolean()} |
43        {high_msgq_watermark, pos_integer()} |
44        {linger, {boolean(), non_neg_integer()}} |
45        {low_msgq_watermark, pos_integer()} |
46        {mode, list | binary} | list | binary |
47        {priority, non_neg_integer()} |
48        {recbuf, non_neg_integer()} |
49        {reuseaddr, boolean()} |
50	{ipv6_v6only, boolean()} |
51        {sctp_adaptation_layer, #sctp_setadaptation{}} |
52        {sctp_associnfo, #sctp_assocparams{}} |
53        {sctp_autoclose, non_neg_integer()} |
54        {sctp_default_send_param, #sctp_sndrcvinfo{}} |
55        {sctp_delayed_ack_time, #sctp_assoc_value{}} |
56        {sctp_disable_fragments, boolean()} |
57        {sctp_events, #sctp_event_subscribe{}} |
58        {sctp_get_peer_addr_info, #sctp_paddrinfo{}} |
59        {sctp_i_want_mapped_v4_addr, boolean()} |
60        {sctp_initmsg, #sctp_initmsg{}} |
61        {sctp_maxseg, non_neg_integer()} |
62        {sctp_nodelay, boolean()} |
63        {sctp_peer_addr_params, #sctp_paddrparams{}} |
64        {sctp_primary_addr, #sctp_prim{}} |
65        {sctp_rtoinfo, #sctp_rtoinfo{}} |
66        {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} |
67        {sctp_status, #sctp_status{}} |
68        {sndbuf, non_neg_integer()} |
69        {tos, non_neg_integer()} |
70        {tclass, non_neg_integer()} |
71        {ttl, non_neg_integer()} |
72        {recvtos, boolean()} |
73        {recvtclass, boolean()} |
74        {recvttl, boolean()}.
75-type option_name() ::
76        active |
77        buffer |
78        dontroute |
79        high_msgq_watermark |
80        linger |
81        low_msgq_watermark |
82        mode |
83        priority |
84        recbuf |
85        reuseaddr |
86	ipv6_v6only |
87        sctp_adaptation_layer |
88        sctp_associnfo |
89        sctp_autoclose |
90        sctp_default_send_param |
91        sctp_delayed_ack_time |
92        sctp_disable_fragments |
93        sctp_events |
94        sctp_get_peer_addr_info |
95        sctp_i_want_mapped_v4_addr |
96        sctp_initmsg |
97        sctp_maxseg |
98        sctp_nodelay |
99        sctp_peer_addr_params |
100        sctp_primary_addr |
101        sctp_rtoinfo |
102        sctp_set_peer_primary_addr |
103        sctp_status |
104        sndbuf |
105        tos |
106        tclass |
107        ttl |
108        recvtos |
109        recvtclass |
110        recvttl.
111-type sctp_socket() :: port().
112
113-export_type([assoc_id/0, option/0, option_name/0, sctp_socket/0]).
114
115-spec open() -> {ok, Socket} | {error, inet:posix()} when
116      Socket :: sctp_socket().
117
118open() ->
119    open([]).
120
121-spec open(Port) -> {ok, Socket} | {error, inet:posix()} when
122              Port :: inet:port_number(),
123              Socket :: sctp_socket();
124          (Opts) -> {ok, Socket} | {error, inet:posix()} when
125              Opts :: [Opt],
126              Opt :: {ip,IP}
127                   | {ifaddr,IP}
128                   | inet:address_family()
129                   | {port,Port}
130		   | {type,SockType}
131                   | {netns, file:filename_all()}
132                   | {bind_to_device, binary()}
133                   | option(),
134              IP :: inet:ip_address() | any | loopback,
135              Port :: inet:port_number(),
136	      SockType :: seqpacket | stream,
137              Socket :: sctp_socket().
138
139open(Opts0) when is_list(Opts0) ->
140    {Mod, Opts} = inet:sctp_module(Opts0),
141    case Mod:open(Opts) of
142	{error,badarg} ->
143	    erlang:error(badarg, [Opts]);
144	{error,einval} ->
145	    erlang:error(badarg, [Opts]);
146	Result -> Result
147    end;
148open(Port) when is_integer(Port) ->
149    open([{port,Port}]);
150open(X) ->
151    erlang:error(badarg, [X]).
152
153-spec open(Port, Opts) -> {ok, Socket} | {error, inet:posix()} when
154      Opts :: [Opt],
155              Opt :: {ip,IP}
156                   | {ifaddr,IP}
157                   | inet:address_family()
158                   | {port,Port}
159		   | {type,SockType}
160                   | option(),
161      IP :: inet:ip_address() | any | loopback,
162      Port :: inet:port_number(),
163      SockType :: seqpacket | stream,
164      Socket :: sctp_socket().
165
166open(Port, Opts) when is_integer(Port), is_list(Opts) ->
167    open([{port,Port}|Opts]);
168open(Port, Opts) ->
169    erlang:error(badarg, [Port,Opts]).
170
171-spec close(Socket) -> ok | {error, inet:posix()} when
172      Socket :: sctp_socket().
173
174close(S) when is_port(S) ->
175    case inet_db:lookup_socket(S) of
176	{ok,Mod} ->
177	    Mod:close(S);
178	{error,closed} -> ok
179    end;
180close(S) ->
181    erlang:error(badarg, [S]).
182
183
184
185-spec listen(Socket, IsServer) -> ok | {error, Reason} when
186      Socket :: sctp_socket(),
187      IsServer :: boolean(),
188      Reason :: term();
189	    (Socket, Backlog) -> ok | {error, Reason} when
190      Socket :: sctp_socket(),
191      Backlog :: integer(),
192      Reason :: term().
193
194listen(S, Backlog)
195  when is_port(S), is_boolean(Backlog);
196       is_port(S), is_integer(Backlog) ->
197    case inet_db:lookup_socket(S) of
198	{ok,Mod} ->
199	    Mod:listen(S, Backlog);
200	Error -> Error
201    end;
202listen(S, Flag) ->
203    erlang:error(badarg, [S,Flag]).
204
205-spec peeloff(Socket, Assoc) -> {ok, NewSocket} | {error, Reason} when
206      Socket :: sctp_socket(),
207      Assoc :: #sctp_assoc_change{} | assoc_id(),
208      NewSocket :: sctp_socket(),
209      Reason :: term().
210
211peeloff(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
212    peeloff(S, AssocId);
213peeloff(S, AssocId) when is_port(S), is_integer(AssocId) ->
214    case inet_db:lookup_socket(S) of
215	{ok,Mod} ->
216	    Mod:peeloff(S, AssocId);
217	Error -> Error
218    end.
219
220-spec connect(Socket, Addr, Port, Opts) ->
221                     {ok, #sctp_assoc_change{state :: 'comm_up'}} |
222                     {error, #sctp_assoc_change{state :: 'cant_assoc'}} |
223                     {error, inet:posix()}
224                         when
225      Socket :: sctp_socket(),
226      Addr :: inet:ip_address() | inet:hostname(),
227      Port :: inet:port_number(),
228      Opts :: [Opt :: option()].
229
230connect(S, Addr, Port, Opts) ->
231    connect(S, Addr, Port, Opts, infinity).
232
233-spec connect(Socket, Addr, Port, Opts, Timeout) ->
234                     {ok, #sctp_assoc_change{state :: 'comm_up'}} |
235                     {error, #sctp_assoc_change{state :: 'cant_assoc'}} |
236                     {error, inet:posix()}
237                         when
238      Socket :: sctp_socket(),
239      Addr :: inet:ip_address() | inet:hostname(),
240      Port :: inet:port_number(),
241      Opts :: [Opt :: option()],
242      Timeout :: timeout().
243
244connect(S, Addr, Port, Opts, Timeout) ->
245    case do_connect(S, Addr, Port, Opts, Timeout, true) of
246	badarg ->
247	    erlang:error(badarg, [S,Addr,Port,Opts,Timeout]);
248	Result ->
249	    Result
250    end.
251
252-spec connect_init(Socket, Addr, Port, Opts) ->
253                          ok | {error, inet:posix()} when
254      Socket :: sctp_socket(),
255      Addr :: inet:ip_address() | inet:hostname(),
256      Port :: inet:port_number(),
257      Opts :: [option()].
258
259connect_init(S, Addr, Port, Opts) ->
260    connect_init(S, Addr, Port, Opts, infinity).
261
262-spec connect_init(Socket, Addr, Port, Opts, Timeout) ->
263                          ok | {error, inet:posix()} when
264      Socket :: sctp_socket(),
265      Addr :: inet:ip_address() | inet:hostname(),
266      Port :: inet:port_number(),
267      Opts :: [option()],
268      Timeout :: timeout().
269
270connect_init(S, Addr, Port, Opts, Timeout) ->
271    case do_connect(S, Addr, Port, Opts, Timeout, false) of
272	badarg ->
273	    erlang:error(badarg, [S,Addr,Port,Opts,Timeout]);
274	Result ->
275	    Result
276    end.
277
278do_connect(S, Addr, Port, Opts, Timeout, ConnWait) when is_port(S), is_list(Opts) ->
279    case inet_db:lookup_socket(S) of
280	{ok,Mod} ->
281	    case Mod:getserv(Port) of
282		{ok,Port} ->
283		    try inet:start_timer(Timeout) of
284			Timer ->
285			    try Mod:getaddr(Addr, Timer) of
286				{ok,IP} ->
287				    ConnectTimer = if ConnWait == false ->
288							   nowait;
289						      true ->
290							   Timer
291						   end,
292				    Mod:connect(S, IP, Port, Opts, ConnectTimer);
293				Error -> Error
294			    after
295				_ = inet:stop_timer(Timer)
296			    end
297		    catch
298			error:badarg ->
299			    badarg
300		    end;
301		Error -> Error
302	    end;
303	Error -> Error
304    end;
305do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) ->
306    badarg.
307
308
309-spec eof(Socket, Assoc) -> ok | {error, Reason} when
310      Socket :: sctp_socket(),
311      Assoc :: #sctp_assoc_change{},
312      Reason :: term().
313
314eof(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
315    eof_or_abort(S, AssocId, eof);
316eof(S, Assoc) ->
317    erlang:error(badarg, [S,Assoc]).
318
319-spec abort(Socket, Assoc) -> ok | {error, inet:posix()} when
320      Socket :: sctp_socket(),
321      Assoc :: #sctp_assoc_change{}.
322
323abort(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
324    eof_or_abort(S, AssocId, abort);
325abort(S, Assoc) ->
326    erlang:error(badarg, [S,Assoc]).
327
328eof_or_abort(S, AssocId, Action) ->
329    case inet_db:lookup_socket(S) of
330	{ok,Mod} ->
331	    Mod:sendmsg(S, #sctp_sndrcvinfo{assoc_id = AssocId,
332					    flags    = [Action]},
333			<<>>);
334	Error -> Error
335    end.
336
337
338-spec send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} when
339      Socket :: sctp_socket(),
340      SndRcvInfo :: #sctp_sndrcvinfo{},
341      Data :: binary() | iolist(),
342      Reason :: term().
343
344%% Full-featured send. Rarely needed.
345send(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) ->
346    case inet_db:lookup_socket(S) of
347	{ok,Mod} ->
348	    Mod:sendmsg(S, SRI, Data);
349	Error -> Error
350    end;
351send(S, SRI, Data) ->
352    erlang:error(badarg, [S,SRI,Data]).
353
354-spec send(Socket, Assoc, Stream, Data) -> ok | {error, Reason} when
355      Socket :: sctp_socket(),
356      Assoc :: #sctp_assoc_change{} | assoc_id(),
357      Stream :: integer(),
358      Data :: binary() | iolist(),
359      Reason :: term().
360
361send(S, #sctp_assoc_change{assoc_id=AssocId}, Stream, Data)
362  when is_port(S), is_integer(Stream) ->
363    case inet_db:lookup_socket(S) of
364	{ok,Mod} ->
365	    Mod:send(S, AssocId, Stream, Data);
366	Error -> Error
367    end;
368send(S, AssocId, Stream, Data)
369  when is_port(S), is_integer(AssocId), is_integer(Stream) ->
370    case inet_db:lookup_socket(S) of
371	{ok,Mod} ->
372	    Mod:send(S, AssocId, Stream, Data);
373	Error -> Error
374    end;
375send(S, AssocChange, Stream, Data) ->
376    erlang:error(badarg, [S,AssocChange,Stream,Data]).
377
378-spec recv(Socket) -> {ok, {FromIP, FromPort, AncData, Data}}
379                          | {error, Reason} when
380      Socket :: sctp_socket(),
381      FromIP   :: inet:ip_address(),
382      FromPort :: inet:port_number(),
383      AncData  :: [#sctp_sndrcvinfo{} | inet:ancillary_data()],
384      Data     :: binary() | string() | #sctp_sndrcvinfo{}
385                | #sctp_assoc_change{} | #sctp_paddr_change{}
386                | #sctp_adaptation_event{},
387      Reason   :: inet:posix() | #sctp_send_failed{} | #sctp_paddr_change{}
388                | #sctp_pdapi_event{} | #sctp_remote_error{}
389                | #sctp_shutdown_event{}.
390
391recv(S) ->
392    recv(S, infinity).
393
394-spec recv(Socket, Timeout) -> {ok, {FromIP, FromPort, AncData, Data}}
395                                   | {error, Reason} when
396      Socket :: sctp_socket(),
397      Timeout :: timeout(),
398      FromIP   :: inet:ip_address(),
399      FromPort :: inet:port_number(),
400      AncData  :: [#sctp_sndrcvinfo{} | inet:ancillary_data()],
401      Data     :: binary() | string() | #sctp_sndrcvinfo{}
402                | #sctp_assoc_change{} | #sctp_paddr_change{}
403                | #sctp_adaptation_event{},
404      Reason   :: inet:posix() | #sctp_send_failed{} | #sctp_paddr_change{}
405                | #sctp_pdapi_event{} | #sctp_remote_error{}
406                | #sctp_shutdown_event{}.
407
408recv(S, Timeout) when is_port(S) ->
409    case inet_db:lookup_socket(S) of
410	{ok,Mod} ->
411	    Mod:recv(S, Timeout);
412	Error -> Error
413    end;
414recv(S, Timeout) ->
415    erlang:error(badarg, [S,Timeout]).
416
417
418-spec error_string(ErrorNumber) -> ok | string() | unknown_error when
419      ErrorNumber :: integer().
420
421error_string(0) ->
422    ok;
423error_string(1) ->
424    "Invalid Stream Identifier";
425error_string(2) ->
426    "Missing Mandatory Parameter";
427error_string(3) ->
428    "Stale Cookie Error";
429error_string(4) ->
430    "Out of Resource";
431error_string(5) ->
432    "Unresolvable Address";
433error_string(6) ->
434    "Unrecognized Chunk Type";
435error_string(7) ->
436    "Invalid Mandatory Parameter";
437error_string(8) ->
438    "Unrecognized Parameters";
439error_string(9) ->
440    "No User Data";
441error_string(10) ->
442    "Cookie Received While Shutting Down";
443error_string(11) ->
444    "Restart of an Association with New Addresses";
445error_string(12) ->
446    "User Initiated Abort";
447error_string(13) ->
448    "Protocol Violation";
449%% For more info on principal SCTP error codes: phone +44 7981131933
450error_string(N) when is_integer(N) ->
451    unknown_error;
452error_string(X) ->
453    erlang:error(badarg, [X]).
454
455
456-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when
457      Socket :: sctp_socket(),
458      Pid :: pid(),
459      Reason :: closed | not_owner | badarg | inet:posix().
460
461controlling_process(S, Pid) when is_port(S), is_pid(Pid) ->
462    inet:udp_controlling_process(S, Pid);
463controlling_process(S, Pid) ->
464    erlang:error(badarg, [S,Pid]).
465