From bfb350ecc30d44e64ed75512690a0e4fbbb6bc2a Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 20 Sep 2011 09:27:02 +0200
Subject: erts,kernel: Rename operations common to TCP and SCTP

---
 lib/kernel/src/inet_int.hrl | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 6f1688c6a2..9154e90e1f 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -83,15 +83,17 @@
 -define(INET_REQ_IFSET,         23).
 -define(INET_REQ_SUBSCRIBE,     24).
 -define(INET_REQ_GETIFADDRS,    25).
+-define(INET_REQ_ACCEPT,        26).
+-define(INET_REQ_LISTEN,        27).
 %% TCP requests
--define(TCP_REQ_ACCEPT,         40).
--define(TCP_REQ_LISTEN,         41).
+%%-define(TCP_REQ_ACCEPT,         40). MOVED
+%%-define(TCP_REQ_LISTEN,         41). MERGED
 -define(TCP_REQ_RECV,           42).
 -define(TCP_REQ_UNRECV,         43).
 -define(TCP_REQ_SHUTDOWN,       44).
 %% UDP and SCTP requests
 -define(PACKET_REQ_RECV,        60).
--define(SCTP_REQ_LISTEN,        61).
+%%-define(SCTP_REQ_LISTEN,        61). MERGED
 -define(SCTP_REQ_BINDX,	        62). %% Multi-home SCTP bind
 
 %% subscribe codes, INET_REQ_SUBSCRIBE
-- 
cgit v1.2.3


From faea8584311fcb5365c585280e5e9b2364ed121e Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 2 Sep 2011 10:38:39 +0200
Subject: erts,kernel: Add type stream sockets to SCTP

---
 lib/kernel/doc/src/gen_sctp.xml | 16 ++++++++++------
 lib/kernel/src/gen_sctp.erl     | 14 ++++++++++++--
 lib/kernel/src/inet.erl         | 27 ++++++++++++++++-----------
 lib/kernel/src/inet6_sctp.erl   |  6 +++---
 lib/kernel/src/inet6_tcp.erl    |  8 ++++----
 lib/kernel/src/inet6_udp.erl    |  6 +++---
 lib/kernel/src/inet_int.hrl     |  3 ++-
 lib/kernel/src/inet_sctp.erl    |  6 +++---
 lib/kernel/src/inet_tcp.erl     |  8 ++++----
 lib/kernel/src/inet_udp.erl     |  4 ++--
 10 files changed, 59 insertions(+), 39 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index 688cd0f78f..b9f23674f0 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -254,15 +254,19 @@
       </desc>
     </func>
     <func>
-      <name name="listen" arity="2"/>
+      <name name="listen" arity="2" clause_i="1"/>
+      <name name="listen" arity="2" clause_i="2"/>
       <fsummary>Set up a socket to listen.</fsummary>
       <desc>
         <p>Sets up a socket to listen on the IP address and port number
-          it is bound to. <c><anno>IsServer</anno></c> must be <c>true</c>
-          or <c>false</c>.
-          In the contrast to TCP, in SCTP there is no listening queue length.
-          If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
-          it will become an SCTP server socket.</p>
+	it is bound to.</p>
+	<p>For type <c>seqpacket</c> sockets (the default)
+	<c><anno>IsServer</anno></c> must be <c>true</c> or <c>false</c>.
+	In the contrast to TCP, in SCTP there is no listening queue length.
+	If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
+	it will become an SCTP server socket.</p>
+	<p>For type <c>stream</c> sockets <anno>Backlog</anno> defines
+	the backlog queue length just like in TCP.</p>
       </desc>
     </func>
     <func>
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 6cebb7ab97..3351796481 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -109,9 +109,11 @@ open() ->
                    | {ifaddr,IP}
                    | inet:address_family()
                    | {port,Port}
+		   | {type,SockType}
                    | option(),
               IP :: inet:ip_address() | any | loopback,
               Port :: inet:port_number(),
+	      SockType :: seqpacket | stream,
               Socket :: sctp_socket().
 
 open(Opts) when is_list(Opts) ->
@@ -134,9 +136,11 @@ open(X) ->
                    | {ifaddr,IP}
                    | inet:address_family()
                    | {port,Port}
+		   | {type,SockType}
                    | option(),
       IP :: inet:ip_address() | any | loopback,
       Port :: inet:port_number(),
+      SockType :: seqpacket | stream,
       Socket :: sctp_socket().
 
 open(Port, Opts) when is_integer(Port), is_list(Opts) ->
@@ -161,12 +165,18 @@ close(S) ->
 -spec listen(Socket, IsServer) -> ok | {error, Reason} when
       Socket :: sctp_socket(),
       IsServer :: boolean(),
+      Reason :: term();
+	    (Socket, Backlog) -> ok | {error, Reason} when
+      Socket :: sctp_socket(),
+      Backlog :: integer(),
       Reason :: term().
 
-listen(S, Flag) when is_port(S), is_boolean(Flag) ->
+listen(S, Backlog)
+  when is_port(S), is_boolean(Backlog);
+       is_port(S), is_integer(Backlog) ->
     case inet_db:lookup_socket(S) of
 	{ok,Mod} ->
-	    Mod:listen(S, Flag);
+	    Mod:listen(S, Backlog);
 	Error -> Error
     end;
 listen(S, Flag) ->
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 48a6f3db65..22056e6974 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -36,7 +36,7 @@
 
 -export([i/0, i/1, i/2]).
 
--export([getll/1, getfd/1, open/7, fdopen/5]).
+-export([getll/1, getfd/1, open/8, fdopen/6]).
 
 -export([tcp_controlling_process/2, udp_controlling_process/2,
 	 tcp_close/1, udp_close/1]).
@@ -115,7 +115,8 @@
       'mtu' | 'netmask' | 'flags' |'hwaddr'.
 
 -type address_family() :: 'inet' | 'inet6'.
--type protocol_option() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_protocol() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_type() :: 'stream' | 'dgram' | 'seqpacket'.
 -type stat_option() :: 
 	'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
 	'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'.
@@ -748,6 +749,8 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
 		    sctp_opt(Opts, Mod, R#sctp_opts{port=P}, As);
 		Error -> Error
 	    end;
+	{type,Type} when Type =:= seqpacket; Type =:= stream ->
+	    sctp_opt(Opts, Mod, R#sctp_opts{type=Type}, As);
 	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
@@ -996,13 +999,14 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
 	   Addr :: ip_address(),
 	   Port :: port_number(),
 	   Opts :: [socket_setopt()],
-	   Protocol :: protocol_option(),
-	   Family :: 'inet' | 'inet6',
+	   Protocol :: socket_protocol(),
+	   Family :: address_family(),
+	   Type :: socket_type(),
 	   Module :: atom()) ->
 	{'ok', socket()} | {'error', posix()}.
 
-open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
-    case prim_inet:open(Protocol, Family) of
+open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
+    case prim_inet:open(Protocol, Family, Type) of
 	{ok,S} ->
 	    case prim_inet:setopts(S, Opts) of
 		ok ->
@@ -1029,18 +1033,19 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
 	Error ->
 	    Error
     end;
-open(Fd, _Addr, _Port, Opts, Protocol, Family, Module) ->
-    fdopen(Fd, Opts, Protocol, Family, Module).
+open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
+    fdopen(Fd, Opts, Protocol, Family, Type, Module).
 
 -spec fdopen(Fd :: non_neg_integer(),
 	     Opts :: [socket_setopt()],
-	     Protocol :: protocol_option(),
+	     Protocol :: socket_protocol(),
 	     Family :: address_family(),
+	     Type :: socket_type(),
 	     Module :: atom()) ->
 	{'ok', socket()} | {'error', posix()}.
 
-fdopen(Fd, Opts, Protocol, Family, Module) ->
-    case prim_inet:fdopen(Protocol, Fd, Family) of
+fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
+    case prim_inet:fdopen(Protocol, Family, Type, Fd) of
 	{ok, S} ->
 	    case prim_inet:setopts(S, Opts) of
 		ok ->
diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl
index 5bf3fca647..f034535ba0 100644
--- a/lib/kernel/src/inet6_sctp.erl
+++ b/lib/kernel/src/inet6_sctp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. 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
@@ -54,8 +54,8 @@ translate_ip(IP) ->
     
 open(Opts) ->
     case inet:sctp_options(Opts, ?MODULE) of
-	{ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
-	    inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+	{ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+	    inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
 	Error -> Error
     end.
 
diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl
index cc45f6c7f6..c714b2bee0 100644
--- a/lib/kernel/src/inet6_tcp.erl
+++ b/lib/kernel/src/inet6_tcp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -93,7 +93,7 @@ do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) when
 			   port=BPort,
 			   opts=SockOpts}}
 	when ?ip6(Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb), ?port(BPort) ->
-	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
 		{ok, S} ->
 		    case prim_inet:connect(S, Addr, Port, Time) of
 			ok    -> {ok,S};
@@ -115,7 +115,7 @@ listen(Port, Opts) ->
 			  port=BPort,
 			  opts=SockOpts}=R}
 	when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
-	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
 		{ok, S} ->
 		    case prim_inet:listen(S, R#listen_opts.backlog) of
 			ok -> {ok, S};
@@ -149,5 +149,5 @@ accept(L,Timeout) ->
 %% Create a port/socket from a file descriptor 
 %%
 fdopen(Fd, Opts) ->
-    inet:fdopen(Fd, Opts, tcp, inet6, ?MODULE).
+    inet:fdopen(Fd, Opts, tcp, inet6, stream, ?MODULE).
 
diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl
index e81d417151..ca43c94211 100644
--- a/lib/kernel/src/inet6_udp.erl
+++ b/lib/kernel/src/inet6_udp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -45,7 +45,7 @@ open(Port, Opts) ->
 		       port=BPort,
 		       opts=SockOpts}}
 	when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
-	    inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,?MODULE);
+	    inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,dgram,?MODULE);
 	{ok, _} -> exit(badarg)
     end.
 
@@ -84,4 +84,4 @@ controlling_process(Socket, NewOwner) ->
 %% Create a port/socket from a file descriptor 
 %%
 fdopen(Fd, Opts) ->
-    inet:fdopen(Fd, Opts, udp, inet6, ?MODULE).
+    inet:fdopen(Fd, Opts, udp, inet6, dgram, ?MODULE).
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 9154e90e1f..735347b4b1 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -29,7 +29,7 @@
 -define(INET_AF_ANY,          3). % Fake for ANY in any address family
 -define(INET_AF_LOOPBACK,     4). % Fake for LOOPBACK in any address family
 
-%% type codes (gettype, INET_REQ_GETTYPE)
+%% type codes to open and gettype - INET_REQ_GETTYPE
 -define(INET_TYPE_STREAM,     1).
 -define(INET_TYPE_DGRAM,      2).
 -define(INET_TYPE_SEQPACKET,  3).
@@ -401,6 +401,7 @@
 	  ifaddr,
 	  port   = 0,
 	  fd	 = -1,
+	  type   = seqpacket,
 	  opts   = [{mode,	  binary},
 		    {buffer,	  ?SCTP_DEF_BUFSZ},
 		    {sndbuf,	  ?SCTP_DEF_BUFSZ},
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index de74b573bd..e670dcfdad 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. 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
@@ -53,8 +53,8 @@ translate_ip(IP) ->
     
 open(Opts) ->
     case inet:sctp_options(Opts, ?MODULE) of
-	{ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
-	    inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+	{ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+	    inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
 	Error -> Error
     end.
 
diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl
index 6dadccd6a9..4c2db16ce3 100644
--- a/lib/kernel/src/inet_tcp.erl
+++ b/lib/kernel/src/inet_tcp.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %% 
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. 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
@@ -95,7 +95,7 @@ do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) ->
 			   port=BPort,
 			   opts=SockOpts}}
 	when ?ip(Ab,Bb,Cb,Db), ?port(BPort) ->
-	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
 		{ok, S} ->
 		    case prim_inet:connect(S, {A,B,C,D}, Port, Time) of
 			ok    -> {ok,S};
@@ -117,7 +117,7 @@ listen(Port, Opts) ->
 			  port=BPort,
 			  opts=SockOpts}=R}
 	when ?ip(A,B,C,D), ?port(BPort) ->
-	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+	    case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
 		{ok, S} ->
 		    case prim_inet:listen(S, R#listen_opts.backlog) of
 			ok -> {ok, S};
@@ -150,4 +150,4 @@ accept(L,Timeout) ->
 %% Create a port/socket from a file descriptor 
 %%
 fdopen(Fd, Opts) ->
-    inet:fdopen(Fd, Opts, tcp, inet, ?MODULE).
+    inet:fdopen(Fd, Opts, tcp, inet, stream, ?MODULE).
diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl
index 60bd96f332..80d930fe10 100644
--- a/lib/kernel/src/inet_udp.erl
+++ b/lib/kernel/src/inet_udp.erl
@@ -52,7 +52,7 @@ open(Port, Opts) ->
 		       ifaddr=BAddr={A,B,C,D},
 		       port=BPort,
 		       opts=SockOpts}} when ?ip(A,B,C,D), ?port(BPort) ->
-	    inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,?MODULE);
+	    inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,dgram,?MODULE);
 	{ok, _} -> exit(badarg)
     end.
 
@@ -93,7 +93,7 @@ controlling_process(Socket, NewOwner) ->
 fdopen(Fd, Opts) ->
     inet:fdopen(Fd, 
 		optuniquify([{recbuf, ?RECBUF} | Opts]), 
-		udp, inet, ?MODULE).
+		udp, inet, dgram, ?MODULE).
 
 
 %% Remove all duplicate options from an option list.
-- 
cgit v1.2.3


From 3b81d47f7f4415661d4371989637374567e2ab2a Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 2 Sep 2011 10:37:08 +0200
Subject: kernel: Add tests for SCTP stream sockets

Also fix tests for new prim_inet:open function.
---
 lib/kernel/test/erl_boot_server_SUITE.erl |   2 +-
 lib/kernel/test/gen_sctp_SUITE.erl        | 100 ++++++++++++++++++++++++++++--
 2 files changed, 97 insertions(+), 5 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/erl_boot_server_SUITE.erl b/lib/kernel/test/erl_boot_server_SUITE.erl
index cea3715ce4..bb64c01058 100644
--- a/lib/kernel/test/erl_boot_server_SUITE.erl
+++ b/lib/kernel/test/erl_boot_server_SUITE.erl
@@ -346,7 +346,7 @@ good_hosts(_Config) ->
     [GoodHost1, GoodHost2, GoodHost3].
 
 open_udp() ->
