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