add基础代码

This commit is contained in:
anlicheng 2025-12-03 17:43:00 +08:00
parent 79650dd319
commit 1e2aa32bde
10 changed files with 274 additions and 7 deletions

View File

@ -0,0 +1,17 @@
-module(dns_cache).
-export([init/0, lookup/1, insert/2]).
-define(TABLE, dns_cache).
init() ->
ets:new(?TABLE, [named_table, set, public, {read_concurrency, true}]).
lookup(Key) ->
case ets:lookup(?TABLE, Key) of
[{_Key, Value}] -> {hit, Value};
[] -> miss
end.
insert(Key, DNSMsg) ->
ets:insert(?TABLE, {Key, DNSMsg}),
ok.

View File

@ -0,0 +1,48 @@
%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 03. 12 2025 17:27
%%%-------------------------------------------------------------------
-module(dns_handler).
-author("anlicheng").
-include_lib("dns_erlang/include/dns.hrl").
-include_lib("dns_erlang/include/dns_records.hrl").
-include_lib("dns_erlang/include/dns_terms.hrl").
-export([handle_request/4]).
handle_request(Sock, Ip, Port, Packet) ->
case dns:decode_message(Packet) of
{ok, Msg} ->
%Qname = (Msg#dns_message.questions)#dns_question.qname,
Qname = <<"www.baidu.com">>,
case dns_cache:lookup(Qname) of
{hit, R} ->
Resp = build_response(Msg, R),
gen_udp:send(Sock, Ip, Port, dns:encode_message(Resp));
miss ->
forward_to_upstream(Sock, Ip, Port, Packet)
end;
_ ->
ok
end.
forward_to_upstream(Sock, Ip, Port, Packet) ->
{SendSock, {UpIP, UpPort}} = dns_socket_pool:get_socket(),
ok = gen_udp:send(SendSock, UpIP, UpPort, Packet),
inet:setopts(SendSock, [{active, once}]),
receive
{udp, SendSock, _UIp, _UPort, Resp} ->
gen_udp:send(Sock, Ip, Port, Resp)
after 2000 ->
ok
end.
build_response(Req, RR) ->
Msg = Req,
Msg#dns_message{answers=[RR], qr=true, aa=true}.

View File

@ -0,0 +1,71 @@
%%%-------------------------------------------------------------------
%%% @author anlicheng
%%% @copyright (C) 2025, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 03. 12 2025 17:29
%%%-------------------------------------------------------------------
-module(dns_handler_sup).
-author("anlicheng").
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-export([start_handler/4]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API functions
%%%===================================================================
%% @doc Starts the supervisor
-spec(start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%% @private
%% @doc Whenever a supervisor is started using supervisor:start_link/[2,3],
%% this function is called by the new process to find out about
%% restart strategy, maximum restart frequency and child
%% specifications.
-spec(init(Args :: term()) ->
{ok, {SupFlags :: {RestartStrategy :: supervisor:strategy(),
MaxR :: non_neg_integer(), MaxT :: non_neg_integer()},
[ChildSpec :: supervisor:child_spec()]}}
| ignore | {error, Reason :: term()}).
init([]) ->
SupFlags = #{strategy => simple_one_for_one, intensity => 0, period => 1},
Spec = #{
id => dns_handler,
start => {'dns_handler', dns_handler, []},
restart => temporary,
shutdown => 2000,
type => worker,
modules => ['dns_handler']
},
{ok, {SupFlags, [Spec]}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
start_handler(Sock, Ip, Port, Packet) ->
case supervisor:start_child(?MODULE, [Sock, Ip, Port, Packet]) of
{ok, Pid} ->
{ok, Pid};
{error, {already_started, Pid}} ->
{ok, Pid};
StartError ->
StartError
end.

View File

@ -4,7 +4,11 @@
{registered, []},
{mod, {dns_proxy_app, []}},
{applications,
[kernel,
[
sync,
lager,
dns_erlang,
kernel,
stdlib
]},
{env,[]},

View File

@ -26,10 +26,17 @@ start_link() ->
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_all,
intensity => 0,
period => 1},
ChildSpecs = [],
SupFlags = #{strategy => one_for_one, intensity => 1000, period => 3600},
ChildSpecs = [
#{
id => dns_handler_sup,
start => {dns_handler_sup, start_link, []},
restart => permanent,
shutdown => 2000,
type => supervisor,
modules => ['dns_handler_sup']
}
],
{ok, {SupFlags, ChildSpecs}}.
%% internal functions

View File

@ -0,0 +1,30 @@
-module(dns_server).
-export([start/0, stop/0]).
-define(LISTEN_PORT, 53).
start() ->
dns_cache:init(),
dns_zone_loader:load("priv/local.zone"),
%% DNS
Upstreams = [
{{8,8,8,8}, 53},
{{1,1,1,1}, 53}
],
dns_socket_pool:start_link(Upstreams),
{ok, Sock} = gen_udp:open(?LISTEN_PORT, [binary, {active, true}]),
io:format("DNS Forwarder started on UDP port ~p~n", [?LISTEN_PORT]),
loop(Sock).
stop() ->
gen_udp:close(?LISTEN_PORT),
ok.
loop(Sock) ->
receive
{udp, Sock, Ip, Port, Packet} ->
dns_handler_sup:start_handler(Sock, Ip, Port, Packet),
loop(Sock)
end.

View File

@ -0,0 +1,26 @@
-module(dns_socket_pool).
-behaviour(gen_server).
-export([start_link/1, get_socket/0]).
-export([init/1, handle_call/3]).
-record(state, {sockets=[]}).
start_link(Ups) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Ups, []).
get_socket() ->
gen_server:call(?MODULE, get).
init(Upstreams) ->
%% DNS socket
Sockets = lists:map(fun({IP, Port}) ->
{ok, Sock} = gen_udp:open(0, [binary, {active, false}]),
{Sock, {IP, Port}}
end, Upstreams),
{ok, #state{sockets=Sockets}}.
handle_call(get, _From, State=#state{sockets=Socks}) ->
%% round-robin
[H|T] = Socks,
{reply, H, State#state{sockets=T ++ [H]}}.

View File

@ -0,0 +1,13 @@
-module(dns_zone_loader).
-export([load/1]).
-include_lib("dns_erlang/include/dns.hrl").
load(Path) ->
{ok, Bin} = file:read_file(Path),
{ok, Records} = dns_zone:decode(Bin), % dns_erlang
lists:foreach(fun(R) ->
Name = R#dns_rr.name,
dns_cache:insert(Name, R)
end, Records),
ok.

View File

@ -1,3 +1,46 @@
[
{dns_proxy, []}
{dns_proxy, [
%% 公共的dns域名解析服务
{public_dns_servers, [
{{8,8,8,8}, 53},
{{1,1,1,1}, 53}
]}
]},
%% 系统日志配置系统日志为lager, 支持日志按日期自动分割
{lager, [
{colored, true},
%% Whether to write a crash log, and where. Undefined means no crash logger.
{crash_log, "trade_hub.crash.log"},
%% Maximum size in bytes of events in the crash log - defaults to 65536
{crash_log_msg_size, 65536},
%% Maximum size of the crash log in bytes, before its rotated, set
%% to 0 to disable rotation - default is 0
{crash_log_size, 10485760},
%% What time to rotate the crash log - default is no time
%% rotation. See the README for a description of this format.
{crash_log_date, "$D0"},
%% Number of rotated crash logs to keep, 0 means keep only the
%% current one - default is 0
{crash_log_count, 5},
%% Whether to redirect error_logger messages into lager - defaults to true
{error_logger_redirect, true},
%% How big the gen_event mailbox can get before it is switched into sync mode
{async_threshold, 20},
%% Switch back to async mode, when gen_event mailbox size decrease from `async_threshold'
%% to async_threshold - async_threshold_window
{async_threshold_window, 5},
{handlers, [
%% debug | info | warning | error, 日志级别
{lager_console_backend, debug},
{lager_file_backend, [{file, "debug.log"}, {level, debug}, {size, 314572800}]},
{lager_file_backend, [{file, "notice.log"}, {level, notice}, {size, 314572800}]},
{lager_file_backend, [{file, "error.log"}, {level, error}, {size, 314572800}]},
{lager_file_backend, [{file, "info.log"}, {level, info}, {size, 314572800}]}
]}
]}
].

View File

@ -1,5 +1,10 @@
{erl_opts, [debug_info]}.
{deps, []}.
{deps, [
{dns_erlang, ".*", {git, "https://github.com/dnsimple/dns_erlang.git", {tag, "v4.4.0"}}},
{sync, ".*", {git, "https://github.com/rustyio/sync.git", {branch, "master"}}},
{parse_trans, ".*", {git, "https://github.com/uwiger/parse_trans", {tag, "3.0.0"}}},
{lager, ".*", {git,"https://github.com/erlang-lager/lager.git", {tag, "3.9.2"}}}
]}.
{relx, [{release, {dns_proxy, "0.1.0"},
[dns_proxy,
@ -30,3 +35,6 @@
%% {mode, minimal}
]
}]}]}.
{erl_opts, [{parse_transform,lager_transform}]}.
{rebar_packages_cdn, "https://hexpm.upyun.com"}.