-    ?line {ok, S} = prim_inet:open(udp, inet),
+    ?line {ok, S} = prim_inet:open(udp, inet, dgram),
     ?line ok = prim_inet:setopts(S, [{mode,list},{active,true}, 
 				     {deliver,term},{broadcast,true}]),
     ?line {ok,_} = prim_inet:bind(S, {0,0,0,0}, 0),
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 1b534a5fc4..1cf3d659d4 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -30,14 +30,15 @@
 -export(
    [basic/1,
     api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
-    xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1]).
+    xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
+    basic_stream/1, xfer_stream_min/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
     [basic, api_open_close, api_listen, api_connect_init,
-     api_opts, xfer_min, xfer_active, def_sndrcvinfo,
-     implicit_inet6].
+     api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
+     basic_stream, xfer_stream_min].
 
 groups() -> 
     [].
@@ -96,7 +97,7 @@ xfer_min(Config) when is_list(Config) ->
     ?line Stream = 0,
     ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
     ?line Loopback = {127,0,0,1},
-    ?line {ok,Sb} = gen_sctp:open(),
+    ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
     ?line {ok,Pb} = inet:port(Sb),
     ?line ok = gen_sctp:listen(Sb, true),
     
@@ -654,6 +655,97 @@ implicit_inet6(S1, Addr) ->
 	  end,
     ?line ok = gen_sctp:close(S2).
 
+basic_stream(doc) ->
+    "Hello world stream socket";
+basic_stream(suite) ->
+    [];
+basic_stream(Config) when is_list(Config) ->
+    ?line {ok,S} = gen_sctp:open([{type,stream}]),
+    ?line ok = gen_sctp:listen(S, true),
+    ?line ok =
+	do_from_other_process(
+	  fun () -> gen_sctp:listen(S, 10) end),
+    ?line ok = gen_sctp:close(S),
+    ok.
+
+xfer_stream_min(doc) ->
+    "Minimal data transfer";
+xfer_stream_min(suite) ->
+    [];
+xfer_stream_min(Config) when is_list(Config) ->
+    ?line Stream = 0,
+    ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
+    ?line Loopback = {127,0,0,1},
+    ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+    ?line {ok,Pb} = inet:port(Sb),
+    ?line ok = gen_sctp:listen(Sb, true),
+
+    ?line {ok,Sa} = gen_sctp:open([{type,stream}]),
+    ?line {ok,Pa} = inet:port(Sa),
+    ?line {ok,#sctp_assoc_change{state=comm_up,
+				 error=0,
+				 outbound_streams=SaOutboundStreams,
+				 inbound_streams=SaInboundStreams,
+				 assoc_id=SaAssocId}} =
+	gen_sctp:connect(Sa, Loopback, Pb, []),
+    ?line {ok,{Loopback,
+	       Pa,[],
+	       #sctp_assoc_change{state=comm_up,
+				  error=0,
+				  outbound_streams=SbOutboundStreams,
+				  inbound_streams=SbInboundStreams,
+				  assoc_id=SbAssocId}}} =
+	gen_sctp:recv(Sb, infinity),
+    ?line SaOutboundStreams = SbInboundStreams,
+    ?line SbOutboundStreams = SaInboundStreams,
+    ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
+    ?line case gen_sctp:recv(Sb, infinity) of
+	      {ok,{Loopback,
+		   Pa,
+		   [#sctp_sndrcvinfo{stream=Stream,
+				     assoc_id=SbAssocId}],
+		   Data}} -> ok;
+	      {ok,{Loopback,
+		   Pa,[],
+		   #sctp_paddr_change{addr = {Loopback,_},
+				      state = addr_available,
+				      error = 0,
+				      assoc_id = SbAssocId}}} ->
+		  {ok,{Loopback,
+		       Pa,
+		       [#sctp_sndrcvinfo{stream=Stream,
+					 assoc_id=SbAssocId}],
+		       Data}} =	gen_sctp:recv(Sb, infinity)
+	  end,
+    ?line ok =
+	do_from_other_process(
+	  fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
+    ?line {ok,{Loopback,
+	       Pb,
+	       [#sctp_sndrcvinfo{stream=Stream,
+				 assoc_id=SaAssocId}],
+	       Data}} =
+	gen_sctp:recv(Sa, infinity),
+    %%
+    ?line ok = gen_sctp:close(Sa),
+    ?line {ok,{Loopback,
+	       Pa,[],
+	       #sctp_shutdown_event{assoc_id=SbAssocId}}} =
+	gen_sctp:recv(Sb, infinity),
+    ?line {ok,{Loopback,
+	       Pa,[],
+	       #sctp_assoc_change{state=shutdown_comp,
+				  error=0,
+				  assoc_id=SbAssocId}}} =
+	gen_sctp:recv(Sb, infinity),
+    ?line ok = gen_sctp:close(Sb),
+
+    ?line receive
+	      Msg -> test_server:fail({received,Msg})
+	  after 17 -> ok
+	  end,
+    ok.
+
 
 
 do_from_other_process(Fun) ->
-- 
cgit v1.2.3


From 1cc7485467b1b4e9efd15259b58e1182d2e9723c Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 2 Sep 2011 10:29:58 +0200
Subject: erts,kernel: Implement gen_sctp:peeloff/2

---
 lib/kernel/src/gen_sctp.erl   | 18 +++++++++++++++++-
 lib/kernel/src/inet.erl       | 16 ++++++++++++----
 lib/kernel/src/inet6_sctp.erl | 11 ++++++++++-
 lib/kernel/src/inet_int.hrl   |  3 ++-
 lib/kernel/src/inet_sctp.erl  | 11 ++++++++++-
 5 files changed, 51 insertions(+), 8 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 3351796481..77ca26b845 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -27,7 +27,8 @@
 -include("inet_sctp.hrl").
 
 -export([open/0,open/1,open/2,close/1]).
--export([listen/2,connect/4,connect/5,connect_init/4,connect_init/5]).
+-export([listen/2,peeloff/2]).
+-export([connect/4,connect/5,connect_init/4,connect_init/5]).
 -export([eof/2,abort/2]).
 -export([send/3,send/4,recv/1,recv/2]).
 -export([error_string/1]).
@@ -182,6 +183,21 @@ listen(S, Backlog)
 listen(S, Flag) ->
     erlang:error(badarg, [S,Flag]).
 
+-spec peeloff(Socket, Assoc) -> {ok, NewSocket} | {error, Reason} when
+      Socket :: sctp_socket(),
+      Assoc :: #sctp_assoc_change{} | assoc_id(),
+      NewSocket :: sctp_socket(),
+      Reason :: term().
+
+peeloff(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
+    peeloff(S, AssocId);
+peeloff(S, AssocId) when is_port(S), is_integer(AssocId) ->
+    case inet_db:lookup_socket(S) of
+	{ok,Mod} ->
+	    Mod:peeloff(S, AssocId);
+	Error -> Error
+    end.
+
 -spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when
       Socket :: sctp_socket(),
       Addr :: inet:ip_address() | inet:hostname(),
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 22056e6974..b60c68e3a1 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1061,18 +1061,24 @@ fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
 %%  socket stat
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-i() -> i(tcp), i(udp).
+i() -> i(tcp), i(udp), i(sctp).
 
 i(Proto) -> i(Proto, [port, module, recv, sent, owner,
-		      local_address, foreign_address, state]).
+		      local_address, foreign_address, state, type]).
 
 i(tcp, Fs) ->
     ii(tcp_sockets(), Fs, tcp);
 i(udp, Fs) ->
-    ii(udp_sockets(), Fs, udp).
+    ii(udp_sockets(), Fs, udp);
+i(sctp, Fs) ->
+    ii(sctp_sockets(), Fs, sctp).
 
 ii(Ss, Fs, Proto) ->
-    LLs = [h_line(Fs) | info_lines(Ss, Fs, Proto)],
+    LLs =
+	case info_lines(Ss, Fs, Proto) of
+	    [] -> [];
+	    InfoLines -> [h_line(Fs) | InfoLines]
+	end,
     Maxs = foldl(
 	     fun(Line,Max0) -> smax(Max0,Line) end, 
 	     duplicate(length(Fs),0),LLs),
@@ -1140,6 +1146,7 @@ info(S, F, Proto) ->
 	    case prim_inet:gettype(S) of
 		{ok,{_,stream}} -> "STREAM";
 		{ok,{_,dgram}}  -> "DGRAM";
+		{ok,{_,seqpacket}} -> "SEQPACKET";
 		_ -> " "
 	    end;
 	fd ->
@@ -1191,6 +1198,7 @@ fmt_port(N, Proto) ->
 %% Return a list of all tcp sockets
 tcp_sockets() -> port_list("tcp_inet").
 udp_sockets() -> port_list("udp_inet").
+sctp_sockets() -> port_list("sctp_inet").
 
 %% Return all ports having the name 'Name'
 port_list(Name) ->
diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl
index f034535ba0..c47483bbdd 100644
--- a/lib/kernel/src/inet6_sctp.erl
+++ b/lib/kernel/src/inet6_sctp.erl
@@ -32,7 +32,8 @@
 
 -define(FAMILY, inet6).
 -export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
 
 
 
@@ -65,6 +66,14 @@ close(S) ->
 listen(S, Flag) ->
     prim_inet:listen(S, Flag).
 
+peeloff(S, AssocId) ->
+    case prim_inet:peeloff(S, AssocId) of
+	{ok, NewS}=Result ->
+	    inet_db:register_socket(NewS, ?MODULE),
+	    Result;
+	Error -> Error
+    end.
+
 connect(S, Addr, Port, Opts, Timer) ->
     inet_sctp:connect(S, Addr, Port, Opts, Timer).
 
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 735347b4b1..f8984b13fe 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -95,6 +95,7 @@
 -define(PACKET_REQ_RECV,        60).
 %%-define(SCTP_REQ_LISTEN,        61). MERGED
 -define(SCTP_REQ_BINDX,	        62). %% Multi-home SCTP bind
+-define(SCTP_REQ_PEELOFF,       63).
 
 %% subscribe codes, INET_REQ_SUBSCRIBE
 -define(INET_SUBS_EMPTY_OUT_Q,  1).
@@ -102,7 +103,7 @@
 %% reply codes for *_REQ_*
 -define(INET_REP_ERROR,    0).
 -define(INET_REP_OK,       1).
--define(INET_REP_SCTP,     2).
+-define(INET_REP,          2).
 
 %% INET, TCP and UDP options:
 -define(INET_OPT_REUSEADDR,      0).
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index e670dcfdad..8482dd1b12 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -31,7 +31,8 @@
 
 -define(FAMILY, inet).
 -export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
 
 
 
@@ -64,6 +65,14 @@ close(S) ->
 listen(S, Flag) ->
     prim_inet:listen(S, Flag).
 
+peeloff(S, AssocId) ->
+    case prim_inet:peeloff(S, AssocId) of
+	{ok, NewS}=Result ->
+	    inet_db:register_socket(NewS, ?MODULE),
+	    Result;
+	Error -> Error
+    end.
+
 %% A non-blocking connect is implemented when the initial call is to
 %% gen_sctp:connect_init which passes the value nowait as the Timer
 connect(S, Addr, Port, Opts, Timer) ->
-- 
cgit v1.2.3


From 5895bb8d7bde52972f74f6b51748230982f762f7 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 2 Sep 2011 16:18:13 +0200
Subject: kernel: Add tests for gen_sctp:peeloff/2

---
 lib/kernel/test/gen_sctp_SUITE.erl | 200 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 197 insertions(+), 3 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 1cf3d659d4..f422ffe442 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -31,14 +31,14 @@
    [basic/1,
     api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
     xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
-    basic_stream/1, xfer_stream_min/1]).
+    basic_stream/1, xfer_stream_min/1, peeloff/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
     [basic, api_open_close, api_listen, api_connect_init,
      api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
-     basic_stream, xfer_stream_min].
+     basic_stream, xfer_stream_min, peeloff].
 
 groups() -> 
     [].
@@ -418,7 +418,11 @@ setopt(S, Opt, Val) ->
     inet:setopts(S, [{Opt,Val}]).
 
 ok({ok,X}) ->
-    io:format("OK: ~p~n", [X]),
+    io:format("OK[~w]: ~p~n", [self(),X]),
+    X.
+
+log(X) ->
+    io:format("LOG[~w]: ~p~n", [self(),X]),
     X.
 
 flush() ->
@@ -773,3 +777,193 @@ do_from_other_process(Fun) ->
 	{'DOWN',Mref,_,_,Reason} ->
 	    erlang:exit(Reason)
     end.
