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