Skip to content

Commit

Permalink
add hostname auto-resolution to prevent DNS DOSing
Browse files Browse the repository at this point in the history
  • Loading branch information
ntrepid8 committed Sep 25, 2016
1 parent 6b930ff commit c40e28f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
56 changes: 56 additions & 0 deletions lib/ex_redis_pool/host_util.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
defmodule ExRedisPool.HostUtil do
@moduledoc """
Helper utilities to parse the Redis host argument.
"""
require Logger

def resolve(host) do
cond do
is_ip_address?(host) == true ->
Logger.debug("ip host: #{host}")
host
true ->
host_ip = resolve_hostname(host)
Logger.debug("host #{host} resolved to: #{host_ip}")
host_ip
end
end

def resolve_hostname(host) do
case lookup(host) do
{:ok, host_ip} -> ip_to_string(host_ip)
{:error, reason} -> raise(reason)
end
end

def is_ip_address?(host) do
case :inet.parse_address(to_char_list(host)) do
{:ok, _} -> true
{:error, _} -> false
end
end

def lookup(host) do
case :inet.gethostbyname(to_char_list(host)) do
{:ok, {:hostent, _, _, _, _, []}} ->
{:error, :invalid_host}
{:ok, {:hostent, _, _, _, _, results}} ->
[host_ip] = Enum.take_random(results, 1)
{:ok, host_ip}
_ ->
{:error, :invalid_host}
end
end

def ip_to_string({octet_1, octet_2, octet_3, octet_4}) do
{octet_1, octet_2, octet_3, octet_4}
|> Tuple.to_list()
|> Enum.join(".")
end

def ip_to_string({hextet_1, hextet_2, hextet_3, hextet_4, hextet_5, hextet_6, hextet_7, hextet_8}) do
{hextet_1, hextet_2, hextet_3, hextet_4, hextet_5, hextet_6, hextet_7, hextet_8}
|> Tuple.to_list()
|> Enum.join(":")
end
end
6 changes: 3 additions & 3 deletions lib/ex_redis_pool/redis_pool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule ExRedisPool.RedisPool do
Server for individual connection pools.
"""
use GenServer
alias ExRedisPool.{PoolsSupervisor, RedisPoolWorker}
alias ExRedisPool.{PoolsSupervisor, RedisPoolWorker, HostUtil}
require Logger

@noreply_timeout 300_000 # 5 minutes
Expand Down Expand Up @@ -35,7 +35,7 @@ defmodule ExRedisPool.RedisPool do
max_overflow: Keyword.get(opts, :sync_pool_max_overflow, 10),
]
sync_worker_opts = [
host: Keyword.get(opts, :host, "127.0.0.1"), # TODO - resolve host if name rather than IP
host: Keyword.get(opts, :host, "127.0.0.1") |> HostUtil.resolve(),
port: Keyword.get(opts, :port, 6379),
database: Keyword.get(opts, :database, :undefined),
password: Keyword.get(opts, :password, ""),
Expand All @@ -53,7 +53,7 @@ defmodule ExRedisPool.RedisPool do
max_overflow: Keyword.get(opts, :async_pool_max_overflow, 10),
]
async_worker_opts = [
host: Keyword.get(opts, :host, "127.0.0.1"), # TODO - resolve host if name rather than IP
host: Keyword.get(opts, :host, "127.0.0.1") |> HostUtil.resolve(),
port: Keyword.get(opts, :port, 6379),
database: Keyword.get(opts, :database, :undefined),
password: Keyword.get(opts, :password, ""),
Expand Down
12 changes: 12 additions & 0 deletions test/ex_redis_pool_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,16 @@ defmodule ExRedisPoolTest do
{:ok, result} = ExRedisPool.q(pid, ["DEL", key])
assert result == "1"
end

test "ExRedisPool.new/2 [start host localhost rather than 127.0.0.1]" do
pid = ExRedisPool.new(host: "localhost")
assert is_pid(pid) == true
key = "test_#{:crypto.rand_uniform(0, 1_000_000_000)}"
val = "#{:crypto.rand_uniform(0, 1_000_000_000)}"
{:ok, "OK"} = ExRedisPool.q(pid, ["SET", key, val])
{:ok, result} = ExRedisPool.q(pid, ["GET", key])
assert result == val
{:ok, result} = ExRedisPool.q(pid, ["DEL", key])
assert result == "1"
end
end

0 comments on commit c40e28f

Please sign in to comment.