+
+
+
+peeloff(doc) ->
+    "Peel off an SCTP stream socket";
+peeloff(suite) ->
+    [];
+peeloff(Config) when is_list(Config) ->
+    ?line Addr = {127,0,0,1},
+    ?line Stream = 0,
+    ?line Timeout = 333,
+    ?line S1 = socket_start(Addr, Timeout),
+    ?line P1 = socket_call(S1, port),
+    ?line Socket1 = socket_call(S1, socket),
+    ?line ok = socket_call(S1, {listen,true}),
+    ?line S2 = socket_start(Addr, Timeout),
+    ?line P2 = socket_call(S2, port),
+    ?line Socket2 = socket_call(S2, socket),
+    %%
+    ?line H_a = socket_req(S1, recv_assoc),
+    ?line {S2Ai,Sa,Sb} = socket_call(S2, {connect,Addr,P1,[]}),
+    ?line {S1Ai,Sb,Sa,Addr,P2} = socket_resp(H_a),
+    %%
+    ?line H_b = socket_req(S1, recv),
+    ?line ok = socket_call(S2, {send,S2Ai,Stream,<<"Data H_b">>}),
+    ?line {Addr,P2,S1Ai,Stream,<<"Data H_b">>} = socket_resp(H_b),
+    ?line H_c = socket_req(S1, {recv,Socket2}),
+    ?line ok =
+	socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Data H_c">>}),
+    ?line {Addr,P1,S2Ai,Stream,<<"Data H_c">>} = socket_resp(H_c),
+    %%
+    ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
+    ?line P3 = socket_call(S3, port),
+    ?line Socket3 = socket_call(S3, socket),
+    ?line S3Ai = S1Ai,
+    %%
+    ?line H_d = socket_req(S2, recv),
+    ?line ok = socket_call(S3, {send,S3Ai,Stream,<<"Data H_d">>}),
+    ?line {Addr,P3,S2Ai,Stream,<<"Data H_d">>} = socket_resp(H_d),
+    ?line ok = socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Data S2">>}),
+    ?line {Addr,P2,S3Ai,Stream,<<"Data S2">>} = socket_call(S2, {recv,Socket3}),
+    %%
+    ?line inet:i(sctp),
+    ?line ok = socket_stop(S1),
+    ?line ok = socket_stop(S2),
+    ?line {Addr,P2,[],#sctp_shutdown_event{assoc_id=S1Ai}} =
+	ok(socket_stop(S3)),
+    ok.
+
+%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% socket gen_server ultra light
+
+socket_peeloff(Socket, AssocId, Timeout) ->
+    Starter =
+	fun () ->
+		{ok,NewSocket} =
+		    gen_sctp:peeloff(Socket, AssocId),
+		NewSocket
+	end,
+    socket_starter(Starter, Timeout).
+
+socket_start(Addr, Timeout) ->
+    Starter =
+	fun () ->
+		{ok,Socket} =
+		    gen_sctp:open([{type,seqpacket},{ifaddr,Addr}]),
+		Socket
+	end,
+    socket_starter(Starter, Timeout).
+
+socket_starter(Starter, Timeout) ->
+    Parent = self(),
+    Owner =
+	spawn_link(
+	  fun () ->
+		  socket_starter(Starter(), Timeout, Parent)
+	  end),
+    io:format("Started socket ~w.~n", [Owner]),
+    Owner.
+
+socket_starter(Socket, Timeout, Parent) ->
+    try
+	Handler = socket_handler(Socket, Timeout),
+	socket_loop(Socket, Timeout, Parent, Handler)
+    catch
+	Class:Reason ->
+	    Stacktrace = erlang:get_stacktrace(),
+	    io:format(?MODULE_STRING":socket exception ~w:~w at~n"
+		      "~p.~n", [Class,Reason,Stacktrace]),
+	    erlang:raise(Class, Reason, Stacktrace)
+    end.
+
+socket_loop(Socket, Timeout, Parent, Handler) ->
+    receive
+	{Parent,Ref} -> % socket_stop()
+	    Result =
+		case log(gen_sctp:recv(Socket, Timeout)) of
+		    {error,timeout} -> ok;
+		    R -> R
+		end,
+	    ok = gen_sctp:close(Socket),
+	    Parent ! {self(),Ref, Result};
+	{Parent,Ref,Msg} ->
+	    Parent ! {self(),Ref,Handler(Msg)},
+	    socket_loop(Socket, Timeout, Parent, Handler)
+    end.
+
+socket_handler(Socket, Timeout) ->
+    fun ({listen,Listen}) ->
+	    gen_sctp:listen(Socket, Listen);
+	(port) ->
+	    ok(inet:port(Socket));
+	(socket) ->
+	    Socket;
+	(recv_assoc) ->
+	    {AssocAddr,AssocPort,[],
+	     #sctp_assoc_change{state=comm_up,
+				error=0,
+				outbound_streams=Os,
+				inbound_streams=Is,
+				assoc_id=AssocId}} =
+		ok(gen_sctp:recv(Socket, infinity)),
+	    case log(gen_sctp:recv(Socket, Timeout)) of
+		{ok,AssocAddr,AssocPort,[],
+		 #sctp_paddr_change{addr = {AssocAddr,AssocPort},
+				    state = addr_available,
+				    error = 0,
+				    assoc_id = AssocId}} -> ok;
+		{error,timeout} -> ok
+	    end,
+	    {AssocId,Os,Is,AssocAddr,AssocPort};
+	({connect,ConAddr,ConPort,ConOpts}) ->
+	    #sctp_assoc_change{state=comm_up,
+			       error=0,
+			       outbound_streams=Os,
+			       inbound_streams=Is,
+			       assoc_id=AssocId} =
+		ok(gen_sctp:connect(Socket, ConAddr, ConPort, ConOpts)),
+	    case log(gen_sctp:recv(Socket, Timeout)) of
+		{ok,ConAddr,ConPort,[],
+		 #sctp_paddr_change{addr = {ConAddr,ConPort},
+				    state = addr_available,
+				    error = 0,
+				    assoc_id = AssocId}} -> ok;
+		{error,timeout} -> ok
+	    end,
+	    {AssocId,Os,Is};
+	({send,AssocId,Stream,Data}) ->
+	    gen_sctp:send(Socket, AssocId, Stream, Data);
+	({send,S,AssocId,Stream,Data}) ->
+	    gen_sctp:send(S, AssocId, Stream, Data);
+	(recv) ->
+	    {Addr,Port,
+	     [#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}],Data} =
+		ok(gen_sctp:recv(Socket, infinity)),
+	    {Addr,Port,AssocId,Stream,Data};
+	({recv,S}) ->
+	    {Addr,Port,
+	     [#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}],Data} =
+		ok(gen_sctp:recv(S, infinity)),
+	    {Addr,Port,AssocId,Stream,Data}
+    end.
+
+socket_stop(Handler) ->
+    Mref = erlang:monitor(process, Handler),
+    Handler ! {self(),Mref},
+    receive
+	{Handler,Mref,Result} ->
+	    receive {'DOWN',Mref,_,_,_} -> Result end;
+	{'DOWN',Mref,_,_,Error} ->
+	    exit(Error)
+    end.
+
+socket_call(Handler, Request) ->
+    socket_resp(socket_req(Handler, Request)).
+
+socket_req(Handler, Request) ->
+    Mref = erlang:monitor(process, Handler),
+    Handler ! {self(),Mref,Request},
+    {Handler,Mref}.
+
+socket_resp({Handler,Mref}) ->
+    receive
+	{'DOWN',Mref,_,_,Error} ->
+	    exit(Error);
+	{Handler,Mref,Reply} ->
+	    erlang:demonitor(Mref),
+	    receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+	    Reply
+    end.
-- 
cgit v1.2.3


From 6dd80f3e9a556dc94ae59280f532e153e463b798 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 9 Sep 2011 17:40:29 +0200
Subject: erts,kernel: Bugfix - collect fragmented SCTP messages on recv

---
 lib/kernel/test/gen_sctp_SUITE.erl | 53 +++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 3 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index f422ffe442..9d12c629de 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -31,14 +31,14 @@
    [basic/1,
     api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
     xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
-    basic_stream/1, xfer_stream_min/1, peeloff/1]).
+    basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1]).
 
 suite() -> [{ct_hooks,[ts_install_cth]}].
 
 all() -> 
     [basic, api_open_close, api_listen, api_connect_init,
      api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
-     basic_stream, xfer_stream_min, peeloff].
+     basic_stream, xfer_stream_min, peeloff, buffers].
 
 groups() -> 
     [].
@@ -826,6 +826,49 @@ peeloff(Config) when is_list(Config) ->
 	ok(socket_stop(S3)),
     ok.
 
+
+
+buffers(doc) ->
+    ["Check sndbuf and recbuf behaviour"];
+buffers(suite) ->
+    [];
+buffers(Config) when is_list(Config) ->
+    ?line Limit = 8192,
+    ?line Data = mk_data(Limit),
+    ?line Addr = {127,0,0,1},
+    ?line Stream = 1,
+    ?line Timeout = 333,
+    ?line S1 = socket_start(Addr, Timeout),
+    ?line P1 = socket_call(S1, port),
+    ?line ok = socket_call(S1, {listen,true}),
+    ?line S2 = socket_start(Addr, Timeout),
+    ?line P2 = socket_call(S2, port),
+    %%
+    ?line H_a = socket_req(S1, recv_assoc),
+    ?line {S2Ai,Sa,Sb} = socket_call(S2, {connect,Addr,P1,[]}),
+    ?line {S1Ai,Sb,Sa,Addr,P2} = socket_resp(H_a),
+    %%
+    ?line ok = socket_call(S1, {setopts,[{recbuf,Limit}]}),
+    ?line case socket_call(S1, {getopts,[recbuf]}) of
+	      {ok,[{recbuf,RB1}]} when RB1 >= Limit -> ok
+	  end,
+    ?line H_b = socket_req(S1, recv),
+    ?line ok = socket_call(S2, {send,S2Ai,Stream,Data}),
+    ?line {Addr,P2,S1Ai,Stream,Data} = socket_resp(H_b),
+    %%
+    ?line ok = socket_stop(S1),
+    ?line {Addr,P1,[],#sctp_shutdown_event{assoc_id=S2Ai}} =
+	ok(socket_stop(S2)),
+    ok.
+
+mk_data(Words) ->
+    mk_data(0, Words, <<>>).
+%%
+mk_data(Words, Words, Bin) ->
+    Bin;
+mk_data(N, Words, Bin) ->
+    mk_data(N+1, Words, <<Bin/binary,N:32>>).
+
 %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%% socket gen_server ultra light
 
@@ -937,7 +980,11 @@ socket_handler(Socket, Timeout) ->
 	    {Addr,Port,
 	     [#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}],Data} =
 		ok(gen_sctp:recv(S, infinity)),
-	    {Addr,Port,AssocId,Stream,Data}
+	    {Addr,Port,AssocId,Stream,Data};
+	({setopts,Opts}) ->
+	    inet:setopts(Socket, Opts);
+	({getopts,Optnames}) ->
+	    inet:getopts(Socket, Optnames)
     end.
 
 socket_stop(Handler) ->
-- 
cgit v1.2.3


From 2831667add272eab3915bcecd9b8f3c89aad78f3 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 20 Sep 2011 15:56:59 +0000
Subject: kernel: Bugfix - SCTP connect with sndrcvinfo in assoc_change event

On some platforms (FreeBSD) the #sctp_assoc_change{state=comm_up}
event arriving during gen_sctp:connect/4,5 contains ancillary
data #sctp_sndrcvinfo{}. That was not expected by the code.
---
 lib/kernel/src/inet_sctp.erl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index 8482dd1b12..2d799d79fa 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -111,7 +111,7 @@ connect(S, Addr, Port, Opts, Timer) ->
 
 connect_get_assoc(S, Addr, Port, false, Timer) ->
     case recv(S, inet:timeout(Timer)) of
-	{ok, {Addr, Port, [], #sctp_assoc_change{state=St}=Ev}} ->
+	{ok, {Addr, Port, _, #sctp_assoc_change{state=St}=Ev}} ->
 	    if St =:= comm_up ->
 		    %% Yes, successfully connected, return the whole
 		    %% sctp_assoc_change event (containing, in particular,
@@ -132,7 +132,7 @@ connect_get_assoc(S, Addr, Port, false, Timer) ->
 connect_get_assoc(S, Addr, Port, Active, Timer) ->
     Timeout = inet:timeout(Timer),
     receive
-	{sctp,S,Addr,Port,{[],#sctp_assoc_change{state=St}=Ev}} ->
+	{sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} ->
 	    case Active of
 		once ->
 		    prim_inet:setopt(S, active, once);
-- 
cgit v1.2.3


From 4ef92bf4416c988a459a361e71e402c286e889b5 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 20 Sep 2011 16:01:29 +0000
Subject: kernel: Fix SCTP tests for the FreeBSD protocol stack

---
 lib/kernel/test/gen_sctp_SUITE.erl | 506 ++++++++++++++++++++++++-------------
 1 file changed, 337 insertions(+), 169 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 9d12c629de..8011c22ce5 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -109,29 +109,44 @@ xfer_min(Config) when is_list(Config) ->
 				 inbound_streams=SaInboundStreams,
 				 assoc_id=SaAssocId}=SaAssocChange} =
 	gen_sctp:connect(Sa, Loopback, Pb, []),
-    ?line {ok,{Loopback,
-	       Pa,[],
+    ?line {SbAssocId,SaOutboundStreams,SaInboundStreams} =
+	case recv_event(ok(gen_sctp:recv(Sb, infinity))) of
+	      {Loopback,Pa,
 	       #sctp_assoc_change{state=comm_up,
 				  error=0,
 				  outbound_streams=SbOutboundStreams,
 				  inbound_streams=SbInboundStreams,
-				  assoc_id=SbAssocId}}} =
-	gen_sctp:recv(Sb, infinity),
-    ?line SaOutboundStreams = SbInboundStreams,
-    ?line SbOutboundStreams = SaInboundStreams,
+				  assoc_id=AssocId}} ->
+		  {AssocId,SbInboundStreams,SbOutboundStreams};
+	      {Loopback,Pa,
+	       #sctp_paddr_change{state=addr_confirmed,
+				  addr={Loopback,Pa},
+				  error=0,
+				  assoc_id=AssocId}} ->
+		{Loopback,Pa,
+		 #sctp_assoc_change{state=comm_up,
+				    error=0,
+				    outbound_streams=SbOutboundStreams,
+				    inbound_streams=SbInboundStreams,
+				    assoc_id=AssocId}} =
+		    ?line recv_event(ok(gen_sctp:recv(Sb, infinity))),
+		{AssocId,SbInboundStreams,SbOutboundStreams}
+	end,
+
     ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
-    ?line case gen_sctp:recv(Sb, infinity) of
-	      {ok,{Loopback,
-		   Pa,
-		   [#sctp_sndrcvinfo{stream=Stream,
-				     assoc_id=SbAssocId}],
-		   Data}} -> ok;
-	      {ok,{Loopback,
-		   Pa,[],
+    ?line case ok(gen_sctp:recv(Sb, infinity)) of
+	      {Loopback,
+	       Pa,
+	       [#sctp_sndrcvinfo{stream=Stream,
+				 assoc_id=SbAssocId}],
+	       Data} -> ok;
+	      Event1 ->
+		  {Loopback,Pa,
 		   #sctp_paddr_change{addr = {Loopback,_},
 				      state = addr_available,
 				      error = 0,
-				      assoc_id = SbAssocId}}} ->
+				      assoc_id = SbAssocId}} =
+		      recv_event(Event1),
 		  {ok,{Loopback,
 		       Pa,
 		       [#sctp_sndrcvinfo{stream=Stream,
@@ -139,30 +154,40 @@ xfer_min(Config) when is_list(Config) ->
 		       Data}} =	gen_sctp:recv(Sb, infinity)
 	  end,
     ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
-    ?line {ok,{Loopback,
-	       Pb,
+    ?line case ok(gen_sctp:recv(Sa, infinity)) of
+	      {Loopback,Pb,
 	       [#sctp_sndrcvinfo{stream=Stream,
 				 assoc_id=SaAssocId}],
-	       Data}} =
-	gen_sctp:recv(Sa, infinity),
+	       Data} ->
+		  ok;
+	      Event2 ->
+		  {Loopback,Pb,
+		   #sctp_paddr_change{addr={_,Pb},
+				      state=addr_confirmed,
+				      error=0,
+				      assoc_id=SaAssocId}} =
+		      ?line recv_event(Event2),
+		  ?line {Loopback,
+			 Pb,
+			 [#sctp_sndrcvinfo{stream=Stream,
+					   assoc_id=SaAssocId}],
+			 Data} =
+		      ok(gen_sctp:recv(Sa, infinity))
+	  end,
     %%
     ?line ok = gen_sctp:eof(Sa, SaAssocChange),
