Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge tag 'erl_1218' into maint

ssl: Patch 1218

   OTP-10600  ssl:ssl_accept/2 timeout is no longer ignored
  • Loading branch information...
commit 214d1aaf4749ae6a7bb0506cf448551a312a9a58 2 parents af68f5a + 2eb32af
Björn-Egil Dahlberg authored December 06, 2012
17  lib/ssl/doc/src/notes.xml
@@ -30,7 +30,22 @@
30 30
   </header>
31 31
   <p>This document describes the changes made to the SSL application.</p>
32 32
   
33  
-  <section><title>SSL 5.1.1</title>
  33
+  <section><title>SSL 5.1.2</title>
  34
+
  35
+    <section><title>Fixed Bugs and Malfunctions</title>
  36
+      <list>
  37
+        <item>
  38
+          <p>
  39
+	    ssl:ssl_accept/2 timeout is no longer ignored</p>
  40
+          <p>
  41
+	    Own Id: OTP-10600</p>
  42
+        </item>
  43
+      </list>
  44
+    </section>
  45
+
  46
+</section>
  47
+
  48
+<section><title>SSL 5.1.1</title>
34 49
 
35 50
     <section><title>Fixed Bugs and Malfunctions</title>
36 51
       <list>
4  lib/ssl/src/ssl.appup.src
... ...
@@ -1,6 +1,8 @@
1 1
 %% -*- erlang -*-
2 2
 {"%VSN%",
3 3
  [
  4
+  {"5.1.1", [{restart_application, ssl}]
  5
+  },
4 6
   {"5.1", [
5 7
 	   {load_module, ssl_connection, soft_purge, soft_purge, []}
6 8
 	  ]
@@ -10,6 +12,8 @@
10 12
   {<<"3\\.*">>, [{restart_application, ssl}]}
11 13
  ], 
12 14
  [
  15
+  {"5.1.1", [{restart_application, ssl}]
  16
+  },
13 17
   {"5.1", [
14 18
 	   {load_module, ssl_connection, soft_purge, soft_purge, []}
15 19
 	  ]
4  lib/ssl/src/ssl.erl
@@ -45,7 +45,7 @@
45 45
 -export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0,
46 46
 	      erl_cipher_suite/0, %% From ssl_cipher.hrl 
47 47
 	      tls_atom_version/0, %% From ssl_internal.hrl
48  
-	      prf_random/0]).
  48
+	      prf_random/0, sslsocket/0]).
49 49
 
50 50
 -record(config, {ssl,               %% SSL parameters
51 51
 		 inet_user,         %% User set inet options
@@ -53,6 +53,8 @@
53 53
 		 inet_ssl,          %% inet options for internal ssl socket 
54 54
 		 cb                 %% Callback info
55 55
 		}).
  56
+
  57
+-type sslsocket()                :: #sslsocket{}.
56 58
 -type connect_option()           :: socket_connect_option() | ssl_option() | transport_option().
57 59
 -type socket_connect_option()    :: gen_tcp:connect_option().
58 60
 -type listen_option()            :: socket_listen_option() | ssl_option() | transport_option().
68  lib/ssl/src/ssl_connection.erl
@@ -90,6 +90,7 @@
90 90
 	  log_alert,           % boolean() 
91 91
 	  renegotiation,       % {boolean(), From | internal | peer}
92 92
 	  start_or_recv_from,  % "gen_fsm From"
  93
+	  timer,               % start_or_recv_timer
93 94
 	  send_queue,          % queue()
94 95
 	  terminated = false,  %
95 96
 	  allow_renegotiate = true
@@ -755,8 +756,9 @@ handle_sync_event({application_data, Data}, From, StateName,
755 756
      get_timeout(State)};
756 757
 
757 758
 handle_sync_event({start, Timeout}, StartFrom, hello, State) ->
758  
-    start_or_recv_cancel_timer(Timeout, StartFrom),
759  
-    hello(start, State#state{start_or_recv_from = StartFrom});
  759
+    Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
  760
+    hello(start, State#state{start_or_recv_from = StartFrom,
  761
+			     timer = Timer});
760 762
 
761 763
 %% The two clauses below could happen if a server upgrades a socket in
762 764
 %% active mode. Note that in this case we are lucky that
@@ -772,8 +774,9 @@ handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) ->
772 774
     {stop, {shutdown, Error}, {error, Error}, State};
773 775
 
774 776
 handle_sync_event({start, Timeout}, StartFrom, StateName, State) ->
775  
-    start_or_recv_cancel_timer(Timeout, StartFrom),
776  
-    {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)};
  777
+    Timer = start_or_recv_cancel_timer(Timeout, StartFrom),
  778
+    {next_state, StateName, State#state{start_or_recv_from = StartFrom,
  779
+					timer = Timer}, get_timeout(State)};
777 780
 
778 781
 handle_sync_event(close, _, StateName, State) ->
779 782
     %% Run terminate before returning
@@ -805,14 +808,16 @@ handle_sync_event({shutdown, How0}, _, StateName,
805 808
     end;
806 809
     
807 810
 handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName, State0) ->
808  
-    start_or_recv_cancel_timer(Timeout, RecvFrom),
809  
-    passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName);
  811
+    Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
  812
+    passive_receive(State0#state{bytes_to_read = N,
  813
+				 start_or_recv_from = RecvFrom, timer = Timer}, StateName);
810 814
 
811 815
 %% Doing renegotiate wait with handling request until renegotiate is
812 816
 %% finished. Will be handled by next_state_is_connection/2.
813 817
 handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) ->
814  
-    start_or_recv_cancel_timer(Timeout, RecvFrom),
815  
-    {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom},
  818
+    Timer = start_or_recv_cancel_timer(Timeout, RecvFrom),
  819
+    {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom,
  820
+					timer = Timer},
816 821
      get_timeout(State)};
817 822
 
818 823
 handle_sync_event({new_user, User}, _From, StateName, 
@@ -981,13 +986,20 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
981 986
 
982 987
 handle_info(allow_renegotiate, StateName, State) ->
983 988
     {next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)};
984  
-   
985  
-handle_info({cancel_start_or_recv, RecvFrom}, connection = StateName, #state{start_or_recv_from = RecvFrom} = State) ->
  989
+
  990
+handle_info({cancel_start_or_recv, StartFrom}, StateName,
  991
+	    #state{renegotiation = {false, first}} = State) when StateName =/= connection ->
  992
+    gen_fsm:reply(StartFrom, {error, timeout}),
  993
+    {stop, {shutdown, user_timeout}, State#state{timer = undefined}};
  994
+
  995
+handle_info({cancel_start_or_recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom} = State) ->
986 996
     gen_fsm:reply(RecvFrom, {error, timeout}),
987  
-    {next_state, StateName, State#state{start_or_recv_from = undefined}, get_timeout(State)};
  997
+    {next_state, StateName, State#state{start_or_recv_from = undefined,
  998
+					bytes_to_read = undefined,
  999
+					timer = undefined}, get_timeout(State)};
988 1000
 
989 1001
 handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) ->
990  
-    {next_state, StateName, State, get_timeout(State)};
  1002
+    {next_state, StateName, State#state{timer = undefined}, get_timeout(State)};
991 1003
 
992 1004
 handle_info(Msg, StateName, State) ->
993 1005
     Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
@@ -1729,10 +1741,11 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
1729 1741
     end.
1730 1742
 
1731 1743
 read_application_data(Data, #state{user_application = {_Mon, Pid},
1732  
-                              socket_options = SOpts,
1733  
-                              bytes_to_read = BytesToRead,
1734  
-                              start_or_recv_from = RecvFrom,
1735  
-                              user_data_buffer = Buffer0} = State0) ->
  1744
+				   socket_options = SOpts,
  1745
+				   bytes_to_read = BytesToRead,
  1746
+				   start_or_recv_from = RecvFrom,
  1747
+				   timer = Timer,
  1748
+				   user_data_buffer = Buffer0} = State0) ->
1736 1749
     Buffer1 = if 
1737 1750
 		  Buffer0 =:= <<>> -> Data;
1738 1751
 		  Data =:= <<>> -> Buffer0;
@@ -1741,9 +1754,11 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
1741 1754
     case get_data(SOpts, BytesToRead, Buffer1) of
1742 1755
 	{ok, ClientData, Buffer} -> % Send data
1743 1756
 	    SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom),
  1757
+	    cancel_timer(Timer),
1744 1758
 	    State = State0#state{user_data_buffer = Buffer,
1745 1759
 				 start_or_recv_from = undefined,
1746  
-				 bytes_to_read = 0,
  1760
+				 timer = undefined,
  1761
+				 bytes_to_read = undefined,
1747 1762
 				 socket_options = SocketOpt 
1748 1763
 				},
1749 1764
 	    if
@@ -1756,6 +1771,8 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},
1756 1771
 	    end;
1757 1772
 	{more, Buffer} -> % no reply, we need more data
