aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/doc/src/rpc.xml2
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl11
-rw-r--r--lib/kernel/src/inet_res.erl203
-rw-r--r--lib/kernel/src/net_kernel.erl2
-rw-r--r--lib/kernel/test/Makefile2
-rw-r--r--lib/kernel/test/file_SUITE.erl4
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl132
-rw-r--r--lib/kernel/test/heart_SUITE.erl13
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl127
-rw-r--r--lib/kernel/test/init_SUITE.erl69
-rw-r--r--lib/kernel/vsn.mk2
12 files changed, 443 insertions, 126 deletions
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index a22c0a8346..f05a224f33 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -432,7 +432,7 @@ fe80::204:acff:fe17:bf38
</desc>
</func>
<func>
- <name>port(Socket) -> {ok, Port}</name>
+ <name>port(Socket) -> {ok, Port} | {error, any()}</name>
<fsummary>Return the local port number for a socket</fsummary>
<type>
<v>Socket = socket()</v>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index 86c6ea9178..2b81de170d 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -454,7 +454,7 @@
</desc>
</func>
<func>
- <name>pmap({Module, Function}, ExtraArgs, List2) -> List1</name>
+ <name>pmap({Module, Function}, ExtraArgs, List1) -> List2</name>
<fsummary>Parallell evaluation of mapping a function over a list </fsummary>
<type>
<v>Module = Function = atom()</v>
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index f289b8110d..1d3eb926ca 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -258,7 +258,7 @@ find_callee_mfas(Patches) when is_list(Patches) ->
amd64 -> [];
arm -> find_callee_mfas(Patches, gb_sets:empty(), false);
powerpc -> find_callee_mfas(Patches, gb_sets:empty(), true);
- %% ppc64 -> find_callee_mfas(Patches, gb_sets:empty(), true);
+ ppc64 -> find_callee_mfas(Patches, gb_sets:empty(), true);
ultrasparc -> [];
x86 -> []
end.
@@ -301,6 +301,7 @@ mk_trampoline_map(CalleeMFAs, Trampolines) ->
SizeofLong =
case erlang:system_info(hipe_architecture) of
amd64 -> 8;
+ ppc64 -> 8;
_ -> 4
end,
mk_trampoline_map(tuple_size(CalleeMFAs), CalleeMFAs,
@@ -625,15 +626,15 @@ patch_instr(Address, Value, Type) ->
%%
%% XXX: It appears this is used for inserting both code addresses
%% and other data. In HiPE, code addresses are still 32-bit on
-%% 64-bit machines.
+%% some 64-bit machines.
write_word(DataAddress, DataWord) ->
case erlang:system_info(hipe_architecture) of
amd64 ->
hipe_bifs:write_u64(DataAddress, DataWord),
DataAddress+8;
- %% ppc64 ->
- %% hipe_bifs:write_u64(DataAddress, DataWord),
- %% DataAddress+8;
+ ppc64 ->
+ hipe_bifs:write_u64(DataAddress, DataWord),
+ DataAddress+8;
_ ->
hipe_bifs:write_u32(DataAddress, DataWord),
DataAddress+4
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index de0f23bf24..93563c6011 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -539,27 +539,41 @@ udp_send(#sock{inet=I}, {A,B,C,D}=IP, Port, Buffer)
when ?ip(A,B,C,D), ?port(Port) ->
gen_udp:send(I, IP, Port, Buffer).
-udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout)
+udp_recv(#sock{inet6=I}, {A,B,C,D,E,F,G,H}=IP, Port, Timeout, Decode)
when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) ->
- do_udp_recv(fun(T) -> gen_udp:recv(I, 0, T) end, IP, Port, Timeout);
-udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout)
+ do_udp_recv(I, IP, Port, Timeout, Decode, erlang:now(), Timeout);
+udp_recv(#sock{inet=I}, {A,B,C,D}=IP, Port, Timeout, Decode)
when ?ip(A,B,C,D), ?port(Port) ->
- do_udp_recv(fun(T) -> gen_udp:recv(I, 0, T) end, IP, Port, Timeout).
-
-do_udp_recv(Recv, IP, Port, Timeout) ->
- do_udp_recv(Recv, IP, Port, Timeout,
- if Timeout =/= 0 -> erlang:now(); true -> undefined end).
-
-do_udp_recv(Recv, IP, Port, Timeout, Then) ->
- case Recv(Timeout) of
- {ok,{IP,Port,Answer}} ->
- {ok,Answer,erlang:max(0, Timeout - now_ms(erlang:now(), Then))};
- {ok,_} when Timeout =:= 0 ->
- {error,timeout};
- {ok,_} ->
- Now = erlang:now(),
- T = erlang:max(0, Timeout - now_ms(Now, Then)),
- do_udp_recv(Recv, IP, Port, T, Now);
+ do_udp_recv(I, IP, Port, Timeout, Decode, erlang:now(), Timeout).
+
+do_udp_recv(_I, _IP, _Port, 0, _Decode, _Start, _T) ->
+ timeout;
+do_udp_recv(I, IP, Port, Timeout, Decode, Start, T) ->
+ case gen_udp:recv(I, 0, T) of
+ {ok,Reply} ->
+ case Decode(Reply) of
+ false when T =:= 0 ->
+ %% This is a compromize between the hard way i.e
+ %% in the clause below if NewT becomes 0 bailout
+ %% immediately and risk that the right reply lies
+ %% ahead after some bad id replies, and the
+ %% forgiving way i.e go on with Timeout 0 until
+ %% the right reply comes or no reply (timeout)
+ %% which opens for a DOS attack by a malicious
+ %% DNS server flooding with bad id replies causing
+ %% an infinite loop here.
+ %%
+ %% Timeout is used as a sanity limit counter
+ %% just to put an end to the loop.
+ NewTimeout = erlang:max(0, Timeout - 50),
+ do_udp_recv(I, IP, Port, NewTimeout, Decode, Start, T);
+ false ->
+ Now = erlang:now(),
+ NewT = erlang:max(0, Timeout - now_ms(Now, Start)),
+ do_udp_recv(I, IP, Port, Timeout, Decode, Start, NewT);
+ Result ->
+ Result
+ end;
Error -> Error
end.
@@ -580,6 +594,17 @@ udp_close(#sock{inet=I,inet6=I6}) ->
%% end
%% end
%%
+%% But that man page also says dig always use num_servers = 1.
+%%
+%% Our man page says: timeout/retry, then double for next retry, i.e
+%% for i = 0 to retry - 1
+%% foreach nameserver
+%% send query
+%% wait((time * (2**i)) / retry)
+%% end
+%% end
+%%
+%% And that is what the code seems to do, now fixed, hopefully...
do_query(_Q, [], _Timer) ->
{error,nxdomain};
@@ -589,19 +614,16 @@ do_query(#q{options=#options{retry=Retry}}=Q, NSs, Timer) ->
query_retries(_Q, _NSs, _Timer, Retry, Retry, S) ->
udp_close(S),
{error,timeout};
+query_retries(_Q, [], _Timer, _Retry, _I, S) ->
+ udp_close(S),
+ {error,timeout};
query_retries(Q, NSs, Timer, Retry, I, S0) ->
- Num = length(NSs),
- if Num =:= 0 ->
- udp_close(S0),
- {error,timeout};
- true ->
- case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
- {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers
- query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S);
- {S,Result} ->
- udp_close(S),
- Result
- end
+ case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
+ {S,{noanswer,ErrNSs}} -> %% remove unreachable nameservers
+ query_retries(Q, NSs--ErrNSs, Timer, Retry, I+1, S);
+ {S,Result} ->
+ udp_close(S),
+ Result
end.
query_nss(_Q, [], _Timer, _Retry, _I, S, ErrNSs) ->
@@ -611,13 +633,13 @@ query_nss(#q{edns=undefined}=Q, NSs, Timer, Retry, I, S, ErrNSs) ->
query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs) ->
query_nss_edns(Q, NSs, Timer, Retry, I, S, ErrNSs).
-query_nss_edns(#q{options=#options{udp_payload_size=PSz}=Options,
- edns={Id,Buffer}}=Q,
- [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) ->
- {S,Res}=Reply = query_ns(S0, Id, Buffer, IP, Port, Timer,
- Retry, I, Options, PSz),
+query_nss_edns(
+ #q{options=#options{udp_payload_size=PSz}=Options,edns={Id,Buffer}}=Q,
+ [{IP,Port}=NS|NSs]=NSs0, Timer, Retry, I, S0, ErrNSs) ->
+ {S,Res}=Reply =
+ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, PSz),
case Res of
- timeout -> {S,{error,timeout}};
+ timeout -> {S,{error,timeout}}; % Bailout timeout
{ok,_} -> Reply;
{error,{nxdomain,_}} -> Reply;
{error,{E,_}} when E =:= qfmterror; E =:= notimp; E =:= servfail;
@@ -629,17 +651,19 @@ query_nss_edns(#q{options=#options{udp_payload_size=PSz}=Options,
query_nss(Q, NSs, Timer, Retry, I, S, ErrNSs)
end.
-query_nss_dns(#q{dns=Qdns}=Q0, [{IP,Port}=NS|NSs],
- Timer, Retry, I, S0, ErrNSs) ->
+query_nss_dns(
+ #q{dns=Qdns}=Q0,
+ [{IP,Port}=NS|NSs], Timer, Retry, I, S0, ErrNSs) ->
#q{options=Options,dns={Id,Buffer}}=Q =
if
is_function(Qdns, 0) -> Q0#q{dns=Qdns()};
true -> Q0
end,
- {S,Res}=Reply = query_ns(S0, Id, Buffer, IP, Port, Timer,
- Retry, I, Options, ?PACKETSZ),
+ {S,Res}=Reply =
+ query_ns(
+ S0, Id, Buffer, IP, Port, Timer, Retry, I, Options, ?PACKETSZ),
case Res of
- timeout -> {S,{error,timeout}};
+ timeout -> {S,{error,timeout}}; % Bailout timeout
{ok,_} -> Reply;
{error,{E,_}} when E =:= nxdomain; E =:= qfmterror -> Reply;
{error,E} when E =:= fmt; E =:= enetunreach; E =:= econnrefused ->
@@ -653,48 +677,66 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I,
PSz) ->
case UseVC orelse iolist_size(Buffer) > PSz of
true ->
- {S0,query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose)};
+ TcpTimeout = inet:timeout(Tm*5, Timer),
+ {S0,query_tcp(TcpTimeout, Id, Buffer, IP, Port, Verbose)};
false ->
case udp_open(S0, IP) of
{ok,S} ->
- {S,case query_udp(S, Id, Buffer, IP, Port, Timer,
- Retry, I, Tm, Verbose) of
- {ok,#dns_rec{header=H}} when H#dns_header.tc ->
- query_tcp(Tm, Id, Buffer,
- IP, Port, Timer, Verbose);
- Reply -> Reply
- end};
+ Timeout =
+ inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
+ {S,
+ case query_udp(
+ S, Id, Buffer, IP, Port, Timeout, Verbose) of
+ {ok,#dns_rec{header=H}} when H#dns_header.tc ->
+ TcpTimeout = inet:timeout(Tm*5, Timer),
+ query_tcp(
+ TcpTimeout, Id, Buffer, IP, Port, Verbose);
+ Reply -> Reply
+ end};
Error ->
{S0,Error}
end
end.
-query_udp(S, Id, Buffer, IP, Port, Timer, Retry, I, Tm, Verbose) ->
- Timeout = inet:timeout( (Tm * (1 bsl I)) div Retry, Timer),
+query_udp(_S, _Id, _Buffer, _IP, _Port, 0, Verbose) ->
+ timeout;
+query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) ->
?verbose(Verbose, "Try UDP server : ~p:~p (timeout=~w)\n",
- [IP, Port, Timeout]),
- udp_connect(S, IP, Port),
- udp_send(S, IP, Port, Buffer),
- query_udp_recv(S, IP, Port, Id, Timeout, Verbose).
-
-query_udp_recv(S, IP, Port, Id, Timeout, Verbose) ->
- case udp_recv(S, IP, Port, Timeout) of
- {ok,Answer,T} ->
- case decode_answer(Answer, Id, Verbose) of
- {error, badid} ->
- query_udp_recv(S, IP, Port, Id, T, Verbose);
- Reply -> Reply
+ [IP,Port,Timeout]),
+ case
+ case udp_connect(S, IP, Port) of
+ ok ->
+ udp_send(S, IP, Port, Buffer);
+ E1 ->
+ E1 end of
+ ok ->
+ Decode =
+ fun ({RecIP,RecPort,Answer})
+ when RecIP =:= IP, RecPort =:= Port ->
+ case decode_answer(Answer, Id, Verbose) of
+ {error,badid} ->
+ false;
+ Reply ->
+ Reply
+ end;
+ ({_,_,_}) ->
+ false
+ end,
+ case udp_recv(S, IP, Port, Timeout, Decode) of
+ {ok,_}=Result ->
+ Result;
+ E2 ->
+ ?verbose(Verbose, "UDP server error: ~p\n", [E2]),
+ E2
end;
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "UDP server timeout\n", []),
- timeout;
- Error ->
- ?verbose(Verbose, "UDP server error: ~p\n", [Error]),
- Error
+ E3 ->
+ ?verbose(Verbose, "UDP send failed: ~p\n", [E3]),
+ {error,econnrefused}
end.
-query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose) ->
- Timeout = inet:timeout(Tm*5, Timer),
+query_tcp(0, _Id, _Buffer, _IP, _Port, Verbose) ->
+ timeout;
+query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) ->
?verbose(Verbose, "Try TCP server : ~p:~p (timeout=~w)\n",
[IP, Port, Timeout]),
Family = case IP of
@@ -716,19 +758,10 @@ query_tcp(Tm, Id, Buffer, IP, Port, Timer, Verbose) ->
end;
Error ->
gen_tcp:close(S),
- case Error of
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "TCP server recv timeout\n", []),
- timeout;
- _ ->
- ?verbose(Verbose, "TCP server recv error: ~p\n",
- [Error]),
- Error
- end
+ ?verbose(Verbose, "TCP server recv error: ~p\n",
+ [Error]),
+ Error
end;
- {error, timeout} when Timeout =:= 0 ->
- ?verbose(Verbose, "TCP server connect timeout\n", []),
- timeout;
Error ->
?verbose(Verbose, "TCP server error: ~p\n", [Error]),
Error
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 49a02359b0..5228d4fe01 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1249,7 +1249,7 @@ protocol_childspecs([H|T]) ->
epmd_module() ->
case init:get_argument(epmd_module) of
{ok,[[Module]]} ->
- Module;
+ list_to_atom(Module);
_ ->
erl_epmd
end.
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 5f8f3a6bf6..95517ffd6a 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -144,7 +144,7 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(APP_FILES) $(RELSYSDIR)
$(INSTALL_DATA) kernel.spec $(EMAKEFILE)\
$(COVERFILE) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 8078c7d021..2f73394c4e 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -2055,6 +2055,10 @@ try_read_file_list(Fd) ->
?line Title = "Real Programmers Don't Use PASCAL</TITLE>\n",
?line Title = io:get_line(Fd, ''),
+ %% Seek past the end of the file.
+
+ ?line {ok, _} = ?FILE_MODULE:position(Fd, 25000),
+
%% Done.
?line ?FILE_MODULE:close(Fd),
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 3b313a6c21..b1ef8826d5 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -39,7 +39,7 @@
accept_timeouts_in_order/1,accept_timeouts_in_order2/1,
accept_timeouts_in_order3/1,accept_timeouts_mixed/1,
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
- several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1,
+ several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1,
otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1]).
%% Internal exports.
@@ -71,7 +71,7 @@ all() ->
accept_timeouts_in_order3, accept_timeouts_mixed,
killing_acceptor, killing_multi_acceptors,
killing_multi_acceptors2, several_accepts_in_one_go,
- active_once_closed, send_timeout, otp_7731,
+ active_once_closed, send_timeout, send_timeout_active, otp_7731,
zombie_sockets, otp_7816, otp_8102].
groups() ->
@@ -1957,6 +1957,60 @@ send_timeout(Config) when is_list(Config) ->
ParaFun(false),
ParaFun(true),
ok.
+mad_sender(S) ->
+ {_, _, USec} = now(),
+ case gen_tcp:send(S, integer_to_list(USec)) of
+ ok ->
+ mad_sender(S);
+ Err ->
+ Err
+ end.
+
+
+flush() ->
+ receive
+ _X ->
+ %erlang:display(_X),
+ flush()
+ after 0 ->
+ ok
+ end.
+
+send_timeout_active(suite) ->
+ [];
+send_timeout_active(doc) ->
+ ["Test the send_timeout socket option for active sockets"];
+send_timeout_active(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ %% Basic
+ BasicFun =
+ fun(AutoClose) ->
+ ?line {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
+ inet:setopts(A, [{active, once}]),
+ ?line Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
+ ?line {error,timeout} =
+ Loop(fun() ->
+ receive
+ {tcp, Sock, _Data} ->
+ inet:setopts(A, [{active, once}]),
+ Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
+ %erlang:display(Res),
+ Res;
+ Err ->
+ io:format("sock closed: ~p~n", [Err]),
+ Err
+ end
+ end),
+ unlink(Mad),
+ exit(Mad,kill),
+ ?line test_server:stop_node(RNode)
+ end,
+ BasicFun(false),
+ flush(),
+ BasicFun(true),
+ flush(),
+ test_server:timetrap_cancel(Dog),
+ ok.
after_send_timeout(AutoClose) ->
case AutoClose of
@@ -2039,35 +2093,35 @@ setup_closed_ao() ->
{Loop,A}.
setup_timeout_sink(Timeout, AutoClose) ->
- Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
[{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
- Fun = fun(F) ->
+ ?line Fun = fun(F) ->
receive
{From,X} when is_function(X) ->
From ! {self(),X()}, F(F);
die -> ok
end
end,
- Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
- {ok, Port} = inet:port(L),
- Remote = fun(Fu) ->
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
Pid ! {self(), Fu},
receive {Pid,X} -> X
end
end,
- {ok, C} = Remote(fun() ->
+ ?line {ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
[{active,false},{packet,2}])
end),
- {ok,A} = gen_tcp:accept(L),
- gen_tcp:send(A,"Hello"),
- {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- Loop2 = fun(_,_,0) ->
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
{failure, timeout};
(L2,F2,N) ->
Ret = F2(),
@@ -2078,9 +2132,53 @@ setup_timeout_sink(Timeout, AutoClose) ->
Other -> Other
end
end,
- Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
{Loop,A,R}.
-
+
+setup_active_timeout_sink(Timeout, AutoClose) ->
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ [{args,"-pa " ++ Dir}]),
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
+ {send_timeout,Timeout},
+ {send_timeout_close,AutoClose}]),
+ ?line Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
+ end,
+ ?line {ok, C} = Remote(fun() ->
+ gen_tcp:connect(Host,Port,
+ [{active,false}])
+ end),
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
+ {failure, timeout};
+ (L2,F2,N) ->
+ Ret = F2(),
+ io:format("~p~n",[Ret]),
+ case Ret of
+ ok -> receive after 1 -> ok end,
+ L2(L2,F2,N-1);
+ Other -> Other
+ end
+ end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ {Loop,A,R,C}.
+
+
millistamp() ->
{Mega, Secs, Micros} = erlang:now(),
(Micros div 1000) + Secs * 1000 + Mega * 1000000000.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 043c753cf8..233e438dc9 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -22,7 +22,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2, start/1, restart/1,
- reboot/1, set_cmd/1, clear_cmd/1,
+ reboot/1, set_cmd/1, clear_cmd/1, get_cmd/1,
dont_drop/1, kill_pid/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -58,7 +58,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [start, restart, reboot, set_cmd, clear_cmd, kill_pid].
+ [start, restart, reboot, set_cmd, clear_cmd, get_cmd, kill_pid].
groups() ->
[].
@@ -246,6 +246,15 @@ clear_cmd(Config) when is_list(Config) ->
end,
ok.
+get_cmd(suite) -> [];
+get_cmd(Config) when is_list(Config) ->
+ ?line {ok, Node} = start_check(slave, heart_test),
+ Cmd = "test",
+ ?line ok = rpc:call(Node, heart, set_cmd, [Cmd]),
+ ?line {ok, Cmd} = rpc:call(Node, heart, get_cmd, []),
+ stop_node(Node),
+ ok.
+
dont_drop(suite) ->
%%% Removed as it may crash epmd/distribution in colourful
%%% ways. While we ARE finding out WHY, it would
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index 5fc8df475d..6064a9b2d9 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -27,7 +27,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2]).
--export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1]).
+-export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1,
+ last_ms_answer/1]).
-export([
gethostbyaddr/0, gethostbyaddr/1,
gethostbyaddr_v6/0, gethostbyaddr_v6/1,
@@ -45,6 +46,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, resolve, edns0, txt_record, files_monitor,
+ last_ms_answer,
gethostbyaddr, gethostbyaddr_v6, gethostbyname,
gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6,
host_and_addr].
@@ -64,16 +66,15 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-zone_dir(basic) ->
- otptest;
-zone_dir(resolve) ->
- otptest;
-zone_dir(edns0) ->
- otptest;
-zone_dir(files_monitor) ->
- otptest;
-zone_dir(_) ->
- undefined.
+zone_dir(TC) ->
+ case TC of
+ basic -> otptest;
+ resolve -> otptest;
+ edns0 -> otptest;
+ files_monitor -> otptest;
+ last_ms_answer -> otptest;
+ _ -> undefined
+ end.
init_per_testcase(Func, Config) ->
PrivDir = ?config(priv_dir, Config),
@@ -184,6 +185,88 @@ ns_printlog(Fname) ->
ok
end.
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Behaviour modifying nameserver proxy
+
+proxy_start(TC, {NS,P}) ->
+ Tag = make_ref(),
+ Parent = self(),
+ Pid =
+ spawn_link(
+ fun () ->
+ try proxy_start(TC, NS, P, Parent, Tag)
+ catch C:X ->
+ io:format(
+ "~w: ~w:~p ~p~n",
+ [self(),C,X,erlang:get_stacktrace()])
+ end
+ end),
+ receive {started,Tag,Port} ->
+ ProxyNS = {{127,0,0,1},Port},
+ {proxy,Pid,Tag,ProxyNS}
+ end.
+
+proxy_start(TC, NS, P, Parent, Tag) ->
+ {ok,Outbound} = gen_udp:open(0, [binary]),
+ ok = gen_udp:connect(Outbound, NS, P),
+ {ok,Inbound} = gen_udp:open(0, [binary]),
+ {ok,Port} = inet:port(Inbound),
+ Parent ! {started,Tag,Port},
+ proxy(TC, Outbound, NS, P, Inbound).
+
+
+%% To provoke the last_ms_answer bug (OTP-9221) the proxy
+%% * Relays the query to the right nameserver
+%% * Intercepts the reply but holds it until the timer that
+%% was started when receiving the query fires.
+%% * Repeats the reply with incorrect query ID a number of
+%% times with a short interval.
+%% * Sends the correct reply, to give a correct test result
+%% after bug correction.
+%%
+%% The repetition of an incorrect answer with tight interval will keep
+%% inet_res in an inner loop in the code that decrements the remaining
+%% time until it hits 0 which triggers a crash, if the outer timeout
+%% parameter to inet_res:resolve is so short that it runs out during
+%% these repetitions.
+proxy(last_ms_answer, Outbound, NS, P, Inbound) ->
+ receive
+ {udp,Inbound,SrcIP,SrcPort,Data} ->
+ Time =
+ inet_db:res_option(timeout) div inet_db:res_option(retry),
+ Tag = erlang:make_ref(),
+ erlang:send_after(Time - 10, self(), {time,Tag}),
+ ok = gen_udp:send(Outbound, NS, P, Data),
+ receive
+ {udp,Outbound,NS,P,Reply} ->
+ {ok,Msg} = inet_dns:decode(Reply),
+ Hdr = inet_dns:msg(Msg, header),
+ Id = inet_dns:header(Hdr, id),
+ BadHdr =
+ inet_dns:make_header(Hdr, id, (Id+1) band 16#ffff),
+ BadMsg = inet_dns:make_msg(Msg, header, BadHdr),
+ BadReply = inet_dns:encode(BadMsg),
+ receive
+ {time,Tag} ->
+ proxy__last_ms_answer(
+ Inbound, SrcIP, SrcPort, BadReply, Reply, 30)
+ end
+ end
+ end.
+
+proxy__last_ms_answer(Socket, IP, Port, _, Reply, 0) ->
+ ok = gen_udp:send(Socket, IP, Port, Reply);
+proxy__last_ms_answer(Socket, IP, Port, BadReply, Reply, N) ->
+ ok = gen_udp:send(Socket, IP, Port, BadReply),
+ receive after 1 -> ok end,
+ proxy__last_ms_answer(Socket, IP, Port, BadReply, Reply, N-1).
+
+proxy_wait({proxy,Pid,_,_}) ->
+ Mref = erlang:monitor(process, Pid),
+ receive {'DOWN',Mref,_,_,_} -> ok end.
+
+proxy_ns({proxy,_,_,ProxyNS}) -> ProxyNS.
+
%%
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -204,7 +287,7 @@ basic(Config) when is_list(Config) ->
{ok,Msg1} = inet_dns:decode(Bin1),
%%
%% resolve
- {ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]}]),
+ {ok,Msg2} = inet_res:resolve(Name, in, a, [{nameservers,[NS]},verbose]),
io:format("~p~n", [Msg2]),
[RR2] = inet_dns:msg(Msg2, anlist),
IP = inet_dns:rr(RR2, data),
@@ -474,6 +557,26 @@ do_files_monitor(Config) ->
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+last_ms_answer(doc) ->
+ ["Answer just when timeout is triggered (OTP-9221)"];
+last_ms_answer(Config) when is_list(Config) ->
+ NS = ns(Config),
+ Name = "ns.otptest",
+ %%IP = {127,0,0,254},
+ Time = inet_db:res_option(timeout) div inet_db:res_option(retry),
+ PSpec = proxy_start(last_ms_answer, NS),
+ ProxyNS = proxy_ns(PSpec),
+ %%
+ %% resolve; whith short timeout to trigger Timeout =:= 0 in inet_res
+ {error,timeout} =
+ inet_res:resolve(
+ Name, in, a, [{nameservers,[ProxyNS]},verbose], Time + 10),
+ %%
+ proxy_wait(PSpec),
+ ok.
+
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compatibility tests. Call the inet_SUITE tests, but with
%% lookup = [file,dns] instead of [native]
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 06bfe97bc4..2db0f7dcb8 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -24,6 +24,7 @@
init_per_group/2,end_per_group/2]).
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
+ many_restarts/1,
get_plain_arguments/1,
reboot/1, stop/1, get_status/1, script_id/1]).
-export([boot1/1, boot2/1]).
@@ -43,6 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[get_arguments, get_argument, boot_var,
+ many_restarts,
get_plain_arguments, restart, get_status, script_id,
{group, boot}].
@@ -317,6 +319,73 @@ is_real_system(KernelVsn, StdlibVsn) ->
%% Therefore the slave process must be killed
%% before restart.
%% ------------------------------------------------
+many_restarts(doc) -> [];
+many_restarts(suite) ->
+ case ?t:os_type() of
+ {Fam, _} when Fam == unix; Fam == win32 ->
+ {req, [distribution, {local_slave_nodes, 1}, {time, 5}]};
+ _ ->
+ {skip, "Only run on unix and win32"}
+ end;
+
+many_restarts(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(480)),
+ ?line {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC),
+ ?line loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])),
+ ?line loose_node:stop(Node),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+loop_restart(0,_,_) ->
+ ok;
+loop_restart(N,Node,EHPid) ->
+ ?line erlang:monitor_node(Node, true),
+ ?line ok = rpc:call(Node, init, restart, []),
+ ?line receive
+ {nodedown, Node} ->
+ ok
+ after 10000 ->
+ loose_node:stop(Node),
+ ?t:fail(not_stopping)
+ end,
+ ?line ok = wait_for(30, Node, EHPid),
+ ?line loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[error_logger])).
+
+wait_for(0,Node,_) ->
+ loose_node:stop(Node),
+ error;
+wait_for(N,Node,EHPid) ->
+ ?line case rpc:call(Node, erlang, whereis, [error_logger]) of
+ Pid when is_pid(Pid), Pid =/= EHPid ->
+ %% ?line erlang:display(ok),
+ ?line ok;
+ _X ->
+ %% ?line erlang:display(_X),
+ %% ?line Procs = rpc:call(Node, erlang, processes, []),
+ %% ?line erlang:display(Procs),
+ %% case is_list(Procs) of
+ %% true ->
+ %% ?line [(catch erlang:display(
+ %% rpc:call(Node,
+ %% erlang,
+ %% process_info,
+ %% [Y,registered_name])))
+ %% || Y <- Procs];
+ %% _ ->
+ %% ok
+ %% end,
+ receive
+ after 100 ->
+ ok
+ end,
+ ?line wait_for(N-1,Node,EHPid)
+ end.
+
+%% ------------------------------------------------
+%% Slave executes erlang:halt() on master nodedown.
+%% Therefore the slave process must be killed
+%% before restart.
+%% ------------------------------------------------
restart(doc) -> [];
restart(suite) ->
case ?t:os_type() of
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e33b90a274..e7b71cc168 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 2.14.3
+KERNEL_VSN = 2.14.4