-    ?line {ok,{Loopback,
- 	       Pa,[],
- 	       #sctp_shutdown_event{assoc_id=SbAssocId}}} =
- 	gen_sctp:recv(Sb, infinity),
-    ?line {ok,{Loopback,
- 	       Pb,[],
- 	       #sctp_assoc_change{state=shutdown_comp,
- 				  error=0,
- 				  assoc_id=SaAssocId}}} =
- 	gen_sctp:recv(Sa, infinity),
-    ?line {ok,{Loopback,
- 	       Pa,[],
- 	       #sctp_assoc_change{state=shutdown_comp,
- 				  error=0,
- 				  assoc_id=SbAssocId}}} =
- 	gen_sctp:recv(Sb, infinity),
+    ?line {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
+	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+    ?line {Loopback,Pb,
+	   #sctp_assoc_change{state=shutdown_comp,
+			      error=0,
+			      assoc_id=SaAssocId}} =
+ 	recv_event(ok(gen_sctp:recv(Sa, infinity))),
+    ?line {Loopback,Pa,
+	   #sctp_assoc_change{state=shutdown_comp,
+			      error=0,
+			      assoc_id=SbAssocId}} =
+ 	recv_event(ok(gen_sctp:recv(Sb, infinity))),
     ?line ok = gen_sctp:close(Sa),
     ?line ok = gen_sctp:close(Sb),
     
@@ -187,32 +212,51 @@ xfer_active(Config) when is_list(Config) ->
     
     ?line {ok,Sa} = gen_sctp:open([{active,true}]),
     ?line {ok,Pa} = inet:port(Sa),
-    ?line {ok,#sctp_assoc_change{state=comm_up,
-				 error=0,
-				 outbound_streams=SaOutboundStreams,
-				 inbound_streams=SaInboundStreams,
-				 assoc_id=SaAssocId}=SaAssocChange} =
-	gen_sctp:connect(Sa, Loopback, Pb, []),
+    ?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
+    ?line #sctp_assoc_change{state=comm_up,
+			     error=0,
+			     outbound_streams=SaOutboundStreams,
+			     inbound_streams=SaInboundStreams,
+			     assoc_id=SaAssocId} = SaAssocChange =
+	recv_assoc_change(Sa, Loopback, Pb, Timeout),
     ?line io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, "
 		    "SaOutboundStreams=~p, SaInboundStreams=~p~n",
 		    [Sa,Pa,Sb,Pb,SaAssocId,
 		     SaOutboundStreams,SaInboundStreams]),
-    ?line SbAssocId = 
-	receive
-	    {sctp,Sb,Loopback,Pa,
-	     {[],
-	      #sctp_assoc_change{state=comm_up,
-				 error=0,
-				   outbound_streams=SbOutboundStreams,
-				 inbound_streams=SbInboundStreams,
-				 assoc_id=SBAI}}} ->
-		?line SaOutboundStreams = SbInboundStreams,
-		?line  SaInboundStreams = SbOutboundStreams,
-		SBAI
-	after Timeout ->
-		?line test_server:fail({unexpected,flush()})
-	end,
+    ?line #sctp_assoc_change{state=comm_up,
+			     error=0,
+			     outbound_streams=SbOutboundStreams,
+			     inbound_streams=SbInboundStreams,
+			     assoc_id=SbAssocId} =
+	recv_assoc_change(Sb, Loopback, Pa, Timeout),
+    ?line SbOutboundStreams = SaInboundStreams,
+    ?line SbInboundStreams = SaOutboundStreams,
     ?line io:format("SbAssocId=~p~n", [SbAssocId]),
+
+    ?line case recv_paddr_change(Sa, Loopback, Pb, 314) of
+	      #sctp_paddr_change{state=addr_confirmed,
+				 addr={_,Pb},
+				 error=0,
+				 assoc_id=SaAssocId} -> ok;
+	      #sctp_paddr_change{state=addr_available,
+				 addr={_,Pb},
+				 error=0,
+				 assoc_id=SaAssocId} -> ok;
+	      timeout -> ok
+	  end,
+    ?line case recv_paddr_change(Sb, Loopback, Pa, 314) of
+	      #sctp_paddr_change{state=addr_confirmed,
+				 addr={Loopback,Pa},
+				 error=0,
+				 assoc_id=SbAssocId} -> ok;
+	      #sctp_paddr_change{state=addr_available,
+				 addr={Loopback,Pa},
+				 error=0,
+				 assoc_id=SbAssocId} -> ok;
+	      timeout -> ok
+	  end,
+    ?line [] = flush(),
+
     ?line ok =
 	do_from_other_process(
 	  fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end),
@@ -220,19 +264,7 @@ xfer_active(Config) when is_list(Config) ->
 	      {sctp,Sb,Loopback,Pa,
 	       {[#sctp_sndrcvinfo{stream=Stream,
 				  assoc_id=SbAssocId}],
-		Data}} -> ok;
-	      {sctp,Sb,Loopback,Pa,
-	       {[],
-		#sctp_paddr_change{addr = {Loopback,_},
-				   state = addr_available,
-				   error = 0,
-				   assoc_id = SbAssocId}}} ->
-		  ?line receive
-			    {sctp,Sb,Loopback,Pa,
-			     {[#sctp_sndrcvinfo{stream=Stream,
-						assoc_id=SbAssocId}],
-			      Data}} -> ok
-			end
+		Data}} -> ok
 	  after Timeout ->
 		  ?line test_server:fail({unexpected,flush()})
 	  end,
@@ -247,23 +279,19 @@ xfer_active(Config) when is_list(Config) ->
 	  end,
     %%
     ?line ok = gen_sctp:abort(Sa, SaAssocChange),
-    ?line receive
-	      {sctp,Sb,Loopback,Pa,
-	       {[],
-		#sctp_assoc_change{state=comm_lost,
-				   assoc_id=SbAssocId}}} -> ok
-	  after Timeout ->
+    ?line case recv_assoc_change(Sb, Loopback, Pa, Timeout) of
+	      #sctp_assoc_change{state=comm_lost,
+				 assoc_id=SbAssocId} -> ok;
+	      timeout ->
 		  ?line test_server:fail({unexpected,flush()})
 	  end,
     ?line ok = gen_sctp:close(Sb),
-    ?line receive
-              {sctp,Sa,Loopback,Pb,
-               {[],
-                #sctp_assoc_change{state=comm_lost,
-                                   assoc_id=SaAssocId}}} -> ok
-          after Timeout ->
-                  ?line test_server:fail({unexpected,flush()})
-          end,
+    ?line case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
+	      #sctp_assoc_change{state=comm_lost,
+				 assoc_id=SaAssocId} -> ok;
+	      timeout ->
+		  ?line test_server:fail({unexpected,flush()})
+	  end,
     ?line receive
               {sctp_error,Sa,enotconn} -> ok % Solaris
           after 17 -> ok %% Only happens on Solaris
@@ -276,6 +304,30 @@ xfer_active(Config) when is_list(Config) ->
 	  end,
     ok.
 
+recv_assoc_change(S, Addr, Port, Timeout) ->
+    receive
+	{sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} ->
+	    AssocChange;
+	{sctp,S,Addr,Port,
+	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+	  #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} ->
+	    AssocChange
+    after Timeout ->
+	    timeout
+    end.
+
+recv_paddr_change(S, Addr, Port, Timeout) ->
+    receive
+	{sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} ->
+	    PaddrChange;
+	{sctp,S,Addr,Port,
+	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+	  #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} ->
+	    PaddrChange
+    after Timeout ->
+	    timeout
+    end.
+
 def_sndrcvinfo(doc) ->
     "Test that #sctp_sndrcvinfo{} parameters set on a socket "
 	"are used by gen_sctp:send/4";
@@ -312,20 +364,33 @@ def_sndrcvinfo(Config) when is_list(Config) ->
        assoc_id=S2AssocId} = S2AssocChange =
 	ok(gen_sctp:connect(S2, Loopback, P1, [])),
     ?LOGVAR(S2AssocChange),
-    ?line case ok(gen_sctp:recv(S1)) of
-	      {Loopback, P2,[],
+    ?line case recv_event(ok(gen_sctp:recv(S1))) of
+	      {Loopback,P2,
 	       #sctp_assoc_change{
+			  state=comm_up,
+			  error=0,
+			  assoc_id=S1AssocId}} ->
+		  ?LOGVAR(S1AssocId);
+	      {Loopback,P2,
+	       #sctp_paddr_change{
+			  state=addr_confirmed,
+			  error=0,
+			  assoc_id=S1AssocId}} ->
+		  ?LOGVAR(S1AssocId),
+		  {Loopback,P2,
+		   #sctp_assoc_change{
 			      state=comm_up,
 			      error=0,
-			      assoc_id=S1AssocId}} ->
-		  ?LOGVAR(S1AssocId)
+			      assoc_id=S1AssocId}} =
+		      recv_event(ok(gen_sctp:recv(S1)))
 	  end,
+
     ?line #sctp_sndrcvinfo{
-       ppid=17, context=0, timetolive=0, assoc_id=S1AssocId} =
+       ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
 	getopt(
 	  S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
     ?line #sctp_sndrcvinfo{
-       ppid=0, context=0, timetolive=0, assoc_id=S2AssocId} =
+       ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} =
 	getopt(
 	  S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
     %%
@@ -335,7 +400,19 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 	      {Loopback,P1,
 	       [#sctp_sndrcvinfo{
 		   stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
-	       <<"1: ",Data/binary>>} -> ok
+	       <<"1: ",Data/binary>>} -> ok;
+	      Event1 ->
+		  ?line {Loopback,P1,
+			 #sctp_paddr_change{state=addr_confirmed,
+					    addr={_,P1},
+					    error=0,
+					    assoc_id=S2AssocId}} =
+		      recv_event(Event1),
+		  ?line {Loopback,P1,
+			 [#sctp_sndrcvinfo{
+			     stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
+			 <<"1: ",Data/binary>>} =
+		      ok(gen_sctp:recv(S2))
 	  end,
     %%
     ?line ok =
@@ -368,10 +445,12 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 	       [#sctp_sndrcvinfo{
 		   stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
 	       <<"3: ",Data/binary>>} -> ok;
-	      {Loopback,P2,[],
-	       #sctp_paddr_change{
-			  addr={Loopback,_}, state=addr_available,
-			  error=0, assoc_id=S1AssocId}} ->
+	      Event2 ->
+		  {Loopback,P2,
+		   #sctp_paddr_change{
+			      addr={Loopback,_}, state=addr_available,
+			      error=0, assoc_id=S1AssocId}} =
+		      recv_event(Event2),
 		  ?line case ok(gen_sctp:recv(S1)) of
 			    {Loopback,P2,
 			     [#sctp_sndrcvinfo{
@@ -525,7 +604,10 @@ api_listen(Config) when is_list(Config) ->
 			     #sctp_assoc_change{
 				  state=comm_lost}}} =
 		      gen_sctp:recv(Sa, infinity);
