Advertisement
Guest User

Untitled

a guest
Oct 4th, 2015
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.25 KB | None | 0 0
  1. %%%-------------------------------------------------------------------
  2. %%% @author <andelf@gmail.com>
  3. %%% @copyright (C) 2013,
  4. %%% @doc
  5. %%%
  6. %%% @end
  7. %%% Created : 18 Apr 2013 by <andelf@gmail.com>
  8. %%%-------------------------------------------------------------------
  9. -module(dns_proxy_srv).
  10.  
  11. -behaviour(gen_server).
  12.  
  13. %% API
  14. -export([start_link/0]).
  15.  
  16. %% gen_server callbacks
  17. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  18. terminate/2, code_change/3]).
  19.  
  20. -compile([export_all]).
  21.  
  22. -define(SERVER, ?MODULE).
  23.  
  24. -record(state, {sock,table}).
  25.  
  26. -include_lib("stdlib/include/ms_transform.hrl").
  27. -include_lib("kernel/src/inet_dns.hrl").
  28.  
  29. -define(DEBUG(Term), io:format("debug: ~p~n", [Term])).
  30. -define(DEBUG(What, Term), io:format("debug ~p: ~p~n", [What, Term])).
  31.  
  32. %%%===================================================================
  33. %%% API
  34. %%%===================================================================
  35.  
  36. %%--------------------------------------------------------------------
  37. %% @doc
  38. %% Starts the server
  39. %%
  40. %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
  41. %% @end
  42. %%--------------------------------------------------------------------
  43. start_link() ->
  44. gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
  45.  
  46. test() ->
  47. io:format("ddddddddddddddddddebug: ~p~n",
  48. [ets:fun2ms(fun(#dns_rr{type=cname,domain="www.baidu.com"}=R) when data =:= "www.baidu.com" -> R end)]).
  49.  
  50. %%%===================================================================
  51. %%% gen_server callbacks
  52. %%%===================================================================
  53.  
  54. %%--------------------------------------------------------------------
  55. %% @private
  56. %% @doc
  57. %% Initializes the server
  58. %%
  59. %% @spec init(Args) -> {ok, State} |
  60. %% {ok, State, Timeout} |
  61. %% ignore |
  62. %% {stop, Reason}
  63. %% @end
  64. %%--------------------------------------------------------------------
  65. init([]) ->
  66. Tid = ets:new(resolve_table, [bag, public, named_table, {keypos, #dns_rr.domain}]),
  67. case gen_udp:open(53, [binary, {active, true}]) of
  68. {ok, Sock} ->
  69. {ok, #state{sock=Sock, table=Tid}};
  70. {error, Reason} ->
  71. {stop, Reason}
  72. end.
  73.  
  74. %%--------------------------------------------------------------------
  75. %% @private
  76. %% @doc
  77. %% Handling call messages
  78. %%
  79. %% @spec handle_call(Request, From, State) ->
  80. %% {reply, Reply, State} |
  81. %% {reply, Reply, State, Timeout} |
  82. %% {noreply, State} |
  83. %% {noreply, State, Timeout} |
  84. %% {stop, Reason, Reply, State} |
  85. %% {stop, Reason, State}
  86. %% @end
  87. %%--------------------------------------------------------------------
  88. handle_call(_Request, _From, State) ->
  89. Reply = ok,
  90. {reply, Reply, State}.
  91.  
  92. %%--------------------------------------------------------------------
  93. %% @private
  94. %% @doc
  95. %% Handling cast messages
  96. %%
  97. %% @spec handle_cast(Msg, State) -> {noreply, State} |
  98. %% {noreply, State, Timeout} |
  99. %% {stop, Reason, State}
  100. %% @end
  101. %%--------------------------------------------------------------------
  102. handle_cast(_Msg, State) ->
  103. {noreply, State}.
  104.  
  105. %%--------------------------------------------------------------------
  106. %% @private
  107. %% @doc
  108. %% Handling all non call/cast messages
  109. %%
  110. %% @spec handle_info(Info, State) -> {noreply, State} |
  111. %% {noreply, State, Timeout} |
  112. %% {stop, Reason, State}
  113. %% @end
  114. %%--------------------------------------------------------------------
  115. handle_info({udp, Sock, FromIP, FromPort, Data}, #state{sock=Sock,table=Tid} = State) ->
  116. io:format("got packet from ~p:~p~n", [FromIP, FromPort]),
  117. handle_dns_packet(Data, fun(D, normal) ->
  118. gen_udp:send(Sock, FromIP, FromPort, D),
  119. save_dns_result_to_table(Tid, D);
  120. (D, cached) ->
  121. gen_udp:send(Sock, FromIP, FromPort, D)
  122. end, Tid),
  123. {noreply, State};
  124. handle_info({test_ok, From}, #state{sock=Sock,table=Tid} = State) ->
  125. From ! ets:select(Tid, ets:fun2ms(fun(#dns_rr{type=cname,domain="www.baidu.com",data=Cname}=R) ->
  126. Cname
  127. end)),
  128.  
  129. {noreply, State};
  130. handle_info(_Info, State) ->
  131. io:format("unhandled message: ~p~n", [_Info]),
  132. {noreply, State}.
  133.  
  134. %%--------------------------------------------------------------------
  135. %% @private
  136. %% @doc
  137. %% This function is called by a gen_server when it is about to
  138. %% terminate. It should be the opposite of Module:init/1 and do any
  139. %% necessary cleaning up. When it returns, the gen_server terminates
  140. %% with Reason. The return value is ignored.
  141. %%
  142. %% @spec terminate(Reason, State) -> void()
  143. %% @end
  144. %%--------------------------------------------------------------------
  145. terminate(_Reason, #state{sock=Sock}) ->
  146. gen_udp:close(Sock),
  147. ok.
  148.  
  149. %%--------------------------------------------------------------------
  150. %% @private
  151. %% @doc
  152. %% Convert process state when code is changed
  153. %%
  154. %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
  155. %% @end
  156. %%--------------------------------------------------------------------
  157. code_change(_OldVsn, State, _Extra) ->
  158. {ok, State}.
  159.  
  160. %%%===================================================================
  161. %%% Internal functions
  162. %%%===================================================================
  163. handle_dns_packet(Data, SendFunc, Tid) ->
  164. {ok, Packet} = inet_dns:decode(Data),
  165. #dns_rec{header=Header, qdlist=Questions, anlist=Answers,
  166. nslist=Authorities, arlist=Resources} = Packet,
  167. handle_dns_header(Header),
  168. case Header#dns_header.qr of
  169. true ->
  170. %% this is response
  171. handle_dns_header(Header);
  172. false ->
  173. %% this is a request
  174. case query_table_by_dns_query(Tid, Questions) of
  175. [] ->
  176. handle_dns_request(Packet, SendFunc);
  177. Cached ->
  178. io:format("!!! found in cache! items=~p~n", [length(Cached)]),
  179. SendFunc(inet_dns:encode(dns_rec_fill_answer(Packet, Cached)),
  180. cached)
  181. end
  182. end,
  183.  
  184. handle_dns_questions(Questions),
  185. %% io:format("got dns packet ~p~n", [Packet]),
  186. ok.
  187.  
  188. handle_dns_request(#dns_rec{header=#dns_header{qr=false,opcode='query'},
  189. qdlist=Queries} = Packet,
  190. SendFunc) ->
  191. QuerySendFunc =
  192. fun() ->
  193. Qdlist = lists:filter(fun(#dns_query{type=aaaa}) -> false;
  194. (_) -> true
  195. end,
  196. Packet#dns_rec.qdlist),
  197. case Qdlist of
  198. [] ->
  199. SendFunc(inet_dns:encode(dns_rec_requst_to_response(Packet)),
  200. normal);
  201. _ ->
  202. query_google_and_send_response(Packet, SendFunc)
  203. end
  204. end,
  205. spawn(QuerySendFunc).
  206.  
  207. dns_rec_requst_to_response(Packet) ->
  208. _OldHeader = Packet#dns_rec.header,
  209. Packet#dns_rec{header=_OldHeader#dns_header{qr=true}}.
  210.  
  211. dns_rec_fill_answer(Packet, Answers) when is_list(Answers) ->
  212. Packet#dns_rec{anlist=Answers}.
  213.  
  214. query_google_and_send_response(Packet, SendFunc) ->
  215. {ok, S} = gen_udp:open(0, [binary]),
  216. gen_udp:send(S, "8.8.8.8", 53, inet_dns:encode(Packet)),
  217. receive
  218. {udp, S, {8,8,8,8}, 53, Data} ->
  219. io:format("query google ok, sent response!~n"),
  220. SendFunc(Data, normal)
  221. after 1000 ->
  222. io:format("query time out.~n")
  223. end,
  224. gen_udp:close(S).
  225.  
  226.  
  227. handle_dns_header(#dns_header{id=Id,qr=RespFlag,opcode=OpCode,rcode=RCode}) ->
  228. ok.
  229.  
  230.  
  231. handle_dns_questions([#dns_query{domain=Domain,type=Type,class=Class}|Rest]) ->
  232. io:format("query type:~p ~p~n", [Type, Domain]),
  233. handle_dns_questions(Rest);
  234. handle_dns_questions([]) ->
  235. ok.
  236.  
  237. handle_dns_answers([#dns_rr{domain=Domain,type=Type,class=Class,
  238. cnt=Count,ttl=TTL,data=Data,tm=Time,
  239. bm=_,func=_}|Rest]) ->
  240. io:format("dns record ~p ~p ~p~n", [Type, Class, Domain]),
  241. handle_dns_answers(Rest);
  242. handle_dns_answers([]) ->
  243. ok.
  244.  
  245.  
  246.  
  247. save_dns_result_to_table(T, Data) when is_binary(Data) ->
  248. {ok, Packet} = inet_dns:decode(Data),
  249. save_dns_result_to_table(T, Packet);
  250. save_dns_result_to_table(T, Packet) when is_record(Packet, dns_rec) ->
  251. case Packet#dns_rec.anlist of
  252. [] ->
  253. ok;
  254. Records ->
  255. Records1 = lists:map(fun(R) -> dns_rr_set_ttl(R, 1000) end,
  256. Records),
  257. ets:insert(T, Records1)
  258. end.
  259.  
  260. query_table_by_dns_query(T, Query) when is_record(Query, dns_query) ->
  261. #dns_query{domain=Domain,type=Type,class=Class} = Query,
  262. Result = table_query(T, Domain),
  263. fill_cname_query(T, Result);
  264. query_table_by_dns_query(T, [Query|Rest]=Queries) when is_list(Queries),
  265. is_record(Query, dns_query) ->
  266. #dns_query{domain=Domain,type=Type,class=Class} = Query,
  267. lists:flatten([query_table_by_dns_query(T, Query),
  268. query_table_by_dns_query(T, Rest)]);
  269. query_table_by_dns_query(_, []) ->
  270. [].
  271.  
  272.  
  273. fill_cname_query(Tid, [R|Rest]) ->
  274. #dns_rr{type=Type, domain=Domain, data=Data} = R,
  275. case Type of
  276. cname ->
  277. [R|fill_cname_query(Tid, table_query(Tid, Data))] ++
  278. fill_cname_query(Tid, Rest);
  279. Other ->
  280. [R|fill_cname_query(Tid, Rest)]
  281. end;
  282. fill_cname_query(_, []) ->
  283. [].
  284.  
  285. %% make ttl longer and store to a `bag`
  286. dns_rr_set_ttl(Query, TTL) when is_record(Query, dns_rr), is_integer(TTL) ->
  287. Query#dns_rr{ttl=TTL}.
  288.  
  289. table_query(Tid, Domain) ->
  290. %% fun2ms is bad here
  291. A = ets:select(Tid, [{{dns_rr,Domain,'_','_','_','_','_','_','_','_'},[],['$_']}]),
  292. A.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement