From d44e7a16f7d2823cc658e39b5d953ba0850e47ba Mon Sep 17 00:00:00 2001 From: juhlig Date: Tue, 17 Sep 2019 17:55:25 +0200 Subject: Enable multiple steps handshake Also fix some Protocol:start_link/4 into start_link/3 left over in the documentation. --- src/ranch.erl | 74 ++++++++++++++++++++++++++++++++++++------------- src/ranch_ssl.erl | 37 +++++++++++++++++++++++-- src/ranch_tcp.erl | 20 +++++++++++++ src/ranch_transport.erl | 6 +++- 4 files changed, 114 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/ranch.erl b/src/ranch.erl index 08eebfc..89ca21e 100644 --- a/src/ranch.erl +++ b/src/ranch.erl @@ -22,6 +22,9 @@ -export([child_spec/5]). -export([handshake/1]). -export([handshake/2]). +-export([handshake_continue/1]). +-export([handshake_continue/2]). +-export([handshake_cancel/1]). -export([recv_proxy_header/2]). -export([remove_connection/1]). -export([get_status/1]). @@ -197,28 +200,61 @@ child_spec(Ref, Transport, TransOpts0, Protocol, ProtoOpts) -> Ref, Transport, TransOpts, Protocol, ProtoOpts ]}, type => supervisor}. --spec handshake(ref()) -> {ok, ranch_transport:socket()}. +-spec handshake(ref()) -> {ok, ranch_transport:socket()} | {continue, any()}. handshake(Ref) -> - handshake(Ref, []). + handshake1(Ref, undefined). --spec handshake(ref(), any()) -> {ok, ranch_transport:socket()}. +-spec handshake(ref(), any()) -> {ok, ranch_transport:socket()} | {continue, any()}. handshake(Ref, Opts) -> - receive {handshake, Ref, Transport, CSocket, HandshakeTimeout} -> - case Transport:handshake(CSocket, Opts, HandshakeTimeout) of - OK = {ok, _} -> - OK; - %% Garbage was most likely sent to the socket, don't error out. - {error, {tls_alert, _}} -> - ok = Transport:close(CSocket), - exit(normal); - %% Socket most likely stopped responding, don't error out. - {error, Reason} when Reason =:= timeout; Reason =:= closed -> - ok = Transport:close(CSocket), - exit(normal); - {error, Reason} -> - ok = Transport:close(CSocket), - error(Reason) - end + handshake1(Ref, {opts, Opts}). + +handshake1(Ref, Opts) -> + receive {handshake, Ref, Transport, CSocket, Timeout} -> + Handshake = handshake_transport(Transport, handshake, CSocket, Opts, Timeout), + handshake_result(Handshake, Ref, Transport, CSocket, Timeout) + end. + +-spec handshake_continue(ref()) -> {ok, ranch_transport:socket()}. +handshake_continue(Ref) -> + handshake_continue1(Ref, undefined). + +-spec handshake_continue(ref(), any()) -> {ok, ranch_transport:socket()}. +handshake_continue(Ref, Opts) -> + handshake_continue1(Ref, {opts, Opts}). + +handshake_continue1(Ref, Opts) -> + receive {handshake_continue, Ref, Transport, CSocket, Timeout} -> + Handshake = handshake_transport(Transport, handshake_continue, CSocket, Opts, Timeout), + handshake_result(Handshake, Ref, Transport, CSocket, Timeout) + end. + +handshake_transport(Transport, Fun, CSocket, undefined, Timeout) -> + Transport:Fun(CSocket, Timeout); +handshake_transport(Transport, Fun, CSocket, {opts, Opts}, Timeout) -> + Transport:Fun(CSocket, Opts, Timeout). + +handshake_result(Result, Ref, Transport, CSocket, Timeout) -> + case Result of + OK = {ok, _} -> + OK; + {ok, CSocket2, Info} -> + self() ! {handshake_continue, Ref, Transport, CSocket2, Timeout}, + {continue, Info}; + {error, {tls_alert, _}} -> + ok = Transport:close(CSocket), + exit(normal); + {error, Reason} when Reason =:= timeout; Reason =:= closed -> + ok = Transport:close(CSocket), + exit(normal); + {error, Reason} -> + ok = Transport:close(CSocket), + error(Reason) + end. + +-spec handshake_cancel(ref()) -> ok. +handshake_cancel(Ref) -> + receive {handshake_continue, Ref, Transport, CSocket, _} -> + Transport:handshake_cancel(CSocket) end. %% Unlike handshake/2 this function always return errors because diff --git a/src/ranch_ssl.erl b/src/ranch_ssl.erl index b6ca50b..b86a35f 100644 --- a/src/ranch_ssl.erl +++ b/src/ranch_ssl.erl @@ -21,7 +21,11 @@ -export([listen/1]). -export([disallowed_listen_options/0]). -export([accept/2]). +-export([handshake/2]). -export([handshake/3]). +-export([handshake_continue/2]). +-export([handshake_continue/3]). +-export([handshake_cancel/1]). -export([connect/3]). -export([connect/4]). -export([recv/3]). @@ -56,6 +60,7 @@ %% @todo Update when ssl exports named_curve(). | {eccs, [atom()]} | {fail_if_no_peer_cert, boolean()} + | {handshake, hello | full} | {hibernate_after, timeout()} | {honor_cipher_order, boolean()} | {honor_ecc_order, boolean()} @@ -138,16 +143,42 @@ disallowed_listen_options() -> accept(LSocket, Timeout) -> ssl:transport_accept(LSocket, Timeout). +-spec handshake(inet:socket() | ssl:sslsocket(), timeout()) + -> {ok, ssl:sslsocket()} | {ok, ssl:sslsocket(), ssl:protocol_extensions()} | {error, any()}. +handshake(CSocket, Timeout) -> + handshake(CSocket, [], Timeout). + -spec handshake(inet:socket() | ssl:sslsocket(), opts(), timeout()) - -> {ok, ssl:sslsocket()} | {error, any()}. + -> {ok, ssl:sslsocket()} | {ok, ssl:sslsocket(), ssl:protocol_extensions()} | {error, any()}. handshake(CSocket, Opts, Timeout) -> case ssl:handshake(CSocket, Opts, Timeout) of - {ok, NewSocket} -> - {ok, NewSocket}; + OK = {ok, _} -> + OK; + OK = {ok, _, _} -> + OK; + Error = {error, _} -> + Error + end. + +-spec handshake_continue(ssl:sslsocket(), timeout()) + -> {ok, ssl:sslsocket()} | {error, any()}. +handshake_continue(CSocket, Timeout) -> + handshake_continue(CSocket, [], Timeout). + +-spec handshake_continue(ssl:sslsocket(), [ssl:tls_server_option()], timeout()) + -> {ok, ssl:sslsocket()} | {error, any()}. +handshake_continue(CSocket, Opts, Timeout) -> + case ssl:handshake_continue(CSocket, Opts, Timeout) of + OK = {ok, _} -> + OK; Error = {error, _} -> Error end. +-spec handshake_cancel(ssl:sslsocket()) -> ok. +handshake_cancel(CSocket) -> + ok = ssl:handshake_cancel(CSocket). + %% @todo Probably filter Opts? -spec connect(inet:ip_address() | inet:hostname(), inet:port_number(), any()) diff --git a/src/ranch_tcp.erl b/src/ranch_tcp.erl index 3b4e4c3..0d8da2e 100644 --- a/src/ranch_tcp.erl +++ b/src/ranch_tcp.erl @@ -21,7 +21,11 @@ -export([listen/1]). -export([disallowed_listen_options/0]). -export([accept/2]). +-export([handshake/2]). -export([handshake/3]). +-export([handshake_continue/2]). +-export([handshake_continue/3]). +-export([handshake_cancel/1]). -export([connect/3]). -export([connect/4]). -export([recv/3]). @@ -105,10 +109,26 @@ disallowed_listen_options() -> accept(LSocket, Timeout) -> gen_tcp:accept(LSocket, Timeout). +-spec handshake(inet:socket(), timeout()) -> {ok, inet:socket()}. +handshake(CSocket, Timeout) -> + handshake(CSocket, [], Timeout). + -spec handshake(inet:socket(), opts(), timeout()) -> {ok, inet:socket()}. handshake(CSocket, _, _) -> {ok, CSocket}. +-spec handshake_continue(inet:socket(), timeout()) -> no_return(). +handshake_continue(CSocket, Timeout) -> + handshake_continue(CSocket, [], Timeout). + +-spec handshake_continue(inet:socket(), opts(), timeout()) -> no_return(). +handshake_continue(_, _, _) -> + error(not_supported). + +-spec handshake_cancel(inet:socket()) -> no_return(). +handshake_cancel(_) -> + error(not_supported). + %% @todo Probably filter Opts? -spec connect(inet:ip_address() | inet:hostname(), inet:port_number(), any()) diff --git a/src/ranch_transport.erl b/src/ranch_transport.erl index 6790977..5fd6242 100644 --- a/src/ranch_transport.erl +++ b/src/ranch_transport.erl @@ -30,7 +30,11 @@ -callback listen(ranch:transport_opts(any())) -> {ok, socket()} | {error, atom()}. -callback accept(socket(), timeout()) -> {ok, socket()} | {error, closed | timeout | atom()}. --callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake(socket(), timeout()) -> {ok, socket()} | {ok, socket(), any()} | {error, any()}. +-callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {ok, socket(), any()} | {error, any()}. +-callback handshake_continue(socket(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake_continue(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake_cancel(socket()) -> ok. -callback connect(string(), inet:port_number(), opts()) -> {ok, socket()} | {error, atom()}. -callback connect(string(), inet:port_number(), opts(), timeout()) -- cgit v1.2.3