-	      {error,#sctp_assoc_change{state=cant_assoc}} -> ok
+	      {error,#sctp_assoc_change{state=cant_assoc}} ->
+		  ok%;
+	      %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
+	      %% 	  ok
 	  end,
     ?line ok = gen_sctp:listen(Sb, true),
     ?line {ok,#sctp_assoc_change{state=comm_up,
@@ -557,29 +639,41 @@ api_connect_init(Config) when is_list(Config) ->
     ?line {ok,Sa} = gen_sctp:open(),
     ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
 	      {error,econnrefused} ->
-		  ?line {ok,{Localhost,
-			     Pb,[],
-			     #sctp_assoc_change{state=comm_lost}}} =
-		      gen_sctp:recv(Sa, infinity);
+		  ?line {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
+		      recv_event(ok(gen_sctp:recv(Sa, infinity)));
 	      ok ->
-		  ?line {ok,{Localhost,
-			     Pb,[],
-			     #sctp_assoc_change{state=cant_assoc}}} =
-		      gen_sctp:recv(Sa, infinity)
+		  ?line {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
+		      recv_event(ok(gen_sctp:recv(Sa, infinity)))
 	  end,
     ?line ok = gen_sctp:listen(Sb, true),
     ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
 	      ok ->
-		  ?line {ok,{Localhost,
-			     Pb,[],
-			     #sctp_assoc_change{
-				  state = comm_up}}} =
-		      gen_sctp:recv(Sa, infinity)
+		  ?line {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
+		      recv_event(ok(gen_sctp:recv(Sa, infinity)))
 	  end,
     ?line ok = gen_sctp:close(Sa),
     ?line ok = gen_sctp:close(Sb),
     ok.
 
+recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) ->
+    {Addr,Port,AssocChange};
+recv_event({Addr,Port,
+	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
+	    #sctp_assoc_change{assoc_id=Assoc}=AssocChange}) ->
+    {Addr,Port,AssocChange};
+recv_event({Addr,Port,[],#sctp_paddr_change{}=PaddrChange}) ->
+    {Addr,Port,PaddrChange};
+recv_event({Addr,Port,
+	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
+	    #sctp_paddr_change{assoc_id=Assoc}=PaddrChange}) ->
+    {Addr,Port,PaddrChange};
+recv_event({Addr,Port,[],#sctp_shutdown_event{}=ShutdownEvent}) ->
+    {Addr,Port,ShutdownEvent};
+recv_event({Addr,Port,
+	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
+	    #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) ->
+    {Addr,Port,ShutdownEvent}.
+
 api_opts(doc) ->
     "Test socket options";
 api_opts(suite) ->
@@ -645,9 +739,14 @@ implicit_inet6(S1, Addr) ->
     ?line P2 = ok(inet:port(S2)),
     ?line #sctp_assoc_change{state=comm_up} =
 	ok(gen_sctp:connect(S2, Addr, P1, [])),
-    ?line case ok(gen_sctp:recv(S1)) of
-	      {Addr,P2,[],#sctp_assoc_change{state=comm_up}} ->
-		  ok
+    ?line case recv_event(ok(gen_sctp:recv(S1))) of
+	      {Addr,P2,#sctp_assoc_change{state=comm_up}} ->
+		  ok;
+	      {Addr,P2,#sctp_paddr_change{state=addr_confirmed,
+					  addr={Addr,P2},
+					  error=0}} ->
+		  {Addr,P2,#sctp_assoc_change{state=comm_up}} =
+		      recv_event(ok(gen_sctp:recv(S1)))
 	  end,
     ?line case ok(inet:sockname(S1)) of
 	      {Addr,P1} -> ok;
@@ -692,14 +791,29 @@ xfer_stream_min(Config) when is_list(Config) ->
 				 inbound_streams=SaInboundStreams,
 				 assoc_id=SaAssocId}} =
 	gen_sctp:connect(Sa, Loopback, Pb, []),
-    ?line {ok,{Loopback,
-	       Pa,[],
-	       #sctp_assoc_change{state=comm_up,
-				  error=0,
-				  outbound_streams=SbOutboundStreams,
-				  inbound_streams=SbInboundStreams,
-				  assoc_id=SbAssocId}}} =
-	gen_sctp:recv(Sb, infinity),
+    ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} =
+	case recv_event(ok(gen_sctp:recv(Sb, infinity))) of
+	    {Loopback,Pa,
+	     #sctp_assoc_change{state=comm_up,
+				error=0,
+				outbound_streams=OS,
+				inbound_streams=IS,
+				assoc_id=AI}} ->
+		{OS,IS,AI};
+	    {Loopback,Pa,
+	     #sctp_paddr_change{state=addr_confirmed,
+				addr={Loopback,Pa},
+				error=0,
+				assoc_id=AI}} ->
+		{Loopback,Pa,
+		 ?line #sctp_assoc_change{state=comm_up,
+					  error=0,
+					  outbound_streams=OS,
+					  inbound_streams=IS,
+					  assoc_id=AI}} =
+		    recv_event(ok(gen_sctp:recv(Sb, infinity))),
+		{OS,IS,AI}
+	end,
     ?line SaOutboundStreams = SbInboundStreams,
     ?line SbOutboundStreams = SaInboundStreams,
     ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
@@ -724,24 +838,33 @@ xfer_stream_min(Config) when is_list(Config) ->
     ?line ok =
 	do_from_other_process(
 	  fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
-    ?line {ok,{Loopback,
-	       Pb,
+    ?line case ok(gen_sctp:recv(Sa, infinity)) of
+	      {Loopback,Pb,
 	       [#sctp_sndrcvinfo{stream=Stream,
 				 assoc_id=SaAssocId}],
-	       Data}} =
-	gen_sctp:recv(Sa, infinity),
-    %%
+	       Data} -> ok;
+	      Event1 ->
+		  ?line {Loopback,Pb,
+			 #sctp_paddr_change{state=addr_confirmed,
+					    addr={_,Pb},
+					    error=0,
+					    assoc_id=SaAssocId}} =
+		      recv_event(Event1),
+		  ?line {Loopback,Pb,
+			 [#sctp_sndrcvinfo{stream=Stream,
+					   assoc_id=SaAssocId}],
+			 Data} =
+		      ok(gen_sctp:recv(Sa, infinity))
+	  end,
     ?line ok = gen_sctp:close(Sa),
-    ?line {ok,{Loopback,
-	       Pa,[],
-	       #sctp_shutdown_event{assoc_id=SbAssocId}}} =
-	gen_sctp:recv(Sb, infinity),
-    ?line {ok,{Loopback,
-	       Pa,[],
-	       #sctp_assoc_change{state=shutdown_comp,
-				  error=0,
-				  assoc_id=SbAssocId}}} =
-	gen_sctp:recv(Sb, infinity),
+    ?line {Loopback,Pa,
+	   #sctp_shutdown_event{assoc_id=SbAssocId}} =
+	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+    ?line {Loopback,Pa,
+	   #sctp_assoc_change{state=shutdown_comp,
+			      error=0,
+			      assoc_id=SbAssocId}} =
+	recv_event(ok(gen_sctp:recv(Sb, infinity))),
     ?line ok = gen_sctp:close(Sb),
 
     ?line receive
@@ -822,8 +945,8 @@ peeloff(Config) when is_list(Config) ->
     ?line inet:i(sctp),
     ?line ok = socket_stop(S1),
     ?line ok = socket_stop(S2),
-    ?line {Addr,P2,[],#sctp_shutdown_event{assoc_id=S1Ai}} =
-	ok(socket_stop(S3)),
+    ?line {Addr,P2,#sctp_shutdown_event{assoc_id=S1Ai}} =
+	recv_event(ok(socket_stop(S3))),
     ok.
 
 
@@ -857,8 +980,8 @@ buffers(Config) when is_list(Config) ->
     ?line {Addr,P2,S1Ai,Stream,Data} = socket_resp(H_b),
     %%
     ?line ok = socket_stop(S1),
-    ?line {Addr,P1,[],#sctp_shutdown_event{assoc_id=S2Ai}} =
-	ok(socket_stop(S2)),
+    ?line {Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}} =
+	recv_event(ok(socket_stop(S2))),
     ok.
 
 mk_data(Words) ->
@@ -935,38 +1058,83 @@ socket_handler(Socket, Timeout) ->
 	(socket) ->
 	    Socket;
 	(recv_assoc) ->
-	    {AssocAddr,AssocPort,[],
-	     #sctp_assoc_change{state=comm_up,
-				error=0,
-				outbound_streams=Os,
-				inbound_streams=Is,
-				assoc_id=AssocId}} =
-		ok(gen_sctp:recv(Socket, infinity)),
-	    case log(gen_sctp:recv(Socket, Timeout)) of
-		{ok,AssocAddr,AssocPort,[],
-		 #sctp_paddr_change{addr = {AssocAddr,AssocPort},
-				    state = addr_available,
-				    error = 0,
-				    assoc_id = AssocId}} -> ok;
-		{error,timeout} -> ok
-	    end,
-	    {AssocId,Os,Is,AssocAddr,AssocPort};
+	    case recv_event(ok(gen_sctp:recv(Socket, Timeout))) of
+		{AssocAddr,AssocPort,
+		 #sctp_paddr_change{state=addr_confirmed,
+				    addr={_,AssocPort},
+				    error=0,
+				    assoc_id=AssocId}} ->
+		    {AssocAddr,AssocPort,
+		     #sctp_assoc_change{state=comm_up,
+					error=0,
+					outbound_streams=Os,
+					inbound_streams=Is,
+					assoc_id=AssocId}} =
+			recv_event(ok(gen_sctp:recv(Socket, infinity))),
+		    {AssocId,Os,Is,AssocAddr,AssocPort};
+		{AssocAddr,AssocPort,
+		 #sctp_assoc_change{state=comm_up,
+				    error=0,
+				    outbound_streams=Os,
+				    inbound_streams=Is,
+				    assoc_id=AssocId}} ->
+		    {AssocId,Os,Is,AssocAddr,AssocPort}
+	    end;
+	    %% {AssocAddr,AssocPort,[],
+	    %%  #sctp_assoc_change{state=comm_up,
+	    %% 			error=0,
+	    %% 			outbound_streams=Os,
+	    %% 			inbound_streams=Is,
+	    %% 			assoc_id=AssocId}} =
+	    %% 	ok(gen_sctp:recv(Socket, infinity)),
+	    %% case log(gen_sctp:recv(Socket, Timeout)) of
+	    %% 	{ok,AssocAddr,AssocPort,[],
+	    %% 	 #sctp_paddr_change{addr = {AssocAddr,AssocPort},
+	    %% 			    state = addr_available,
+	    %% 			    error = 0,
+	    %% 			    assoc_id = AssocId}} -> ok;
+	    %% 	{error,timeout} -> ok
+	    %% end,
+	    %% {AssocId,Os,Is,AssocAddr,AssocPort};
 	({connect,ConAddr,ConPort,ConOpts}) ->
-	    #sctp_assoc_change{state=comm_up,
-			       error=0,
-			       outbound_streams=Os,
-			       inbound_streams=Is,
-			       assoc_id=AssocId} =
-		ok(gen_sctp:connect(Socket, ConAddr, ConPort, ConOpts)),
-	    case log(gen_sctp:recv(Socket, Timeout)) of
-		{ok,ConAddr,ConPort,[],
-		 #sctp_paddr_change{addr = {ConAddr,ConPort},
-				    state = addr_available,
-				    error = 0,
-				    assoc_id = AssocId}} -> ok;
-		{error,timeout} -> ok
-	    end,
-	    {AssocId,Os,Is};
+	    ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts),
+	    case recv_event(ok(gen_sctp:recv(Socket, Timeout))) of
+		{ConAddr,ConPort,
+		 #sctp_paddr_change{state=addr_confirmed,
+				    addr={_,ConPort},
+				    error=0,
+				    assoc_id=AssocId}} ->
+		    {ConAddr,ConPort,
+		     #sctp_assoc_change{state=comm_up,
+					error=0,
+					outbound_streams=Os,
+					inbound_streams=Is,
+					assoc_id=AssocId}} =
+			recv_event(ok(gen_sctp:recv(Socket, infinity))),
+		    {AssocId,Os,Is};
+		{ConAddr,ConPort,
+		 #sctp_assoc_change{state=comm_up,
+				    error=0,
+				    outbound_streams=Os,
+				    inbound_streams=Is,
+				    assoc_id=AssocId}} ->
+		    {AssocId,Os,Is}
+	    end;
+	    %% #sctp_assoc_change{state=comm_up,
+	    %% 		       error=0,
+	    %% 		       outbound_streams=Os,
+	    %% 		       inbound_streams=Is,
+	    %% 		       assoc_id=AssocId} =
+	    %% 	ok(gen_sctp:connect(Socket, ConAddr, ConPort, ConOpts)),
+	    %% case log(gen_sctp:recv(Socket, Timeout)) of
+	    %% 	{ok,ConAddr,ConPort,[],
+	    %% 	 #sctp_paddr_change{addr = {ConAddr,ConPort},
+	    %% 			    state = addr_available,
+	    %% 			    error = 0,
+	    %% 			    assoc_id = AssocId}} -> ok;
+	    %% 	{error,timeout} -> ok
+	    %% end,
+	    %% {AssocId,Os,Is};
 	({send,AssocId,Stream,Data}) ->
 	    gen_sctp:send(Socket, AssocId, Stream, Data);
 	({send,S,AssocId,Stream,Data}) ->
-- 
cgit v1.2.3


From c501ca26c01d222af4b05b1732059a7a9684b1c3 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Thu, 22 Sep 2011 11:11:57 +0200
Subject: kernel: Rewrite SCTP test socket handler

The socket handler needs more flexibility regarding which events
are expected to be received.
---
 lib/kernel/test/gen_sctp_SUITE.erl | 603 ++++++++++++++++++++++---------------
 1 file changed, 360 insertions(+), 243 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 8011c22ce5..07434df8da 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -110,7 +110,7 @@ xfer_min(Config) when is_list(Config) ->
 				 assoc_id=SaAssocId}=SaAssocChange} =
 	gen_sctp:connect(Sa, Loopback, Pb, []),
     ?line {SbAssocId,SaOutboundStreams,SaInboundStreams} =
-	case recv_event(ok(gen_sctp:recv(Sb, infinity))) of
+	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
 	      {Loopback,Pa,
 	       #sctp_assoc_change{state=comm_up,
 				  error=0,
@@ -129,12 +129,12 @@ xfer_min(Config) when is_list(Config) ->
 				    outbound_streams=SbOutboundStreams,
 				    inbound_streams=SbInboundStreams,
 				    assoc_id=AssocId}} =
-		    ?line recv_event(ok(gen_sctp:recv(Sb, infinity))),
+		    ?line recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
 		{AssocId,SbInboundStreams,SbOutboundStreams}
 	end,
 
     ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
-    ?line case ok(gen_sctp:recv(Sb, infinity)) of
+    ?line case log_ok(gen_sctp:recv(Sb, infinity)) of
 	      {Loopback,
 	       Pa,
 	       [#sctp_sndrcvinfo{stream=Stream,
@@ -154,7 +154,7 @@ xfer_min(Config) when is_list(Config) ->
 		       Data}} =	gen_sctp:recv(Sb, infinity)
 	  end,
     ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
-    ?line case ok(gen_sctp:recv(Sa, infinity)) of
+    ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
 	      {Loopback,Pb,
 	       [#sctp_sndrcvinfo{stream=Stream,
 				 assoc_id=SaAssocId}],
@@ -172,22 +172,22 @@ xfer_min(Config) when is_list(Config) ->
 			 [#sctp_sndrcvinfo{stream=Stream,
 					   assoc_id=SaAssocId}],
 			 Data} =
-		      ok(gen_sctp:recv(Sa, infinity))
+		      log_ok(gen_sctp:recv(Sa, infinity))
 	  end,
     %%
     ?line ok = gen_sctp:eof(Sa, SaAssocChange),
     ?line {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
-	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
     ?line {Loopback,Pb,
 	   #sctp_assoc_change{state=shutdown_comp,
 			      error=0,
 			      assoc_id=SaAssocId}} =
- 	recv_event(ok(gen_sctp:recv(Sa, infinity))),
+	recv_event(log_ok(gen_sctp:recv(Sa, infinity))),
     ?line {Loopback,Pa,
 	   #sctp_assoc_change{state=shutdown_comp,
 			      error=0,
 			      assoc_id=SbAssocId}} =
- 	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
     ?line ok = gen_sctp:close(Sa),
     ?line ok = gen_sctp:close(Sb),
     
@@ -338,11 +338,11 @@ def_sndrcvinfo(Config) when is_list(Config) ->
     ?line Data = <<"What goes up, must come down.">>,
     %%
     ?line S1 =
