diff --git a/lib/chronoscope/gemini/connection_client.ex b/lib/chronoscope/gemini/connection_client.ex index 05566aa..c5406c0 100644 --- a/lib/chronoscope/gemini/connection_client.ex +++ b/lib/chronoscope/gemini/connection_client.ex @@ -12,8 +12,7 @@ defmodule Chronoscope.Gemini.ConnectionClient do def connect(%{host: host, port: port, path: _} = resource) do case ssl_connect(host, port) do {:ok, socket} -> make_request(socket, resource) - {:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, String.trim("#{error}")} - {:error, {:tls_alert, {:no_application_protocol, error}}} -> {:error, String.trim("#{error}")} + {:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, handshake_failure_message("#{error}")} {:error, :timeout} -> {:error, :timeout} {:error, error} -> {:error, inspect(error)} error -> {:error, inspect(error)} @@ -73,4 +72,17 @@ defmodule Chronoscope.Gemini.ConnectionClient do {:error, error} end end + + defp handshake_failure_message(error) do + cond do + error =~ ~r/\{bad_cert,hostname_check_failed\}$/ -> + "The certificate is NOT trusted. The name in the certificate does not match the expected." + + error =~ ~r/\{bad_cert,unable_to_match_altnames\}$/ -> + "The certificate is NOT trusted. The name in the certificate does not match the expected." + + true -> + String.trim(error) + end + end end diff --git a/lib/chronoscope/nts/key_establishment_client.ex b/lib/chronoscope/nts/key_establishment_client.ex index c6959bf..626bcea 100644 --- a/lib/chronoscope/nts/key_establishment_client.ex +++ b/lib/chronoscope/nts/key_establishment_client.ex @@ -12,7 +12,7 @@ defmodule Chronoscope.NTS.KeyEstablishmentClient do def key_establishment(%{host: host, port: port}) do case ssl_connect(host, port) do {:ok, socket} -> perform_key_establishment(socket) - {:error, {:tls_alert, {:handshake_failure, error}}} -> handshake_failure_message("#{error}") + {:error, {:tls_alert, {:handshake_failure, error}}} -> {:error, handshake_failure_message("#{error}")} {:error, {:tls_alert, {:no_application_protocol, error}}} -> {:error, String.trim("#{error}")} {:error, :timeout} -> {:error, :timeout} {:error, error} -> {:error, inspect(error)} @@ -64,10 +64,13 @@ defmodule Chronoscope.NTS.KeyEstablishmentClient do defp handshake_failure_message(error) do cond do error =~ ~r/\{bad_cert,hostname_check_failed\}$/ -> - {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."} + "The certificate is NOT trusted. The name in the certificate does not match the expected." + + error =~ ~r/\{bad_cert,unable_to_match_altnames\}$/ -> + "The certificate is NOT trusted. The name in the certificate does not match the expected." true -> - {:error, String.trim(error)} + String.trim(error) end end end diff --git a/test/chronoscope/gemini/connection_client_test.exs b/test/chronoscope/gemini/connection_client_test.exs index c462204..e36181f 100644 --- a/test/chronoscope/gemini/connection_client_test.exs +++ b/test/chronoscope/gemini/connection_client_test.exs @@ -55,5 +55,51 @@ defmodule Chronoscope.Gemini.ConnectionClientTest do assert {:error, "bad response: HTTP/1.1 400 Bad Request\r\nServer: nginx\r\n"} = ConnectionClient.connect(resource) end + + test "handles a bad certificate hostname failure", %{resource: resource} do + SSLMock + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> + {:error, {:tls_alert, {:handshake_failure, "connection failed {bad_cert,hostname_check_failed}"}}} + end) + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> + {:error, {:tls_alert, {:handshake_failure, "connection failed {bad_cert,unable_to_match_altnames}"}}} + end) + + assert ConnectionClient.connect(resource) == + {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."} + + assert ConnectionClient.connect(resource) == + {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."} + end + + test "handles a handshake failure", %{resource: resource} do + SSLMock + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> + {:error, {:tls_alert, {:handshake_failure, "unsatisfactory handshake"}}} + end) + + assert ConnectionClient.connect(resource) == {:error, "unsatisfactory handshake"} + end + + test "handles a timeout failure", %{resource: resource} do + SSLMock + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> {:error, :timeout} end) + + assert ConnectionClient.connect(resource) == {:error, :timeout} + end + + test "handles an unknown error", %{resource: resource} do + SSLMock + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> {:error, {:unsatisfactory, "client"}} end) + + assert ConnectionClient.connect(resource) == {:error, "{:unsatisfactory, \"client\"}"} + end + + test "handles an unexpected error", %{resource: resource} do + SSLMock + |> expect(:connect, fn ~c"localhost", 1965, _tls_options, @timeout -> {:unsatisfactory, "client"} end) + + assert ConnectionClient.connect(resource) == {:error, "{:unsatisfactory, \"client\"}"} + end end end diff --git a/test/chronoscope/nts/key_establishment_client_test.exs b/test/chronoscope/nts/key_establishment_client_test.exs index 9de6a99..4a5758f 100644 --- a/test/chronoscope/nts/key_establishment_client_test.exs +++ b/test/chronoscope/nts/key_establishment_client_test.exs @@ -78,6 +78,12 @@ defmodule Chronoscope.NTS.KeyEstablishmentClientTest do |> expect(:connect, fn ~c"localhost", 2222, _tls_options, @timeout -> {:error, {:tls_alert, {:handshake_failure, "connection failed {bad_cert,hostname_check_failed}"}}} end) + |> expect(:connect, fn ~c"localhost", 2222, _tls_options, @timeout -> + {:error, {:tls_alert, {:handshake_failure, "connection failed {bad_cert,unable_to_match_altnames}"}}} + end) + + assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == + {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."} assert KeyEstablishmentClient.key_establishment(%{host: "localhost", port: 2222}) == {:error, "The certificate is NOT trusted. The name in the certificate does not match the expected."}