diff --git a/apps/sdlan/src/dns_proxy/dns_handler.erl b/apps/sdlan/src/dns_proxy/dns_handler.erl index b1011ba..67d3f18 100644 --- a/apps/sdlan/src/dns_proxy/dns_handler.erl +++ b/apps/sdlan/src/dns_proxy/dns_handler.erl @@ -9,18 +9,11 @@ -module(dns_handler). -author("anlicheng"). --behaviour(gen_server). - -include_lib("dns_erlang/include/dns.hrl"). -include_lib("pkt/include/pkt.hrl"). -include("dns_proxy.hrl"). -%% API --export([start_link/0]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([handle_ip_packet/5]). +-export([handle/4]). -define(SERVER, ?MODULE). -define(RESOLVER_POOL, dns_resolver_pool). @@ -35,44 +28,7 @@ %%% API %%%=================================================================== -start_link() -> - gen_server:start_link(?MODULE, [], []). - -handle_ip_packet(Pid, Sock, SrcIp, SrcPort, Packet) when is_pid(Pid) -> - gen_server:cast(Pid, {handle_ip_packet, Sock, SrcIp, SrcPort, Packet}). - -%%%=================================================================== -%%% gen_server callbacks -%%%=================================================================== - -%% @private -%% @doc Initializes the server --spec(init(Args :: term()) -> - {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} | - {stop, Reason :: term()} | ignore). -init([]) -> - {ok, #state{}}. - -%% @private -%% @doc Handling call messages --spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()}, - State :: #state{}) -> - {reply, Reply :: term(), NewState :: #state{}} | - {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} | - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} | - {stop, Reason :: term(), NewState :: #state{}}). -handle_call(_Request, _From, State = #state{}) -> - {reply, ok, State}. - -%% @private -%% @doc Handling cast messages --spec(handle_cast(Request :: term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}). -handle_cast({handle_ip_packet, Sock, SrcIp, SrcPort, IpPacket}, State) -> +handle(Sock, SrcIp, SrcPort, IpPacket) -> {#ipv4{saddr = ReqSAddr, daddr = ReqDAddr, p = Protocol}, ReqIpPayload} = pkt:ipv4(IpPacket), case Protocol =:= ?UDP_PROTOCOL of true -> @@ -86,35 +42,7 @@ handle_cast({handle_ip_packet, Sock, SrcIp, SrcPort, IpPacket}, State) -> end; false -> logger:notice("[dns_handler] resolver invalid protocol: ~p", [Protocol]) - end, - {stop, normal, State}. - -%% @private -%% @doc Handling all non call/cast messages --spec(handle_info(Info :: timeout() | term(), State :: #state{}) -> - {noreply, NewState :: #state{}} | - {noreply, NewState :: #state{}, timeout() | hibernate} | - {stop, Reason :: term(), NewState :: #state{}}). -handle_info(_Info, State) -> - {noreply, State}. - -%% @private -%% @doc This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any -%% necessary cleaning up. When it returns, the gen_server terminates -%% with Reason. The return value is ignored. --spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()), - State :: #state{}) -> term()). -terminate(_Reason, _State = #state{}) -> - ok. - -%% @private -%% @doc Convert process state when code is changed --spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{}, - Extra :: term()) -> - {ok, NewState :: #state{}} | {error, Reason :: term()}). -code_change(_OldVsn, State = #state{}, _Extra) -> - {ok, State}. + end. %%%=================================================================== %%% Internal functions @@ -162,11 +90,8 @@ resolver0(Packet, QueryMsg = #dns_message{qc = 1, questions = [Question = #dns_q RespMsg = build_response(QueryMsg, Cache), {ok, dns:encode_message(RespMsg)}; miss -> - Ref = make_ref(), - forward_to_upstream(Ref, Packet, QueryMsg), - logger:debug("[dns_handler] cache is miss, forward_to_upstream"), - receive - {dns_resolver_reply, Ref, Resp} -> + case forward_to_upstream(Packet, QueryMsg) of + {ok, Resp} -> case dns:decode_message(Resp) of RespMsg = #dns_message{answers = Answers} -> logger:debug("[dns_handler] get a response answers: ~p", [Answers]), @@ -175,10 +100,10 @@ resolver0(Packet, QueryMsg = #dns_message{qc = 1, questions = [Question = #dns_q Error -> logger:debug("[dns_handler] parse reply get error: ~p", [Error]), {ok, EmptyDnsResp} - end - after 5000 -> - logger:debug("[dns_handler] forward_to_upstream timeout"), - {ok, EmptyDnsResp} + end; + {error, Reason} -> + logger:debug("[dns_handler] parse reply get error: ~p", [Reason]), + {ok, EmptyDnsResp} end end end @@ -187,11 +112,6 @@ resolver0(_, Error) -> logger:warning("[dns_handler] decode dns_query get error: ~p", [Error]), {error, Error}. --spec forward_to_upstream(Ref :: reference(), Request :: binary(), QueryMsg :: #dns_message{}) -> no_return(). -forward_to_upstream(Ref, Request, QueryMsg) -> - ReceiverPid = self(), - poolboy:transaction(?RESOLVER_POOL, fun(Pid) -> dns_resolver:forward(Pid, ReceiverPid, Ref, Request, QueryMsg) end). - -spec build_response(QueryMsg :: #dns_message{}, Dns_cache :: #dns_cache{}) -> RespMsg :: #dns_message{}. build_response(QueryMsg, #dns_cache{expire_at = ExpireAt, answers = Answers, authority = Authority, additional = Additional, rc = RCode, flags = #{aa := AA}}) -> Now = os:system_time(second), @@ -260,4 +180,24 @@ build_ip_packet(SAddr, DAddr, SPort, DPort, UdpPayload) when is_integer(SPort), IpCheckSum = dns_utils:ip_checksum(IpPacket0), IpHeader = pkt:ipv4(IpPacket0#ipv4{sum = IpCheckSum}), - <>. \ No newline at end of file + <>. + +-spec forward_to_upstream(Request :: binary(), QueryMsg :: #dns_message{}) -> no_return(). +forward_to_upstream(Request, #dns_message{id = TxId, questions = [#dns_query{name = QName, type = QType, class = QClass}|_]}) -> + {ok, {DnsIp, DnsPort}} = application:get_env(sdlan, public_dns_server), + {ok, Socket} = gen_udp:open(0, [binary, {active, true}]), + ok = gen_udp:send(Socket, DnsIp, DnsPort, Request), + logger:debug("[dns_resolver] send to: ~p, packet: ~p", [{DnsIp, DnsPort}, Request]), + receive + {udp, Socket, DnsIp, DnsPort, Resp} -> + try dns:decode_message(Resp) of + #dns_message{id = TxId, questions = [#dns_query{name = QName, type = QType, class = QClass}|_]} -> + {ok, Resp}; + _ -> + {error, invalid_dns_response} + catch error:Err -> + {error, Err} + end + after 2000 -> + {error, timeout} + end. \ No newline at end of file diff --git a/apps/sdlan/src/dns_proxy/dns_server.erl b/apps/sdlan/src/dns_proxy/dns_server.erl index 228f2c4..4c6abfe 100644 --- a/apps/sdlan/src/dns_proxy/dns_server.erl +++ b/apps/sdlan/src/dns_proxy/dns_server.erl @@ -14,11 +14,6 @@ loop(Sock) -> receive {udp, Sock, Ip, Port, Packet} -> logger:debug("[dns_server] ip: ~p, get a packet: ~p", [{Ip, Port}, Packet]), - case dns_handler_sup:start_handler() of - {ok, HandlerPid} -> - dns_handler:handle_ip_packet(HandlerPid, Sock, Ip, Port, Packet); - Error -> - logger:debug("[dns_server] start handler get error: ~p", [Error]) - end, + spawn(fun() -> dns_handler:handle(Sock, Ip, Port, Packet) end), loop(Sock) end. \ No newline at end of file