-	ok(gen_sctp:open(
+	log_ok(gen_sctp:open(
 	     0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
     ?LOGVAR(S1),
     ?line P1 =
-	ok(inet:port(S1)),
+	log_ok(inet:port(S1)),
     ?LOGVAR(P1),
     ?line #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
 	getopt(S1, sctp_default_send_param),
@@ -350,10 +350,10 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 	gen_sctp:listen(S1, true),
     %%
     ?line S2 =
-	ok(gen_sctp:open()),
+	log_ok(gen_sctp:open()),
     ?LOGVAR(S2),
     ?line P2 =
-	ok(inet:port(S2)),
+	log_ok(inet:port(S2)),
     ?LOGVAR(P2),
     ?line #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
 	getopt(S2, sctp_default_send_param),
@@ -362,9 +362,9 @@ def_sndrcvinfo(Config) when is_list(Config) ->
        state=comm_up,
        error=0,
        assoc_id=S2AssocId} = S2AssocChange =
-	ok(gen_sctp:connect(S2, Loopback, P1, [])),
+	log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
     ?LOGVAR(S2AssocChange),
-    ?line case recv_event(ok(gen_sctp:recv(S1))) of
+    ?line case recv_event(log_ok(gen_sctp:recv(S1))) of
 	      {Loopback,P2,
 	       #sctp_assoc_change{
 			  state=comm_up,
@@ -382,7 +382,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 			      state=comm_up,
 			      error=0,
 			      assoc_id=S1AssocId}} =
-		      recv_event(ok(gen_sctp:recv(S1)))
+		      recv_event(log_ok(gen_sctp:recv(S1)))
 	  end,
 
     ?line #sctp_sndrcvinfo{
@@ -396,7 +396,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
     %%
     ?line ok =
 	gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
-    ?line case ok(gen_sctp:recv(S2)) of
+    ?line case log_ok(gen_sctp:recv(S2)) of
 	      {Loopback,P1,
 	       [#sctp_sndrcvinfo{
 		   stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
@@ -412,7 +412,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 			 [#sctp_sndrcvinfo{
 			     stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
 			 <<"1: ",Data/binary>>} =
-		      ok(gen_sctp:recv(S2))
+		      log_ok(gen_sctp:recv(S2))
 	  end,
     %%
     ?line ok =
@@ -432,7 +432,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
     %%
     ?line ok =
 	gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
-    ?line case ok(gen_sctp:recv(S2)) of
+    ?line case log_ok(gen_sctp:recv(S2)) of
 	      {Loopback,P1,
 	       [#sctp_sndrcvinfo{
 		   stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
@@ -440,7 +440,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 	  end,
     ?line ok =
 	gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
-    ?line case ok(gen_sctp:recv(S1)) of
+    ?line case log_ok(gen_sctp:recv(S1)) of
 	      {Loopback,P2,
 	       [#sctp_sndrcvinfo{
 		   stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
@@ -451,7 +451,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 			      addr={Loopback,_}, state=addr_available,
 			      error=0, assoc_id=S1AssocId}} =
 		      recv_event(Event2),
-		  ?line case ok(gen_sctp:recv(S1)) of
+		  ?line case log_ok(gen_sctp:recv(S1)) of
 			    {Loopback,P2,
 			     [#sctp_sndrcvinfo{
 				 stream=1, ppid=0, context=0,
@@ -467,7 +467,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
 		    #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
 		    <<"4: ",Data/binary>>)
 	  end),
-    ?line case ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
+    ?line case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
 	      {Loopback,P2,
 	       [#sctp_sndrcvinfo{
 		   stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
@@ -496,9 +496,9 @@ getopt(S, Opt, Param) ->
 setopt(S, Opt, Val) ->
     inet:setopts(S, [{Opt,Val}]).
 
-ok({ok,X}) ->
-    io:format("OK[~w]: ~p~n", [self(),X]),
-    X.
+log_ok(X) -> log(ok(X)).
+
+ok({ok,X}) -> X.
 
 log(X) ->
     io:format("LOG[~w]: ~p~n", [self(),X]),
@@ -640,16 +640,16 @@ api_connect_init(Config) when is_list(Config) ->
     ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
 	      {error,econnrefused} ->
 		  ?line {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
-		      recv_event(ok(gen_sctp:recv(Sa, infinity)));
+		      recv_event(log_ok(gen_sctp:recv(Sa, infinity)));
 	      ok ->
 		  ?line {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
-		      recv_event(ok(gen_sctp:recv(Sa, infinity)))
+		      recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
 	  end,
     ?line ok = gen_sctp:listen(Sb, true),
     ?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
 	      ok ->
 		  ?line {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
-		      recv_event(ok(gen_sctp:recv(Sa, infinity)))
+		      recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
 	  end,
     ?line ok = gen_sctp:close(Sa),
     ?line ok = gen_sctp:close(Sb),
@@ -699,7 +699,7 @@ api_opts(Config) when is_list(Config) ->
 	  end.
 
 implicit_inet6(Config) when is_list(Config) ->
-    ?line Hostname = ok(inet:gethostname()),
+    ?line Hostname = log_ok(inet:gethostname()),
     ?line
 	case gen_sctp:open(0, [inet6]) of
 	    {ok,S1} ->
@@ -712,16 +712,16 @@ implicit_inet6(Config) when is_list(Config) ->
 			    ?line ok = gen_sctp:close(S1),
 			    %%
 			    ?line Localhost =
-				ok(inet:getaddr("localhost", inet6)),
+				log_ok(inet:getaddr("localhost", inet6)),
 			    ?line io:format("~s ~p~n", ["localhost",Localhost]),
 			    ?line S2 =
-				ok(gen_sctp:open(0, [{ip,Localhost}])),
+				log_ok(gen_sctp:open(0, [{ip,Localhost}])),
 			    ?line implicit_inet6(S2, Localhost),
 			    ?line ok = gen_sctp:close(S2),
 			    %%
 			    ?line io:format("~s ~p~n", [Hostname,Host]),
 			    ?line S3 =
-				ok(gen_sctp:open(0, [{ifaddr,Host}])),
+				log_ok(gen_sctp:open(0, [{ifaddr,Host}])),
 			    ?line implicit_inet6(S3, Host),
 			    ?line ok = gen_sctp:close(S1);
 			{error,eafnosupport} ->
@@ -734,25 +734,25 @@ implicit_inet6(Config) when is_list(Config) ->
 
 implicit_inet6(S1, Addr) ->
     ?line ok = gen_sctp:listen(S1, true),
-    ?line P1 = ok(inet:port(S1)),
-    ?line S2 = ok(gen_sctp:open(0, [inet6])),
-    ?line P2 = ok(inet:port(S2)),
+    ?line P1 = log_ok(inet:port(S1)),
+    ?line S2 = log_ok(gen_sctp:open(0, [inet6])),
+    ?line P2 = log_ok(inet:port(S2)),
     ?line #sctp_assoc_change{state=comm_up} =
-	ok(gen_sctp:connect(S2, Addr, P1, [])),
-    ?line case recv_event(ok(gen_sctp:recv(S1))) of
+	log_ok(gen_sctp:connect(S2, Addr, P1, [])),
+    ?line case recv_event(log_ok(gen_sctp:recv(S1))) of
 	      {Addr,P2,#sctp_assoc_change{state=comm_up}} ->
 		  ok;
 	      {Addr,P2,#sctp_paddr_change{state=addr_confirmed,
 					  addr={Addr,P2},
 					  error=0}} ->
 		  {Addr,P2,#sctp_assoc_change{state=comm_up}} =
-		      recv_event(ok(gen_sctp:recv(S1)))
+		      recv_event(log_ok(gen_sctp:recv(S1)))
 	  end,
-    ?line case ok(inet:sockname(S1)) of
+    ?line case log_ok(inet:sockname(S1)) of
 	      {Addr,P1} -> ok;
 	      {{0,0,0,0,0,0,0,0},P1} -> ok
 	  end,
-    ?line case ok(inet:sockname(S2)) of
+    ?line case log_ok(inet:sockname(S2)) of
 	      {Addr,P2} -> ok;
 	      {{0,0,0,0,0,0,0,0},P2} -> ok
 	  end,
@@ -792,7 +792,7 @@ xfer_stream_min(Config) when is_list(Config) ->
 				 assoc_id=SaAssocId}} =
 	gen_sctp:connect(Sa, Loopback, Pb, []),
     ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} =
-	case recv_event(ok(gen_sctp:recv(Sb, infinity))) of
+	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
 	    {Loopback,Pa,
 	     #sctp_assoc_change{state=comm_up,
 				error=0,
@@ -811,7 +811,7 @@ xfer_stream_min(Config) when is_list(Config) ->
 					  outbound_streams=OS,
 					  inbound_streams=IS,
 					  assoc_id=AI}} =
-		    recv_event(ok(gen_sctp:recv(Sb, infinity))),
+		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
 		{OS,IS,AI}
 	end,
     ?line SaOutboundStreams = SbInboundStreams,
@@ -838,7 +838,7 @@ xfer_stream_min(Config) when is_list(Config) ->
     ?line ok =
 	do_from_other_process(
 	  fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
-    ?line case ok(gen_sctp:recv(Sa, infinity)) of
+    ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
 	      {Loopback,Pb,
 	       [#sctp_sndrcvinfo{stream=Stream,
 				 assoc_id=SaAssocId}],
@@ -854,17 +854,17 @@ xfer_stream_min(Config) when is_list(Config) ->
 			 [#sctp_sndrcvinfo{stream=Stream,
 					   assoc_id=SaAssocId}],
 			 Data} =
-		      ok(gen_sctp:recv(Sa, infinity))
+		      log_ok(gen_sctp:recv(Sa, infinity))
 	  end,
     ?line ok = gen_sctp:close(Sa),
     ?line {Loopback,Pa,
 	   #sctp_shutdown_event{assoc_id=SbAssocId}} =
-	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
     ?line {Loopback,Pa,
 	   #sctp_assoc_change{state=shutdown_comp,
 			      error=0,
 			      assoc_id=SbAssocId}} =
-	recv_event(ok(gen_sctp:recv(Sb, infinity))),
+	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
     ?line ok = gen_sctp:close(Sb),
 
     ?line receive
@@ -911,42 +911,97 @@ peeloff(Config) when is_list(Config) ->
     ?line Addr = {127,0,0,1},
     ?line Stream = 0,
     ?line Timeout = 333,
-    ?line S1 = socket_start(Addr, Timeout),
-    ?line P1 = socket_call(S1, port),
-    ?line Socket1 = socket_call(S1, socket),
-    ?line ok = socket_call(S1, {listen,true}),
-    ?line S2 = socket_start(Addr, Timeout),
-    ?line P2 = socket_call(S2, port),
-    ?line Socket2 = socket_call(S2, socket),
+    ?line S1 = socket_open([{ifaddr,Addr}], Timeout),
+    ?line ?LOGVAR(S1),
+    ?line P1 = socket_call(S1, get_port),
+    ?line ?LOGVAR(P1),
+    ?line Socket1 = socket_call(S1, get_socket),
+    ?line ?LOGVAR(Socket1),
+    ?line socket_call(S1, {listen,true}),
+    ?line S2 = socket_open([{ifaddr,Addr}], Timeout),
+    ?line ?LOGVAR(S2),
+    ?line P2 = socket_call(S2, get_port),
+    ?line ?LOGVAR(P2),
+    ?line Socket2 = socket_call(S2, get_socket),
+    ?line ?LOGVAR(Socket2),
     %%
-    ?line H_a = socket_req(S1, recv_assoc),
-    ?line {S2Ai,Sa,Sb} = socket_call(S2, {connect,Addr,P1,[]}),
-    ?line {S1Ai,Sb,Sa,Addr,P2} = socket_resp(H_a),
+    ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+    ?line S2Ai =
+	receive
+	    {S2,{Addr,P1,
+		 #sctp_assoc_change{
+			state=comm_up,
+			assoc_id=AssocId2}}} -> AssocId2
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
+    ?line ?LOGVAR(S2Ai),
+    ?line S1Ai =
+	receive
+	      {S1,{Addr,P2,
+		   #sctp_assoc_change{
+			  state=comm_up,
+			  assoc_id=AssocId1}}} -> AssocId1
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
+    ?line ?LOGVAR(S1Ai),
     %%
-    ?line H_b = socket_req(S1, recv),
-    ?line ok = socket_call(S2, {send,S2Ai,Stream,<<"Data H_b">>}),
-    ?line {Addr,P2,S1Ai,Stream,<<"Data H_b">>} = socket_resp(H_b),
-    ?line H_c = socket_req(S1, {recv,Socket2}),
-    ?line ok =
-	socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Data H_c">>}),
-    ?line {Addr,P1,S2Ai,Stream,<<"Data H_c">>} = socket_resp(H_c),
+    ?line socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}),
+    ?line
+	receive
+	    {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
+    ?line socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
+    ?line
+	receive
+	    {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
     %%
     ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
-    ?line P3 = socket_call(S3, port),
-    ?line Socket3 = socket_call(S3, socket),
+    ?line ?LOGVAR(S3),
+    ?line P3 = socket_call(S3, get_port),
+    ?line ?LOGVAR(P3),
     ?line S3Ai = S1Ai,
+    ?line ?LOGVAR(S3Ai),
     %%
-    ?line H_d = socket_req(S2, recv),
-    ?line ok = socket_call(S3, {send,S3Ai,Stream,<<"Data H_d">>}),
-    ?line {Addr,P3,S2Ai,Stream,<<"Data H_d">>} = socket_resp(H_d),
-    ?line ok = socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Data S2">>}),
-    ?line {Addr,P2,S3Ai,Stream,<<"Data S2">>} = socket_call(S2, {recv,Socket3}),
+    ?line socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
+    ?line
+	receive
+	    {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
+	after Timeout ->
+		socket_bailout([S1,S2,S3])
+	end,
+    ?line socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
+    ?line
+	receive
+	    {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
+	after Timeout ->
+		socket_bailout([S1,S2,S3])
+	end,
     %%
     ?line inet:i(sctp),
-    ?line ok = socket_stop(S1),
-    ?line ok = socket_stop(S2),
-    ?line {Addr,P2,#sctp_shutdown_event{assoc_id=S1Ai}} =
-	recv_event(ok(socket_stop(S3))),
+    ?line socket_close_verbose(S1),
+    ?line socket_close_verbose(S2),
+    ?line
+	receive
+	    {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai}}} -> ok
+	after Timeout ->
+		socket_bailout([S3])
+	end,
+    ?line
+	receive
+	    {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
+					    assoc_id=S3Ai}}} -> ok
+	after Timeout ->
+		socket_bailout([S3])
+	end,
+    ?line socket_close_verbose(S3),
+    ?line [] = flush(),
     ok.
 
 
@@ -960,28 +1015,65 @@ buffers(Config) when is_list(Config) ->
     ?line Data = mk_data(Limit),
     ?line Addr = {127,0,0,1},
     ?line Stream = 1,
-    ?line Timeout = 333,
-    ?line S1 = socket_start(Addr, Timeout),
-    ?line P1 = socket_call(S1, port),
+    ?line Timeout = 1333,
+    ?line S1 = socket_open([{ip,Addr}], Timeout),
+    ?line ?LOGVAR(S1),
+    ?line P1 = socket_call(S1, get_port),
+    ?line ?LOGVAR(P1),
     ?line ok = socket_call(S1, {listen,true}),
-    ?line S2 = socket_start(Addr, Timeout),
-    ?line P2 = socket_call(S2, port),
+    ?line S2 = socket_open([{ip,Addr}], Timeout),
+    ?line ?LOGVAR(S2),
+    ?line P2 = socket_call(S2, get_port),
+    ?line ?LOGVAR(P2),
     %%
-    ?line H_a = socket_req(S1, recv_assoc),
-    ?line {S2Ai,Sa,Sb} = socket_call(S2, {connect,Addr,P1,[]}),
-    ?line {S1Ai,Sb,Sa,Addr,P2} = socket_resp(H_a),
+    ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+    ?line S2Ai =
+	receive
+	    {S2,{Addr,P1,
+		 #sctp_assoc_change{
+			state=comm_up,
+			assoc_id=AssocId2}}} -> AssocId2
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
+    ?line S1Ai =
+	receive
+	    {S1,{Addr,P2,
+		 #sctp_assoc_change{
+			state=comm_up,
+			assoc_id=AssocId1}}} -> AssocId1
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
     %%
-    ?line ok = socket_call(S1, {setopts,[{recbuf,Limit}]}),
+    ?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
     ?line case socket_call(S1, {getopts,[recbuf]}) of
-	      {ok,[{recbuf,RB1}]} when RB1 >= Limit -> ok
+	      [{recbuf,RB1}] when RB1 >= Limit -> ok
 	  end,
-    ?line H_b = socket_req(S1, recv),
-    ?line ok = socket_call(S2, {send,S2Ai,Stream,Data}),
-    ?line {Addr,P2,S1Ai,Stream,Data} = socket_resp(H_b),
+    ?line socket_call(S2, {send,S2Ai,Stream,Data}),
+    ?line
+	receive
+	    {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
+	after Timeout ->
+		socket_bailout([S1,S2])
+	end,
     %%
-    ?line ok = socket_stop(S1),
-    ?line {Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}} =
-	recv_event(ok(socket_stop(S2))),
+    ?line socket_close_verbose(S1),
+    ?line
+	receive
+	    {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
+	after Timeout ->
+		socket_bailout([S2])
+	end,
+    ?line
+	receive
+	    {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
+					    assoc_id=S2Ai}}} -> ok
+	after Timeout ->
+		socket_bailout([S2])
+	end,
+    ?line socket_close_verbose(S2),
+    ?line [] = flush(),
     ok.
 
 mk_data(Words) ->
@@ -995,38 +1087,101 @@ mk_data(N, Words, Bin) ->
 %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%% socket gen_server ultra light
 
+socket_open(SocketOpts, Timeout) ->
+    Opts = [{type,seqpacket},{active,once},binary|SocketOpts],
+    Starter =
+	fun () ->
+		{ok,Socket} =
+		    gen_sctp:open(Opts),
+		Socket
+	end,
+    s_start(Starter, Timeout).
+
 socket_peeloff(Socket, AssocId, Timeout) ->
+    Opts = [{active,once},binary],
     Starter =
 	fun () ->
 		{ok,NewSocket} =
 		    gen_sctp:peeloff(Socket, AssocId),
+		ok = inet:setopts(NewSocket, Opts),
 		NewSocket
 	end,
-    socket_starter(Starter, Timeout).
+    s_start(Starter, Timeout).
+
+socket_close_verbose(S) ->
+    History = socket_history(socket_close(S)),
+    io:format("socket_close ~p:~n    ~p.~n", [S,History]),
+    History.
+
+socket_close(S) ->
+    s_req(S, close).
+
+socket_call(S, Request) ->
+    s_req(S, {Request}).
+
+%% socket_get(S, Key) ->
+%%     s_req(S, {get,Key}).
+
+socket_bailout([S|Ss]) ->
+    History = socket_history(socket_close(S)),
+    io:format("bailout ~p:~n    ~p.~n", [S,History]),
+    socket_bailout(Ss);
+socket_bailout([]) ->
+    io:format("flush: ~p.~n", [flush()]),
+    test_server:fail(socket_bailout).
+
+socket_history({State,Flush}) ->
+    {lists:keysort(
+      2,
+      lists:flatten(
+	[[{Key,Val} || Val <- Vals]
+	 || {Key,Vals} <- gb_trees:to_list(State)])),
+     Flush}.
+
+s_handler(Socket) ->
+    fun ({listen,Listen}) ->
+	    ok = gen_sctp:listen(Socket, Listen);
+	(get_port) ->
+	    ok(inet:port(Socket));
+	(get_socket) ->
+	    Socket;
+	({connect_init,ConAddr,ConPort,ConOpts}) ->
+	    ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts);
+	({send,AssocId,Stream,Data}) ->
+	    ok = gen_sctp:send(Socket, AssocId, Stream, Data);
+	({send,OtherSocket,AssocId,Stream,Data}) ->
+	    ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data);
+	({setopts,Opts}) ->
+	    ok = inet:setopts(Socket, Opts);
+	({getopts,Optnames}) ->
+	    ok(inet:getopts(Socket, Optnames))
+    end.
 
