1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1998-2021. 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(gen_tcp_api_SUITE). 21 22%% Tests the documented API for the gen_tcp functions. The "normal" cases 23%% are not tested here, because they are tested indirectly in this and 24%% and other test suites. 25 26-include_lib("common_test/include/ct.hrl"). 27-include_lib("kernel/include/inet.hrl"). 28-include("kernel_test_lib.hrl"). 29 30-export([ 31 all/0, suite/0, groups/0, 32 init_per_suite/1, end_per_suite/1, 33 init_per_group/2,end_per_group/2, 34 init_per_testcase/2, end_per_testcase/2, 35 36 t_connect_timeout/1, t_accept_timeout/1, 37 t_connect_src_port/1, t_connect_bad/1, 38 t_recv_timeout/1, t_recv_eof/1, t_recv_delim/1, 39 t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, 40 t_shutdown_async/1, 41 t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1, 42 t_local_basic/1, t_local_unbound/1, t_local_fdopen/1, 43 t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1, 44 t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1, 45 t_local_abstract/1, t_accept_inet6_tclass/1, 46 s_accept_with_explicit_socket_backend/1 47 ]). 48 49-export([getsockfd/0, closesockfd/1]). 50 51 52%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 53 54suite() -> 55 [ 56 {ct_hooks,[ts_install_cth]}, 57 {timetrap,{minutes,1}} 58 ]. 59 60all() -> 61 %% This is a temporary messure to ensure that we can 62 %% test the socket backend without effecting *all* 63 %% applications on *all* machines. 64 %% This flag is set only for *one* host. 65 case ?TEST_INET_BACKENDS() of 66 true -> 67 [ 68 {group, inet_backend_default}, 69 {group, inet_backend_inet}, 70 {group, inet_backend_socket}, 71 {group, s_misc} 72 ]; 73 _ -> 74 [ 75 {group, inet_backend_default}, 76 {group, s_misc} 77 ] 78 end. 79 80groups() -> 81 [ 82 {inet_backend_default, [], inet_backend_default_cases()}, 83 {inet_backend_inet, [], inet_backend_inet_cases()}, 84 {inet_backend_socket, [], inet_backend_socket_cases()}, 85 {t_accept, [], t_accept_cases()}, 86 {t_connect, [], t_connect_cases()}, 87 {t_recv, [], t_recv_cases()}, 88 {t_shutdown, [], t_shutdown_cases()}, 89 {t_misc, [], t_misc_cases()}, 90 {t_local, [], t_local_cases()}, 91 {s_misc, [], s_misc_cases()} 92 ]. 93 94inet_backend_default_cases() -> 95 [ 96 {group, t_accept}, 97 {group, t_connect}, 98 {group, t_recv}, 99 {group, t_shutdown}, 100 {group, t_misc}, 101 {group, t_local} 102 ]. 103 104inet_backend_inet_cases() -> 105 inet_backend_default_cases(). 106 107inet_backend_socket_cases() -> 108 inet_backend_default_cases(). 109 110t_accept_cases() -> 111 [ 112 t_accept_timeout 113 ]. 114 115t_connect_cases() -> 116 [ 117 t_connect_timeout, 118 t_connect_src_port, 119 t_connect_bad 120 ]. 121 122t_recv_cases() -> 123 [ 124 t_recv_timeout, 125 t_recv_eof, 126 t_recv_delim 127 ]. 128 129t_shutdown_cases() -> 130 [ 131 t_shutdown_write, 132 t_shutdown_both, 133 t_shutdown_error, 134 t_shutdown_async 135 ]. 136 137t_misc_cases() -> 138 [ 139 t_fdopen, 140 t_fdconnect, 141 t_implicit_inet6, 142 t_accept_inet6_tclass 143 ]. 144 145t_local_cases() -> 146 [ 147 t_local_basic, 148 t_local_unbound, 149 t_local_fdopen, 150 t_local_fdopen_listen, 151 t_local_fdopen_listen_unbound, 152 t_local_fdopen_connect, 153 t_local_fdopen_connect_unbound, 154 t_local_abstract 155 ]. 156 157s_misc_cases() -> 158 [ 159 s_accept_with_explicit_socket_backend 160 ]. 161 162init_per_suite(Config0) -> 163 164 ?P("init_per_suite -> entry with" 165 "~n Config: ~p" 166 "~n Nodes: ~p", [Config0, erlang:nodes()]), 167 168 case ?LIB:init_per_suite(Config0) of 169 {skip, _} = SKIP -> 170 SKIP; 171 172 Config1 when is_list(Config1) -> 173 174 ?P("init_per_suite -> end when " 175 "~n Config: ~p", [Config1]), 176 177 %% We need a monitor on this node also 178 kernel_test_sys_monitor:start(), 179 180 Config1 181 end. 182 183 184end_per_suite(Config0) -> 185 186 ?P("end_per_suite -> entry with" 187 "~n Config: ~p" 188 "~n Nodes: ~p", [Config0, erlang:nodes()]), 189 190 %% Stop the local monitor 191 kernel_test_sys_monitor:stop(), 192 193 Config1 = ?LIB:end_per_suite(Config0), 194 195 ?P("end_per_suite -> " 196 "~n Nodes: ~p", [erlang:nodes()]), 197 198 Config1. 199 200 201init_per_group(inet_backend_default = _GroupName, Config) -> 202 [{socket_create_opts, []} | Config]; 203init_per_group(inet_backend_inet = _GroupName, Config) -> 204 case ?EXPLICIT_INET_BACKEND() of 205 true -> 206 %% The environment trumps us, 207 %% so only the default group should be run! 208 {skip, "explicit inet backend"}; 209 false -> 210 [{socket_create_opts, [{inet_backend, inet}]} | Config] 211 end; 212init_per_group(inet_backend_socket = _GroupName, Config) -> 213 case ?EXPLICIT_INET_BACKEND() of 214 true -> 215 %% The environment trumps us, 216 %% so only the default group should be run! 217 {skip, "explicit inet backend"}; 218 false -> 219 [{socket_create_opts, [{inet_backend, socket}]} | Config] 220 end; 221init_per_group(t_local = _GroupName, Config) -> 222 try gen_tcp:connect({local,<<"/">>}, 0, []) of 223 {error, eafnosupport} -> 224 {skip, "AF_LOCAL not supported"}; 225 {error,_} -> 226 Config 227 catch 228 _C:_E:_S -> 229 {skip, "AF_LOCAL not supported"} 230 end; 231init_per_group(_GroupName, Config) -> 232 Config. 233 234end_per_group(t_local, _Config) -> 235 delete_local_filenames(); 236end_per_group(_, _Config) -> 237 ok. 238 239 240init_per_testcase(Func, Config) 241 when Func =:= undefined -> % Insert your testcase name here 242 dbg:tracer(), 243 dbg:p(self(), c), 244 dbg:tpl(prim_inet, cx), 245 dbg:tpl(local_tcp, cx), 246 dbg:tpl(inet, cx), 247 dbg:tpl(gen_tcp, cx), 248 Config; 249init_per_testcase(_Func, Config) -> 250 ?P("init_per_testcase -> entry with" 251 "~n Config: ~p" 252 "~n Nodes: ~p" 253 "~n Links: ~p" 254 "~n Monitors: ~p", 255 [Config, erlang:nodes(), pi(links), pi(monitors)]), 256 257 kernel_test_global_sys_monitor:reset_events(), 258 259 ?P("init_per_testcase -> done when" 260 "~n Nodes: ~p" 261 "~n Links: ~p" 262 "~n Monitors: ~p", [erlang:nodes(), pi(links), pi(monitors)]), 263 Config. 264 265end_per_testcase(Func, _Config) 266 when Func =:= undefined -> % Insert your testcase name here 267 dbg:stop(); 268end_per_testcase(_Func, Config) -> 269 ?P("end_per_testcase -> entry with" 270 "~n Config: ~p" 271 "~n Nodes: ~p" 272 "~n Links: ~p" 273 "~n Monitors: ~p", 274 [Config, erlang:nodes(), pi(links), pi(monitors)]), 275 276 ?P("system events during test: " 277 "~n ~p", [kernel_test_global_sys_monitor:events()]), 278 279 ?P("end_per_testcase -> done with" 280 "~n Nodes: ~p" 281 "~n Links: ~p" 282 "~n Monitors: ~p", [erlang:nodes(), pi(links), pi(monitors)]), 283 ok. 284 285%%% gen_tcp:accept/1,2 286 287 288%% Test that gen_tcp:accept/2 (with timeout) works. 289t_accept_timeout(Config) when is_list(Config) -> 290 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 291 timeout({gen_tcp, accept, [L, 200]}, 0.2, 1.0). 292 293%%% gen_tcp:connect/X 294 295 296%% Test that gen_tcp:connect/4 (with timeout) works. 297t_connect_timeout(Config) when is_list(Config) -> 298 ?TC_TRY(t_connect_timeout, fun() -> do_connect_timeout(Config) end). 299 300do_connect_timeout(Config)-> 301 %%BadAddr = {134,138,177,16}, 302 %%TcpPort = 80, 303 {ok, BadAddr} = unused_ip(), 304 TcpPort = 45638, 305 ok = ?P("Connecting to ~p, port ~p", [BadAddr, TcpPort]), 306 connect_timeout({gen_tcp,connect, [BadAddr,TcpPort, ?INET_BACKEND_OPTS(Config),200]}, 0.2, 5.0). 307 308 309%% Test that setting only the source port for a connection works. 310t_connect_src_port(Config) when is_list(Config) -> 311 Timeout = 1000, 312 Loopback = {127,0,0,1}, 313 %% Allocate a port to later use as source port 314 {ok, Tmp} = gen_tcp:listen(0, [{ip,Loopback}, {linger,{true,0}}]), 315 {ok, SrcPort} = inet:port(Tmp), 316 io:format("SrcPort = ~w~n", [SrcPort]), 317 {ok, L} = gen_tcp:listen(0, [{ip,Loopback}]), 318 ok = gen_tcp:close(Tmp), 319 {ok, DstPort} = inet:port(L), 320 io:format("DstPort = ~w~n", [DstPort]), 321 ConnectOpts = [{port,SrcPort}, {linger,{true,0}}], 322 {ok, C} = gen_tcp:connect(Loopback, DstPort, ConnectOpts, Timeout), 323 {ok, A} = gen_tcp:accept(L, Timeout), 324 {ok, {_, SrcPort}} = inet:peername(A), 325 ok = gen_tcp:close(L), 326 ok = gen_tcp:close(C), 327 ok = gen_tcp:close(A). 328 329 330%% Test that gen_tcp:connect/3 handles non-existings hosts, and other 331%% invalid things. 332t_connect_bad(Config) when is_list(Config) -> 333 NonExistingPort = 45638, % Not in use, I hope. 334 {error, Reason1} = gen_tcp:connect(localhost, NonExistingPort, 335 ?INET_BACKEND_OPTS(Config)), 336 io:format("Error for connection attempt to port not in use: ~p", 337 [Reason1]), 338 339 {error, Reason2} = gen_tcp:connect("non-existing-host-xxx", 7, 340 ?INET_BACKEND_OPTS(Config)), 341 io:format("Error for connection attempt to non-existing host: ~p", 342 [Reason2]), 343 ok. 344 345 346%%% gen_tcp:recv/X 347 348 349%% Test that gen_tcp:recv/3 (with timeout works). 350t_recv_timeout(Config) when is_list(Config) -> 351 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 352 {ok, Port} = inet:port(L), 353 {ok, Client} = gen_tcp:connect(localhost, Port, 354 ?INET_BACKEND_OPTS(Config) ++ 355 [{active, false}]), 356 {ok, _A} = gen_tcp:accept(L), 357 timeout({gen_tcp, recv, [Client, 0, 200]}, 0.2, 5.0). 358 359%% Test that end of file on a socket is reported correctly. 360t_recv_eof(Config) when is_list(Config) -> 361 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 362 {ok, Port} = inet:port(L), 363 {ok, Client} = gen_tcp:connect(localhost, Port, 364 ?INET_BACKEND_OPTS(Config) ++ 365 [{active, false}]), 366 {ok, A} = gen_tcp:accept(L), 367 ok = gen_tcp:close(A), 368 {error, closed} = gen_tcp:recv(Client, 0), 369 ok. 370 371%% Test using message delimiter $X. 372t_recv_delim(Config) when is_list(Config) -> 373 ?TC_TRY(t_recv_delim, fun() -> do_recv_delim(Config) end). 374 375do_recv_delim(Config) -> 376 ?P("init"), 377 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 378 {ok, Port} = inet:port(L), 379 Opts = ?INET_BACKEND_OPTS(Config) ++ 380 [{active,false}, {packet,line}, {line_delimiter,$X}], 381 {ok, Client} = gen_tcp:connect(localhost, Port, Opts), 382 {ok, A} = gen_tcp:accept(L), 383 384 ?P("send the data"), 385 ok = gen_tcp:send(A, "abcXefgX"), 386 387 %% Why do we need a timeout? 388 %% Sure, normally there would be no delay, 389 %% but this testcase has nothing to do with timeouts? 390 ?P("read the first chunk"), 391 {ok, "abcX"} = gen_tcp:recv(Client, 0), % 200), 392 ?P("read the second chunk"), 393 {ok, "efgX"} = gen_tcp:recv(Client, 0), % 200), 394 395 ?P("set active = 2"), 396 ok = inet:setopts(Client, [{active,2}]), 397 398 ?P("send the data again"), 399 ok = gen_tcp:send(A, "abcXefgX"), 400 401 ?P("await the first chunk"), 402 receive {tcp, Client, "abcX"} -> ?P("received first chunck") end, 403 ?P("await the second chunk"), 404 receive {tcp, Client, "efgX"} -> ?P("received second chunck") end, 405 406 ?P("cleanup"), 407 ok = gen_tcp:close(Client), 408 ok = gen_tcp:close(A), 409 ?P("done"), 410 ok. 411 412%%% gen_tcp:shutdown/2 413 414t_shutdown_write(Config) when is_list(Config) -> 415 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 416 {ok, Port} = inet:port(L), 417 {ok, Client} = gen_tcp:connect(localhost, Port, 418 ?INET_BACKEND_OPTS(Config) ++ 419 [{active, false}]), 420 {ok, A} = gen_tcp:accept(L), 421 ok = gen_tcp:shutdown(A, write), 422 {error, closed} = gen_tcp:recv(Client, 0), 423 ok. 424 425t_shutdown_both(Config) when is_list(Config) -> 426 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 427 {ok, Port} = inet:port(L), 428 {ok, Client} = gen_tcp:connect(localhost, Port, 429 ?INET_BACKEND_OPTS(Config) ++ 430 [{active, false}]), 431 {ok, A} = gen_tcp:accept(L), 432 ok = gen_tcp:shutdown(A, read_write), 433 {error, closed} = gen_tcp:recv(Client, 0), 434 ok. 435 436t_shutdown_error(Config) when is_list(Config) -> 437 ?TC_TRY(t_shutdown_error, fun() -> do_shutdown_error(Config) end). 438 439do_shutdown_error(Config) -> 440 ?P("create listen socket"), 441 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config)), 442 ?P("shutdown socket (with How = read_write)"), 443 {error, enotconn} = gen_tcp:shutdown(L, read_write), 444 ?P("close socket"), 445 ok = gen_tcp:close(L), 446 ?P("shutdown socket again (with How = read_write)"), 447 {error, closed} = gen_tcp:shutdown(L, read_write), 448 ?P("done"), 449 ok. 450 451t_shutdown_async(Config) when is_list(Config) -> 452 ?TC_TRY(t_shutdown_async, fun() -> do_shutdown_async(Config) end). 453 454do_shutdown_async(Config) -> 455 ?P("create listen socket"), 456 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config) ++ [{sndbuf, 4096}]), 457 if 458 is_port(L) -> 459 do_shutdown_async2(Config, L); 460 true -> 461 (catch gen_tcp:close(L)), 462 exit({skip, "inet-only testcase"}) 463 end. 464 465do_shutdown_async2(Config, L) -> 466 {OS, _} = os:type(), 467 {ok, Port} = inet:port(L), 468 ?P("connect"), 469 {ok, Client} = gen_tcp:connect(localhost, Port, 470 ?INET_BACKEND_OPTS(Config) ++ 471 [{recbuf, 4096}, 472 {active, false}]), 473 ?P("accept connection"), 474 {ok, S} = gen_tcp:accept(L), 475 ?P("create payload"), 476 PayloadSize = 1024 * 1024, 477 Payload = lists:duplicate(PayloadSize, $.), 478 ?P("send payload"), 479 ok = gen_tcp:send(S, Payload), 480 ?P("verify queue size"), 481 case erlang:port_info(S, queue_size) of 482 {queue_size, N} when N > 0 -> ok; 483 {queue_size, 0} when OS =:= win32 -> ok; 484 {queue_size, 0} = T -> ct:fail({unexpected, T}) 485 end, 486 487 ?P("shutdown(write) accepted socket"), 488 ok = gen_tcp:shutdown(S, write), 489 ?P("recv from connected socket"), 490 {ok, Buf} = gen_tcp:recv(Client, PayloadSize), 491 ?P("recv(0) from connected socket (expect closed)"), 492 {error, closed} = gen_tcp:recv(Client, 0), 493 ?P("verify recv data"), 494 case length(Buf) of 495 PayloadSize -> ?P("done"), ok; 496 Sz -> ?P("ERROR: " 497 "~n extected: ~p" 498 "~n received: ~p", [PayloadSize, Sz]), 499 ct:fail({payload_size, 500 {expected, PayloadSize}, 501 {received, Sz}}) 502 end. 503 504 505%%% gen_tcp:fdopen/2 506 507t_fdopen(Config) when is_list(Config) -> 508 Question = "Aaaa... Long time ago in a small town in Germany,", 509 Question1 = list_to_binary(Question), 510 Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", 511 ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], 512 Question1 = iolist_to_binary(Question2), 513 Answer = "there was a shoemaker, Schumacher was his name.", 514 {ok, L} = gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config) ++ [{active, false}]), 515 {ok, Port} = inet:port(L), 516 {ok, Client} = gen_tcp:connect(localhost, Port, 517 ?INET_BACKEND_OPTS(Config) ++ 518 [{active, false}]), 519 {A, FD} = case gen_tcp:accept(L) of 520 {ok, ASock} when is_port(ASock) -> 521 {ok, FileDesc} = prim_inet:getfd(ASock), 522 {ASock, FileDesc}; 523 {ok, ASock} -> % socket 524 {ok, [{fd, FileDesc}]} = 525 gen_tcp_socket:getopts(ASock, [fd]), 526 {ASock, FileDesc} 527 end, 528 ?P("fdopen -> accepted: " 529 "~n A: ~p" 530 "~n FD: ~p", [A, FD]), 531 {ok, Server} = gen_tcp:fdopen(FD, ?INET_BACKEND_OPTS(Config)), 532 ok = gen_tcp:send(Client, Question), 533 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 534 ok = gen_tcp:send(Client, Question1), 535 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 536 ok = gen_tcp:send(Client, Question2), 537 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 538 ok = gen_tcp:send(Server, Answer), 539 {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000), 540 ok = gen_tcp:close(Client), 541 {error, closed} = gen_tcp:recv(A, 1, 2000), 542 ok = gen_tcp:close(Server), 543 ok = gen_tcp:close(A), 544 ok = gen_tcp:close(L), 545 ok. 546 547 548t_fdconnect(Config) when is_list(Config) -> 549 ?TC_TRY(t_fdconnect, fun() -> do_t_fdconnect(Config) end). 550 551do_t_fdconnect(Config) -> 552 Question = "Aaaa... Long time ago in a small town in Germany,", 553 Question1 = list_to_binary(Question), 554 Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", 555 ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], 556 Question1 = iolist_to_binary(Question2), 557 Answer = "there was a shoemaker, Schumacher was his name.", 558 Path = proplists:get_value(data_dir, Config), 559 Lib = "gen_tcp_api_SUITE", 560 ?P("try load util nif lib"), 561 case erlang:load_nif(filename:join(Path, Lib), []) of 562 ok -> 563 ok; 564 {error, {reload, ReasonStr}} -> 565 ?P("already loaded: " 566 "~n ~s", [ReasonStr]), 567 ok; 568 {error, Reason} -> 569 ?P("UNEXPECTED - failed loading util nif lib: " 570 "~n ~p", [Reason]), 571 ?SKIPT("failed loading util nif lib") 572 end, 573 ?P("try create listen socket"), 574 L = case gen_tcp:listen(0, 575 ?INET_BACKEND_OPTS(Config) ++ [{active, false}]) of 576 {ok, LSock} -> 577 LSock; 578 {error, eaddrnotavail = LReason} -> 579 ?SKIPT(listen_failed_str(LReason)) 580 end, 581 {ok, Port} = inet:port(L), 582 ?P("try create file descriptor"), 583 FD = gen_tcp_api_SUITE:getsockfd(), 584 ?P("try connect using file descriptor ~w", [FD]), 585 Client = case gen_tcp:connect(localhost, Port, 586 ?INET_BACKEND_OPTS(Config) ++ 587 [{fd, FD}, 588 {active, false}]) of 589 {ok, CSock} -> 590 CSock; 591 {error, eaddrnotavail = CReason} -> 592 gen_tcp:close(L), 593 gen_tcp_api_SUITE:closesockfd(FD), 594 ?SKIPT(connect_failed_str(CReason)) 595 end, 596 ?P("try accept connection"), 597 Server = case gen_tcp:accept(L) of 598 {ok, ASock} -> 599 ASock; 600 {error, eaddrnotavail = AReason} -> 601 gen_tcp:close(Client), 602 gen_tcp:close(L), 603 gen_tcp_api_SUITE:closesockfd(FD), 604 ?SKIPT(accept_failed_str(AReason)) 605 end, 606 ?P("begin validation"), 607 ok = gen_tcp:send(Client, Question), 608 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 609 ok = gen_tcp:send(Client, Question1), 610 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 611 ok = gen_tcp:send(Client, Question2), 612 {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), 613 ok = gen_tcp:send(Server, Answer), 614 {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000), 615 ?P("cleanup"), 616 ok = gen_tcp:close(Client), 617 FD = gen_tcp_api_SUITE:closesockfd(FD), 618 {error, closed} = gen_tcp:recv(Server, 1, 2000), 619 ok = gen_tcp:close(Server), 620 ok = gen_tcp:close(L), 621 ?P("done"), 622 ok. 623 624 625%%% implicit inet6 option to api functions 626 627t_implicit_inet6(Config) when is_list(Config) -> 628 ?TC_TRY(t_implicit_inet6, fun() -> do_t_implicit_inet6(Config) end). 629 630do_t_implicit_inet6(Config) -> 631 ?P("try get hostname"), 632 Host = ok(inet:gethostname()), 633 ?P("try get address for host ~p", [Host]), 634 case inet:getaddr(Host, inet6) of 635 {ok, Addr} -> 636 ?P("address: ~p", [Addr]), 637 t_implicit_inet6(Config, Host, Addr); 638 {error, Reason} -> 639 {skip, 640 "Can not look up IPv6 address: " 641 ++atom_to_list(Reason)} 642 end. 643 644t_implicit_inet6(Config, Host, Addr) -> 645 Loopback = {0,0,0,0,0,0,0,1}, 646 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 647 case gen_tcp:listen(0, InetBackendOpts ++ [inet6, {ip,Loopback}]) of 648 {ok, S1} -> 649 ?P("try ~s ~p", ["::1", Loopback]), 650 implicit_inet6(Config, S1, Loopback), 651 ok = gen_tcp:close(S1), 652 %% 653 LocalAddr = ok(get_localaddr()), 654 S2 = case gen_tcp:listen(0, InetBackendOpts ++ [{ip, LocalAddr}]) of 655 {ok, LSock2} -> 656 LSock2; 657 {error, Reason2} -> 658 ?P("Listen failed (ip):" 659 "~n Reason2: ~p", [Reason2]), 660 ?SKIPT(listen_failed_str(Reason2)) 661 end, 662 implicit_inet6(Config, S2, LocalAddr), 663 ok = gen_tcp:close(S2), 664 %% 665 ?P("try ~s ~p", [Host, Addr]), 666 S3 = case gen_tcp:listen(0, InetBackendOpts ++ [{ifaddr,Addr}]) of 667 {ok, LSock3} -> 668 LSock3; 669 {error, Reason3} -> 670 ?P("Listen failed (ifaddr):" 671 "~n Reason3: ~p", [Reason3]), 672 ?SKIPT(listen_failed_str(Reason3)) 673 end, 674 implicit_inet6(Config, S3, Addr), 675 ok = gen_tcp:close(S3), 676 ?P("done"), 677 ok; 678 {error, Reason1} -> 679 ?SKIPT(listen_failed_str(Reason1)) 680 end. 681 682implicit_inet6(Config, S, Addr) -> 683 P = ok(inet:port(S)), 684 S2 = case gen_tcp:connect(Addr, P, ?INET_BACKEND_OPTS(Config)) of 685 {ok, CSock} -> 686 CSock; 687 {error, CReason} -> 688 ?SKIPT(connect_failed_str(CReason)) 689 end, 690 P2 = ok(inet:port(S2)), 691 S1 = case gen_tcp:accept(S) of 692 {ok, ASock} -> 693 ASock; 694 {error, AReason} -> 695 ?SKIPT(accept_failed_str(AReason)) 696 end, 697 P1 = P = ok(inet:port(S1)), 698 {Addr,P2} = ok(inet:peername(S1)), 699 {Addr,P1} = ok(inet:peername(S2)), 700 {Addr,P1} = ok(inet:sockname(S1)), 701 {Addr,P2} = ok(inet:sockname(S2)), 702 ok = gen_tcp:close(S2), 703 ok = gen_tcp:close(S1). 704 705 706t_local_basic(Config) -> 707 SFile = local_filename(server), 708 SAddr = {local, bin_filename(SFile)}, 709 CFile = local_filename(client), 710 CAddr = {local,bin_filename(CFile)}, 711 _ = file:delete(SFile), 712 _ = file:delete(CFile), 713 %% 714 ?P("try create listen socket"), 715 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 716 L = 717 ok( 718 gen_tcp:listen(0, InetBackendOpts ++ 719 [{ifaddr,{local,SFile}},{active,false}])), 720 ?P("try connect"), 721 C = 722 ok( 723 gen_tcp:connect( 724 {local,SFile}, 0, InetBackendOpts ++ 725 [{ifaddr,{local,CFile}},{active,false}])), 726 ?P("try accept connection"), 727 S = ok(gen_tcp:accept(L)), 728 ?P("try get sockname for listen socket"), 729 %% SAddr = ok(inet:sockname(L)), 730 case inet:sockname(L) of 731 {ok, SAddr} -> 732 ok; 733 {ok, SAddr2} -> 734 ?P("Invalid sockname: " 735 "~n Expected: ~p" 736 "~n Actual: ~p", [SAddr, SAddr2]), 737 exit({sockename, SAddr, SAddr2}); 738 {error, Reason} -> 739 exit({sockname, Reason}) 740 end, 741 ?P("try get peername for listen socket"), 742 {error, enotconn} = inet:peername(L), 743 ?P("try handshake"), 744 local_handshake(S, SAddr, C, CAddr), 745 ?P("try close listen socket"), 746 ok = gen_tcp:close(L), 747 ?P("try close accept socket"), 748 ok = gen_tcp:close(S), 749 ?P("try close connect socket"), 750 ok = gen_tcp:close(C), 751 %% 752 ?P("try 'local' files"), 753 ok = file:delete(SFile), 754 ok = file:delete(CFile), 755 ?P("done"), 756 ok. 757 758 759t_local_unbound(Config) -> 760 ?TC_TRY(t_local_unbound, fun() -> do_local_unbound(Config) end). 761 762do_local_unbound(Config) -> 763 ?P("create local (server) filename"), 764 SFile = local_filename(server), 765 SAddr = {local,bin_filename(SFile)}, 766 _ = file:delete(SFile), 767 %% 768 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 769 ?P("create listen socket with ifaddr ~p", [SAddr]), 770 L = ok(gen_tcp:listen(0, InetBackendOpts ++ 771 [{ifaddr,SAddr},{active,false}])), 772 ?P("listen socket created: ~p" 773 "~n => try connect", [L]), 774 C = ok(gen_tcp:connect(SAddr, 0, 775 InetBackendOpts ++ [{active,false}])), 776 ?P("connected: ~p" 777 "~n => try accept", [C]), 778 S = ok(gen_tcp:accept(L)), 779 ?P("accepted: ~p" 780 "~n => sockname", [S]), 781 SAddr = ok(inet:sockname(L)), 782 ?P("sockname: ~p" 783 "~n => peername (expect enotconn)", [SAddr]), 784 {error, enotconn} = inet:peername(L), 785 ?P("try local handshake"), 786 local_handshake(S, SAddr, C, {local,<<>>}), 787 ?P("close listen socket"), 788 ok = gen_tcp:close(L), 789 ?P("close accepted socket"), 790 ok = gen_tcp:close(S), 791 ?P("close connected socket"), 792 ok = gen_tcp:close(C), 793 ?P("delete (local) file"), 794 ok = file:delete(SFile), 795 ?P("done"), 796 ok. 797 798 799t_local_fdopen(Config) -> 800 ?TC_TRY(t_local_fdopen, fun() -> do_local_fdopen(Config) end). 801 802do_local_fdopen(Config) -> 803 ?P("create local (server) filename"), 804 SFile = local_filename(server), 805 SAddr = {local,bin_filename(SFile)}, 806 _ = file:delete(SFile), 807 %% 808 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 809 ListenOpts = InetBackendOpts ++ [{ifaddr,SAddr},{active,false}], 810 ?P("create listen socket with ListenOpts ~p", [ListenOpts]), 811 L = ok(gen_tcp:listen(0, ListenOpts)), 812 ConnectOpts = InetBackendOpts ++ [{active,false}], 813 ?P("listen socket created: ~p" 814 "~n => try connect ~p", [L, ConnectOpts]), 815 C0 = ok(gen_tcp:connect(SAddr, 0, ConnectOpts)), 816 ?P("connected: ~p" 817 "~n => get fd", [C0]), 818 Fd = if 819 is_port(C0) -> 820 FD0 = ok(prim_inet:getfd(C0)), 821 ?P("FD: ~p" 822 "~n => ignore fd", [FD0]), 823 %% Turn off C0, so it does not generate any events! 824 ok = prim_inet:ignorefd(C0, true), 825 FD0; 826 true -> 827 [{fd, FD0}] = ok(inet:getopts(C0, [fd])), 828 ?P("FD: ~p", [FD0]), 829 FD0 830 end, 831 ?P("ignored fd:" 832 "~n => try fdopen (local)"), 833 C = ok(gen_tcp:fdopen(Fd, ?INET_BACKEND_OPTS(Config) ++ [local])), 834 ?P("fd open: ~p" 835 "~n => try accept", [C]), 836 S = ok(gen_tcp:accept(L)), 837 ?P("accepted: ~p" 838 "~n => get sockname", [S]), 839 SAddr = ok(inet:sockname(L)), 840 ?P("sockname: ~p" 841 "~n => try get peername (expect enotconn)", [SAddr]), 842 {error,enotconn} = inet:peername(L), 843 ?P("try local handshake"), 844 local_handshake(S, SAddr, C, {local,<<>>}), 845 ?P("close listen socket"), 846 ok = gen_tcp:close(L), 847 ?P("close accepted socket"), 848 ok = gen_tcp:close(S), 849 ?P("close connected socket (final)"), 850 ok = gen_tcp:close(C), 851 ?P("close connected socket (pre)"), 852 ok = gen_tcp:close(C0), 853 ?P("delete (local) file"), 854 ok = file:delete(SFile), 855 ?P("done"), 856 ok. 857 858t_local_fdopen_listen(Config) -> 859 ?TC_TRY(t_local_fdopen_listen, fun() -> do_local_fdopen_listen(Config) end). 860 861do_local_fdopen_listen(Config) -> 862 ?P("create local (server) filename"), 863 SFile = local_filename(server), 864 SAddr = {local,bin_filename(SFile)}, 865 _ = file:delete(SFile), 866 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 867 ?P("create dummy listen socket with ifaddr ~p", [SAddr]), 868 L0 = ok(gen_tcp:listen(0, InetBackendOpts ++ 869 [{ifaddr,SAddr},{active,false}])), 870 ?P("dummy listen socket created: ~p" 871 "~n => try get FD", [L0]), 872 Fd = if 873 is_port(L0) -> 874 ok(prim_inet:getfd(L0)); 875 true -> 876 [{fd, FD0}] = ok(inet:getopts(L0, [fd])), 877 FD0 878 end, 879 ?P("FD: ~p" 880 "~n => try create proper listen socket (using fd)", [Fd]), 881 L = ok(gen_tcp:listen(0, InetBackendOpts ++ 882 [{fd,Fd},local,{active,false}])), 883 ?P("try connect"), 884 C = ok(gen_tcp:connect(SAddr, 0, InetBackendOpts ++ [{active,false}])), 885 ?P("try accept (connection)"), 886 S = ok(gen_tcp:accept(L)), 887 ?P("verify (proper) listen socket sockname"), 888 SAddr = ok(inet:sockname(L)), 889 ?P("verify (proper) listen socket peername (expect enotconn)"), 890 {error, enotconn} = inet:peername(L), 891 ?P("perform handshake"), 892 local_handshake(S, SAddr, C, {local,<<>>}), 893 ?P("close (proper) listen socket"), 894 ok = gen_tcp:close(L), 895 ?P("close (dummy) listen socket"), 896 ok = gen_tcp:close(L0), 897 ?P("close accepted socket"), 898 ok = gen_tcp:close(S), 899 ?P("close connected socket"), 900 ok = gen_tcp:close(C), 901 ?P("delete file (used for socket)"), 902 ok = file:delete(SFile), 903 ?P("done"), 904 ok. 905 906t_local_fdopen_listen_unbound(Config) -> 907 SFile = local_filename(server), 908 SAddr = {local,bin_filename(SFile)}, 909 _ = file:delete(SFile), 910 P = ok(prim_inet:open(tcp, local, stream)), 911 Fd = ok(prim_inet:getfd(P)), 912 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 913 L = 914 ok(gen_tcp:listen( 915 0, InetBackendOpts ++ [{fd,Fd},{ifaddr,SAddr},{active,false}])), 916 C = ok(gen_tcp:connect(SAddr, 0, InetBackendOpts ++ [{active,false}])), 917 S = ok(gen_tcp:accept(L)), 918 SAddr = ok(inet:sockname(L)), 919 {error,enotconn} = inet:peername(L), 920 local_handshake(S, SAddr, C, {local,<<>>}), 921 ok = gen_tcp:close(L), 922 ok = gen_tcp:close(P), 923 ok = gen_tcp:close(S), 924 ok = gen_tcp:close(C), 925 ok = file:delete(SFile), 926 ok. 927 928t_local_fdopen_connect(Config) -> 929 SFile = local_filename(server), 930 SAddr = {local,bin_filename(SFile)}, 931 CFile = local_filename(client), 932 CAddr = {local,bin_filename(CFile)}, 933 _ = file:delete(SFile), 934 _ = file:delete(CFile), 935 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 936 L = ok(gen_tcp:listen(0, InetBackendOpts ++ [{ifaddr,SAddr},{active,false}])), 937 P = ok(prim_inet:open(tcp, local, stream)), 938 Fd = ok(prim_inet:getfd(P)), 939 C = 940 ok(gen_tcp:connect( 941 SAddr, 0, InetBackendOpts ++ 942 [{fd,Fd},{ifaddr,CAddr},{active,false}])), 943 S = ok(gen_tcp:accept(L)), 944 SAddr = ok(inet:sockname(L)), 945 {error,enotconn} = inet:peername(L), 946 local_handshake(S, SAddr, C, CAddr), 947 ok = gen_tcp:close(L), 948 ok = gen_tcp:close(S), 949 ok = gen_tcp:close(C), 950 ok = gen_tcp:close(P), 951 ok = file:delete(SFile), 952 ok. 953 954t_local_fdopen_connect_unbound(Config) -> 955 SFile = local_filename(server), 956 SAddr = {local,bin_filename(SFile)}, 957 _ = file:delete(SFile), 958 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 959 L = ok(gen_tcp:listen(0, InetBackendOpts ++ [{ifaddr,SAddr},{active,false}])), 960 P = ok(prim_inet:open(tcp, local, stream)), 961 Fd = ok(prim_inet:getfd(P)), 962 C = ok(gen_tcp:connect(SAddr, 0, InetBackendOpts ++ [{fd,Fd},{active,false}])), 963 S = ok(gen_tcp:accept(L)), 964 SAddr = ok(inet:sockname(L)), 965 {error,enotconn} = inet:peername(L), 966 local_handshake(S, SAddr, C, {local,<<>>}), 967 ok = gen_tcp:close(L), 968 ok = gen_tcp:close(S), 969 ok = gen_tcp:close(C), 970 ok = gen_tcp:close(P), 971 ok = file:delete(SFile), 972 ok. 973 974t_local_abstract(Config) -> 975 ?TC_TRY(t_local_abstract, fun() -> do_local_abstract(Config) end). 976 977do_local_abstract(Config) -> 978 ?P("only run on linux"), 979 case os:type() of 980 {unix, linux} -> 981 AbstAddr = {local,<<>>}, 982 InetBackendOpts = ?INET_BACKEND_OPTS(Config), 983 ?P("create listen socket"), 984 L = 985 ok(gen_tcp:listen( 986 0, InetBackendOpts ++ [{ifaddr,AbstAddr},{active,false}])), 987 ?P("listen socket created: ~p" 988 "~n => sockname", [L]), 989 {local, _} = SAddr = ok(inet:sockname(L)), 990 ?P("(listen socket) sockname verified" 991 "~n => try connect"), 992 C = 993 ok(gen_tcp:connect( 994 SAddr, 0, 995 InetBackendOpts ++ [{ifaddr,AbstAddr},{active,false}])), 996 ?P("connected: ~p" 997 "~n => sockname", [C]), 998 {local,_} = CAddr = ok(inet:sockname(C)), 999 ?P("(connected socket) sockname verified" 1000 "~n => try accept"), 1001 S = ok(gen_tcp:accept(L)), 1002 ?P("accepted: ~p" 1003 "~n => peername (expect enotconn)", [S]), 1004 {error,enotconn} = inet:peername(L), 1005 ?P("try local handshake"), 1006 local_handshake(S, SAddr, C, CAddr), 1007 ?P("close listen socket"), 1008 ok = gen_tcp:close(L), 1009 ?P("close accepted socket"), 1010 ok = gen_tcp:close(S), 1011 ?P("close connected socket"), 1012 ok = gen_tcp:close(C), 1013 ?P("done"), 1014 ok; 1015 _ -> 1016 ?P("skip (unless linux)"), 1017 {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} 1018 end. 1019 1020 1021local_handshake(S, SAddr, C, CAddr) -> 1022 ?P("~w(~p, ~p, ~p, ~p)~n", [?FUNCTION_NAME, S, SAddr, C, CAddr]), 1023 SData = "9876543210", 1024 CData = "0123456789", 1025 SAddr = ok(inet:sockname(S)), 1026 CAddr = ok(inet:sockname(C)), 1027 CAddr = ok(inet:peername(S)), 1028 SAddr = ok(inet:peername(C)), 1029 ok = gen_tcp:send(C, CData), 1030 ok = gen_tcp:send(S, SData), 1031 CData = ok(gen_tcp:recv(S, length(CData))), 1032 SData = ok(gen_tcp:recv(C, length(SData))), 1033 ok. 1034 1035t_accept_inet6_tclass(Config) when is_list(Config) -> 1036 ?TC_TRY(t_accept_inet6_tclass, fun() -> do_accept_inet6_tclass(Config) end). 1037 1038do_accept_inet6_tclass(Config) -> 1039 TClassOpt = {tclass, 8#56 bsl 2}, % Expedited forwarding 1040 Loopback = {0,0,0,0,0,0,0,1}, 1041 ?P("create listen socket with tclass: ~p", [TClassOpt]), 1042 case gen_tcp:listen(0, ?INET_BACKEND_OPTS(Config) ++ 1043 [inet6, {ip, Loopback}, TClassOpt]) of 1044 {ok, L} -> 1045 ?P("listen socket created: " 1046 "~n ~p", [L]), 1047 LPort = ok(inet:port(L)), 1048 ?P("try to connect to port ~p", [LPort]), 1049 Sa = ok(gen_tcp:connect(Loopback, LPort, 1050 ?INET_BACKEND_OPTS(Config))), 1051 ?P("connected: ~p" 1052 "~n => accept connection", [Sa]), 1053 Sb = ok(gen_tcp:accept(L)), 1054 ?P("accepted: ~p" 1055 "~n => getopts (tclass)", [Sb]), 1056 [TClassOpt] = ok(inet:getopts(Sb, [tclass])), 1057 ?P("tclass verified => close accepted socket"), 1058 ok = gen_tcp:close(Sb), 1059 ?P("close connected socket"), 1060 ok = gen_tcp:close(Sa), 1061 ?P("close listen socket"), 1062 ok = gen_tcp:close(L), 1063 ?P("done"), 1064 ok; 1065 {error, _Reason} -> 1066 ?P("ERROR: Failed create listen socket" 1067 "~n ~p", [_Reason]), 1068 {skip,"IPv6 TCLASS not supported"} 1069 end. 1070 1071 1072%% On MacOS (maybe more), accepting a connection resulted in a crash. 1073%% Note that since 'socket' currently does not work on windows 1074%% we have to skip on that platform. 1075s_accept_with_explicit_socket_backend(Config) when is_list(Config) -> 1076 ?TC_TRY(s_accept_with_explicit_socket_backend, 1077 fun() -> is_not_windows() end, 1078 fun() -> do_s_accept_with_explicit_socket_backend() end). 1079 1080do_s_accept_with_explicit_socket_backend() -> 1081 {ok, S} = gen_tcp:listen(0, [{inet_backend, socket}]), 1082 {ok, {_, Port}} = inet:sockname(S), 1083 ClientF = fun() -> 1084 {ok, _} = gen_tcp:connect("localhost", Port, []), 1085 receive die -> exit(normal) after infinity -> ok end 1086 end, 1087 Client = spawn_link(ClientF), 1088 {ok, _} = gen_tcp:accept(S), 1089 Client ! die, 1090 ok. 1091 1092 1093%%% Utilities 1094 1095is_not_windows() -> 1096 case os:type() of 1097 {win32, _} -> 1098 {skip, "Windows not supported"}; 1099 _ -> 1100 ok 1101 end. 1102 1103 1104%% Calls M:F/length(A), which should return a timeout error, and complete 1105%% within the given time. 1106 1107timeout({M,F,A}, Lower, Upper) -> 1108 case test_server:timecall(M, F, A) of 1109 {Time, Result} when Time < Lower -> 1110 ct:fail({too_short_time, Time, Result}); 1111 {Time, Result} when Time > Upper -> 1112 ct:fail({too_long_time, Time, Result}); 1113 {_, {error, timeout}} -> 1114 ok; 1115 {_, Result} -> 1116 ct:fail({unexpected_result, Result}) 1117 end. 1118 1119connect_timeout({M,F,A}, Lower, Upper) -> 1120 case test_server:timecall(M, F, A) of 1121 {Time, Result} when Time < Lower -> 1122 case Result of 1123 {error, econnrefused = E} -> 1124 {skip, "Not tested -- got error " ++ atom_to_list(E)}; 1125 {error, enetunreach = E} -> 1126 {skip, "Not tested -- got error " ++ atom_to_list(E)}; 1127 {error, ehostunreach = E} -> 1128 {skip, "Not tested -- got error " ++ atom_to_list(E)}; 1129 {ok, Socket} -> % What the... 1130 Pinfo = erlang:port_info(Socket), 1131 Db = inet_db:lookup_socket(Socket), 1132 Peer = inet:peername(Socket), 1133 ct:fail({too_short_time, Time, 1134 [Result,Pinfo,Db,Peer]}); 1135 _ -> 1136 ct:fail({too_short_time, Time, Result}) 1137 end; 1138 {Time, Result} when Time > Upper -> 1139 ct:fail({too_long_time, Time, Result}); 1140 {_, {error, timeout}} -> 1141 ok; 1142 {_, Result} -> 1143 ct:fail({unexpected_result, Result}) 1144 end. 1145 1146%% Try to obtain an unused IP address in the local network. 1147 1148unused_ip() -> 1149 {ok, Host} = inet:gethostname(), 1150 {ok, Hent} = inet:gethostbyname(Host), 1151 #hostent{h_addr_list=[{A, B, C, _D}|_]} = Hent, 1152 %% Note: In our net, addresses below 16 are reserved for routers and 1153 %% other strange creatures. 1154 IP = unused_ip(A, B, C, 16), 1155 if 1156 (IP =:= error) -> 1157 %% This is not supported on all platforms (yet), so... 1158 try net:getifaddrs() of 1159 {ok, IfAddrs} -> 1160 ?P("~n we = ~p" 1161 "~n unused_ip = ~p" 1162 "~n ~p", [Hent, IP, IfAddrs]); 1163 {error, _} -> 1164 ?P("~n we: ~p" 1165 "~n unused_ip: ~p", [Hent, IP]) 1166 catch 1167 _:_:_ -> 1168 ?P("~n we: ~p" 1169 "~n unused_ip: ~p", [Hent, IP]) 1170 end; 1171 true -> 1172 ?P("~n we: ~p" 1173 "~n unused_ip: ~p", [Hent, IP]) 1174 end, 1175 IP. 1176 1177unused_ip(255, 255, 255, 255) -> error; 1178unused_ip(255, B, C, D) -> unused_ip(1, B + 1, C, D); 1179unused_ip(A, 255, C, D) -> unused_ip(A, 1, C + 1, D); 1180unused_ip(A, B, 255, D) -> unused_ip(A, B, 1, D + 1); 1181unused_ip(A, B, C, D) -> 1182 case inet:gethostbyaddr({A, B, C, D}) of 1183 {ok, _} -> unused_ip(A + 1, B, C, D); 1184 {error, _} -> {ok, {A, B, C, D}} 1185 end. 1186 1187ok({ok,V}) -> V; 1188ok(NotOk) -> 1189 try throw(not_ok) 1190 catch 1191 throw:Thrown:Stacktrace -> 1192 erlang:raise( 1193 error, {Thrown, NotOk}, tl(Stacktrace)) 1194 end. 1195 1196get_localaddr() -> 1197 get_localaddr(["localhost", "localhost6", "ip6-localhost"]). 1198 1199get_localaddr([]) -> 1200 {error, localaddr_not_found}; 1201get_localaddr([Localhost|Ls]) -> 1202 case inet:getaddr(Localhost, inet6) of 1203 {ok, LocalAddr} -> 1204 ?P("~s ~p", [Localhost, LocalAddr]), 1205 {ok, LocalAddr}; 1206 _ -> 1207 get_localaddr(Ls) 1208 end. 1209 1210getsockfd() -> undefined. 1211closesockfd(_FD) -> undefined. 1212 1213local_filename(Tag) -> 1214 "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). 1215 1216bin_filename(String) -> 1217 unicode:characters_to_binary(String, file:native_name_encoding()). 1218 1219delete_local_filenames() -> 1220 _ = 1221 [file:delete(F) || 1222 F <- 1223 filelib:wildcard( 1224 "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], 1225 ok. 1226 1227 1228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1229 1230pi(Item) -> 1231 {Item, Val} = process_info(self(), Item), 1232 Val. 1233 1234 1235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1236 1237connect_failed_str(Reason) -> 1238 ?F("Connect failed: ~w", [Reason]). 1239 1240listen_failed_str(Reason) -> 1241 ?F("Listen failed: ~w", [Reason]). 1242 1243accept_failed_str(Reason) -> 1244 ?F("Accept failed: ~w", [Reason]). 1245 1246 1247