1758 1773
 	    next_record(State0#state{user_data_buffer = Buffer});
  1774
+	{passive, Buffer} ->
  1775
+	    next_record_if_active(State0#state{user_data_buffer = Buffer});
1759 1776
 	{error,_Reason} -> %% Invalid packet in packet mode
1760 1777
 	    deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom),
1761 1778
 	    {stop, normal, State0}
@@ -1797,6 +1814,9 @@ is_time_to_renegotiate(_,_) ->
1797 1814
 %% Picks ClientData 
1798 1815
 get_data(_, _, <<>>) ->
1799 1816
     {more, <<>>};
  1817
+%% Recv timed out save buffer data until next recv
  1818
+get_data(#socket_options{active=false}, undefined, Buffer) ->
  1819
+    {passive, Buffer};
1800 1820
 get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) 
1801 1821
   when Raw =:= raw; Raw =:= 0 ->   %% Raw Mode
1802 1822
     if 
@@ -2102,7 +2122,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
2102 2122
 	   tls_record_buffer = <<>>,
2103 2123
 	   tls_cipher_texts = [],
2104 2124
 	   user_application = {Monitor, User},
2105  
-	   bytes_to_read = 0,
2106 2125
 	   user_data_buffer = <<>>,
2107 2126
 	   log_alert = true,
2108 2127
 	   session_cache_cb = SessionCacheCb,
@@ -2325,9 +2344,11 @@ ack_connection(#state{renegotiation = {true, From}} = State) ->
2325 2344
     gen_fsm:reply(From, ok),
2326 2345
     State#state{renegotiation = undefined};
2327 2346
 ack_connection(#state{renegotiation = {false, first}, 
2328  
-		      start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined ->
  2347
+		      start_or_recv_from = StartFrom,
  2348
+		      timer = Timer} = State) when StartFrom =/= undefined ->
2329 2349
     gen_fsm:reply(StartFrom, connected),
2330  
-    State#state{renegotiation = undefined, start_or_recv_from = undefined};
  2350
+    cancel_timer(Timer),
  2351
+    State#state{renegotiation = undefined, start_or_recv_from = undefined, timer = undefined};
2331 2352
 ack_connection(State) ->
2332 2353
     State.
2333 2354
 
@@ -2465,10 +2486,15 @@ default_hashsign(_Version, KeyExchange)
2465 2486
     {null, anon}.
2466 2487
 
2467 2488
 start_or_recv_cancel_timer(infinity, _RecvFrom) ->
2468  
-    ok;
  2489
+    undefined;
2469 2490
 start_or_recv_cancel_timer(Timeout, RecvFrom) ->
2470 2491
     erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}).    
2471 2492
 
  2493
+cancel_timer(undefined) ->
  2494
+    ok;
  2495
+cancel_timer(Timer) ->
  2496
+    erlang:cancel_timer(Timer).
  2497
+
2472 2498
 handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) ->
2473 2499
     inet:setopts(Socket, [{active, false}]),
2474 2500
     case Transport:recv(Socket, 0, 0) of
77  lib/ssl/test/ssl_basic_SUITE.erl
@@ -257,7 +257,9 @@ api_tests() ->
257 257
      shutdown_write,
258 258
      shutdown_both,
259 259
      shutdown_error,
260  
-     hibernate
  260
+     hibernate,
  261
+     ssl_accept_timeout,
  262
+     ssl_recv_timeout
261 263
     ].
262 264
 
263 265
 certificate_verify_tests() ->
@@ -3777,6 +3779,62 @@ hibernate(Config) ->
3777 3779
     ssl_test_lib:close(Client).
3778 3780
 
3779 3781
 %%--------------------------------------------------------------------
  3782
+ssl_accept_timeout(doc) ->
  3783
+    ["Test ssl:ssl_accept timeout"];
  3784
+ssl_accept_timeout(suite) ->
  3785
+    [];
  3786
+ssl_accept_timeout(Config) ->
  3787
+    process_flag(trap_exit, true),
  3788
+    ServerOpts = ?config(server_opts, Config),
  3789
+    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
  3790
+    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
  3791
+					{from, self()},
  3792
+					{timeout, 5000},
  3793
+					{mfa, {ssl_test_lib,
  3794
+					       no_result_msg, []}},
  3795
+					{options, ServerOpts}]),
  3796
+    Port = ssl_test_lib:inet_port(Server),
  3797
+    {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]),
  3798
+
  3799
+    receive
  3800
+	{tcp_closed, CSocket} ->
  3801
+	    ssl_test_lib:check_result(Server, {error, timeout}),
  3802
+	    receive
  3803
+		{'EXIT', Server, _} ->
  3804
+		    [] = supervisor:which_children(ssl_connection_sup)
  3805
+	    end
  3806
+    end.
  3807
+
  3808
+%%--------------------------------------------------------------------
  3809
+ssl_recv_timeout(doc) ->
  3810
+    ["Test ssl:ssl_accept timeout"];
  3811
+ssl_recv_timeout(suite) ->
  3812
+    [];
  3813
+ssl_recv_timeout(Config) ->
  3814
+    ServerOpts = ?config(server_opts, Config),
  3815
+    ClientOpts = ?config(client_opts, Config),
  3816
+
  3817
+    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
  3818
+
  3819
+    Server =
  3820
+	ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
  3821
+				   {from, self()},
  3822
+				   {mfa, {?MODULE, send_recv_result_timeout_server, []}},
  3823
+				   {options, [{active, false} | ServerOpts]}]),
  3824
+    Port = ssl_test_lib:inet_port(Server),
  3825
+
  3826
+    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
  3827
+					{host, Hostname},
  3828
+					{from, self()},
  3829
+					{mfa, {?MODULE,
  3830
+					       send_recv_result_timeout_client, []}},
  3831
+					{options, [{active, false} | ClientOpts]}]),
  3832
+
  3833
+    ssl_test_lib:check_result(Client, ok, Server, ok),
  3834
+    ssl_test_lib:close(Server),
  3835
+    ssl_test_lib:close(Client).
  3836
+
  3837
+%%--------------------------------------------------------------------
3780 3838
 
3781 3839
 connect_twice(doc) ->
3782 3840
     [""];
@@ -4019,6 +4077,23 @@ send_recv_result(Socket) ->
4019 4077
     {ok,"Hello world"} = ssl:recv(Socket, 11),
4020 4078
     ok.
4021 4079
 
  4080
+send_recv_result_timeout_client(Socket) ->
  4081
+    {error, timeout} = ssl:recv(Socket, 11, 500),
  4082
+    ssl:send(Socket, "Hello world"),
  4083
+    receive
  4084
+	Msg ->
  4085
+	    io:format("Msg ~p~n",[Msg])
  4086
+    after 500 ->
  4087
+	    ok
  4088
+    end,
  4089
+    {ok, "Hello world"} = ssl:recv(Socket, 11, 500),
  4090
+    ok.
  4091
+send_recv_result_timeout_server(Socket) ->
  4092
+    ssl:send(Socket, "Hello"),
  4093
+    {ok, "Hello world"} = ssl:recv(Socket, 11),
  4094
+    ssl:send(Socket, " world"),
  4095
+    ok.
  4096
+
4022 4097
 recv_close(Socket) ->
4023 4098
     {error, closed} = ssl:recv(Socket, 11),
4024 4099
     receive
27  lib/ssl/test/ssl_test_lib.erl
@@ -72,7 +72,13 @@ run_server(Opts) ->
72 72
     run_server(ListenSocket, Opts).
73 73
 
74 74
 run_server(ListenSocket, Opts) ->
75  
-    AcceptSocket = connect(ListenSocket, Opts),
  75
+    do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts).
  76
+
  77
+do_run_server(_, {error, timeout} = Result, Opts)  ->
  78
+    Pid = proplists:get_value(from, Opts),
  79
+    Pid ! {self(), Result};
  80
+
  81
+do_run_server(ListenSocket, AcceptSocket, Opts) ->
76 82
     Node = proplists:get_value(node, Opts),
77 83
     Pid = proplists:get_value(from, Opts),
78 84
     {Module, Function, Args} = proplists:get_value(mfa, Opts),
@@ -102,7 +108,8 @@ run_server(ListenSocket, Opts) ->
102 108
 connect(ListenSocket, Opts) ->
103 109
     Node = proplists:get_value(node, Opts),
104 110
     ReconnectTimes =  proplists:get_value(reconnect_times, Opts, 0),
105  
-    AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy),
  111
+    Timeout = proplists:get_value(timeout, Opts, infinity),
  112
+    AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout),
106 113
     case ReconnectTimes of
107 114
 	0 ->
108 115
 	    AcceptSocket;
@@ -111,15 +118,21 @@ connect(ListenSocket, Opts) ->
111 118
 	  AcceptSocket
112 119
     end.
113 120
     
114  
-connect(_, _, 0, AcceptSocket) ->
  121
+connect(_, _, 0, AcceptSocket, _) ->
115 122
     AcceptSocket;
116  
-connect(ListenSocket, Node, N, _) ->
  123
+connect(ListenSocket, Node, N, _, Timeout) ->
117 124
     test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
118 125
     {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, 
119 126
 				  [ListenSocket]),    
120  
-    test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]),
121  
-    ok = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
122  
-    connect(ListenSocket, Node, N-1, AcceptSocket).
  127
+    test_server:format("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]),
  128
+
  129
+    case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of
  130
+	ok ->
  131
+	    connect(ListenSocket, Node, N-1, AcceptSocket, Timeout);
  132
+	Result ->
  133
+	    Result
  134
+    end.
  135
+
123 136
   
124 137
 remove_close_msg(0) ->
125 138
     ok;
2  lib/ssl/vsn.mk
... ...
@@ -1 +1 @@
1  
-SSL_VSN = 5.1.1
  1
+SSL_VSN = 5.1.2

0 notes on commit 214d1aa

Please sign in to comment.
Something went wrong with that request. Please try again.