-socket_start(Addr, Timeout) ->
-    Starter =
-	fun () ->
-		{ok,Socket} =
-		    gen_sctp:open([{type,seqpacket},{ifaddr,Addr}]),
-		Socket
-	end,
-    socket_starter(Starter, Timeout).
+s_req(S, Req) ->
+    Mref = erlang:monitor(process, S),
+    S ! {self(),Mref,Req},
+    receive
+	{'DOWN',Mref,_,_,Error} ->
+	    exit(Error);
+	{S,Mref,Reply} ->
+	    erlang:demonitor(Mref),
+	    receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+	    Reply
+    end.
 
-socket_starter(Starter, Timeout) ->
+s_start(Starter, Timeout) ->
     Parent = self(),
     Owner =
 	spawn_link(
 	  fun () ->
-		  socket_starter(Starter(), Timeout, Parent)
+		  s_start(Starter(), Timeout, Parent)
 	  end),
-    io:format("Started socket ~w.~n", [Owner]),
     Owner.
 
-socket_starter(Socket, Timeout, Parent) ->
+s_start(Socket, Timeout, Parent) ->
+    Handler = s_handler(Socket),
     try
-	Handler = socket_handler(Socket, Timeout),
-	socket_loop(Socket, Timeout, Parent, Handler)
+	s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
     catch
 	Class:Reason ->
 	    Stacktrace = erlang:get_stacktrace(),
@@ -1035,150 +1190,112 @@ socket_starter(Socket, Timeout, Parent) ->
 	    erlang:raise(Class, Reason, Stacktrace)
     end.
 
-socket_loop(Socket, Timeout, Parent, Handler) ->
+s_loop(Socket, Timeout, Parent, Handler, State) ->
     receive
-	{Parent,Ref} -> % socket_stop()
-	    Result =
-		case log(gen_sctp:recv(Socket, Timeout)) of
-		    {error,timeout} -> ok;
-		    R -> R
-		end,
+	{Parent,Ref,close} -> % socket_close()
+	    erlang:send_after(Timeout, self(), {Parent,Ref,exit}),
+	    s_loop(Socket, Timeout, Parent, Handler, State);
+	{Parent,Ref,exit} ->
 	    ok = gen_sctp:close(Socket),
-	    Parent ! {self(),Ref, Result};
-	{Parent,Ref,Msg} ->
-	    Parent ! {self(),Ref,Handler(Msg)},
-	    socket_loop(Socket, Timeout, Parent, Handler)
+	    Key = exit,
+	    Val = {now(),Socket},
+	    NewState = gb_push(Key, Val, State),
+	    Parent ! {self(),Ref,{NewState,flush()}};
+	{Parent,Ref,{Msg}} ->
+	    Result = Handler(Msg),
+	    Key = req,
+	    Val = {now(),{Msg,Result}},
+	    NewState = gb_push(Key, Val, State),
+	    Parent ! {self(),Ref,Result},
+	    s_loop(Socket, Timeout, Parent, Handler, NewState);
+	%% {Parent,Ref,{get,Key}} ->
+	%%     Parent ! {self(),Ref,gb_get(Key, State)},
+	%%     s_loop(Socket, Timeout, Parent, Handler, State);
+	{sctp,Socket,Addr,Port,
+	 {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
+	  when not is_tuple(Data) ->
+	    case gb_get({assoc_change,AssocId}, State) of
+		[{_,{Addr,Port,
+		     #sctp_assoc_change{
+			    state=comm_up,
+			    inbound_streams=Is}}}|_]
+		 when 0 =< Stream, Stream < Is-> ok;
+		[] -> ok
+	    end,
+	    Key = {msg,AssocId,Stream},
+	    Val = {now(),{Addr,Port,SRI,Data}},
+	    NewState = gb_push(Key, Val, State),
+	    Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
+	    again(Socket),
+	    s_loop(Socket, Timeout, Parent, Handler, NewState);
+	{sctp,Socket,Addr,Port,
+	 {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} ->
+	    case SRI of
+		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+		[] -> ok
+	    end,
+	    Key = {assoc_change,AssocId},
+	    Val = {now(),{Addr,Port,SAC}},
+	    case {gb_get(Key, State),St} of
+		{[],_} -> ok;
+		{[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+		when St =:= comm_lost; St =:= shutdown_comp -> ok
+	    end,
+	    NewState = gb_push(Key, Val, State),
+	    Parent ! {self(),{Addr,Port,SAC}},
+	    again(Socket),
+	    s_loop(Socket, Timeout, Parent, Handler, NewState);
+	{sctp,Socket,Addr,Port,
+	 {SRI,#sctp_paddr_change{assoc_id=AssocId,
+				 addr={_,Port},
+				 state=St}=SPC}} ->
+	    case SRI of
+		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+		[] -> ok
+	    end,
+	    case {gb_get({assoc_change,AssocId}, State),St} of
+		{[],_} when St =:= addr_confirmed -> ok
+	    end,
+	    Key = {paddr_change,AssocId},
+	    Val = {now(),{Addr,Port,SPC}},
+	    NewState = gb_push(Key, Val, State),
+	    again(Socket),
+	    s_loop(Socket, Timeout, Parent, Handler, NewState);
+	{sctp,Socket,Addr,Port,
+	 {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} ->
+	    case SRI of
+		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+		[] -> ok
+	    end,
+	    case gb_get({assoc_change,AssocId}, State) of
+		[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok;
+		[] -> ok
+	    end,
+	    Key = {shutdown_event,AssocId},
+	    Val = {now(),{Addr,Port}},
+	    NewState = gb_push(Key, Val, State),
+	    Parent ! {self(), {Addr,Port,SSE}},
+	    again(Socket),
+	    s_loop(Socket, Timeout, Parent, Handler, NewState);
+	Unexpected ->
+	    erlang:error({unexpected,Unexpected})
     end.
 
-socket_handler(Socket, Timeout) ->
-    fun ({listen,Listen}) ->
-	    gen_sctp:listen(Socket, Listen);
-	(port) ->
-	    ok(inet:port(Socket));
-	(socket) ->
-	    Socket;
-	(recv_assoc) ->
-	    case recv_event(ok(gen_sctp:recv(Socket, Timeout))) of
-		{AssocAddr,AssocPort,
-		 #sctp_paddr_change{state=addr_confirmed,
-				    addr={_,AssocPort},
-				    error=0,
-				    assoc_id=AssocId}} ->
-		    {AssocAddr,AssocPort,
-		     #sctp_assoc_change{state=comm_up,
-					error=0,
-					outbound_streams=Os,
-					inbound_streams=Is,
-					assoc_id=AssocId}} =
-			recv_event(ok(gen_sctp:recv(Socket, infinity))),
-		    {AssocId,Os,Is,AssocAddr,AssocPort};
-		{AssocAddr,AssocPort,
-		 #sctp_assoc_change{state=comm_up,
-				    error=0,
-				    outbound_streams=Os,
-				    inbound_streams=Is,
-				    assoc_id=AssocId}} ->
-		    {AssocId,Os,Is,AssocAddr,AssocPort}
-	    end;
-	    %% {AssocAddr,AssocPort,[],
-	    %%  #sctp_assoc_change{state=comm_up,
-	    %% 			error=0,
-	    %% 			outbound_streams=Os,
-	    %% 			inbound_streams=Is,
-	    %% 			assoc_id=AssocId}} =
-	    %% 	ok(gen_sctp:recv(Socket, infinity)),
-	    %% case log(gen_sctp:recv(Socket, Timeout)) of
-	    %% 	{ok,AssocAddr,AssocPort,[],
-	    %% 	 #sctp_paddr_change{addr = {AssocAddr,AssocPort},
-	    %% 			    state = addr_available,
-	    %% 			    error = 0,
-	    %% 			    assoc_id = AssocId}} -> ok;
-	    %% 	{error,timeout} -> ok
-	    %% end,
-	    %% {AssocId,Os,Is,AssocAddr,AssocPort};
-	({connect,ConAddr,ConPort,ConOpts}) ->
-	    ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts),
-	    case recv_event(ok(gen_sctp:recv(Socket, Timeout))) of
-		{ConAddr,ConPort,
-		 #sctp_paddr_change{state=addr_confirmed,
-				    addr={_,ConPort},
-				    error=0,
-				    assoc_id=AssocId}} ->
-		    {ConAddr,ConPort,
-		     #sctp_assoc_change{state=comm_up,
-					error=0,
-					outbound_streams=Os,
-					inbound_streams=Is,
-					assoc_id=AssocId}} =
-			recv_event(ok(gen_sctp:recv(Socket, infinity))),
-		    {AssocId,Os,Is};
-		{ConAddr,ConPort,
-		 #sctp_assoc_change{state=comm_up,
-				    error=0,
-				    outbound_streams=Os,
-				    inbound_streams=Is,
-				    assoc_id=AssocId}} ->
-		    {AssocId,Os,Is}
-	    end;
-	    %% #sctp_assoc_change{state=comm_up,
-	    %% 		       error=0,
-	    %% 		       outbound_streams=Os,
-	    %% 		       inbound_streams=Is,
-	    %% 		       assoc_id=AssocId} =
-	    %% 	ok(gen_sctp:connect(Socket, ConAddr, ConPort, ConOpts)),
-	    %% case log(gen_sctp:recv(Socket, Timeout)) of
-	    %% 	{ok,ConAddr,ConPort,[],
-	    %% 	 #sctp_paddr_change{addr = {ConAddr,ConPort},
-	    %% 			    state = addr_available,
-	    %% 			    error = 0,
-	    %% 			    assoc_id = AssocId}} -> ok;
-	    %% 	{error,timeout} -> ok
-	    %% end,
-	    %% {AssocId,Os,Is};
-	({send,AssocId,Stream,Data}) ->
-	    gen_sctp:send(Socket, AssocId, Stream, Data);
-	({send,S,AssocId,Stream,Data}) ->
-	    gen_sctp:send(S, AssocId, Stream, Data);
-	(recv) ->
-	    {Addr,Port,
-	     [#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}],Data} =
-		ok(gen_sctp:recv(Socket, infinity)),
-	    {Addr,Port,AssocId,Stream,Data};
-	({recv,S}) ->
-	    {Addr,Port,
-	     [#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}],Data} =
-		ok(gen_sctp:recv(S, infinity)),
-	    {Addr,Port,AssocId,Stream,Data};
-	({setopts,Opts}) ->
-	    inet:setopts(Socket, Opts);
-	({getopts,Optnames}) ->
-	    inet:getopts(Socket, Optnames)
-    end.
+again(Socket) ->
+    inet:setopts(Socket, [{active,once}]).
 
-socket_stop(Handler) ->
-    Mref = erlang:monitor(process, Handler),
-    Handler ! {self(),Mref},
-    receive
-	{Handler,Mref,Result} ->
-	    receive {'DOWN',Mref,_,_,_} -> Result end;
-	{'DOWN',Mref,_,_,Error} ->
-	    exit(Error)
+gb_push(Key, Val, GBT) ->
+    case gb_trees:lookup(Key, GBT) of
+	none ->
+	    gb_trees:insert(Key, [Val], GBT);
+	{value,V} ->
+	    gb_trees:update(Key, [Val|V], GBT)
     end.
 
-socket_call(Handler, Request) ->
-    socket_resp(socket_req(Handler, Request)).
-
-socket_req(Handler, Request) ->
-    Mref = erlang:monitor(process, Handler),
-    Handler ! {self(),Mref,Request},
-    {Handler,Mref}.
-
-socket_resp({Handler,Mref}) ->
-    receive
-	{'DOWN',Mref,_,_,Error} ->
-	    exit(Error);
-	{Handler,Mref,Reply} ->
-	    erlang:demonitor(Mref),
-	    receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
-	    Reply
+gb_get(Key, GBT) ->
+    case gb_trees:lookup(Key, GBT) of
+	none ->
+	    [];
+	{value,V} ->
+	    V
     end.
-- 
cgit v1.2.3


From 3be53d45e394dab127563d7a8d6da109cd9bc108 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Wed, 28 Sep 2011 10:19:17 +0200
Subject: kernel: Adjust SCTP tests to Solaris quirks

---
 lib/kernel/test/gen_sctp_SUITE.erl | 94 +++++++++++++++++++++++++-------------
 1 file changed, 63 insertions(+), 31 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 07434df8da..301f69ce09 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -250,9 +250,10 @@ xfer_active(Config) when is_list(Config) ->
 				 error=0,
 				 assoc_id=SbAssocId} -> ok;
 	      #sctp_paddr_change{state=addr_available,
-				 addr={Loopback,Pa},
+				 addr={Loopback,P},
 				 error=0,
-				 assoc_id=SbAssocId} -> ok;
+				 assoc_id=SbAssocId} ->
+		  ?line match_unless_solaris(Pa, P);
 	      timeout -> ok
 	  end,
     ?line [] = flush(),
@@ -266,7 +267,7 @@ xfer_active(Config) when is_list(Config) ->
 				  assoc_id=SbAssocId}],
 		Data}} -> ok
 	  after Timeout ->
