Skip to content

Instantly share code, notes, and snippets.

@maxlapshin
Created May 20, 2013 09:54
Show Gist options
  • Save maxlapshin/5611366 to your computer and use it in GitHub Desktop.
Save maxlapshin/5611366 to your computer and use it in GitHub Desktop.
Geo DNS server in erlang
%% @doc This is a simple geo-dns server
%% It takes existing erlang dns query parser and https://github.com/mochi/egeoip
%% Here is no implemented database for path routing with some region affinity,
%% so if you are going to use it, you need to implement following databases:
%% 1) Domain -> existing servers
%% 2) Country -> region
%% 3) region -> existing server
-module(gdns).
-author("Max Lapshin <[email protected]>").
-export([start_link/0]).
-include_lib("kernel/src/inet_dns.hrl").
-include_lib("egeoip/include/egeoip.hrl").
-export([init/1, handle_info/2, terminate/2]).
start_link() ->
gen_server:start_link({local,?MODULE},?MODULE, [], []).
-record(dns, {
socket,
upstream
}).
init([]) ->
{ok, S} = gen_udp:open(5555, [{reuseaddr,true}, binary]),
inet:setopts(S,[{active,once}]),
{ok, U} = gen_udp:open(5556, [{reuseaddr,true}, binary, {active,false}]),
{ok, #dns{socket = S, upstream = U}}.
handle_info({udp, S, Addr, Port, Bin}, #dns{upstream = _U} = DNS) ->
case inet_dns:decode(Bin) of
{ok, #dns_rec{header = Header, qdlist = [#dns_query{domain = Domain, type = Type}]}} ->
Path = lists:reverse(string:tokens(Domain, ".")),
io:format("dns request ~p for domain ~p\n", [Type, Path]),
Answer = case Path of
["com", "flussonic"|_] ->
IP = case egeoip:lookup(Addr) of
{ok, #geoip{country_code = Country}} ->
case Country of
"US" -> {15,15,15,15};
"RU" -> {195,34,32,10};
_ -> {6,7,8,9}
end;
_ ->
{6,7,8,9}
end,
RR = #dns_rr{domain = Domain, type = Type, ttl = 60, data = IP, tm = undefined},
#dns_rec{header = Header#dns_header{qr = true, ra = false}, anlist = [RR]};
_ ->
#dns_rec{header = Header#dns_header{qr = true, ra = false}, anlist = []}
end,
gen_udp:send(S, Addr, Port, inet_dns:encode(Answer)),
ok;
{error, _} ->
io:format("invalid dns request: ~p\n", [Bin])
end,
inet:setopts(S, [{active,once}]),
{noreply, DNS};
handle_info(Msg, #dns{} = DNS) ->
lager:info("msg: ~p", [Msg]),
{noreply, DNS}.
terminate(_,_) ->
ok.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment