defmodule Adbc.Helper do @moduledoc false @doc false def error_to_exception(string) when is_binary(string) do ArgumentError.exception(string) end def error_to_exception(list) when is_list(list) do ArgumentError.exception(List.to_string(list)) end def error_to_exception({:adbc_error, message, vendor_code, state}) do Adbc.Error.exception(message: message, vendor_code: vendor_code, state: state) end @doc false def download(url, ignore_proxy) do url_charlist = String.to_charlist(url) {:ok, _} = Application.ensure_all_started(:inets) {:ok, _} = Application.ensure_all_started(:ssl) {:ok, _} = Application.ensure_all_started(:public_key) if !ignore_proxy do if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do %{host: host, port: port} = URI.parse(proxy) :httpc.set_options([{:proxy, {{String.to_charlist(host), port}, []}}]) end if proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") do %{host: host, port: port} = URI.parse(proxy) :httpc.set_options([{:https_proxy, {{String.to_charlist(host), port}, []}}]) end end # https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/inets # TODO: This may no longer be necessary from Erlang/OTP 25.0 or later. https_options = [ ssl: [ verify: :verify_peer, customize_hostname_check: [ match_fun: :public_key.pkix_verify_hostname_match_fun(:https) ] ] ++ cacerts_options() ] options = [body_format: :binary] case :httpc.request(:get, {url_charlist, []}, https_options, options) do {:ok, {{_, 200, _}, _headers, body}} -> {:ok, body} other -> {:error, "couldn't fetch file from #{url}: #{inspect(other)}"} end end defp cacerts_options do cond do path = System.get_env("ADBC_CACERT") -> [cacertfile: path] Application.spec(:castore, :vsn) -> [cacertfile: Application.app_dir(:castore, "priv/cacerts.pem")] Application.spec(:certifi, :vsn) -> [cacertfile: Application.app_dir(:certifi, "priv/cacerts.pem")] certs = otp_cacerts() -> [cacerts: certs] path = cacerts_from_os() -> [cacertfile: path] true -> [] end end defp otp_cacerts do if System.otp_release() >= "25" do # cacerts_get/0 raises if no certs found try do :public_key.cacerts_get() rescue _ -> nil end end end # https_opts and related code are taken from # https://github.com/elixir-cldr/cldr_utils/blob/v2.19.1/lib/cldr/http/http.ex @certificate_locations [ # Debian/Ubuntu/Gentoo etc. "/etc/ssl/certs/ca-certificates.crt", # Fedora/RHEL 6 "/etc/pki/tls/certs/ca-bundle.crt", # OpenSUSE "/etc/ssl/ca-bundle.pem", # OpenELEC "/etc/pki/tls/cacert.pem", # CentOS/RHEL 7 "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", # Open SSL on MacOS "/usr/local/etc/openssl/cert.pem", # MacOS & Alpine Linux "/etc/ssl/cert.pem" ] defp cacerts_from_os do Enum.find(@certificate_locations, &File.exists?/1) end end