-		  ?line test_server:fail({unexpected,flush()})
+		  ?line test_server:fail({timeout,flush()})
 	  end,
     ?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
     ?line receive
@@ -275,7 +276,7 @@ xfer_active(Config) when is_list(Config) ->
 				  assoc_id=SaAssocId}],
 		Data}} -> ok
 	  after Timeout ->
-		  ?line test_server:fail({unexpected,flush()})
+		  ?line test_server:fail({timeout,flush()})
 	  end,
     %%
     ?line ok = gen_sctp:abort(Sa, SaAssocChange),
@@ -283,19 +284,20 @@ xfer_active(Config) when is_list(Config) ->
 	      #sctp_assoc_change{state=comm_lost,
 				 assoc_id=SbAssocId} -> ok;
 	      timeout ->
-		  ?line test_server:fail({unexpected,flush()})
+		  ?line test_server:fail({timeout,flush()})
 	  end,
     ?line ok = gen_sctp:close(Sb),
     ?line case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
 	      #sctp_assoc_change{state=comm_lost,
 				 assoc_id=SaAssocId} -> ok;
 	      timeout ->
-		  ?line test_server:fail({unexpected,flush()})
+		  ?line io:format("timeout waiting for comm_lost on Sa~n"),
+		  ?line match_unless_solaris(ok, {timeout,flush()})
 	  end,
     ?line receive
-              {sctp_error,Sa,enotconn} -> ok % Solaris
-          after 17 -> ok %% Only happens on Solaris
-          end,
+	      {sctp_error,Sa,enotconn} -> ok % Solaris
+	  after 17 -> ok
+	  end,
     ?line ok = gen_sctp:close(Sa),
     %%
     ?line receive
@@ -780,17 +782,28 @@ xfer_stream_min(Config) when is_list(Config) ->
     ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
     ?line Loopback = {127,0,0,1},
     ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+    ?line ?LOGVAR(Sb),
     ?line {ok,Pb} = inet:port(Sb),
+    ?line ?LOGVAR(Pb),
     ?line ok = gen_sctp:listen(Sb, true),
 
     ?line {ok,Sa} = gen_sctp:open([{type,stream}]),
+    ?line ?LOGVAR(Sa),
     ?line {ok,Pa} = inet:port(Sa),
-    ?line {ok,#sctp_assoc_change{state=comm_up,
-				 error=0,
-				 outbound_streams=SaOutboundStreams,
-				 inbound_streams=SaInboundStreams,
-				 assoc_id=SaAssocId}} =
-	gen_sctp:connect(Sa, Loopback, Pb, []),
+    ?line ?LOGVAR(Pa),
+    ?line #sctp_assoc_change{state=comm_up,
+			     error=0,
+			     outbound_streams=SaOutboundStreams,
+			     inbound_streams=SaInboundStreams,
+			     assoc_id=SaAssocId_X} =
+	log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])),
+    ?line ?LOGVAR(SaAssocId_X),
+    ?line [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] =
+	log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info,
+				  #sctp_paddrinfo{address={Loopback,Pb}}}])),
+    ?line ?LOGVAR(SaAssocId),
+    ?line match_unless_solaris(SaAssocId_X, SaAssocId),
+
     ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} =
 	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
 	    {Loopback,Pa,
@@ -814,8 +827,11 @@ xfer_stream_min(Config) when is_list(Config) ->
 		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
 		{OS,IS,AI}
 	end,
+    ?line ?LOGVAR(SbAssocId),
     ?line SaOutboundStreams = SbInboundStreams,
+    ?line ?LOGVAR(SaOutboundStreams),
     ?line SbOutboundStreams = SaInboundStreams,
+    ?line ?LOGVAR(SbOutboundStreams),
     ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
     ?line case gen_sctp:recv(Sb, infinity) of
 	      {ok,{Loopback,
@@ -966,7 +982,11 @@ peeloff(Config) when is_list(Config) ->
     ?line ?LOGVAR(S3),
     ?line P3 = socket_call(S3, get_port),
     ?line ?LOGVAR(P3),
-    ?line S3Ai = S1Ai,
+    ?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
+	socket_call(S3,
+	    {getopts,[{sctp_get_peer_addr_info,
+		       #sctp_paddrinfo{address={Addr,P2}}}]}),
+    %%?line S3Ai = S1Ai,
     ?line ?LOGVAR(S3Ai),
     %%
     ?line socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
@@ -989,7 +1009,8 @@ peeloff(Config) when is_list(Config) ->
     ?line socket_close_verbose(S2),
     ?line
 	receive
-	    {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai}}} -> ok
+	    {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
+		?line match_unless_solaris(S3Ai, S3Ai_X)
 	after Timeout ->
 		socket_bailout([S3])
 	end,
@@ -1011,11 +1032,10 @@ buffers(doc) ->
 buffers(suite) ->
     [];
 buffers(Config) when is_list(Config) ->
-    ?line Limit = 8192,
-    ?line Data = mk_data(Limit),
+    ?line Limit = 4096,
     ?line Addr = {127,0,0,1},
     ?line Stream = 1,
-    ?line Timeout = 1333,
+    ?line Timeout = 3333,
     ?line S1 = socket_open([{ip,Addr}], Timeout),
     ?line ?LOGVAR(S1),
     ?line P1 = socket_call(S1, get_port),
@@ -1047,9 +1067,12 @@ buffers(Config) when is_list(Config) ->
 	end,
     %%
     ?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
-    ?line case socket_call(S1, {getopts,[recbuf]}) of
-	      [{recbuf,RB1}] when RB1 >= Limit -> ok
-	  end,
+    ?line Recbuf =
+	    case socket_call(S1, {getopts,[recbuf]}) of
+		[{recbuf,RB1}] when RB1 >= Limit -> RB1
+	    end,
+    ?line Data = mk_data(Recbuf+Limit),
+    ?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
     ?line socket_call(S2, {send,S2Ai,Stream,Data}),
     ?line
 	receive
@@ -1076,13 +1099,13 @@ buffers(Config) when is_list(Config) ->
     ?line [] = flush(),
     ok.
 
-mk_data(Words) ->
-    mk_data(0, Words, <<>>).
+mk_data(Bytes) ->
+    mk_data(0, Bytes, <<>>).
 %%
-mk_data(Words, Words, Bin) ->
-    Bin;
-mk_data(N, Words, Bin) ->
-    mk_data(N+1, Words, <<Bin/binary,N:32>>).
+mk_data(N, Bytes, Bin) when N < Bytes ->
+    mk_data(N+4, Bytes, <<Bin/binary,N:32>>);
+mk_data(_, _, Bin) ->
+    Bin.
 
 %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%% socket gen_server ultra light
@@ -1247,14 +1270,17 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
 	    s_loop(Socket, Timeout, Parent, Handler, NewState);
 	{sctp,Socket,Addr,Port,
 	 {SRI,#sctp_paddr_change{assoc_id=AssocId,
-				 addr={_,Port},
+				 addr={_,P},
 				 state=St}=SPC}} ->
+	    match_unless_solaris(Port, P),
 	    case SRI of
 		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
 		[] -> ok
 	    end,
 	    case {gb_get({assoc_change,AssocId}, State),St} of
-		{[],_} when St =:= addr_confirmed -> ok
+		{[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],
+		 addr_available} -> ok;
+		{[],addr_confirmed} -> ok
 	    end,
 	    Key = {paddr_change,AssocId},
 	    Val = {now(),{Addr,Port,SPC}},
@@ -1299,3 +1325,9 @@ gb_get(Key, GBT) ->
 	{value,V} ->
 	    V
     end.
+
+match_unless_solaris(A, B) ->
+    case os:type() of
+	{unix,sunos} -> B;
+	_ -> A = B
+    end.
-- 
cgit v1.2.3


From 4f8e3e94357cd4730c9e3135f2bafd9b6d4fd13e Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Wed, 28 Sep 2011 11:48:08 +0200
Subject: erts,kernel: Return eprotonosupport when SCTP is not supported

It is better that gen_sctp:open/0-2 returns the informative Posix
return code {error,eprotonosupport} than previously {error,badarg}
when SCTP is not supported since it is so platform dependent.
---
 lib/kernel/test/gen_sctp_SUITE.erl | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 301f69ce09..d0b0bdf4d4 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -44,17 +44,12 @@ groups() ->
     [].
 
 init_per_suite(Config) ->
-    try gen_sctp:open() of
+    case gen_sctp:open() of
 	{ok,Socket} ->
 	    gen_sctp:close(Socket),
 	    [];
-	_ ->
-	    []
-    catch
-	error:badarg ->
-	    {skip,"SCTP not supported on this machine"};
-	_:_ ->
-	    Config
+	{error,eprotonosupport} ->
+	    {skip,"SCTP not supported on this machine"}
     end.
 
 end_per_suite(_Conifig) ->
-- 
cgit v1.2.3


From 3d4c4eaf917e92c52c6533e17c98aeab81125f3b Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 18 Oct 2011 16:27:00 +0200
Subject: kernel: Adjust SCTP test to SuSE quirk

inet:port/1 on peeled off socket returns 0. Bug? Who's?
---
 lib/kernel/test/gen_sctp_SUITE.erl | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index d0b0bdf4d4..300152ddce 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -43,7 +43,7 @@ all() ->
 groups() -> 
     [].
 
-init_per_suite(Config) ->
+init_per_suite(_Config) ->
     case gen_sctp:open() of
 	{ok,Socket} ->
 	    gen_sctp:close(Socket),
@@ -52,7 +52,7 @@ init_per_suite(Config) ->
 	    {skip,"SCTP not supported on this machine"}
     end.
 
-end_per_suite(_Conifig) ->
+end_per_suite(_Config) ->
     ok.
 
 init_per_group(_GroupName, Config) ->
@@ -975,8 +975,9 @@ peeloff(Config) when is_list(Config) ->
     %%
     ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
     ?line ?LOGVAR(S3),
-    ?line P3 = socket_call(S3, get_port),
-    ?line ?LOGVAR(P3),
+    ?line P3_X = socket_call(S3, get_port),
+    ?line ?LOGVAR(P3_X),
+    ?line P3 = case P3_X of 0 -> P1; _ -> P3_X end,
     ?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
 	socket_call(S3,
 	    {getopts,[{sctp_get_peer_addr_info,
-- 
cgit v1.2.3


From d6d9371d50e7e9ad8708e914b72fa755ce5a5385 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Fri, 21 Oct 2011 17:51:34 +0200
Subject: kernel: Documented gen_sctp:peeloff/2

---
 lib/kernel/doc/src/gen_sctp.xml | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

(limited to 'lib/kernel')

diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index b9f23674f0..418bfae4b8 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -45,7 +45,15 @@
       SUSE Linux Enterprise Server 10 (x86_64) kernel 2.6.16.27-0.6-smp,
       with lksctp-tools-1.0.6, briefly on Solaris 10, and later on
       SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64)
-      kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7.</p>
+      kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7,
+      and later also on FreeBSD 8.2.
+    </p>
+    <p>
+      This module was written for one-to-many style sockets
+      (type <c>seqpacket</c>). With the addition of
+      <seealso marker="#peeloff/2">peeloff/2</seealso>, one-to-one style
+      sockets (type <c>stream</c>) were introduced.
+    </p>
     <p>Record definitions for the <c>gen_sctp</c> module can be found using:</p>
 <pre>  -include_lib("kernel/include/inet_sctp.hrl").    </pre>
     <p>These record definitions use the "new" spelling 'adaptation',
@@ -299,11 +307,39 @@
           is used. In particular, the socket is opened in 
           <seealso marker="#option-binary">binary</seealso> and
           <seealso marker="#option-active">passive</seealso> mode,
+	  with <anno>SockType</anno> <c>seqpacket</c>,
           and with reasonably large
           <seealso marker="#option-sndbuf">kernel</seealso> and driver
           <seealso marker="#option-buffer">buffers.</seealso></p>
       </desc>
     </func>
+    <func>
+      <name name="peeloff" arity="2"/>
+      <fsummary>
+	Peel off a type <c>stream</c> socket from a type <c>seqpacket</c> one
+      </fsummary>
+      <desc>
+	<p>
+	  Branch off an existing association <anno>Assoc</anno>
+	  in a socket <anno>Socket</anno> of type <c>seqpacket</c>
+	  (one-to-may style) into
+	  a new socket <anno>NewSocket</anno> of type <c>stream</c>
+	  (one-to-one style).
+	</p>
+	<p>
+	  The existing association argument <anno>Assoc</anno>
+	  can be either a
+	  <seealso marker="#record-sctp_assoc_change">
+	    #sctp_assoc_change{}
+	  </seealso>
+	  record as returned from e.g
+	  <seealso marker="#recv-2">recv/*</seealso>,
+	  <seealso marker="#connect-5">connect/*</seealso> or
+	  from a listening socket in active mode. Or it can be just
+	  the field <c>assoc_id</c> integer from such a record.
+	</p>
+      </desc>
+    </func>
     <func>
       <name name="recv" arity="1"/>
       <name name="recv" arity="2"/>
-- 
cgit v1.2.3