defmodule ChronoscopeWeb.API.NTS.KeyExchangeController do use ChronoscopeWeb, :controller require Logger @default_port "4460" @timeout_in_milliseconds 3000 def get(conn, params) do host = to_charlist(params["host"]) port = String.to_integer(params["port"] || @default_port) tls_options = :tls_certificate_check.options(host) ++ [alpn_advertised_protocols: ["ntse/1"]] case :ssl.connect(host, port, tls_options, @timeout_in_milliseconds) do {:ok, socket} -> key_exchange_request = <<0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0x80, 0x04, 0x00, 0x04, 0x00, 0x1E, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00>> :ok = :ssl.send(socket, key_exchange_request) receive do {:ssl, _socket, data} -> :ssl.close(socket) ok_response(conn, %{status: :ok, response: parse_response(data)}) msg -> :ssl.close(socket) Logger.error("received unexpected message: #{inspect(msg)}") ok_response(conn, %{status: :error, reason: :no_response}) after @timeout_in_milliseconds -> :ssl.close(socket) Logger.error("timed out waiting for response") ok_response(conn, %{status: :error, reason: :timeout}) end {:error, {:tls_alert, {:handshake_failure, error}}} -> ok_response(conn, %{status: :error, reason: to_string(error)}) {:error, error} -> ok_response(conn, %{status: :error, reason: inspect(error)}) error -> ok_response(conn, %{status: :error, reason: inspect(error)}) end end defp parse_response([0x80 | _] = _data) do "success" end defp parse_response(_data) do "failure" end defp ok_response(conn, body) do conn |> Plug.Conn.put_resp_content_type("application/json") |> Plug.Conn.send_resp(:ok, Jason.encode!(body)) end end