1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2020. 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-module(inet_config). 21 22-include("inet_config.hrl"). 23-include("inet.hrl"). 24 25-import(lists, [foreach/2, member/2, reverse/1]). 26 27%% Avoid warning for local function error/2 clashing with autoimported BIF. 28-compile({no_auto_import,[error/2]}). 29-export([init/0]). 30 31-export([do_load_resolv/2]). 32 33%% 34%% Must be called after inet_db:start 35%% 36%% Order in which to load inet_db data: 37%% 1. Hostname (possibly derive domain and search) 38%% 2. OS default /etc/resolv.conf, Windows registry etc 39%% a) Hosts database 40%% b) Resolver options 41%% 3. Config (kernel app) 42%% 4. Root (otp root) 43%% 5. Home (user inetrc) 44%% 45%% 46-spec init() -> 'ok'. 47init() -> 48 set_hostname(), 49 50 %% Note: In shortnames (or non-distributed) mode we don't need to know 51 %% our own domain name. In longnames mode we do and we can't rely on 52 %% the user to provide it (by means of inetrc), so we need to look 53 %% for it ourselves. 54 55 OsType = os:type(), 56 do_load_resolv(OsType, erl_dist_mode()), 57 58 case OsType of 59 {unix,Type} -> 60 if Type =:= linux -> 61 %% It may be the case that the domain name was not set 62 %% because the hostname was short. But NOW we can look it 63 %% up and get the long name and the domain name from it. 64 65 %% FIXME: The second call to set_hostname will insert 66 %% a duplicate entry in the search list. 67 68 case inet_db:res_option(domain) of 69 "" -> 70 case inet:gethostbyname(inet_db:gethostname()) of 71 {ok,#hostent{h_name = []}} -> 72 ok; 73 {ok,#hostent{h_name = HostName}} -> 74 set_hostname({ok,HostName}); 75 _ -> 76 ok 77 end; 78 _ -> 79 ok 80 end; 81 true -> ok 82 end, 83 add_dns_lookup(inet_db:res_option(lookup)); 84 _ -> 85 ok 86 end, 87 88 %% Read inetrc file, if it exists. 89 {RcFile,CfgFiles,CfgList} = read_rc(), 90 91 %% Possibly read config files or system registry 92 lists:foreach(fun({file,hosts,File}) -> 93 load_hosts(File, unix); 94 ({file,Func,File}) -> 95 load_resolv(File, Func); 96 ({registry,win32}) -> 97 case OsType of 98 {win32,WinType} -> 99 win32_load_from_registry(WinType); 100 _ -> 101 error("cannot read win32 system registry~n", []) 102 end 103 end, CfgFiles), 104 105 %% Add inetrc config entries 106 case inet_db:add_rc_list(CfgList) of 107 ok -> ok; 108 _ -> error("syntax error in ~ts~n", [RcFile]) 109 end, 110 111 %% Set up a resolver configuration file for inet_res, 112 %% unless that already has been done 113 case OsType of 114 {unix,_} -> 115 %% The Etc variable enables us to run tests with other 116 %% configuration files than the normal ones 117 Etc = os:getenv("ERL_INET_ETC_DIR", ?DEFAULT_ETC), 118 case inet_db:res_option(resolv_conf) of 119 undefined -> 120 inet_db:res_option( 121 resolv_conf_name, 122 filename:join(Etc, ?DEFAULT_RESOLV)); 123 _ -> ok 124 end, 125 case inet_db:res_option(hosts_file) of 126 undefined -> 127 inet_db:res_option( 128 hosts_file_name, 129 filename:join(Etc, ?DEFAULT_HOSTS)); 130 _ -> ok 131 end; 132 _ -> ok 133 end. 134 135 136 137erl_dist_mode() -> 138 case init:get_argument(sname) of 139 {ok,[[_SName]]} -> shortnames; 140 _ -> 141 case init:get_argument(name) of 142 {ok,[[_Name]]} -> longnames; 143 _ -> nonames 144 end 145 end. 146 147do_load_resolv({unix,Type}, longnames) -> 148 %% The Etc variable enables us to run tests with other 149 %% configuration files than the normal ones 150 Etc = os:getenv("ERL_INET_ETC_DIR", ?DEFAULT_ETC), 151 load_resolv(filename:join(Etc, ?DEFAULT_RESOLV), resolv), 152 case Type of 153 freebsd -> %% we may have to check version (2.2.2) 154 load_resolv(filename:join(Etc,"host.conf"), host_conf_freebsd); 155 'bsd/os' -> 156 load_resolv(filename:join(Etc,"irs.conf"), host_conf_bsdos); 157 sunos -> 158 case os:version() of 159 {Major,_,_} when Major >= 5 -> 160 load_resolv(filename:join(Etc,"nsswitch.conf"), 161 nsswitch_conf); 162 _ -> 163 ok 164 end; 165 netbsd -> 166 case os:version() of 167 {Major,Minor,_} when Major >= 1, Minor >= 4 -> 168 load_resolv(filename:join(Etc,"nsswitch.conf"), 169 nsswitch_conf); 170 _ -> 171 ok 172 end; 173 linux -> 174 case load_resolv(filename:join(Etc,"host.conf"), 175 host_conf_linux) of 176 ok -> 177 ok; 178 _ -> 179 load_resolv(filename:join(Etc,"nsswitch.conf"), 180 nsswitch_conf) 181 end; 182 _ -> 183 ok 184 end, 185 inet_db:set_lookup([native]); 186 187do_load_resolv({win32,Type}, longnames) -> 188 win32_load_from_registry(Type), 189 inet_db:set_lookup([native]); 190 191do_load_resolv(_, _) -> 192 inet_db:set_lookup([native]). 193 194add_dns_lookup(L) -> 195 case lists:member(dns,L) of 196 true -> ok; 197 _ -> 198 case application:get_env(kernel,inet_dns_when_nis) of 199 {ok,true} -> 200 add_dns_lookup(L,[]); 201 _ -> 202 ok 203 end 204 end. 205 206add_dns_lookup([yp|T],Acc) -> 207 add_dns_lookup(T,[yp,dns|Acc]); 208add_dns_lookup([H|T],Acc) -> 209 add_dns_lookup(T,[H|Acc]); 210add_dns_lookup([],Acc) -> 211 inet_db:set_lookup(reverse(Acc)). 212 213%% 214%% Set the hostname (SHORT) 215%% If hostname is long use the suffix as default domain 216%% and initalize the search option with the parts of domain 217%% 218set_hostname() -> 219 case inet_udp:open(0,[]) of 220 {ok,U} -> 221 Res = inet:gethostname(U), 222 inet_udp:close(U), 223 set_hostname(Res); 224 _ -> 225 set_hostname({ok, []}) 226 end. 227 228set_hostname({ok,Name}) when length(Name) > 0 -> 229 {Host, Domain} = lists:splitwith(fun($.) -> false; 230 (_) -> true 231 end, Name), 232 inet_db:set_hostname(Host), 233 set_search_dom(Domain); 234set_hostname({ok,[]}) -> 235 inet_db:set_hostname("nohost"), 236 set_search_dom("nodomain"). 237 238set_search_dom([$.|Domain]) -> 239 %% leading . not removed by dropwhile above. 240 inet_db:set_domain(Domain), 241 inet_db:ins_search(Domain), 242 ok; 243set_search_dom([]) -> 244 ok; 245set_search_dom(Domain) -> 246 inet_db:set_domain(Domain), 247 inet_db:ins_search(Domain), 248 ok. 249 250%% 251%% Load resolver data 252%% 253load_resolv(File, Func) -> 254 case get_file(File) of 255 {ok,Bin} -> 256 case inet_parse:Func(File, {chars, Bin}) of 257 {ok, Ls} -> 258 inet_db:add_rc_list(Ls); 259 {error, Reason} -> 260 error("parse error in file ~ts: ~p", [File, Reason]) 261 end; 262 Error -> 263 warning("file not found ~ts: ~p~n", [File, Error]) 264 end. 265 266%% 267%% Load a UNIX hosts file 268%% 269load_hosts(File,Os) -> 270 case get_file(File) of 271 {ok,Bin} -> 272 case inet_parse:hosts(File,{chars,Bin}) of 273 {ok, Ls} -> 274 foreach( 275 fun({IP, Name, Aliases}) -> 276 inet_db:add_host(IP, [Name|Aliases]) end, 277 Ls); 278 {error, Reason} -> 279 error("parse error in file ~ts: ~p", [File, Reason]) 280 end; 281 Error -> 282 case Os of 283 unix -> 284 error("file not found ~ts: ~p~n", [File, Error]); 285 _ -> 286 %% for windows or nt the hosts file is not always there 287 %% and we don't require it 288 ok 289 end 290 end. 291 292%% 293%% Load resolver data from Windows registry 294%% 295win32_load_from_registry(Type) -> 296 %% The TcpReg variable enables us to run tests with other registry configurations than 297 %% the normal ones 298 TcpReg = os:getenv("ERL_INET_ETC_DIR", ""), 299 {ok, Reg} = win32reg:open([read]), 300 {TcpIp,HFileKey} = 301 case Type of 302 nt -> 303 case TcpReg of 304 [] -> 305 {"\\hklm\\system\\CurrentControlSet\\Services\\TcpIp\\Parameters", 306 "DataBasePath" }; 307 Other -> 308 {Other,"DataBasePath"} 309 end; 310 windows -> 311 case TcpReg of 312 [] -> 313 {"\\hklm\\system\\CurrentControlSet\\Services\\VxD\\MSTCP", 314 "LMHostFile" }; 315 Other -> 316 {Other,"LMHostFile"} 317 end 318 end, 319 Result = 320 case win32reg:change_key(Reg,TcpIp) of 321 ok -> 322 win32_load1(Reg,Type,HFileKey); 323 {error, _Reason} -> 324 error("Failed to locate TCP/IP parameters (is TCP/IP installed)?", 325 []) 326 end, 327 win32reg:close(Reg), 328 Result. 329 330win32_load1(Reg,Type,HFileKey) -> 331 Names = [HFileKey, "Domain", "DhcpDomain", 332 "EnableDNS", "NameServer", "SearchList"], 333 case win32_get_strings(Reg, Names) of 334 [DBPath0, Domain, DhcpDomain, 335 _EnableDNS, NameServers0, Search] -> 336 inet_db:set_domain( 337 case Domain of "" -> DhcpDomain; _ -> Domain end), 338 NameServers = win32_split_line(NameServers0,Type), 339 AddNs = fun(Addr) -> 340 case inet_parse:address(Addr) of 341 {ok, Address} -> 342 inet_db:add_ns(Address); 343 {error, _} -> 344 error("Bad TCP/IP address in registry", []) 345 end 346 end, 347 foreach(AddNs, NameServers), 348 Searches0 = win32_split_line(Search,Type), 349 Searches = case member(Domain, Searches0) of 350 true -> Searches0; 351 false -> [Domain|Searches0] 352 end, 353 foreach(fun(D) -> inet_db:add_search(D) end, Searches), 354 if Type =:= nt -> 355 DBPath = win32reg:expand(DBPath0), 356 load_hosts(filename:join(DBPath, "hosts"),nt); 357 Type =:= windows -> 358 load_hosts(filename:join(DBPath0,""),windows) 359 end, 360%% Maybe activate this later as an optimization 361%% For now we use native always as the SAFE way 362%% case NameServers of 363%% [] -> inet_db:set_lookup([native, file]); 364%% _ -> inet_db:set_lookup([dns, file, native]) 365%% end; 366 true; 367 {error, _Reason} -> 368 error("Failed to read TCP/IP parameters from registry", []) 369 end. 370 371win32_split_line(Line,nt) -> inet_parse:split_line(Line); 372win32_split_line(Line,windows) -> string:lexemes(Line, ","). 373 374win32_get_strings(Reg, Names) -> 375 win32_get_strings(Reg, Names, []). 376 377win32_get_strings(Reg, [Name|Rest], Result) -> 378 case win32reg:value(Reg, Name) of 379 {ok, Value} when is_list(Value) -> 380 win32_get_strings(Reg, Rest, [Value|Result]); 381 {ok, _NotString} -> 382 {error, not_string}; 383 {error, _Reason} -> 384 win32_get_strings(Reg, Rest, [""|Result]) 385 end; 386win32_get_strings(_, [], Result) -> 387 lists:reverse(Result). 388 389read_rc() -> 390 {RcFile,CfgList} = read_inetrc(), 391 case extract_cfg_files(CfgList, [], []) of 392 {CfgFiles,CfgList1} -> 393 {RcFile,CfgFiles,CfgList1}; 394 error -> 395 {error,[],[]} 396 end. 397 398 399 400extract_cfg_files([E = {file,Type,_File} | Es], CfgFiles, CfgList) -> 401 extract_cfg_files1(Type, E, Es, CfgFiles, CfgList); 402extract_cfg_files([E = {registry,Type} | Es], CfgFiles, CfgList) -> 403 extract_cfg_files1(Type, E, Es, CfgFiles, CfgList); 404extract_cfg_files([E | Es], CfgFiles, CfgList) -> 405 extract_cfg_files(Es, CfgFiles, [E | CfgList]); 406extract_cfg_files([], CfgFiles, CfgList) -> 407 {reverse(CfgFiles),reverse(CfgList)}. 408 409extract_cfg_files1(Type, E, Es, CfgFiles, CfgList) -> 410 case valid_type(Type) of 411 true -> 412 extract_cfg_files(Es, [E | CfgFiles], CfgList); 413 false -> 414 error("invalid config value ~w in inetrc~n", [Type]), 415 error 416 end. 417 418valid_type(resolv) -> true; 419valid_type(host_conf_freebsd) -> true; 420valid_type(host_conf_bsdos) -> true; 421valid_type(host_conf_linux) -> true; 422valid_type(nsswitch_conf) -> true; 423valid_type(hosts) -> true; 424valid_type(win32) -> true; 425valid_type(_) -> false. 426 427read_inetrc() -> 428 case application:get_env(inetrc) of 429 {ok,File} -> 430 try_get_rc(File); 431 _ -> 432 case os:getenv("ERL_INETRC") of 433 false -> 434 {nofile,[]}; 435 File -> 436 try_get_rc(File) 437 end 438 end. 439 440try_get_rc(File) -> 441 case get_rc(File) of 442 error -> {nofile,[]}; 443 Ls -> {File,Ls} 444 end. 445 446get_rc(File) -> 447 case get_file(File) of 448 {ok,Bin} -> 449 case parse_inetrc(Bin) of 450 {ok,Ls} -> 451 Ls; 452 _Error -> 453 error("parse error in ~ts~n", [File]), 454 error 455 end; 456 _Error -> 457 error("file ~ts not found~n", [File]), 458 error 459 end. 460 461%% XXX Check if we really need to prim load the stuff 462get_file(File) -> 463 case erl_prim_loader:get_file(File) of 464 {ok,Bin,_} -> {ok,Bin}; 465 Error -> Error 466 end. 467 468error(Fmt, Args) -> 469 error_logger:error_msg("inet_config: " ++ Fmt, Args). 470 471warning(Fmt, Args) -> 472 case application:get_env(kernel,inet_warnings) of 473 %{ok,silent} -> ok; 474 {ok,on} -> 475 error_logger:info_msg("inet_config:" ++ Fmt, Args); 476 _ -> 477 ok 478 end. 479 480%% 481%% Parse inetrc, i.e. make a binary of a term list. 482%% The extra newline is to let the user ignore the whitespace !!! 483%% Ignore leading whitespace before a token (due to bug in erl_scan) ! 484%% 485parse_inetrc(Bin) -> 486 case file_binary_to_list(Bin) of 487 {ok, String} -> 488 parse_inetrc(String ++ "\n", 1, []); 489 error -> 490 {error, 'bad_encoding'} 491 end. 492 493parse_inetrc_skip_line([], _Line, Ack) -> 494 {ok, reverse(Ack)}; 495parse_inetrc_skip_line([$\n|Str], Line, Ack) -> 496 parse_inetrc(Str, Line+1, Ack); 497parse_inetrc_skip_line([_|Str], Line, Ack) -> 498 parse_inetrc_skip_line(Str, Line, Ack). 499 500parse_inetrc([$%|Str], Line, Ack) -> 501 parse_inetrc_skip_line(Str, Line, Ack); 502parse_inetrc([$\s|Str], Line, Ack) -> 503 parse_inetrc(Str, Line, Ack); 504parse_inetrc([$\n |Str], Line, Ack) -> 505 parse_inetrc(Str, Line+1, Ack); 506parse_inetrc([$\t|Str], Line, Ack) -> 507 parse_inetrc(Str, Line, Ack); 508parse_inetrc([], _, Ack) -> 509 {ok, reverse(Ack)}; 510 511 512%% The clauses above are here due to a bug in erl_scan (OTP-1449). 513 514parse_inetrc(Str, Line, Ack) -> 515 case erl_scan:tokens([], Str, Line) of 516 {done, {ok, Tokens, EndLine}, MoreChars} -> 517 case erl_parse:parse_term(Tokens) of 518 {ok, Term} -> 519 parse_inetrc(MoreChars, EndLine, [Term|Ack]); 520 Error -> 521 {error, {'parse_inetrc', Error}} 522 end; 523 {done, {eof, _}, _} -> 524 {ok, reverse(Ack)}; 525 {done, Error, _} -> 526 {error, {'scan_inetrc', Error}}; 527 {more, _} -> %% Bug in erl_scan !! 528 {error, {'scan_inetrc', {eof, Line}}} 529 end. 530 531file_binary_to_list(Bin) -> 532 Enc = case epp:read_encoding_from_binary(Bin) of 533 none -> epp:default_encoding(); 534 Encoding -> Encoding 535 end, 536 case catch unicode:characters_to_list(Bin, Enc) of 537 String when is_list(String) -> 538 {ok, String}; 539 _ -> 540 error 541 end. 542 543