1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2017. 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_parse). 21 22%% Parser for all kinds of ineternet configuration files 23 24%% Avoid warning for local function error/2 clashing with autoimported BIF. 25-compile({no_auto_import,[error/2]}). 26-export([hosts/1, hosts/2]). 27-export([protocols/1, protocols/2]). 28-export([netmasks/1, netmasks/2]). 29-export([networks/1, networks/2]). 30-export([services/1, services/2]). 31-export([rpc/1, rpc/2]). 32-export([resolv/1, resolv/2]). 33-export([host_conf_linux/1, host_conf_linux/2]). 34-export([host_conf_freebsd/1, host_conf_freebsd/2]). 35-export([host_conf_bsdos/1, host_conf_bsdos/2]). 36-export([nsswitch_conf/1, nsswitch_conf/2]). 37 38-export([ipv4_address/1, ipv6_address/1]). 39-export([ipv4strict_address/1, ipv6strict_address/1]). 40-export([address/1, strict_address/1]). 41-export([visible_string/1, domain/1]). 42-export([ntoa/1, dots/1]). 43-export([split_line/1]). 44 45-import(lists, [reverse/1]). 46 47-include_lib("kernel/include/file.hrl"). 48 49%% -------------------------------------------------------------------------- 50%% Parse services internet style 51%% Syntax: 52%% Name Port/Protocol [Aliases] \n 53%% # comment 54%% -------------------------------------------------------------------------- 55 56services(File) -> 57 services(noname, File). 58 59services(Fname, File) -> 60 Fn = fun([Name, PortProto | Aliases]) -> 61 {Proto,Port} = port_proto(PortProto, 0), 62 {Name,Proto,Port,Aliases} 63 end, 64 parse_file(Fname, File, Fn). 65 66%% -------------------------------------------------------------------------- 67%% Parse rpc program names 68%% Syntax: 69%% Name Program [Aliases] \n | 70%% # comment 71%% -------------------------------------------------------------------------- 72 73rpc(File) -> 74 rpc(noname, File). 75 76rpc(Fname, File) -> 77 Fn = fun([Name,Program | Aliases]) -> 78 Prog = list_to_integer(Program), 79 {Name,Prog,Aliases} 80 end, 81 parse_file(Fname, File, Fn). 82 83%% -------------------------------------------------------------------------- 84%% Parse hosts file unix style 85%% Syntax: 86%% IP Name [Aliases] \n | 87%% # comment 88%% -------------------------------------------------------------------------- 89hosts(File) -> 90 hosts(noname,File). 91 92hosts(Fname,File) -> 93 Fn = fun([Address, Name | Aliases]) -> 94 %% XXX Fix for link-local IPv6 addresses that specify 95 %% interface with a %if suffix. These kind of 96 %% addresses maybe need to be gracefully handled 97 %% throughout inet* and inet_drv. 98 case string:lexemes(Address, "%") of 99 [Addr,_] -> 100 {ok,_} = address(Addr), 101 skip; 102 _ -> 103 {ok,IP} = address(Address), 104 {IP, Name, Aliases} 105 end 106 end, 107 parse_file(Fname, File, Fn). 108 109%% -------------------------------------------------------------------------- 110%% Parse resolv file unix style 111%% Syntax: 112%% domain Domain \n 113%% nameserver IP \n 114%% search Dom1 Dom2 ... \n 115%% lookup Method1 Method2 Method3 \n 116%% # comment 117%% -------------------------------------------------------------------------- 118 119resolv(File) -> 120 resolv(noname,File). 121 122resolv(Fname, File) -> 123 Fn = fun(["domain", Domain]) -> 124 {domain, Domain}; 125 (["nameserver", Address]) -> 126 {ok,IP} = address(Address), 127 {nameserver,IP}; 128 (["search" | List]) -> 129 {search, List}; 130 (["lookup" | Types]) -> 131 {lookup, Types}; 132 (_) -> 133 skip %% there are too many local options, we MUST skip 134 end, 135 parse_file(Fname, File, Fn). 136 137%% -------------------------------------------------------------------------- 138%% 139%% Parse Linux host.conf file 140%% find "order" only. 141%% 142%% -------------------------------------------------------------------------- 143host_conf_linux(File) -> 144 host_conf_linux(noname,File). 145 146host_conf_linux(Fname, File) -> 147 Fn = fun(["order" | Order]) -> 148 %% XXX remove ',' between entries 149 {lookup, split_comma(Order)}; 150 (_) -> 151 skip 152 end, 153 parse_file(Fname, File, Fn). 154 155%% -------------------------------------------------------------------------- 156%% 157%% Parse Freebsd/Netbsd host.conf file 158%% find "order" only. 159%% 160%% -------------------------------------------------------------------------- 161host_conf_freebsd(File) -> 162 host_conf_freebsd(noname,File). 163 164host_conf_freebsd(Fname, File) -> 165 Fn = fun([Type]) -> Type end, 166 case parse_file(Fname, File, Fn) of 167 {ok, Ls} -> {ok, [{lookup, Ls}]}; 168 Error -> Error 169 end. 170 171 172 173%% -------------------------------------------------------------------------- 174%% 175%% Parse BSD/OS irs.conf file 176%% find "hosts" only and ignore options. 177%% 178%% Syntax: 179%% Map AccessMethod [,AccessMethod] [continue|merge [,merge|,continue]] \n 180%% # comment 181 182%% -------------------------------------------------------------------------- 183host_conf_bsdos(File) -> 184 host_conf_bsdos(noname,File). 185 186host_conf_bsdos(Fname, File) -> 187 Fn = fun(["hosts" | List]) -> 188 delete_options(split_comma(List)); 189 (_) -> 190 skip 191 end, 192 case parse_file(Fname, File, Fn) of 193 {ok, Ls} -> 194 {ok, [{lookup, lists:append(Ls)}]}; 195 Error -> Error 196 end. 197 198delete_options(["continue"|T]) -> 199 delete_options(T); 200delete_options(["merge"|T]) -> 201 delete_options(T); 202delete_options([H|T]) -> 203 [H|delete_options(T)]; 204delete_options([]) -> 205 []. 206 207 208%% -------------------------------------------------------------------------- 209%% 210%% Parse Solaris nsswitch.conf 211%% find "hosts:" only 212%% 213%% -------------------------------------------------------------------------- 214 215nsswitch_conf(File) -> 216 nsswitch_conf(noname,File). 217 218nsswitch_conf(Fname, File) -> 219 Fn = fun(["hosts:" | Types]) -> 220 {lookup, Types}; 221 (_) -> skip 222 end, 223 parse_file(Fname, File, Fn). 224 225%% -------------------------------------------------------------------------- 226%% Parse protocol file unix style 227%% Syntax: 228%% name protocol number name \n 229%% # comment 230%% -------------------------------------------------------------------------- 231 232protocols(File) -> 233 protocols(noname,File). 234 235protocols(Fname, File) -> 236 Fn = fun([Name, Number, DName]) -> 237 {list_to_atom(Name), list_to_integer(Number), DName} 238 end, 239 parse_file(Fname, File, Fn). 240 241%% -------------------------------------------------------------------------- 242%% Parse netmasks file unix style 243%% Syntax: 244%% Network Subnetmask 245%% # comment 246%% -------------------------------------------------------------------------- 247 248netmasks(File) -> 249 netmasks(noname, File). 250 251netmasks(Fname, File) -> 252 Fn = fun([Net, Subnetmask]) -> 253 {ok, NetIP} = address(Net), 254 {ok, Mask} = address(Subnetmask), 255 {NetIP, Mask} 256 end, 257 parse_file(Fname, File, Fn). 258 259%% -------------------------------------------------------------------------- 260%% Parse networks file unix style 261%% Syntax: 262%% network-name network-number aliases ... 263%% # comment 264%% -------------------------------------------------------------------------- 265 266networks(File) -> 267 networks(noname, File). 268 269networks(Fname, File) -> 270 Fn = fun([NetName, NetNumber]) -> 271 Number = list_to_integer(NetNumber), 272 {NetName, Number} 273 end, 274 parse_file(Fname, File, Fn). 275 276%% -------------------------------------------------------------------------- 277%% 278%% Simple Line by Line parser 279%% 280%% -------------------------------------------------------------------------- 281 282parse_file(Fname, {fd,Fd}, Fn) -> 283 parse_fd(Fname,Fd, 1, Fn, []); 284parse_file(Fname, {chars,Cs}, Fn) when is_list(Cs) -> 285 parse_cs(Fname, Cs, 1, Fn, []); 286parse_file(Fname, {chars,Cs}, Fn) when is_binary(Cs) -> 287 parse_cs(Fname, binary_to_list(Cs), 1, Fn, []); 288parse_file(_, File, Fn) -> 289 case file:open(File, [read]) of 290 {ok, Fd} -> 291 Result = parse_fd(File,Fd, 1, Fn, []), 292 _ = file:close(Fd), 293 Result; 294 Error -> Error 295 end. 296 297parse_fd(Fname,Fd, Line, Fun, Ls) -> 298 case read_line(Fd) of 299 eof -> {ok, reverse(Ls)}; 300 Cs -> 301 case split_line(Cs) of 302 [] -> parse_fd(Fname, Fd, Line+1, Fun, Ls); 303 Toks -> 304 case catch Fun(Toks) of 305 {'EXIT',_} -> 306 error("~p:~p: erroneous line, SKIPPED~n",[Fname,Line]), 307 parse_fd(Fname, Fd,Line+1,Fun,Ls); 308 {warning,Wlist,Val} -> 309 warning("~p:~p: warning! strange domain name(s) ~p ~n",[Fname,Line,Wlist]), 310 parse_fd(Fname, Fd,Line+1,Fun,[Val|Ls]); 311 312 skip -> 313 parse_fd(Fname, Fd, Line+1, Fun, Ls); 314 Val -> parse_fd(Fname, Fd, Line+1, Fun, [Val|Ls]) 315 end 316 end 317 end. 318 319parse_cs(Fname, Chars, Line, Fun, Ls) -> 320 case get_line(Chars) of 321 eof -> {ok, reverse(Ls)}; 322 {Cs,Chars1} -> 323 case split_line(Cs) of 324 [] -> parse_cs(Fname, Chars1, Line+1, Fun, Ls); 325 Toks -> 326 case catch Fun(Toks) of 327 {'EXIT',_} -> 328 error("~p:~p: erroneous line, SKIPPED~n",[Fname,Line]), 329 parse_cs(Fname, Chars1, Line+1, Fun, Ls); 330 {warning,Wlist,Val} -> 331 warning("~p:~p: warning! strange domain name(s) ~p ~n",[Fname,Line,Wlist]), 332 parse_cs(Fname, Chars1, Line+1, Fun, [Val|Ls]); 333 334 skip -> parse_cs(Fname, Chars1, Line+1, Fun, Ls); 335 Val -> parse_cs(Fname, Chars1, Line+1, Fun, [Val|Ls]) 336 end 337 end 338 end. 339 340get_line([]) -> eof; 341get_line(Chars) -> get_line(Chars,[]). 342 343get_line([], Acc) -> {reverse(Acc), []}; 344get_line([$\r, $\n | Cs], Acc) -> {reverse([$\n|Acc]), Cs}; 345get_line([$\n | Cs], Acc) -> {reverse([$\n|Acc]), Cs}; 346get_line([C | Cs], Acc) -> get_line(Cs, [C|Acc]). 347 348%% 349%% Read a line 350%% 351read_line(Fd) when is_pid(Fd) -> io:get_line(Fd, ''); 352read_line(Fd = #file_descriptor{}) -> 353 collect_line(Fd, []). 354 355collect_line(Fd, Cs) -> 356 case file:read(Fd, 80) of 357 {ok, Line} when is_binary(Line) -> 358 collect_line(Fd, byte_size(Line), binary_to_list(Line), Cs); 359 {ok, Line} -> 360 collect_line(Fd, length(Line), Line, Cs); 361 eof when Cs =:= [] -> 362 eof; 363 eof -> reverse(Cs) 364 end. 365 366collect_line(Fd, N, [$\r, $\n|_], Cs) -> 367 {ok, _} = file:position(Fd, {cur,-(N-2)}), 368 reverse([$\n|Cs]); 369collect_line(Fd, N, [$\n|_], Cs) -> 370 {ok, _} = file:position(Fd, {cur,-(N-1)}), 371 reverse([$\n|Cs]); 372collect_line(Fd, _, [], Cs) -> 373 collect_line(Fd, Cs); 374collect_line(Fd, N, [X|Xs], Cs) -> 375 collect_line(Fd, N-1, Xs, [X|Cs]). 376 377 378%% split Port/Proto -> {Port, Proto} 379port_proto([X|Xs], N) when X >= $0, X =< $9 -> 380 port_proto(Xs, N*10 + (X - $0)); 381port_proto([$/ | Proto], Port) when Port =/= 0 -> 382 {list_to_atom(Proto), Port}. 383 384%% 385%% Check if a String is a string with visible characters #21..#7E 386%% visible_string(String) -> Bool 387%% 388visible_string([H|T]) -> 389 is_vis1([H|T]); 390visible_string(_) -> 391 false. 392 393is_vis1([C | Cs]) when C >= 16#21, C =< 16#7e -> is_vis1(Cs); 394is_vis1([]) -> true; 395is_vis1(_) -> false. 396 397%% 398%% Check if a String is a domain name according to RFC XXX. 399%% domain(String) -> Bool 400%% 401domain([H|T]) -> 402 is_dom1([H|T]); 403domain(_) -> 404 false. 405 406is_dom1([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs); 407is_dom1([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs); 408is_dom1([C | Cs]) when C >= $0, C =< $9 -> 409 case is_dom_ldh(Cs) of 410 true -> is_dom2(string:lexemes([C | Cs],".")); 411 false -> false 412 end; 413is_dom1(_) -> false. 414 415is_dom_ldh([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs); 416is_dom_ldh([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs); 417is_dom_ldh([C | Cs]) when C >= $0, C =< $9 -> is_dom_ldh(Cs); 418is_dom_ldh([$-,$. | _]) -> false; 419is_dom_ldh([$_,$. | _]) -> false; 420is_dom_ldh([$_ | Cs]) -> is_dom_ldh(Cs); 421is_dom_ldh([$- | Cs]) -> is_dom_ldh(Cs); 422is_dom_ldh([$. | Cs]) -> is_dom1(Cs); 423is_dom_ldh([]) -> true; 424is_dom_ldh(_) -> false. 425 426%%% Check that we don't get a IP-address as a domain name. 427 428-define(L2I(L), (catch list_to_integer(L))). 429 430is_dom2([A,B,C,D]) -> 431 case ?L2I(D) of 432 Di when is_integer(Di) -> 433 case {?L2I(A),?L2I(B),?L2I(C)} of 434 {Ai,Bi,Ci} when is_integer(Ai), 435 is_integer(Bi), 436 is_integer(Ci) -> false; 437 _ -> true 438 end; 439 _ -> true 440 end; 441is_dom2(_) -> 442 true. 443 444 445 446%% 447%% Parse ipv4 address or ipv6 address 448%% Return {ok, Address} | {error, Reason} 449%% 450address(Cs) when is_list(Cs) -> 451 case ipv4_address(Cs) of 452 {ok,IP} -> 453 {ok,IP}; 454 _ -> 455 ipv6strict_address(Cs) 456 end; 457address(_) -> 458 {error, einval}. 459 460%%Parse ipv4 strict address or ipv6 strict address 461strict_address(Cs) when is_list(Cs) -> 462 case ipv4strict_address(Cs) of 463 {ok,IP} -> 464 {ok,IP}; 465 _ -> 466 ipv6strict_address(Cs) 467 end; 468strict_address(_) -> 469 {error, einval}. 470 471%% 472%% Parse IPv4 address: 473%% d1.d2.d3.d4 474%% d1.d2.d4 475%% d1.d4 476%% d4 477%% Any d may be octal, hexadecimal or decimal by C language standards. 478%% d4 fills all LSB bytes. This is legacy behaviour from Solaris 479%% and FreeBSD. And partly Linux that behave the same except 480%% it does not accept hexadecimal. 481%% 482%% Return {ok, IP} | {error, einval} 483%% 484ipv4_address(Cs) -> 485 try ipv4_addr(Cs) of 486 Addr -> 487 {ok,Addr} 488 catch 489 error:badarg -> 490 {error,einval} 491 end. 492 493ipv4_addr(Cs) -> 494 case ipv4_addr(Cs, []) of 495 [D] when D < (1 bsl 32) -> 496 <<D1,D2,D3,D4>> = <<D:32>>, 497 {D1,D2,D3,D4}; 498 [D,D1] when D < (1 bsl 24), D1 < 256 -> 499 <<D2,D3,D4>> = <<D:24>>, 500 {D1,D2,D3,D4}; 501 [D,D2,D1] when D < (1 bsl 16), (D2 bor D1) < 256 -> 502 <<D3,D4>> = <<D:16>>, 503 {D1,D2,D3,D4}; 504 [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> 505 {D1,D2,D3,D4}; 506 _ -> 507 erlang:error(badarg) 508 end. 509 510ipv4_addr([_|_], [_,_,_,_]) -> 511 %% Early bailout for extra characters 512 erlang:error(badarg); 513ipv4_addr("0x"++Cs, Ds) -> 514 ipv4_addr(strip0(Cs), Ds, [], 16, 8); 515ipv4_addr("0X"++Cs, Ds) -> 516 ipv4_addr(strip0(Cs), Ds, [], 16, 8); 517ipv4_addr("0"++Cs, Ds) -> 518 ipv4_addr(strip0(Cs), Ds, [$0], 8, 11); 519ipv4_addr(Cs, Ds) when is_list(Cs) -> 520 ipv4_addr(Cs, Ds, [], 10, 10). 521 522ipv4_addr(Cs0, Ds, Rs, Base, N) -> 523 case ipv4_field(Cs0, N, Rs, Base) of 524 {D,""} -> 525 [D|Ds]; 526 {D,[$.|[_|_]=Cs]} -> 527 ipv4_addr(Cs, [D|Ds]); 528 {_,_} -> 529 erlang:error(badarg) 530 end. 531 532strip0("0"++Cs) -> 533 strip0(Cs); 534strip0(Cs) when is_list(Cs) -> 535 Cs. 536 537 538%% 539%% Parse IPv4 strict dotted decimal address, no leading zeros: 540%% d1.d2.d3.d4 541%% 542%% Return {ok, IP} | {error, einval} 543%% 544ipv4strict_address(Cs) -> 545 try ipv4strict_addr(Cs) of 546 Addr -> 547 {ok,Addr} 548 catch 549 error:badarg -> 550 {error,einval} 551 end. 552 553ipv4strict_addr(Cs) -> 554 case ipv4strict_addr(Cs, []) of 555 [D4,D3,D2,D1] when (D4 bor D3 bor D2 bor D1) < 256 -> 556 {D1,D2,D3,D4}; 557 _ -> 558 erlang:error(badarg) 559 end. 560 561ipv4strict_addr([_|_], [_,_,_,_]) -> 562 %% Early bailout for extra characters 563 erlang:error(badarg); 564ipv4strict_addr("0", Ds) -> 565 [0|Ds]; 566ipv4strict_addr("0."++Cs, Ds) -> 567 ipv4strict_addr(Cs, [0|Ds]); 568ipv4strict_addr(Cs0, Ds) when is_list(Cs0) -> 569 case ipv4_field(Cs0, 3, [], 10) of 570 {D,""} -> 571 [D|Ds]; 572 {D,[$.|[_|_]=Cs]} -> 573 ipv4strict_addr(Cs, [D|Ds]); 574 {_,_} -> 575 erlang:error(badarg) 576 end. 577 578 579 580ipv4_field("", _, Rs, Base) -> 581 {ipv4_field(Rs, Base),""}; 582ipv4_field("."++_=Cs, _, Rs, Base) -> 583 {ipv4_field(Rs, Base),Cs}; 584ipv4_field("0"++_, _, [], _) -> 585 erlang:error(badarg); 586ipv4_field([C|Cs], N, Rs, Base) when N > 0 -> 587 ipv4_field(Cs, N-1, [C|Rs], Base); 588ipv4_field(Cs, _, _, _) when is_list(Cs) -> 589 erlang:error(badarg). 590 591ipv4_field(Rs, Base) -> 592 V = erlang:list_to_integer(lists:reverse(Rs), Base), 593 if V < 0 -> 594 erlang:error(badarg); 595 true -> 596 V 597 end. 598 599 600 601%% 602%% Forgiving IPv6 address 603%% 604%% Accepts IPv4 address and returns it as a IPv4 compatible IPv6 address 605%% 606ipv6_address(Cs) -> 607 case ipv4_address(Cs) of 608 {ok,{D1,D2,D3,D4}} -> 609 {ok,{0,0,0,0,0,16#ffff,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4}}; 610 _ -> 611 ipv6strict_address(Cs) 612 end. 613 614%% 615%% Parse IPv6 address according to RFC 4291: 616%% x1:x2:x3:x4:x5:x6:x7:x8 617%% x1:x2::x7:x8 618%% ::x7:x8 619%% x1:x2:: 620%% :: 621%% x1:x2:x3:x4:x5:x6:d7a.d7b.d8a.d8b 622%% x1:x2::x5:x6:d7a.d7b.d8a.d8b 623%% ::x5:x6:d7a.d7b.d8a.d8b 624%% x1:x2::d7a.d7b.d8a.d8b 625%% ::d7a.d7b.d8a.d8b 626%% etc 627%% 628%% Return {ok, IP} | {error, einval} 629%% 630ipv6strict_address(Cs) -> 631 try ipv6_addr(Cs) of 632 Addr -> 633 {ok,Addr} 634 catch 635 error:badarg -> 636 {error,einval} 637 end. 638 639ipv6_addr("::") -> 640 ipv6_addr_done([], [], 0); 641ipv6_addr("::"++Cs) -> 642 ipv6_addr(hex(Cs), [], [], 0); 643ipv6_addr(Cs) -> 644 ipv6_addr(hex(Cs), [], 0). 645 646%% Before "::" 647ipv6_addr({Cs0,"%"++Cs1}, A, N) when N == 7 -> 648 ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []); 649ipv6_addr({Cs0,[]}, A, N) when N == 7 -> 650 ipv6_addr_done([hex_to_int(Cs0)|A]); 651ipv6_addr({Cs0,"::%"++Cs1}, A, N) when N =< 6 -> 652 ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []); 653ipv6_addr({Cs0,"::"}, A, N) when N =< 6 -> 654 ipv6_addr_done([hex_to_int(Cs0)|A], [], N+1); 655ipv6_addr({Cs0,"::"++Cs1}, A, N) when N =< 5 -> 656 ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], [], N+1); 657ipv6_addr({Cs0,":"++Cs1}, A, N) when N =< 6 -> 658 ipv6_addr(hex(Cs1), [hex_to_int(Cs0)|A], N+1); 659ipv6_addr({Cs0,"."++_=Cs1}, A, N) when N == 6 -> 660 ipv6_addr_done(A, [], N, ipv4strict_addr(Cs0++Cs1)); 661ipv6_addr(_, _, _) -> 662 erlang:error(badarg). 663 664%% After "::" 665ipv6_addr({Cs0,"%"++Cs1}, A, B, N) when N =< 6 -> 666 ipv6_addr_scope(Cs1, A, [hex_to_int(Cs0)|B], N+1, []); 667ipv6_addr({Cs0,[]}, A, B, N) when N =< 6 -> 668 ipv6_addr_done(A, [hex_to_int(Cs0)|B], N+1); 669ipv6_addr({Cs0,":"++Cs1}, A, B, N) when N =< 5 -> 670 ipv6_addr(hex(Cs1), A, [hex_to_int(Cs0)|B], N+1); 671ipv6_addr({Cs0,"."++_=Cs1}, A, B, N) when N =< 5 -> 672 ipv6_addr_done(A, B, N, ipv4strict_addr(Cs0++Cs1)); 673ipv6_addr(_, _, _, _) -> 674 erlang:error(badarg). 675 676%% After "%" 677ipv6_addr_scope([], Ar, Br, N, Sr) -> 678 ScopeId = 679 case lists:reverse(Sr) of 680 %% Empty scope id 681 "" -> 0; 682 %% Scope id starts with 0 683 "0"++S -> dec16(S); 684 _ -> 0 685 end, 686 %% Suggested formats for scope id parsing: 687 %% "" -> "0" 688 %% "0" -> Scope id 0 689 %% "1" - "9", "10" - "99" -> "0"++S 690 %% "0"++DecimalScopeId -> decimal scope id 691 %% "25"++PercentEncoded -> Percent encoded interface name 692 %% S -> Interface name (Unicode?) 693 %% Missing: translation from interface name into integer scope id. 694 %% XXX: scope id is actually 32 bit, but we only have room for 695 %% 16 bit in the second address word - ignore or fix (how)? 696 ipv6_addr_scope(ScopeId, Ar, Br, N); 697ipv6_addr_scope([C|Cs], Ar, Br, N, Sr) -> 698 ipv6_addr_scope(Cs, Ar, Br, N, [C|Sr]). 699%% 700ipv6_addr_scope(ScopeId, [P], Br, N) 701 when N =< 7, P =:= 16#fe80; 702 N =< 7, P =:= 16#ff02 -> 703 %% Optimized special case 704 ipv6_addr_done([ScopeId,P], Br, N+1); 705ipv6_addr_scope(ScopeId, Ar, Br, N) -> 706 case lists:reverse(Br++dup(8-N, 0, Ar)) of 707 [P,0|Xs] when P =:= 16#fe80; P =:= 16#ff02 -> 708 list_to_tuple([P,ScopeId|Xs]); 709 _ -> 710 erlang:error(badarg) 711 end. 712 713ipv6_addr_done(Ar, Br, N, {D1,D2,D3,D4}) -> 714 ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br], N+2). 715 716ipv6_addr_done(Ar, Br, N) -> 717 ipv6_addr_done(Br++dup(8-N, 0, Ar)). 718 719ipv6_addr_done(Ar) -> 720 list_to_tuple(lists:reverse(Ar)). 721 722%% Collect 1-4 Hex digits 723hex(Cs) -> hex(Cs, [], 4). 724%% 725hex([C|Cs], R, N) when C >= $0, C =< $9, N > 0 -> 726 hex(Cs, [C|R], N-1); 727hex([C|Cs], R, N) when C >= $a, C =< $f, N > 0 -> 728 hex(Cs, [C|R], N-1); 729hex([C|Cs], R, N) when C >= $A, C =< $F, N > 0 -> 730 hex(Cs, [C|R], N-1); 731hex(Cs, [_|_]=R, _) when is_list(Cs) -> 732 {lists:reverse(R),Cs}; 733hex(_, _, _) -> 734 erlang:error(badarg). 735 736%% Parse a reverse decimal integer string, empty is 0 737dec16(Cs) -> dec16(Cs, 0). 738%% 739dec16([], I) -> I; 740dec16([C|Cs], I) when C >= $0, C =< $9 -> 741 case 10*I + (C - $0) of 742 J when 16#ffff < J -> 743 erlang:error(badarg); 744 J -> 745 dec16(Cs, J) 746 end; 747dec16(_, _) -> erlang:error(badarg). 748 749%% Hex string to integer 750hex_to_int(Cs) -> erlang:list_to_integer(Cs, 16). 751 752%% Dup onto head of existing list 753dup(0, _, L) -> 754 L; 755dup(N, E, L) when is_integer(N), N >= 1 -> 756 dup(N-1, E, [E|L]). 757 758 759 760%% Convert IPv4 address to ascii 761%% Convert IPv6 / IPV4 address to ascii (plain format) 762ntoa({A,B,C,D}) when (A band B band C band D band (bnot 16#ff)) =:= 0 -> 763 integer_to_list(A) ++ "." ++ integer_to_list(B) ++ "." ++ 764 integer_to_list(C) ++ "." ++ integer_to_list(D); 765%% ANY 766ntoa({0,0,0,0,0,0,0,0}) -> "::"; 767%% LOOPBACK 768ntoa({0,0,0,0,0,0,0,1}) -> "::1"; 769%% IPV4 ipv6 host address 770ntoa({0,0,0,0,0,0,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 -> 771 "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); 772%% IPV4 non ipv6 host address 773ntoa({0,0,0,0,0,16#ffff,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 -> 774 "::ffff:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B); 775ntoa({A,B,C,D,E,F,G,H}) 776 when (A band B band C band D band E band F band G band H band 777 (bnot 16#ffff)) =:= 0 -> 778 if 779 A =:= 16#fe80, B =/= 0; 780 A =:= 16#ff02, B =/= 0 -> 781 %% Find longest sequence of zeros, at least 2, 782 %% to replace with "::" 783 ntoa([A,0,C,D,E,F,G,H], []) ++ "%0" ++ integer_to_list(B); 784 true -> 785 %% Find longest sequence of zeros, at least 2, 786 %% to replace with "::" 787 ntoa([A,B,C,D,E,F,G,H], []) 788 end; 789ntoa(_) -> 790 {error, einval}. 791 792%% Find first double zero 793ntoa([], R) -> 794 ntoa_done(R); 795ntoa([0,0|T], R) -> 796 ntoa(T, R, 2); 797ntoa([D|T], R) -> 798 ntoa(T, [D|R]). 799 800%% Count consecutive zeros 801ntoa([], R, _) -> 802 ntoa_done(R, []); 803ntoa([0|T], R, N) -> 804 ntoa(T, R, N+1); 805ntoa([D|T], R, N) -> 806 ntoa(T, R, N, [D]). 807 808%% Find alternate double zero 809ntoa([], R1, _N1, R2) -> 810 ntoa_done(R1, R2); 811ntoa([0,0|T], R1, N1, R2) -> 812 ntoa(T, R1, N1, R2, 2); 813ntoa([D|T], R1, N1, R2) -> 814 ntoa(T, R1, N1, [D|R2]). 815 816%% Count consecutive alternate zeros 817ntoa(T, R1, N1, R2, N2) when N2 > N1 -> 818 %% Alternate zero sequence is longer - use it instead 819 ntoa(T, R2++dup(N1, 0, R1), N2); 820ntoa([], R1, _N1, R2, N2) -> 821 ntoa_done(R1, dup(N2, 0, R2)); 822ntoa([0|T], R1, N1, R2, N2) -> 823 ntoa(T, R1, N1, R2, N2+1); 824ntoa([D|T], R1, N1, R2, N2) -> 825 ntoa(T, R1, N1, [D|dup(N2, 0, R2)]). 826 827ntoa_done(R1, R2) -> 828 lists:append( 829 separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R1)))++ 830 ["::"|separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R2)))]). 831 832ntoa_done(R) -> 833 lists:append(separate(":", lists:map(fun dig_to_hex/1, lists:reverse(R)))). 834 835separate(_E, []) -> 836 []; 837separate(E, [_|_]=L) -> 838 separate(E, L, []). 839 840separate(E, [H|[_|_]=T], R) -> 841 separate(E, T, [E,H|R]); 842separate(_E, [H], R) -> 843 lists:reverse(R, [H]). 844 845%% convert to A.B decimal form 846dig_to_dec(0) -> "0.0"; 847dig_to_dec(X) -> 848 integer_to_list((X bsr 8) band 16#ff) ++ "." ++ 849 integer_to_list(X band 16#ff). 850 851%% Convert a integer to hex string (lowercase) 852dig_to_hex(0) -> "0"; 853dig_to_hex(X) when is_integer(X), 0 < X -> 854 dig_to_hex(X, ""). 855%% 856dig_to_hex(0, Acc) -> Acc; 857dig_to_hex(X, Acc) -> 858 dig_to_hex( 859 X bsr 4, 860 [case X band 15 of 861 D when D < 10 -> D + $0; 862 D -> D - 10 + $a 863 end|Acc]). 864 865%% 866%% Count number of '.' in a name 867%% return {Number of non-terminating dots, has-terminating dot?} 868%% {integer, bool} 869%% 870dots(Name) -> dots(Name, 0). 871 872dots([$.], N) -> {N, true}; 873dots([$. | T], N) -> dots(T, N+1); 874dots([_C | T], N) -> dots(T, N); 875dots([], N) -> {N, false}. 876 877 878split_line(Line) -> 879 split_line(Line, []). 880 881split_line([$# | _], Tokens) -> reverse(Tokens); 882split_line([$\s| L], Tokens) -> split_line(L, Tokens); 883split_line([$\t | L], Tokens) -> split_line(L, Tokens); 884split_line([$\n | L], Tokens) -> split_line(L, Tokens); 885split_line([], Tokens) -> reverse(Tokens); 886split_line([C|Cs], Tokens) -> split_mid(Cs, [C], Tokens). 887 888split_mid([$# | _Cs], Acc, Tokens) -> split_end(Acc, Tokens); 889split_mid([$\s | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]); 890split_mid([$\t | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]); 891split_mid([$\r, $\n | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]); 892split_mid([$\n | Cs], Acc, Tokens) -> split_line(Cs, [reverse(Acc) | Tokens]); 893split_mid([], Acc, Tokens) -> split_end(Acc, Tokens); 894split_mid([C|Cs], Acc, Tokens) -> split_mid(Cs, [C|Acc], Tokens). 895 896split_end(Acc, Tokens) -> reverse([reverse(Acc) | Tokens]). 897 898 899%% Split a comma separated tokens. Because we already have split on 900%% spaces we may have the cases 901%% 902%% ",foo" 903%% "foo," 904%% "foo,bar..." 905 906split_comma([]) -> 907 []; 908split_comma([Token | Tokens]) -> 909 split_comma(Token, []) ++ split_comma(Tokens). 910 911split_comma([], Tokens) -> reverse(Tokens); 912split_comma([$, | L], Tokens) -> split_comma(L, Tokens); 913split_comma([C|Cs], Tokens) -> split_mid_comma(Cs, [C], Tokens). 914 915split_mid_comma([$, | Cs], Acc, Tokens) -> 916 split_comma(Cs, [reverse(Acc) | Tokens]); 917split_mid_comma([], Acc, Tokens) -> 918 split_end(Acc, Tokens); 919split_mid_comma([C|Cs], Acc, Tokens) -> 920 split_mid_comma(Cs, [C|Acc], Tokens). 921 922%% 923 924warning(Fmt, Args) -> 925 case application:get_env(kernel,inet_warnings) of 926 {ok,on} -> 927 error_logger:info_msg("inet_parse:" ++ Fmt, Args); 928 _ -> 929 ok 930 end. 931 932error(Fmt, Args) -> 933 error_logger:info_msg("inet_parse:" ++ Fmt, Args). 934 935