PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/kernel/src/inet_res.erl

https://github.com/bsmr-erlang/otp
Erlang | 1044 lines | 765 code | 108 blank | 171 comment | 8 complexity | 6681c6cb4cf53da045d39701d10669ee MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1997-2018. All Rights Reserved.
  5. %%
  6. %% Licensed under the Apache License, Version 2.0 (the "License");
  7. %% you may not use this file except in compliance with the License.
  8. %% You may obtain a copy of the License at
  9. %%
  10. %% http://www.apache.org/licenses/LICENSE-2.0
  11. %%
  12. %% Unless required by applicable law or agreed to in writing, software
  13. %% distributed under the License is distributed on an "AS IS" BASIS,
  14. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. %% See the License for the specific language governing permissions and
  16. %% limitations under the License.
  17. %%
  18. %% %CopyrightEnd%
  19. %%
  20. %% RFC 1035, 2671, 2782, 2915.
  21. %%
  22. -module(inet_res).
  23. %-compile(export_all).
  24. -export([gethostbyname/1, gethostbyname/2, gethostbyname/3,
  25. gethostbyname_tm/3]).
  26. -export([gethostbyaddr/1, gethostbyaddr/2,
  27. gethostbyaddr_tm/2]).
  28. -export([getbyname/2, getbyname/3,
  29. getbyname_tm/3]).
  30. -export([resolve/3, resolve/4, resolve/5]).
  31. -export([lookup/3, lookup/4, lookup/5]).
  32. -export([dns_msg/1]).
  33. -export([nslookup/3, nslookup/4]).
  34. -export([nnslookup/4, nnslookup/5]).
  35. -include_lib("kernel/include/inet.hrl").
  36. -include("inet_res.hrl").
  37. -include("inet_dns.hrl").
  38. -include("inet_int.hrl").
  39. -define(verbose(Cond, Format, Args),
  40. case begin Cond end of
  41. true -> io:format(begin Format end, begin Args end);
  42. false -> ok
  43. end).
  44. -type res_option() ::
  45. {alt_nameservers, [nameserver()]}
  46. | {edns, 0 | false}
  47. | {inet6, boolean()}
  48. | {nameservers, [nameserver()]}
  49. | {recurse, boolean()}
  50. | {retry, integer()}
  51. | {timeout, integer()}
  52. | {udp_payload_size, integer()}
  53. | {usevc, boolean()}.
  54. -type nameserver() :: {inet:ip_address(), Port :: 1..65535}.
  55. -type res_error() :: formerr | qfmterror | servfail | nxdomain |
  56. notimp | refused | badvers | timeout.
  57. -type dns_name() :: string().
  58. -type rr_type() :: a | aaaa | cname | gid | hinfo | ns | mb | md | mg | mf
  59. | minfo | mx | naptr | null | ptr | soa | spf | srv | txt
  60. | uid | uinfo | unspec | wks.
  61. -type dns_class() :: in | chaos | hs | any.
  62. -type dns_msg() :: term().
  63. -type dns_data() ::
  64. dns_name()
  65. | inet:ip4_address()
  66. | inet:ip6_address()
  67. | {MName :: dns_name(),
  68. RName :: dns_name(),
  69. Serial :: integer(),
  70. Refresh :: integer(),
  71. Retry :: integer(),
  72. Expiry :: integer(),
  73. Minimum :: integer()}
  74. | {inet:ip4_address(), Proto :: integer(), BitMap :: binary()}
  75. | {CpuString :: string(), OsString :: string()}
  76. | {RM :: dns_name(), EM :: dns_name()}
  77. | {Prio :: integer(), dns_name()}
  78. | {Prio :: integer(),Weight :: integer(),Port :: integer(),dns_name()}
  79. | {Order :: integer(),Preference :: integer(),Flags :: string(),
  80. Services :: string(),Regexp :: string(), dns_name()}
  81. | [string()]
  82. | binary().
  83. %% --------------------------------------------------------------------------
  84. %% resolve:
  85. %%
  86. %% Nameserver query
  87. %%
  88. -spec resolve(Name, Class, Type) -> {ok, dns_msg()} | Error when
  89. Name :: dns_name() | inet:ip_address(),
  90. Class :: dns_class(),
  91. Type :: rr_type(),
  92. Error :: {error, Reason} | {error,{Reason,dns_msg()}},
  93. Reason :: inet:posix() | res_error().
  94. resolve(Name, Class, Type) ->
  95. resolve(Name, Class, Type, [], infinity).
  96. -spec resolve(Name, Class, Type, Opts) ->
  97. {ok, dns_msg()} | Error when
  98. Name :: dns_name() | inet:ip_address(),
  99. Class :: dns_class(),
  100. Type :: rr_type(),
  101. Opts :: [Opt],
  102. Opt :: res_option() | verbose | atom(),
  103. Error :: {error, Reason} | {error,{Reason,dns_msg()}},
  104. Reason :: inet:posix() | res_error().
  105. resolve(Name, Class, Type, Opts) ->
  106. resolve(Name, Class, Type, Opts, infinity).
  107. -spec resolve(Name, Class, Type, Opts, Timeout) ->
  108. {ok, dns_msg()} | Error when
  109. Name :: dns_name() | inet:ip_address(),
  110. Class :: dns_class(),
  111. Type :: rr_type(),
  112. Opts :: [Opt],
  113. Opt :: res_option() | verbose | atom(),
  114. Timeout :: timeout(),
  115. Error :: {error, Reason} | {error,{Reason,dns_msg()}},
  116. Reason :: inet:posix() | res_error().
  117. resolve(Name, Class, Type, Opts, Timeout) ->
  118. case nsdname(Name) of
  119. {ok, Nm} ->
  120. Timer = inet:start_timer(Timeout),
  121. Res = res_query(Nm, Class, Type, Opts, Timer),
  122. _ = inet:stop_timer(Timer),
  123. Res;
  124. Error ->
  125. Error
  126. end.
  127. %% --------------------------------------------------------------------------
  128. %% lookup:
  129. %%
  130. %% Convenience wrapper to resolve/3,4,5 that filters out all answer data
  131. %% fields of the class and type asked for.
  132. -spec lookup(Name, Class, Type) -> [dns_data()] when
  133. Name :: dns_name() | inet:ip_address(),
  134. Class :: dns_class(),
  135. Type :: rr_type().
  136. lookup(Name, Class, Type) ->
  137. lookup(Name, Class, Type, []).
  138. -spec lookup(Name, Class, Type, Opts) -> [dns_data()] when
  139. Name :: dns_name() | inet:ip_address(),
  140. Class :: dns_class(),
  141. Type :: rr_type(),
  142. Opts :: [res_option() | verbose].
  143. lookup(Name, Class, Type, Opts) ->
  144. lookup(Name, Class, Type, Opts, infinity).
  145. -spec lookup(Name, Class, Type, Opts, Timeout) -> [dns_data()] when
  146. Name :: dns_name() | inet:ip_address(),
  147. Class :: dns_class(),
  148. Type :: rr_type(),
  149. Opts :: [res_option() | verbose],
  150. Timeout :: timeout().
  151. lookup(Name, Class, Type, Opts, Timeout) ->
  152. lookup_filter(resolve(Name, Class, Type, Opts, Timeout),
  153. Class, Type).
  154. lookup_filter({ok,#dns_rec{anlist=Answers}}, Class, Type) ->
  155. [A#dns_rr.data || A <- Answers,
  156. Class =:= any orelse A#dns_rr.class =:= Class,
  157. Type =:= any orelse A#dns_rr.type =:= Type];
  158. lookup_filter({error,_}, _, _) -> [].
  159. %% --------------------------------------------------------------------------
  160. %% nslookup:
  161. %%
  162. %% Do a general nameserver lookup
  163. %%
  164. %% Perform nslookup on standard config !!
  165. %%
  166. %% To be deprecated
  167. -spec nslookup(Name, Class, Type) -> {ok, dns_msg()} | {error, Reason} when
  168. Name :: dns_name() | inet:ip_address(),
  169. Class :: dns_class(),
  170. Type :: rr_type(),
  171. Reason :: inet:posix() | res_error().
  172. nslookup(Name, Class, Type) ->
  173. do_nslookup(Name, Class, Type, [], infinity).
  174. -spec nslookup(Name, Class, Type, Timeout) ->
  175. {ok, dns_msg()} | {error, Reason} when
  176. Name :: dns_name() | inet:ip_address(),
  177. Class :: dns_class(),
  178. Type :: rr_type(),
  179. Timeout :: timeout(),
  180. Reason :: inet:posix() | res_error();
  181. (Name, Class, Type, Nameservers) ->
  182. {ok, dns_msg()} | {error, Reason} when
  183. Name :: dns_name() | inet:ip_address(),
  184. Class :: dns_class(),
  185. Type :: rr_type(),
  186. Nameservers :: [nameserver()],
  187. Reason :: inet:posix() | res_error().
  188. nslookup(Name, Class, Type, Timeout) when is_integer(Timeout), Timeout >= 0 ->
  189. do_nslookup(Name, Class, Type, [], Timeout);
  190. nslookup(Name, Class, Type, NSs) -> % For backwards compatibility
  191. nnslookup(Name, Class, Type, NSs). % with OTP R6B only
  192. -spec nnslookup(Name, Class, Type, Nameservers) ->
  193. {ok, dns_msg()} | {error, Reason} when
  194. Name :: dns_name() | inet:ip_address(),
  195. Class :: dns_class(),
  196. Type :: rr_type(),
  197. Nameservers :: [nameserver()],
  198. Reason :: inet:posix().
  199. nnslookup(Name, Class, Type, NSs) ->
  200. nnslookup(Name, Class, Type, NSs, infinity).
  201. -spec nnslookup(Name, Class, Type, Nameservers, Timeout) ->
  202. {ok, dns_msg()} | {error, Reason} when
  203. Name :: dns_name() | inet:ip_address(),
  204. Class :: dns_class(),
  205. Type :: rr_type(),
  206. Timeout :: timeout(),
  207. Nameservers :: [nameserver()],
  208. Reason :: inet:posix().
  209. nnslookup(Name, Class, Type, NSs, Timeout) ->
  210. do_nslookup(Name, Class, Type, [{nameservers,NSs}], Timeout).
  211. do_nslookup(Name, Class, Type, Opts, Timeout) ->
  212. case resolve(Name, Class, Type, Opts, Timeout) of
  213. {error,{qfmterror,_}} -> {error,einval};
  214. {error,{Reason,_}} -> {error,Reason};
  215. Result -> Result
  216. end.
  217. %% --------------------------------------------------------------------------
  218. %% options record
  219. %%
  220. -record(options, { % These must be sorted!
  221. alt_nameservers,edns,inet6,nameservers,recurse,
  222. retry,timeout,udp_payload_size,usevc,
  223. verbose}). % this is a local option, not in inet_db
  224. %%
  225. %% Opts when is_list(Opts) -> #options{}
  226. make_options(Opts0) ->
  227. Opts = [if is_atom(Opt) ->
  228. case atom_to_list(Opt) of
  229. "no"++X -> {list_to_atom(X),false};
  230. _ -> {Opt,true}
  231. end;
  232. true -> Opt
  233. end || Opt <- Opts0],
  234. %% If the caller gives the nameservers option, the inet_db
  235. %% alt_nameservers option should be regarded as empty, i.e
  236. %% use only the nameservers the caller supplies.
  237. SortedOpts =
  238. lists:ukeysort(1,
  239. case lists:keymember(nameservers, 1, Opts) of
  240. true ->
  241. case lists:keymember(alt_nameservers, 1, Opts) of
  242. false ->
  243. [{alt_nameservers,[]}|Opts];
  244. true ->
  245. Opts
  246. end;
  247. false ->
  248. Opts
  249. end),
  250. SortedNames = record_info(fields, options),
  251. inet_db:res_update_conf(),
  252. list_to_tuple([options|make_options(SortedOpts, SortedNames)]).
  253. make_options([_|_]=Opts0, []=Names0) ->
  254. erlang:error(badarg, [Opts0,Names0]);
  255. make_options([], []) -> [];
  256. make_options([{verbose,Val}|Opts]=Opts0, [verbose|Names]=Names0) ->
  257. if is_boolean(Val) ->
  258. [Val|make_options(Opts, Names)];
  259. true ->
  260. erlang:error(badarg, [Opts0,Names0])
  261. end;
  262. make_options([{Opt,Val}|Opts]=Opts0, [Opt|Names]=Names0) ->
  263. case inet_db:res_check_option(Opt, Val) of
  264. true ->
  265. [Val|make_options(Opts, Names)];
  266. false ->
  267. erlang:error(badarg, [Opts0,Names0])
  268. end;
  269. make_options(Opts, [verbose|Names]) ->
  270. [false|make_options(Opts, Names)];
  271. make_options(Opts, [Name|Names]) ->
  272. [inet_db:res_option(Name)|make_options(Opts, Names)].
  273. %% --------------------------------------------------------------------------
  274. %%
  275. %% gethostbyaddr(ip_address()) => {ok, hostent()} | {error, Reason}
  276. %%
  277. %% where ip_address() is {A,B,C,D} ipv4 address
  278. %% | {A,B,C,D,E,F,G,H} ipv6 address
  279. %% | string versions of the above
  280. %% | atom version
  281. %%
  282. %% --------------------------------------------------------------------------
  283. -spec gethostbyaddr(Address) -> {ok, Hostent} | {error, Reason} when
  284. Address :: inet:ip_address(),
  285. Hostent :: inet:hostent(),
  286. Reason :: inet:posix() | res_error().
  287. gethostbyaddr(IP) -> gethostbyaddr_tm(IP,false).
  288. -spec gethostbyaddr(Address, Timeout) -> {ok, Hostent} | {error, Reason} when
  289. Address :: inet:ip_address(),
  290. Timeout :: timeout(),
  291. Hostent :: inet:hostent(),
  292. Reason :: inet:posix() | res_error().
  293. gethostbyaddr(IP,Timeout) ->
  294. Timer = inet:start_timer(Timeout),
  295. Res = gethostbyaddr_tm(IP,Timer),
  296. _ = inet:stop_timer(Timer),
  297. Res.
  298. gethostbyaddr_tm({A,B,C,D} = IP, Timer) when ?ip(A,B,C,D) ->
  299. inet_db:res_update_conf(),
  300. case inet_db:gethostbyaddr(IP) of
  301. {ok, HEnt} -> {ok, HEnt};
  302. _ -> res_gethostbyaddr(dn_in_addr_arpa(A,B,C,D), IP, Timer)
  303. end;
  304. gethostbyaddr_tm({A,B,C,D,E,F,G,H} = IP, Timer) when ?ip6(A,B,C,D,E,F,G,H) ->
  305. inet_db:res_update_conf(),
  306. case inet_db:gethostbyaddr(IP) of
  307. {ok, HEnt} -> {ok, HEnt};
  308. _ -> res_gethostbyaddr(dn_ip6_int(A,B,C,D,E,F,G,H), IP, Timer)
  309. end;
  310. gethostbyaddr_tm(Addr,Timer) when is_list(Addr) ->
  311. case inet_parse:address(Addr) of
  312. {ok, IP} -> gethostbyaddr_tm(IP,Timer);
  313. _Error -> {error, formerr}
  314. end;
  315. gethostbyaddr_tm(Addr,Timer) when is_atom(Addr) ->
  316. gethostbyaddr_tm(atom_to_list(Addr),Timer);
  317. gethostbyaddr_tm(_,_) -> {error, formerr}.
  318. %%
  319. %% Send the gethostbyaddr query to:
  320. %% 1. the list of normal names servers
  321. %% 2. the list of alternative name servers
  322. %%
  323. res_gethostbyaddr(Addr, IP, Timer) ->
  324. case res_query(Addr, in, ptr, [], Timer) of
  325. {ok, Rec} ->
  326. inet_db:res_gethostbyaddr(IP, Rec);
  327. {error,{qfmterror,_}} -> {error,einval};
  328. {error,{Reason,_}} -> {error,Reason};
  329. Error ->
  330. Error
  331. end.
  332. %% --------------------------------------------------------------------------
  333. %%
  334. %% gethostbyname(domain_name()[,family [,Timer])
  335. %% => {ok, hostent()} | {error, Reason}
  336. %%
  337. %% where domain_name() is domain string or atom
  338. %%
  339. %% Caches the answer.
  340. %% --------------------------------------------------------------------------
  341. -spec gethostbyname(Name) -> {ok, Hostent} | {error, Reason} when
  342. Name :: dns_name(),
  343. Hostent :: inet:hostent(),
  344. Reason :: inet:posix() | res_error().
  345. gethostbyname(Name) ->
  346. case inet_db:res_option(inet6) of
  347. true ->
  348. gethostbyname_tm(Name, inet6, false);
  349. false ->
  350. gethostbyname_tm(Name, inet, false)
  351. end.
  352. -spec gethostbyname(Name, Family) -> {ok, Hostent} | {error, Reason} when
  353. Name :: dns_name(),
  354. Hostent :: inet:hostent(),
  355. Family :: inet:address_family(),
  356. Reason :: inet:posix() | res_error().
  357. gethostbyname(Name,Family) ->
  358. gethostbyname_tm(Name,Family,false).
  359. -spec gethostbyname(Name, Family, Timeout) ->
  360. {ok, Hostent} | {error, Reason} when
  361. Name :: dns_name(),
  362. Hostent :: inet:hostent(),
  363. Timeout :: timeout(),
  364. Family :: inet:address_family(),
  365. Reason :: inet:posix() | res_error().
  366. gethostbyname(Name,Family,Timeout) ->
  367. Timer = inet:start_timer(Timeout),
  368. Res = gethostbyname_tm(Name,Family,Timer),
  369. _ = inet:stop_timer(Timer),
  370. Res.
  371. gethostbyname_tm(Name,inet,Timer) ->
  372. getbyname_tm(Name,?S_A,Timer);
  373. gethostbyname_tm(Name,inet6,Timer) ->
  374. getbyname_tm(Name,?S_AAAA,Timer);
  375. gethostbyname_tm(_Name, _Family, _Timer) ->
  376. {error, einval}.
  377. %% --------------------------------------------------------------------------
  378. %%
  379. %% getbyname(domain_name(), Type) => {ok, hostent()} | {error, Reason}
  380. %%
  381. %% where domain_name() is domain string and Type is ?S_A, ?S_MX ...
  382. %%
  383. %% Caches the answer.
  384. %% --------------------------------------------------------------------------
  385. -spec getbyname(Name, Type) -> {ok, Hostent} | {error, Reason} when
  386. Name :: dns_name(),
  387. Type :: rr_type(),
  388. Hostent :: inet:hostent(),
  389. Reason :: inet:posix() | res_error().
  390. getbyname(Name, Type) ->
  391. getbyname_tm(Name,Type,false).
  392. -spec getbyname(Name, Type, Timeout) -> {ok, Hostent} | {error, Reason} when
  393. Name :: dns_name(),
  394. Type :: rr_type(),
  395. Timeout :: timeout(),
  396. Hostent :: inet:hostent(),
  397. Reason :: inet:posix() | res_error().
  398. getbyname(Name, Type, Timeout) ->
  399. Timer = inet:start_timer(Timeout),
  400. Res = getbyname_tm(Name, Type, Timer),
  401. _ = inet:stop_timer(Timer),
  402. Res.
  403. getbyname_tm(Name, Type, Timer) when is_list(Name) ->
  404. case type_p(Type) of
  405. true ->
  406. case inet_parse:visible_string(Name) of
  407. false -> {error, formerr};
  408. true ->
  409. inet_db:res_update_conf(),
  410. case inet_db:getbyname(Name, Type) of
  411. {ok, HEnt} -> {ok, HEnt};
  412. _ -> res_getbyname(Name, Type, Timer)
  413. end
  414. end;
  415. false ->
  416. {error, formerr}
  417. end;
  418. getbyname_tm(Name,Type,Timer) when is_atom(Name) ->
  419. getbyname_tm(atom_to_list(Name), Type,Timer);
  420. getbyname_tm(_, _, _) -> {error, formerr}.
  421. type_p(Type) ->
  422. lists:member(Type, [?S_A, ?S_AAAA, ?S_MX, ?S_NS,
  423. ?S_MD, ?S_MF, ?S_CNAME, ?S_SOA,
  424. ?S_MB, ?S_MG, ?S_MR, ?S_NULL,
  425. ?S_WKS, ?S_HINFO, ?S_TXT, ?S_SRV, ?S_NAPTR, ?S_SPF,
  426. ?S_UINFO, ?S_UID, ?S_GID]).
  427. %% This function and inet_db:getbyname/2 must look up names
  428. %% in the same manner, but not from the same places.
  429. %%
  430. %% Assuming search path, i.e return value from inet_db:get_searchlist()
  431. %% to be ["dom1", "dom2"]:
  432. %%
  433. %% Old behaviour (not this code but the previous version):
  434. %% * For Name = "foo"
  435. %% Name = "foo." try "foo.dom1", "foo.dom2" at normal nameservers
  436. %% * For Name = "foo.bar"
  437. %% Name = "foo.bar." try "foo.bar" at normal then alt. nameservers
  438. %% then try "foo.bar.dom1", "foo.bar.dom2"
  439. %% at normal nameservers
  440. %%
  441. %% New behaviour (this code), honoring the old behaviour but
  442. %% doing better for absolute names:
  443. %% * For Name = "foo" try "foo.dom1", "foo.dom2" at normal nameservers
  444. %% * For Name = "foo.bar" try "foo.bar" at normal then alt. nameservers
  445. %% then try "foo.bar.dom1", "foo.bar.dom2"
  446. %% at normal nameservers
  447. %% * For Name = "foo." try "foo" at normal then alt. nameservers
  448. %% * For Name = "foo.bar." try "foo.bar" at normal then alt. nameservers
  449. %%
  450. %%
  451. %% FIXME This is probably how it should be done:
  452. %% Common behaviour (Solaris resolver) is:
  453. %% * For Name = "foo." try "foo"
  454. %% * For Name = "foo.bar." try "foo.bar"
  455. %% * For Name = "foo" try "foo.dom1", "foo.dom2", "foo"
  456. %% * For Name = "foo.bar" try "foo.bar.dom1", "foo.bar.dom2", "foo.bar"
  457. %% That is to try Name as it is as a last resort if it is not absolute.
  458. %%
  459. res_getbyname(Name, Type, Timer) ->
  460. {EmbeddedDots, TrailingDot} = inet_parse:dots(Name),
  461. Dot = if TrailingDot -> ""; true -> "." end,
  462. if TrailingDot ->
  463. res_getby_query(Name, Type, Timer);
  464. EmbeddedDots =:= 0 ->
  465. res_getby_search(Name, Dot,
  466. inet_db:get_searchlist(),
  467. nxdomain, Type, Timer);
  468. true ->
  469. case res_getby_query(Name, Type, Timer) of
  470. {error,_Reason}=Error ->
  471. res_getby_search(Name, Dot,
  472. inet_db:get_searchlist(),
  473. Error, Type, Timer);
  474. Other -> Other
  475. end
  476. end.
  477. res_getby_search(Name, Dot, [Dom | Ds], _Reason, Type, Timer) ->
  478. case res_getby_query(Name++Dot++Dom, Type, Timer,
  479. inet_db:res_option(nameservers)) of
  480. {ok, HEnt} -> {ok, HEnt};
  481. {error, NewReason} ->
  482. res_getby_search(Name, Dot, Ds, NewReason, Type, Timer)
  483. end;
  484. res_getby_search(_Name, _, [], Reason,_,_) ->
  485. {error, Reason}.
  486. res_getby_query(Name, Type, Timer) ->
  487. case res_query(Name, in, Type, [], Timer) of
  488. {ok, Rec} ->
  489. inet_db:res_hostent_by_domain(Name, Type, Rec);
  490. {error,{qfmterror,_}} -> {error,einval};
  491. {error,{Reason,_}} -> {error,Reason};
  492. Error -> Error
  493. end.
  494. res_getby_query(Name, Type, Timer, NSs) ->
  495. case res_query(Name, in, Type, [], Timer, NSs) of
  496. {ok, Rec} ->
  497. inet_db:res_hostent_by_domain(Name, Type, Rec);
  498. {error,{qfmterror,_}} -> {error,einval};
  499. {error,{Reason,_}} -> {error,Reason};
  500. Error -> Error
  501. end.
  502. %% --------------------------------------------------------------------------
  503. %% query record
  504. %%
  505. -record(q, {options,edns,dns}).
  506. %% Query first nameservers list then alt_nameservers list
  507. res_query(Name, Class, Type, Opts, Timer) ->
  508. #q{options=#options{nameservers=NSs}}=Q =
  509. make_query(Name, Class, Type, Opts),
  510. case do_query(Q, NSs, Timer) of
  511. {error,nxdomain}=Error ->
  512. res_query_alt(Q, Error, Timer);
  513. {error,{nxdomain,_}}=Error ->
  514. res_query_alt(Q, Error, Timer);
  515. {ok,#dns_rec{anlist=[]}}=Reply ->
  516. res_query_alt(Q, Reply, Timer);
  517. Reply -> Reply
  518. end.
  519. %% Query just the argument nameservers list
  520. res_query(Name, Class, Type, Opts, Timer, NSs) ->
  521. Q = make_query(Name, Class, Type, Opts),
  522. do_query(Q, NSs, Timer).
  523. res_query_alt(#q{options=#options{alt_nameservers=NSs}}=Q, Reply, Timer) ->
  524. case NSs of
  525. [] -> Reply;
  526. _ ->
  527. do_query(Q, NSs, Timer)
  528. end.
  529. make_query(Dname, Class, Type, Opts) ->
  530. Options = make_options(Opts),
  531. case Options#options.edns of
  532. false ->
  533. #q{options=Options,
  534. edns=undefined,
  535. dns=make_query(Dname, Class, Type, Options, false)};
  536. Edns ->
  537. #q{options=Options,
  538. edns=make_query(Dname, Class, Type, Options, Edns),
  539. dns=fun () ->
  540. make_query(Dname, Class, Type, Options, false)
  541. end}
  542. end.
  543. %% XXX smarter would be to always construct both queries,
  544. %% but make the EDNS query point into the DNS query binary.
  545. %% It is only the header ARList length that need to be changed,
  546. %% and the OPT record appended.
  547. make_query(Dname, Class, Type, Options, Edns) ->
  548. Id = inet_db:res_option(next_id),
  549. Recurse = Options#options.recurse,
  550. ARList = case Edns of
  551. false -> [];
  552. _ ->
  553. PSz = Options#options.udp_payload_size,
  554. [#dns_rr_opt{udp_payload_size=PSz,
  555. version=Edns}]
  556. end,
  557. Msg = #dns_rec{header=#dns_header{id=Id,
  558. opcode='query',
  559. rd=Recurse,
  560. rcode=?NOERROR},
  561. qdlist=[#dns_query{domain=Dname,
  562. type=Type,
  563. class=Class}],
  564. arlist=ARList},
  565. ?verbose(Options#options.verbose, "Query: ~p~n", [dns_msg(Msg)]),
  566. Buffer = inet_dns:encode(Msg),
  567. {Id, Buffer}.
  568. %% --------------------------------------------------------------------------
  569. %% socket helpers
  570. %%
  571. -record(sock, {inet=undefined, inet6=undefined}).
  572. udp_open(#sock{inet6=I}=S, {A,B,C,D,E,F,G,H}) when ?ip6(A,B,C,D,E,F,G,H) ->
  573. case I of
  574. undefined ->
  575. case gen_udp:open(0, [{active,false},binary,inet6]) of
  576. {ok,J} ->
  577. {ok,S#sock{inet6=J}};
  578. Error ->
  579. Error
  580. end;
  581. _ ->
  582. {ok,S}
  583. end;
  584. udp_open(#sock{inet=I}=S, {A,B,C,D}) when ?ip(A,B,C,D) ->
  585. case I of
  586. undefined ->
  587. case gen_udp:open(0, [{active,false},binary,inet]) of
  588. {ok,J} ->
  589. {ok,S#sock{inet=J}};
  590. Error ->
  591. Error
  592. end;
  593. _ ->
  594. {ok,S}
  595. end.
  596. udp_connect(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port)
  597. when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) ->
  598. gen_udp:connect(I, IP, Port);
  599. udp_connect(#sock{inet=I}, {A,B,C,D}=IP, Port)
  600. when ?ip(A,B,C,D) ->
  601. gen_udp:connect(I, IP, Port).
  602. udp_send(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Buffer)
  603. when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) ->
  604. gen_udp:send(I, IP, Port, Buffer);
  605. udp_send(#sock{inet=I}, {A,B,C,D}=IP, Port, Buffer)
  606. when ?ip(A,B,C,D), ?port(Port) ->
  607. gen_udp:send(I, IP, Port, Buffer).
  608. udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout, Decode)
  609. when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) ->
  610. do_udp_recv(I, IP, Port, Timeout, Decode, time_now(), Timeout);
  611. udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout, Decode)
  612. when ?ip(A,B,C,D), ?port(Port) ->
  613. do_udp_recv(I, IP, Port, Timeout, Decode, time_now(), Timeout).
  614. do_udp_recv(_I, _IP, _Port, 0, _Decode, _Start, _T) ->
  615. timeout;
  616. do_udp_recv(I, IP, Port, Timeout, Decode, Start, T) ->
  617. case gen_udp:recv(I, 0, T) of
  618. {ok,Reply} ->
  619. case Decode(Reply) of
  620. false when T =:= 0 ->
  621. %% This is a compromize between the hard way i.e
  622. %% in the clause below if NewT becomes 0 bailout
  623. %% immediately and risk that the right reply lies
  624. %% ahead after some bad id replies, and the
  625. %% forgiving way i.e go on with Timeout 0 until
  626. %% the right reply comes or no reply (timeout)
  627. %% which opens for a DOS attack by a malicious
  628. %% DNS server flooding with bad id replies causing
  629. %% an infinite loop here.
  630. %%
  631. %% Timeout is used as a sanity limit counter
  632. %% just to put an end to the loop.
  633. NewTimeout = erlang:max(0, Timeout - 50),
  634. do_udp_recv(I, IP, Port, NewTimeout, Decode, Start, T);
  635. false ->
  636. Now = time_now(),
  637. NewT = erlang:max(0, Timeout - now_ms(Now, Start)),
  638. do_udp_recv(I, IP, Port, Timeout, Decode, Start, NewT);
  639. Result ->
  640. Result
  641. end;
  642. Error -> Error
  643. end.
  644. udp_close(#sock{inet=I,inet6=I6}) ->
  645. if I =/= undefined -> gen_udp:close(I); true -> ok end,
  646. if I6 =/= undefined -> gen_udp:close(I6); true -> ok end,
  647. ok.
  648. %%
  649. %% Send a query to the nameserver and return a reply
  650. %% We first use socket server then we add the udp version
  651. %%
  652. %% Algorithm: (from manual page for dig)
  653. %% for i = 0 to retry - 1
  654. %% for j = 1 to num_servers
  655. %% send_query
  656. %% wait((time * (2**i)) / num_servers)
  657. %% end
  658. %% end
  659. %%
  660. %% But that man page also says dig always use num_servers = 1.
  661. %%
  662. %% Our man page says: timeout/retry, then double for next retry, i.e
  663. %% for i = 0 to retry - 1
  664. %% foreach nameserver
  665. %% send query
  666. %% wait((time * (2**i)) / retry)
  667. %% end
  668. %% end
  669. %%
  670. %% And that is what the code seems to do, now fixed, hopefully...
  671. do_query(_Q, [], _Timer) ->
  672. {error,nxdomain};
  673. do_query(#q{options=#options{retry=Retry}}=Q, NSs, Timer) ->
  674. query_retries(Q, NSs, Timer, Retry, 0, #sock{}).
  675. query_retries(_Q, _NSs, _Timer, Retry, Retry, S) ->
  676. udp_close(S),
  677. {error,timeout};
  678. query_retries(_Q, [], _Timer, _Retry, _I, S) ->
  679. udp_close(S),
  680. {error,timeout};
  681. query_retries(Q, NSs, Timer, Retry, I, S0) ->
  682. case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
  683. {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers
  684. query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S);
  685. {S,Result} ->
  686. udp_close(S),
  687. Result
  688. end.
  689. query_nss(_Q, [], _Timer, _Retry, _I, S, ErrNSs) ->
  690. {S,{noanswer,ErrNSs}};
  691. query_nss(#q{edns=undefined}=Q, NSs, Timer, Retry, I, S, ErrNSs) ->
  692. query_nss_dns(Q, NSs, Timer, Retry, I, S, ErrNSs);
  693. query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) ->
  694. query_nss_edns(Q, NSs, Timer, Retry, I, S, ErrNSs).
  695. query_nss_edns(
  696. #q{options=#options{udp_payload_size=PSz}=Options,edns={Id,Buffer}}=Q,
  697. [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) ->
  698. {S,Res}=Reply =
  699. query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz),
  700. case Res of
  701. timeout -> {S,{error,timeout}}; % Bailout timeout
  702. {ok,_} -> Reply;
  703. {error,{nxdomain,_}} -> Reply;
  704. {error,{E,_}} when E =:= qfmterror; E =:= notimp; E =:= servfail;
  705. E =:= badvers ->
  706. query_nss_dns(Q, NSs0, Timer, Retry, I, S, ErrNSs);
  707. {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused ->
  708. query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]);
  709. _Error ->
  710. query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs)
  711. end.
  712. query_nss_dns(
  713. #q{dns=Qdns}=Q0,
  714. [{IP,Port}=NS|NSs], Timer, Retry, I, S0, ErrNSs) ->
  715. #q{options=Options,dns={Id,Buffer}}=Q =
  716. if
  717. is_function(Qdns, 0) -> Q0#q{dns=Qdns()};
  718. true -> Q0
  719. end,
  720. {S,Res}=Reply =
  721. query_ns(
  722. S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ),
  723. case Res of
  724. timeout -> {S,{error,timeout}}; % Bailout timeout
  725. {ok,_} -> Reply;
  726. {error,{E,_}} when E =:= nxdomain; E =:= qfmterror -> Reply;
  727. {error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused ->
  728. query_nss(Q, NSs, Timer, Retry, I, S, [NS|ErrNSs]);
  729. _Error ->
  730. query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs)
  731. end.
  732. query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
  733. #options{timeout=Tm,usevc=UseVC,verbose=Verbose},
  734. PSz) ->
  735. case UseVC orelse iolist_size(Buffer) > PSz of
  736. true ->
  737. TcpTimeout = inet:timeout(Tm*5, Timer),
  738. {S0,query_tcp(TcpTimeout, Id, Buffer, IP, Port, Verbose)};
  739. false ->
  740. case udp_open(S0, IP) of
  741. {ok,S} ->
  742. Timeout =
  743. inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
  744. case query_udp(
  745. S, Id, Buffer, IP, Port, Timeout, Verbose) of
  746. {ok,#dns_rec{header=H}} when H#dns_header.tc ->
  747. TcpTimeout = inet:timeout(Tm*5, Timer),
  748. {S, query_tcp(
  749. TcpTimeout, Id, Buffer, IP, Port, Verbose)};
  750. {error, econnrefused} = Err ->
  751. ok = udp_close(S),
  752. {#sock{}, Err};
  753. Reply -> {S, Reply}
  754. end;
  755. Error ->
  756. {S0,Error}
  757. end
  758. end.
  759. query_udp(_S, _Id, _Buffer, _IP, _Port, 0, _Verbose) ->
  760. timeout;
  761. query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
  762. ?verbose(Verbose, "Try UDP server : ~p:~p (timeout=~w)\n",
  763. [IP,Port,Timeout]),
  764. case
  765. case udp_connect(S, IP, Port) of
  766. ok ->
  767. udp_send(S, IP, Port, Buffer);
  768. E1 ->
  769. E1 end of
  770. ok ->
  771. Decode =
  772. fun ({RecIP,RecPort,Answer})
  773. when RecIP =:= IP, RecPort =:= Port ->
  774. case decode_answer(Answer, Id, Verbose) of
  775. {error,badid} ->
  776. false;
  777. Reply ->
  778. Reply
  779. end;
  780. ({_,_,_}) ->
  781. false
  782. end,
  783. case udp_recv(S, IP, Port, Timeout, Decode) of
  784. {ok,_}=Result ->
  785. Result;
  786. E2 ->
  787. ?verbose(Verbose, "UDP server error: ~p\n", [E2]),
  788. E2
  789. end;
  790. E3 ->
  791. ?verbose(Verbose, "UDP send failed: ~p\n", [E3]),
  792. {error,econnrefused}
  793. end.
  794. query_tcp(0, _Id, _Buffer, _IP, _Port, _Verbose) ->
  795. timeout;
  796. query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
  797. ?verbose(Verbose, "Try TCP server : ~p:~p (timeout=~w)\n",
  798. [IP, Port, Timeout]),
  799. Family = case IP of
  800. {A,B,C,D} when ?ip(A,B,C,D) -> inet;
  801. {A,B,C,D,E,F,G,H} when ?ip6(A,B,C,D,E,F,G,H) -> inet6
  802. end,
  803. try gen_tcp:connect(IP, Port,
  804. [{active,false},{packet,2},binary,Family],
  805. Timeout) of
  806. {ok, S} ->
  807. case gen_tcp:send(S, Buffer) of
  808. ok ->
  809. case gen_tcp:recv(S, 0, Timeout) of
  810. {ok, Answer} ->
  811. gen_tcp:close(S),
  812. case decode_answer(Answer, Id, Verbose) of
  813. {ok, _} = OK -> OK;
  814. {error, badid} -> {error, servfail};
  815. Error -> Error
  816. end;
  817. Error ->
  818. gen_tcp:close(S),
  819. ?verbose(Verbose, "TCP server recv error: ~p\n",
  820. [Error]),
  821. Error
  822. end;
  823. Error ->
  824. gen_tcp:close(S),
  825. ?verbose(Verbose, "TCP server send error: ~p\n",
  826. [Error]),
  827. Error
  828. end;
  829. Error ->
  830. ?verbose(Verbose, "TCP server error: ~p\n", [Error]),
  831. Error
  832. catch
  833. _:_ -> {error, einval}
  834. end.
  835. decode_answer(Answer, Id, Verbose) ->
  836. case inet_dns:decode(Answer) of
  837. {ok, Msg} ->
  838. ?verbose(Verbose, "Got reply: ~p~n", [dns_msg(Msg)]),
  839. E = case lists:keyfind(dns_rr_opt, 1, Msg#dns_rec.arlist) of
  840. false -> 0;
  841. #dns_rr_opt{ext_rcode=ExtRCode} -> ExtRCode
  842. end,
  843. H = Msg#dns_rec.header,
  844. RCode = (E bsl 4) bor H#dns_header.rcode,
  845. case RCode of
  846. ?NOERROR ->
  847. if H#dns_header.id =/= Id ->
  848. {error,badid};
  849. length(Msg#dns_rec.qdlist) =/= 1 ->
  850. {error,{noquery,Msg}};
  851. true ->
  852. {ok, Msg}
  853. end;
  854. ?FORMERR -> {error,{qfmterror,Msg}};
  855. ?SERVFAIL -> {error,{servfail,Msg}};
  856. ?NXDOMAIN -> {error,{nxdomain,Msg}};
  857. ?NOTIMP -> {error,{notimp,Msg}};
  858. ?REFUSED -> {error,{refused,Msg}};
  859. ?BADVERS -> {error,{badvers,Msg}};
  860. _ -> {error,{unknown,Msg}}
  861. end;
  862. Error ->
  863. ?verbose(Verbose, "Got reply: ~p~n", [Error]),
  864. Error
  865. end.
  866. %%
  867. %% Transform domain name or address
  868. %% 1. "a.b.c" =>
  869. %% "a.b.c"
  870. %% 2. "1.2.3.4" =>
  871. %% "4.3.2.1.IN-ADDR.ARPA"
  872. %% 3. "4321:0:1:2:3:4:567:89ab" =>
  873. %% "b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.1.2.3.4.IP6.ARPA"
  874. %% 4. {1,2,3,4} => as 2.
  875. %% 5. {1,2,3,4,5,6,7,8} => as 3.
  876. %%
  877. nsdname({A,B,C,D}) ->
  878. {ok, dn_in_addr_arpa(A,B,C,D)};
  879. nsdname({A,B,C,D,E,F,G,H}) ->
  880. {ok, dn_ip6_int(A,B,C,D,E,F,G,H)};
  881. nsdname(Name) when is_list(Name) ->
  882. case inet_parse:visible_string(Name) of
  883. true ->
  884. case inet_parse:address(Name) of
  885. {ok, Addr} ->
  886. nsdname(Addr);
  887. _ ->
  888. {ok, Name}
  889. end;
  890. _ -> {error, formerr}
  891. end;
  892. nsdname(Name) when is_atom(Name) ->
  893. nsdname(atom_to_list(Name));
  894. nsdname(_) -> {error, formerr}.
  895. dn_in_addr_arpa(A,B,C,D) ->
  896. integer_to_list(D) ++
  897. ("." ++ integer_to_list(C) ++
  898. ("." ++ integer_to_list(B) ++
  899. ("." ++ integer_to_list(A) ++ ".IN-ADDR.ARPA"))).
  900. dn_ip6_int(A,B,C,D,E,F,G,H) ->
  901. dnib(H) ++
  902. (dnib(G) ++
  903. (dnib(F) ++
  904. (dnib(E) ++
  905. (dnib(D) ++
  906. (dnib(C) ++
  907. (dnib(B) ++
  908. (dnib(A) ++ "IP6.ARPA"))))))).
  909. -compile({inline, [dnib/1, dnib/3]}).
  910. dnib(X) ->
  911. L = erlang:integer_to_list(X, 16),
  912. dnib(4-length(L), L, []).
  913. %%
  914. dnib(0, [], Acc) -> Acc;
  915. dnib(0, [C|Cs], Acc) ->
  916. dnib(0, Cs, [C,$.|Acc]);
  917. dnib(N, Cs, Acc) ->
  918. dnib(N-1, Cs, [$0,$.|Acc]).
  919. dns_msg([]) -> [];
  920. dns_msg([{Field,Msg}|Fields]) ->
  921. [{Field,dns_msg(Msg)}|dns_msg(Fields)];
  922. dns_msg([Msg|Msgs]) ->
  923. [dns_msg(Msg)|dns_msg(Msgs)];
  924. dns_msg(Msg) ->
  925. case inet_dns:record_type(Msg) of
  926. undefined -> Msg;
  927. Type ->
  928. Fields = inet_dns:Type(Msg),
  929. {Type,dns_msg(Fields)}
  930. end.
  931. -compile({inline, [now_ms/2]}).
  932. now_ms(Int1, Int0) ->
  933. Int1 - Int0.
  934. -compile({inline, [time_now/0]}).
  935. time_now() ->
  936. erlang:monotonic_time(1000).