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