diff --git a/src/emqtt.erl b/src/emqtt.erl index 31080250..8a007423 100644 --- a/src/emqtt.erl +++ b/src/emqtt.erl @@ -1029,6 +1029,16 @@ do_connect(ConnMod, #state{pending_calls = Pendings, case mqtt_connect(State3) of {ok, State4} -> {ok, State4}; + {error, closed} -> + %% We may receive the `closed' error when attempting to perform MQTT + %% connect on a TLS socket, for example, if the client's TLS + %% certificate is revoked and the server closes the connection. + {error, closed}; + {error, {tls_alert, _} = Reason} -> + %% If we receive a TLS alert here such as `Certificate Revoked`, there + %% is no other socket event to be received, and thus we must terminate + %% now to avoid hanging and then getting a `{error, connack_timeout}`. + {error, Reason}; {error, Reason} -> ?LOG(info, "failed_to_send_connect_packet", #{reason => Reason}, State), %% Failed to send CONNECT packet. @@ -1539,9 +1549,17 @@ handle_event(info, {Error, Sock, Reason}, _StateName, #state{socket = Sock} = St ?LOG(error, "socket_error", #{error => Error, reason => Reason}, State), shutdown(Reason, State); +%% ssl connection is wrapped in a `#ssl_socket{}' record defined in +%% `emqtt_sock', which is not the same as what `ssl' uses in its +%% errors. handle_event(info, {ssl_error = Error, SSLSock, Reason}, _StateName, #state{socket = #ssl_socket{ssl = SSLSock}} = State) -> - ?LOG(error, "socket_error", #{error => Error, reason => Reason}, State), - shutdown(Reason, State); + ?LOG(error, "connection_error", + #{error => Error, reason => Reason}, State), + {stop, {shutdown, Reason}, State}; + +handle_event(info, {Closed, _Sock}, connected, #state{} = State) + when ?SOCK_CLOSED(Closed) -> + maybe_reconnect(Closed, State); handle_event(info, {ssl_closed, {sslsocket,{gen_tcp, Port, tls_connection,undefined}, _}} = Event, StateName, #state{socket = {ssl_socket, PortInUse, _}} = State) diff --git a/test/emqtt_SUITE.erl b/test/emqtt_SUITE.erl index dbae4f89..00be7d41 100644 --- a/test/emqtt_SUITE.erl +++ b/test/emqtt_SUITE.erl @@ -96,6 +96,7 @@ groups() -> t_init, t_init_external_secret, t_connected, + t_ssl_error, t_qos2_flow_autoack_never, t_ssl_error_client_reject_server, t_ssl_error_server_reject_client]}, @@ -307,6 +308,36 @@ t_ssl_error_server_reject_client(Config) -> ?assertMatch({ssl_error, _Sock, {tls_alert, {unknown_ca, _}}}, Reason), ok. +t_ssl_error(Config) -> + ct:timetrap({seconds, 1}), + Port = proplists:get_value(ssl_port, Config, 8883), + DataDir = cert_dir(Config), + emqtt_test_lib:gen_ca(DataDir, "ca"), + emqtt_test_lib:gen_ca(DataDir, "ca2"), + emqtt_test_lib:gen_host_cert("server", "ca", DataDir, true), + emqtt_test_lib:gen_host_cert("client", "ca2", DataDir, true), + emqtt_test_lib:set_ssl_options(<<"ssl:default">>, + #{ verify => verify_peer + , cacertfile => emqtt_test_lib:ca_cert_name(DataDir, "ca") + , certfile => emqtt_test_lib:cert_name(DataDir, "server") + , keyfile => emqtt_test_lib:key_name(DataDir, "server") + }), + process_flag(trap_exit, true), + {ok, C} = emqtt:start_link([{port, Port}, + {ssl, true}, + {ssl_opts, [ {certfile, emqtt_test_lib:cert_name(DataDir, "client")} + , {keyfile, emqtt_test_lib:key_name(DataDir, "client")} + , {verify, verify_none} + ]} + ]), + case emqtt:connect(C) of + {error, {{shutdown, {tls_alert, {unknown_ca, _}}}, _}} -> + ok; + {error, {ssl_error, _, {tls_alert, {unknown_ca, _}}}} -> + ok + end, + ok. + t_reconnect_enabled(Config) -> ConnFun = ?config(conn_fun, Config), Port = ?config(port, Config), @@ -1256,7 +1287,7 @@ t_qos2_flow_autoack_never(Config) -> after 100 -> ok = emqtt:disconnect(C2) end. - + t_inflight_full(_) -> error('TODO'). diff --git a/test/emqtt_test_lib.erl b/test/emqtt_test_lib.erl index 2b77a601..4f81ecfe 100644 --- a/test/emqtt_test_lib.erl +++ b/test/emqtt_test_lib.erl @@ -71,7 +71,7 @@ compile_emqx_test_module(M) -> EmqttDir = code:lib_dir(emqtt), MFilename= filename:join([EmqxDir, "test", M]), OutDir = filename:join([EmqttDir, "test"]), - {ok, _} = compile:file(MFilename, [{outdir, OutDir}]), + {{ok, _}, M} = {compile:file(MFilename, [{outdir, OutDir}]), M}, ok. -spec ensure_quic_listener(atom(), inet:port_number()) -> ok.