aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/inets/doc/src/Makefile3
-rw-r--r--lib/inets/doc/src/httpd.xml10
-rw-r--r--lib/inets/doc/src/httpd_custom_api.xml63
-rw-r--r--lib/inets/doc/src/notes.xml18
-rw-r--r--lib/inets/doc/src/ref_man.xml3
-rw-r--r--lib/inets/src/http_server/Makefile3
-rw-r--r--lib/inets/src/http_server/httpd_custom.erl69
-rw-r--r--lib/inets/src/http_server/httpd_request.erl140
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl11
-rw-r--r--lib/inets/src/http_server/httpd_response.erl52
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/test/httpc_SUITE.erl23
-rw-r--r--lib/inets/test/httpd_SUITE.erl1256
-rw-r--r--lib/ssh/doc/src/notes.xml27
-rw-r--r--lib/ssh/src/ssh.erl13
-rw-r--r--lib/ssh/src/ssh_auth.erl80
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl51
-rw-r--r--lib/ssh/src/ssh_transport.erl9
-rw-r--r--lib/ssl/doc/src/notes.xml18
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl_handshake.erl32
21 files changed, 489 insertions, 1403 deletions
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index 1a8e1c7ca8..961bfa838d 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2015. 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
@@ -52,6 +52,7 @@ XML_REF3_FILES = \
httpc.xml\
httpd.xml \
httpd_conf.xml \
+ httpd_custom_api.xml \
httpd_socket.xml \
httpd_util.xml \
mod_alias.xml \
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index e40660ab39..435f99ee23 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -204,7 +204,15 @@
<marker id="props_limit"></marker>
<p><em>Limit properties</em> </p>
- <taglist>
+ <taglist>
+
+ <marker id="prop_customize"></marker>
+ <tag>{customize, atom()}</tag>
+ <item>
+ <p>A callback module to customize the inets HTTP servers behaviour
+ see <seealso marker="http_custom_api"> httpd_custom_api</seealso> </p>
+ </item>
+
<marker id="prop_disable_chunked_encoding"></marker>
<tag>{disable_chunked_transfer_encoding_send, boolean()}</tag>
<item>
diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml
new file mode 100644
index 0000000000..faf1d277df
--- /dev/null
+++ b/lib/inets/doc/src/httpd_custom_api.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>httpd_custom_api</title>
+ <file>httpd_custom_api.xml</file>
+ </header>
+ <module>httpd_custom_api</module>
+ <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary>
+ <description>
+ <p> The module implementing this behaviour shall be supplied to to the servers
+ configuration with the option <seealso marker="httpd:prop_customize"> customize</seealso></p>
+
+ </description>
+ <funcs>
+ <func>
+ <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP response headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP response headers before they are sent to the client.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP request headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP request headers before they are processed by the server.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
+
+
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index bae8e327a3..f563a8c4b0 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,23 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.8</title>
+ <section><title>Inets 5.10.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add behaviour with optional callbacks to customize the
+ inets HTTP server.</p>
+ <p>
+ Own Id: OTP-12776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index aaedf330b4..3afb020431 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -39,6 +39,7 @@
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
<xi:include href="httpd_conf.xml"/>
+ <xi:include href="httpd_custom_api.xml"/>
<xi:include href="httpd_socket.xml"/>
<xi:include href="httpd_util.xml"/>
<xi:include href="mod_alias.xml"/>
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 51e3dd9212..00bad51ff9 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2013. All Rights Reserved.
+# Copyright Ericsson AB 2005-2015. 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
@@ -46,6 +46,7 @@ MODULES = \
httpd_connection_sup\
httpd_cgi \
httpd_conf \
+ httpd_custom \
httpd_example \
httpd_esi \
httpd_file\
diff --git a/lib/inets/src/http_server/httpd_custom.erl b/lib/inets/src/http_server/httpd_custom.erl
new file mode 100644
index 0000000000..342469a579
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_custom.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(httpd_custom).
+
+-export([response_header/1, request_header/1]).
+-export([customize_headers/3]).
+
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
+response_header(Header) ->
+ {true, httpify(Header)}.
+request_header(Header) ->
+ {true, Header}.
+
+customize_headers(?MODULE, Function, Arg) ->
+ ?MODULE:Function(Arg);
+customize_headers(Module, Function, Arg) ->
+ try Module:Function(Arg) of
+ {true, Value} ->
+ ?MODULE:Function(Value);
+ false ->
+ false
+ catch
+ _:_ ->
+ ?MODULE:Function(Arg)
+ end.
+
+httpify({Key0, Value}) ->
+ %% make sure first letter is capital (defacto standard)
+ Words1 = string:tokens(Key0, "-"),
+ Words2 = upify(Words1, []),
+ Key = new_key(Words2),
+ Key ++ ": " ++ Value ++ ?CRLF .
+
+new_key([]) ->
+ "";
+new_key([W]) ->
+ W;
+new_key([W1,W2]) ->
+ W1 ++ "-" ++ W2;
+new_key([W|R]) ->
+ W ++ "-" ++ new_key(R).
+
+upify([], Acc) ->
+ lists:reverse(Acc);
+upify([Key|Rest], Acc) ->
+ upify(Rest, [upify2(Key)|Acc]).
+
+upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
+ [C-($a-$A)|Rest];
+upify2(Str) ->
+ Str.
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 3ff07616f9..782120c284 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -42,28 +42,28 @@
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
-parse([Bin, MaxSizes]) ->
- ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
- parse_method(Bin, [], 0, proplists:get_value(max_method, MaxSizes), MaxSizes, []);
+parse([Bin, Options]) ->
+ ?hdrt("parse", [{bin, Bin}, {max_sizes, Options}]),
+ parse_method(Bin, [], 0, proplists:get_value(max_method, Options), Options, []);
parse(Unknown) ->
?hdrt("parse", [{unknown, Unknown}]),
exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-parse_method([Bin, Method, Current, Max, MaxSizes, Result]) ->
- parse_method(Bin, Method, Current, Max, MaxSizes, Result).
+parse_method([Bin, Method, Current, Max, Options, Result]) ->
+ parse_method(Bin, Method, Current, Max, Options, Result).
-parse_uri([Bin, URI, Current, Max, MaxSizes, Result]) ->
- parse_uri(Bin, URI, Current, Max, MaxSizes, Result).
+parse_uri([Bin, URI, Current, Max, Options, Result]) ->
+ parse_uri(Bin, URI, Current, Max, Options, Result).
-parse_version([Bin, Rest, Version, Current, Max, MaxSizes, Result]) ->
- parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, MaxSizes,
+parse_version([Bin, Rest, Version, Current, Max, Options, Result]) ->
+ parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, Options,
Result).
-parse_headers([Bin, Rest, Header, Headers, Current, Max, MaxSizes, Result]) ->
+parse_headers([Bin, Rest, Header, Headers, Current, Max, Options, Result]) ->
parse_headers(<<Rest/binary, Bin/binary>>,
- Header, Headers, Current, Max, MaxSizes, Result).
+ Header, Headers, Current, Max, Options, Result).
whole_body([Bin, Body, Length]) ->
whole_body(<<Body/binary, Bin/binary>>, Length).
@@ -134,13 +134,13 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-parse_method(<<>>, Method, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_method, [Method, Current, Max, MaxSizes, Result]};
-parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, MaxSizes, Result) ->
- parse_uri(Rest, [], 0, proplists:get_value(max_uri, MaxSizes), MaxSizes,
+parse_method(<<>>, Method, Current, Max, Options, Result) ->
+ {?MODULE, parse_method, [Method, Current, Max, Options, Result]};
+parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, Options, Result) ->
+ parse_uri(Rest, [], 0, proplists:get_value(max_uri, Options), Options,
[string:strip(lists:reverse(Method)) | Result]);
-parse_method(<<Octet, Rest/binary>>, Method, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_method(Rest, [Octet | Method], Current + 1, Max, MaxSizes, Result);
+parse_method(<<Octet, Rest/binary>>, Method, Current, Max, Options, Result) when Current =< Max ->
+ parse_method(Rest, [Octet | Method], Current + 1, Max, Options, Result);
parse_method(_, _, _, Max, _, _) ->
%% We do not know the version of the client as it comes after the
%% method send the lowest version in the response so that the client
@@ -153,30 +153,30 @@ parse_uri(_, _, Current, MaxURI, _, _)
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
{error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()};
-parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]};
-parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) ->
- parse_version(Rest, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<>>, URI, Current, Max, Options, Result) ->
+ {?MODULE, parse_uri, [URI, Current, Max, Options, Result]};
+parse_uri(<<?SP, Rest/binary>>, URI, _, _, Options, Result) ->
+ parse_version(Rest, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, MaxSizes, Result) ->
- parse_version(Data, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes,
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, Options, Result) ->
+ parse_version(Data, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
-parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, MaxSizes, Result) ->
- parse_uri(Rest, [Octet | URI], Current + 1, Max, MaxSizes, Result).
+parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, Options, Result) ->
+ parse_uri(Rest, [Octet | URI], Current + 1, Max, Options, Result).
-parse_version(<<>>, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [<<>>, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result) ->
+parse_version(<<>>, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [<<>>, Version, Current, Max, Options, Result]};
+parse_version(<<?LF, Rest/binary>>, Version, Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result);
-parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, MaxSizes, Result) ->
- parse_headers(Rest, [], [], 0, proplists:get_value(max_header, MaxSizes), MaxSizes,
+ parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, Options, Result);
+parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, Options, Result) ->
+ parse_headers(Rest, [], [], 0, proplists:get_value(max_header, Options), Options,
[string:strip(lists:reverse(Version)) | Result]);
-parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) ->
- {?MODULE, parse_version, [Data, Version, Current, Max, MaxSizes, Result]};
-parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max ->
- parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result);
+parse_version(<<?CR>> = Data, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [Data, Version, Current, Max, Options, Result]};
+parse_version(<<Octet, Rest/binary>>, Version, Current, Max, Options, Result) when Current =< Max ->
+ parse_version(Rest, [Octet | Version], Current + 1, Max, Options, Result);
parse_version(_, _, _, Max,_,_) ->
{error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}.
@@ -185,34 +185,42 @@ parse_headers(_, _, _, Current, Max, _, Result)
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion};
-parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) ->
+parse_headers(<<>>, Header, Headers, Current, Max, Options, Result) ->
{?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
-parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) ->
+parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
- MaxSizes, Result) ->
+ Options, Result) ->
+ Customize = proplists:get_value(customize, Options),
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(Headers, #http_request_h{}), Headers} | Result]))};
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end,
+ Headers),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), FinalHeaders} | Result]))};
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
- {ok, list_to_tuple(lists:reverse([Body, {http_request:headers([NewHeader | Headers],
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end, [NewHeader | Headers]),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders,
#http_request_h{}),
- [NewHeader | Headers]} | Result]))};
+ FinalHeaders} | Result]))};
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
@@ -221,12 +229,12 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
end;
parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
-parse_headers(<<?LF>>, [], [], Current, Max, MaxSizes, Result) ->
+ Options, Result]};
+parse_headers(<<?LF>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, [], [], Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, [], [], Current, Max, Options, Result);
%% There where no headers, which is unlikely to happen.
parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
@@ -235,30 +243,30 @@ parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
{ok, NewResult};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, Options, Result);
parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
case http_request:key_value(lists:reverse(Header)) of
undefined -> %% Skip headers with missing :
parse_headers(Rest, [Octet], Headers,
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
NewHeader ->
- case check_header(NewHeader, MaxSizes) of
+ case check_header(NewHeader, Options) of
ok ->
parse_headers(Rest, [Octet], [NewHeader | Headers],
- 0, Max, MaxSizes, Result);
+ 0, Max, Options, Result);
{error, Reason} ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
{error, Reason, HttpVersion}
@@ -266,19 +274,19 @@ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
end;
parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
{?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
- MaxSizes, Result]};
+ Options, Result]};
parse_headers(<<?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result) ->
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max,
- MaxSizes, Result);
+ Options, Result);
parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current,
- Max, MaxSizes, Result) ->
+ Max, Options, Result) ->
parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max,
- MaxSizes, Result).
+ Options, Result).
whole_body(Body, Length) ->
case size(Body) of
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index f7a9fe5d49..9947e17b47 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -121,13 +121,15 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
MaxURISize = max_uri_size(ConfigDB),
NrOfRequest = max_keep_alive_request(ConfigDB),
MaxContentLen = max_content_length(ConfigDB),
+ Customize = customize(ConfigDB),
{_, Status} = httpd_manager:new_connection(Manager),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
State = #state{mod = Mod,
@@ -550,11 +552,13 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
MaxHeaderSize = max_header_size(ModData#mod.config_db),
MaxURISize = max_uri_size(ModData#mod.config_db),
MaxContentLen = max_content_length(ModData#mod.config_db),
+ Customize = customize(ModData#mod.config_db),
MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
{max_version, ?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, MaxContentLen}
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
]]},
TmpState = State#state{mod = NewModData,
mfa = MFA,
@@ -640,3 +644,6 @@ max_keep_alive_request(ConfigDB) ->
max_content_length(ConfigDB) ->
httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
+
+customize(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, customize, httpd_custom).
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 2fa91d47a0..71dc05e46d 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2015. 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
@@ -176,7 +176,7 @@ send_header(#mod{socket_type = Type,
StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
httpd_util:reason_phrase(NewStatusCode), ?CRLF],
ConnectionHeader = get_connection(Conn, NewVer),
- Head = list_to_binary([StatusLine, Headers, ConnectionHeader , ?CRLF]),
+ Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
httpd_socket:deliver(Type, Sock, Head).
map_status_code("HTTP/1.0", Code)
@@ -286,45 +286,21 @@ create_header(ConfigDb, KeyValueTupleHeaders) ->
Date = httpd_util:rfc1123_date(),
ContentType = "text/html",
Server = server(ConfigDb),
- NewHeaders = add_default_headers([{"date", Date},
- {"content-type", ContentType}
- | if Server=="" -> [];
- true -> [{"server", Server}]
- end
- ],
- KeyValueTupleHeaders),
- lists:map(fun fix_header/1, NewHeaders).
-
-
+ Headers0 = add_default_headers([{"date", Date},
+ {"content-type", ContentType}
+ | if Server=="" -> [];
+ true -> [{"server", Server}]
+ end
+ ],
+ KeyValueTupleHeaders),
+ CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
+ lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(CustomizeCB, response_header, H)
+ end,
+ [Header || Header <- Headers0]).
server(ConfigDb) ->
httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
-fix_header({Key0, Value}) ->
- %% make sure first letter is capital
- Words1 = string:tokens(Key0, "-"),
- Words2 = upify(Words1, []),
- Key = new_key(Words2),
- Key ++ ": " ++ Value ++ ?CRLF .
-
-new_key([]) ->
- "";
-new_key([W]) ->
- W;
-new_key([W1,W2]) ->
- W1 ++ "-" ++ W2;
-new_key([W|R]) ->
- W ++ "-" ++ new_key(R).
-
-upify([], Acc) ->
- lists:reverse(Acc);
-upify([Key|Rest], Acc) ->
- upify(Rest, [upify2(Key)|Acc]).
-
-upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
- [C-($a-$A)|Rest];
-upify2(Str) ->
- Str.
-
add_default_headers([], Headers) ->
Headers;
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index b7c3e341e8..6ba9795d9e 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -63,6 +63,7 @@
httpd_cgi,
httpd_connection_sup,
httpd_conf,
+ httpd_custom,
httpd_esi,
httpd_example,
httpd_file,
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0dfc65e8f7..ab7ffadf75 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1289,7 +1289,9 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
{max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}]]},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
[], ListenSocket);
dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
@@ -1305,7 +1307,8 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
{max_method, ?HTTP_MAX_METHOD_STRING},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]]},
[], ListenSocket).
@@ -1384,18 +1387,20 @@ handle_request(Module, Function, Args, Socket) ->
stop;
<<>> ->
{httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
- {max_version,?HTTP_MAX_VERSION_STRING},
- {max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
- ]]};
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]};
Data ->
handle_request(httpd_request, parse,
[Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
- {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
{max_version,?HTTP_MAX_VERSION_STRING},
{max_method, ?HTTP_MAX_METHOD_STRING},
- {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
]], Socket)
end;
NewMFA ->
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 7670c2cc60..854ffa8981 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -53,6 +53,8 @@ all() ->
{group, https_basic},
{group, http_limit},
{group, https_limit},
+ {group, http_custom},
+ {group, https_custom},
{group, http_basic_auth},
{group, https_basic_auth},
{group, http_auth_api},
@@ -76,6 +78,8 @@ groups() ->
{https_basic, [], basic_groups()},
{http_limit, [], [{group, limit}]},
{https_limit, [], [{group, limit}]},
+ {http_custom, [], [{group, custom}]},
+ {https_custom, [], [{group, custom}]},
{http_basic_auth, [], [{group, basic_auth}]},
{https_basic_auth, [], [{group, basic_auth}]},
{http_auth_api, [], [{group, auth_api}]},
@@ -92,1253 +96,7 @@ groups() ->
{https_reload, [], [{group, reload}]},
{http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]},
{limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
- {reload, [], [non_disturbing_reconfiger_dies,
- disturbing_reconfiger_dies,
- non_disturbing_1_1,
- non_disturbing_1_0,
- non_disturbing_0_9,
- disturbing_1_1,
- disturbing_1_0,
- disturbing_0_9
- ]},
- {basic_auth, [], [basic_auth_1_1, basic_auth_1_0, basic_auth_0_9]},
- {auth_api, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
- ]},
- {auth_api_dets, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
- ]},
- {auth_api_mnesia, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
- ]},
- {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
- {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
- {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since] ++ http_head() ++ http_get() ++ load()},
- {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
- {http_0_9, [], http_head() ++ http_get() ++ load()}
- ].
-
-basic_groups ()->
- [{group, http_1_1},
- {group, http_1_0},
- {group, http_0_9}
- ].
-
-http_head() ->
- [head].
-http_get() ->
- [alias,
- get,
- %%actions, Add configuration so that this test mod_action
- esi,
- content_length,
- bad_hex,
- missing_CR,
- max_header,
- max_content_length,
- ipv6
- ].
-
-load() ->
- [light, medium
- %%,heavy
- ].
-
-init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
- inets_test_lib:stop_apps([inets]),
- ServerRoot = filename:join(PrivDir, "server_root"),
- inets_test_lib:del_dirs(ServerRoot),
- DocRoot = filename:join(ServerRoot, "htdocs"),
- setup_server_dirs(ServerRoot, DocRoot, DataDir),
- {ok, Hostname0} = inet:gethostname(),
- Inet =
- case (catch ct:get_config(ipv6_hosts)) of
- undefined ->
- inet;
- Hosts when is_list(Hosts) ->
- case lists:member(list_to_atom(Hostname0), Hosts) of
- true ->
- inet6;
- false ->
- inet
- end;
- _ ->
- inet
- end,
- [{server_root, ServerRoot},
- {doc_root, DocRoot},
- {ipfamily, Inet},
- {node, node()},
- {host, inets_test_lib:hostname()},
- {address, getaddr()} | Config].
-
-end_per_suite(_Config) ->
- ok.
-
-%%--------------------------------------------------------------------
-init_per_group(Group, Config0) when Group == https_basic;
- Group == https_limit;
- Group == https_basic_auth;
- Group == https_auth_api;
- Group == https_auth_api_dets;
- Group == https_auth_api_mnesia;
- Group == https_security;
- Group == https_reload
- ->
- init_ssl(Group, Config0);
-init_per_group(Group, Config0) when Group == http_basic;
- Group == http_limit;
- Group == http_basic_auth;
- Group == http_auth_api;
- Group == http_auth_api_dets;
- Group == http_auth_api_mnesia;
- Group == http_security;
- Group == http_reload;
- Group == http_mime_types
- ->
- ok = start_apps(Group),
- init_httpd(Group, [{type, ip_comm} | Config0]);
-init_per_group(http_1_1, Config) ->
- [{http_version, "HTTP/1.1"} | Config];
-init_per_group(http_1_0, Config) ->
- [{http_version, "HTTP/1.0"} | Config];
-init_per_group(http_0_9, Config) ->
- case {os:type(), os:version()} of
- {{win32, _}, {5,1,2600}} ->
- {skip, "eaddrinuse XP problem"};
- _ ->
- [{http_version, "HTTP/0.9"} | Config]
- end;
-init_per_group(http_htaccess = Group, Config) ->
- Path = ?config(doc_root, Config),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
- ok = start_apps(Group),
- init_httpd(Group, [{type, ip_comm} | Config]);
-init_per_group(https_htaccess = Group, Config) ->
- Path = ?config(doc_root, Config),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
- init_ssl(Group, Config);
-init_per_group(auth_api, Config) ->
- [{auth_prefix, ""} | Config];
-init_per_group(auth_api_dets, Config) ->
- [{auth_prefix, "dets_"} | Config];
-init_per_group(auth_api_mnesia, Config) ->
- start_mnesia(?config(node, Config)),
- [{auth_prefix, "mnesia_"} | Config];
-init_per_group(_, Config) ->
- Config.
-
-end_per_group(Group, _Config) when Group == http_basic;
- Group == http_limit;
- Group == http_basic_auth;
- Group == http_auth_api;
- Group == http_auth_api_dets;
- Group == http_auth_api_mnesia;
- Group == http_htaccess;
- Group == http_security;
- Group == http_reload;
- Group == http_mime_types
- ->
- inets:stop();
-end_per_group(Group, _Config) when Group == https_basic;
- Group == https_limit;
- Group == https_basic_auth;
- Group == https_auth_api;
- Group == https_auth_api_dets;
- Group == https_auth_api_mnesia;
- Group == https_htaccess;
- Group == https_security;
- Group == https_reload
- ->
- ssl:stop(),
- inets:stop();
-
-end_per_group(auth_api_mnesia, _) ->
- cleanup_mnesia();
-
-end_per_group(_, _) ->
- ok.
-
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) when Case == host; Case == trace ->
- Prop = ?config(tc_group_properties, Config),
- Name = proplists:get_value(name, Prop),
- Cb = case Name of
- http_1_0 ->
- httpd_1_0;
- http_1_1 ->
- httpd_1_1
- end,
- [{version_cb, Cb} | proplists:delete(version_cb, Config)];
-
-init_per_testcase(range, Config) ->
- DocRoot = ?config(doc_root, Config),
- create_range_data(DocRoot),
- Config;
-
-init_per_testcase(_, Config) ->
- Config.
-
-end_per_testcase(_Case, _Config) ->
- ok.
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-head() ->
- [{doc, "HTTP HEAD request for static page"}].
-
-head(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("HEAD /index.html ", Version, Host),
- [{statuscode, head_status(Version)},
- {version, Version}]).
-
-get() ->
- [{doc, "HTTP GET request for static page"}].
-
-get(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
- transport_opts(Type, Config),
- ?config(node, Config),
- http_request("GET /index.html ", Version, Host),
- [{statuscode, 200},
- {header, "Content-Type", "text/html"},
- {header, "Date"},
- {header, "Server"},
- {version, Version}]).
-
-basic_auth_1_1(Config) when is_list(Config) ->
- basic_auth([{http_version, "HTTP/1.1"} | Config]).
-
-basic_auth_1_0(Config) when is_list(Config) ->
- basic_auth([{http_version, "HTTP/1.0"} | Config]).
-
-basic_auth_0_9(Config) when is_list(Config) ->
- basic_auth([{http_version, "HTTP/0.9"} | Config]).
-
-basic_auth() ->
- [{doc, "Test Basic authentication with WWW-Authenticate header"}].
-
-basic_auth(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- basic_auth_requiered(Config),
- %% Authentication OK! ["one:OnePassword" user first in user list]
- ok = auth_status(auth_request("/open/dummy.html", "one", "onePassword", Version, Host), Config,
- [{statuscode, 200}]),
- %% Authentication OK and a directory listing is supplied!
- %% ["Aladdin:open sesame" user second in user list]
- ok = auth_status(auth_request("/open/", "Aladdin", "AladdinPassword", Version, Host), Config,
- [{statuscode, 200}]),
- %% User correct but wrong password! ["one:one" user first in user list]
- ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config,
- [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
- %% Make sure Authenticate header is received even the second time
- %% we try a incorrect password! Otherwise a browser client will hang!
- ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config,
- [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
- %% Neither user or password correct! ["dummy:dummy"]
- ok = auth_status(auth_request("/open/dummy.html", "dummy", "dummy", Version, Host), Config,
- [{statuscode, 401}]),
- %% Nested secret/top_secret OK! ["Aladdin:open sesame"]
- ok = http_status(auth_request("/secret/top_secret/", "Aladdin", "AladdinPassword", Version, Host),
- Config, [{statuscode, 200}]),
- %% Authentication still required!
- basic_auth_requiered(Config).
-
-auth_api_1_1(Config) when is_list(Config) ->
- auth_api([{http_version, "HTTP/1.1"} | Config]).
-
-auth_api_1_0(Config) when is_list(Config) ->
- auth_api([{http_version, "HTTP/1.0"} | Config]).
-
-auth_api_0_9(Config) when is_list(Config) ->
- auth_api([{http_version, "HTTP/0.9"} | Config]).
-
-auth_api() ->
- [{doc, "Test mod_auth API"}].
-
-auth_api(Config) when is_list(Config) ->
- Prefix = ?config(auth_prefix, Config),
- do_auth_api(Prefix, Config).
-
-do_auth_api(AuthPrefix, Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
- ServerRoot = ?config(server_root, Config),
- ok = http_status("GET / ", Config,
- [{statuscode, 200}]),
- ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Make sure Authenticate header is received even the second time
- %% we try a incorrect password! Otherwise a browser client will hang!
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "dummy", "WrongPassword", Version, Host), Config,
- [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/", "dummy", "WrongPassword",
- Version, Host), Config, [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
-
- %% Change the password to DummyPassword then try to add a user
- %% Get an error and set it to NoPassword
- ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix,
- "open", "NoPassword", "DummyPassword"),
- {error,bad_password} =
- add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
- "onePassword", []),
- ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix, "open",
- "DummyPassword", "NoPassword"),
-
- %% Test /*open, require user one Aladdin
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
-
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "one", "onePassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "two", "twoPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "Aladdin", "onePassword", Version, Host),
- Config, [{statuscode, 401}]),
-
- true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
- "onePassword", []),
- true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "two",
- "twoPassword", []),
- true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "Aladdin",
- "AladdinPassword", []),
- {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
- AuthPrefix, "open"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "one", "WrongPassword", Version, Host),
- Config, [{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "one", "onePassword", Version, Host),
- Config, [{statuscode, 200}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "two", "twoPassword", Version, Host),
- Config,[{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "Aladdin", "WrongPassword", Version, Host),
- Config,[{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
- "Aladdin", "AladdinPassword", Version, Host),
- Config, [{statuscode, 200}]),
-
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
- {ok, []} = list_users(Node, ServerRoot, Host, Port,
- AuthPrefix, "open"),
-
- %% Phase 2
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
- {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix,
- "secret"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "one", "onePassword", Version, Host),
- Config, [{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "two", "twoPassword", Version, Host),
- Config, [{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "three", "threePassword", Version, Host),
- Config, [{statuscode, 401}]),
- add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "one",
- "onePassword",
- []),
- add_user(Node, ServerRoot, Port, AuthPrefix, "secret",
- "two", "twoPassword", []),
- add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "Aladdin",
- "AladdinPassword",[]),
- add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
- "one", "group1"),
- add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
- "two", "group1"),
- add_group_member(Node, ServerRoot, Port, AuthPrefix,
- "secret", "Aladdin", "group2"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "one", "onePassword", Version, Host),
- Config, [{statuscode, 200}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "two", "twoPassword", Version, Host),
- Config,[{statuscode, 200}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "Aladdin", "AladdinPassword", Version, Host),
- Config, [{statuscode, 200}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
- "three", "threePassword", Version, Host),
- Config, [{statuscode, 401}]),
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
- {ok, []} = list_users(Node, ServerRoot, Host, Port,
- AuthPrefix, "secret"),
- remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
-
- {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
-
- %% Phase 3
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
- remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
-
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/",
- "three", "threePassword", Version, Host),
- Config, [{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/", "two", "twoPassword", Version, Host),
- Config, [{statuscode, 401}]),
- add_user(Node, ServerRoot, Port, AuthPrefix,
- "secret/top_secret","three",
- "threePassword",[]),
- add_user(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret",
- "two","twoPassword", []),
- add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "three", "group3"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/", "three", "threePassword",
- Version, Host),
- Config, [{statuscode, 200}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/", "two", "twoPassword", Version, Host),
- Config, [{statuscode, 401}]),
- add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "two", "group3"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/",
- "two", "twoPassword", Version, Host),
- Config, [{statuscode, 200}]),
- remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
- {ok, []} = list_users(Node, ServerRoot, Host, Port,
- AuthPrefix, "secret/top_secret"),
- remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
- {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/", "two", "twoPassword", Version, Host),
- Config, [{statuscode, 401}]),
- ok = auth_status(auth_request("/" ++ AuthPrefix ++
- "secret/top_secret/","three", "threePassword", Version, Host),
- Config, [{statuscde, 401}]).
-%%-------------------------------------------------------------------------
-ipv6() ->
- [{require, ipv6_hosts},
- {doc,"Test ipv6."}].
-ipv6(Config) when is_list(Config) ->
- {ok, Hostname0} = inet:gethostname(),
- case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
- true ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- URI = http_request("GET / ", Version, Host),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), [inet6],
- ?config(code, Config),
- URI,
- [{statuscode, 200}, {version, Version}]);
- false ->
- {skip, "Host does not support IPv6"}
- end.
-
-%%-------------------------------------------------------------------------
-htaccess_1_1(Config) when is_list(Config) ->
- htaccess([{http_version, "HTTP/1.1"} | Config]).
-
-htaccess_1_0(Config) when is_list(Config) ->
- htaccess([{http_version, "HTTP/1.0"} | Config]).
-
-htaccess_0_9(Config) when is_list(Config) ->
- htaccess([{http_version, "HTTP/0.9"} | Config]).
-
-htaccess() ->
- [{doc, "Test mod_auth API"}].
-
-htaccess(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
- %% Control that authentication required!
- %% Control that the pages that shall be
- %% authenticated really need authenticatin
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/open/ ", Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/secret/ ", Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/secret/top_secret/ ",
- Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]),
-
- %% Make sure Authenticate header is received even the second time
- %% we try a incorrect password! Otherwise a browser client will hang!
- ok = auth_status(auth_request("/ht/open/",
- "dummy", "WrongPassword", Version, Host), Config,
- [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
- ok = auth_status(auth_request("/ht/open/",
- "dummy", "WrongPassword", Version, Host), Config,
- [{statuscode, 401},
- {header, "WWW-Authenticate"}]),
-
- %% Control that not just the first user in the list is valid
- %% Control the first user
- %% Authennticating ["one:OnePassword" user first in user list]
- ok = auth_status(auth_request("/ht/open/dummy.html", "one", "OnePassword",
- Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Control the second user
- %% Authentication OK and a directory listing is supplied!
- %% ["Aladdin:open sesame" user second in user list]
- ok = auth_status(auth_request("/ht/open/","Aladdin",
- "AladdinPassword", Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Contro that bad passwords and userids get a good denial
- %% User correct but wrong password! ["one:one" user first in user list]
- ok = auth_status(auth_request("/ht/open/", "one", "one", Version, Host), Config,
- [{statuscode, 401}]),
- %% Neither user or password correct! ["dummy:dummy"]
- ok = auth_status(auth_request("/ht/open/", "dummy", "dummy", Version, Host), Config,
- [{statuscode, 401}]),
-
- %% Control that authetication still works, even if its a member in a group
- %% Authentication OK! ["two:TwoPassword" user in first group]
- ok = auth_status(auth_request("/ht/secret/dummy.html", "two",
- "TwoPassword", Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Authentication OK and a directory listing is supplied!
- %% ["three:ThreePassword" user in second group]
- ok = auth_status(auth_request("/ht/secret/", "three",
- "ThreePassword", Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Deny users with bad passwords even if the user is a group member
- %% User correct but wrong password! ["two:two" user in first group]
- ok = auth_status(auth_request("/ht/secret/", "two", "two", Version, Host), Config,
- [{statuscode, 401}]),
- %% Neither user or password correct! ["dummy:dummy"]
- ok = auth_status(auth_request("/ht/secret/", "dummy", "dummy", Version, Host), Config,
- [{statuscode, 401}]),
-
- %% control that we deny the users that are in subnet above the allowed
- ok = auth_status(auth_request("/ht/blocknet/dummy.html", "four",
- "FourPassword", Version, Host), Config,
- [{statuscode, 403}]),
- %% Control that we only applies the rules to the right methods
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("HEAD /ht/blocknet/dummy.html ", Version, Host),
- [{statuscode, head_status(Version)},
- {version, Version}]),
-
- %% Control that the rerquire directive can be overrideen
- ok = auth_status(auth_request("/ht/secret/top_secret/ ", "Aladdin", "AladdinPassword",
- Version, Host), Config,
- [{statuscode, 401}]),
-
- %% Authentication still required!
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/open/ ", Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/secret/ ", Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]),
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- http_request("GET /ht/secret/top_secret/ ", Version, Host),
- [{statuscode, 401},
- {version, Version},
- {header, "WWW-Authenticate"}]).
-
-%%-------------------------------------------------------------------------
-host() ->
- [{doc, "Test host header"}].
-
-host(Config) when is_list(Config) ->
- Cb = ?config(version_cb, Config),
- Cb:host(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
-%%-------------------------------------------------------------------------
-chunked() ->
- [{doc, "Check that the server accepts chunked requests."}].
-
-chunked(Config) when is_list(Config) ->
- httpd_1_1:chunked(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
-%%-------------------------------------------------------------------------
-expect() ->
- ["Check that the server handles request with the expect header "
- "field appropiate"].
-expect(Config) when is_list(Config) ->
- httpd_1_1:expect(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
-%%-------------------------------------------------------------------------
-max_clients_1_1() ->
- [{doc, "Test max clients limit"}].
-
-max_clients_1_1(Config) when is_list(Config) ->
- do_max_clients([{http_version, "HTTP/1.1"} | Config]).
-
-max_clients_1_0() ->
- [{doc, "Test max clients limit"}].
-
-max_clients_1_0(Config) when is_list(Config) ->
- do_max_clients([{http_version, "HTTP/1.0"} | Config]).
-
-max_clients_0_9() ->
- [{doc, "Test max clients limit"}].
-
-max_clients_0_9(Config) when is_list(Config) ->
- do_max_clients([{http_version, "HTTP/0.9"} | Config]).
-%%-------------------------------------------------------------------------
-esi() ->
- [{doc, "Test mod_esi"}].
-
-esi(Config) when is_list(Config) ->
- ok = http_status("GET /eval?httpd_example:print(\"Hi!\") ",
- Config, [{statuscode, 200}]),
- ok = http_status("GET /eval?not_allowed:print(\"Hi!\") ",
- Config, [{statuscode, 403}]),
- ok = http_status("GET /eval?httpd_example:undef(\"Hi!\") ",
- Config, [{statuscode, 500}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example ",
- Config, [{statuscode, 400}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
- Config, [{statuscode, 200}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example:"
- "get?input=4711 ", Config,
- [{statuscode, 200}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example:post ",
- Config, [{statuscode, 200}]),
- ok = http_status("GET /cgi-bin/erl/not_allowed:post ",
- Config, [{statuscode, 403}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example:undef ",
- Config, [{statuscode, 404}]),
- ok = http_status("GET /cgi-bin/erl/httpd_example/yahoo ",
- Config, [{statuscode, 302}]),
- %% Check "ErlScriptNoCache" directive (default: false)
- ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
- Config, [{statuscode, 200},
- {no_header, "cache-control"}]).
-%%-------------------------------------------------------------------------
-cgi() ->
- [{doc, "Test mod_cgi"}].
-
-cgi(Config) when is_list(Config) ->
- {Script, Script2, Script3} =
- case test_server:os_type() of
- {win32, _} ->
- {"printenv.bat", "printenv.sh", "cgi_echo.exe"};
- _ ->
- {"printenv.sh", "printenv.bat", "cgi_echo"}
- end,
-
- %%The length (> 100) is intentional
- ok = http_status("POST /cgi-bin/" ++ Script3 ++ " ",
- {"Content-Length:100 \r\n",
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
- "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"},
- Config,
- [{statuscode, 200},
- {header, "content-type", "text/plain"}]),
-
- ok = http_status("GET /cgi-bin/"++ Script ++ " ", Config, [{statuscode, 200}]),
-
- ok = http_status("GET /cgi-bin/not_there ", Config,
- [{statuscode, 404}, {statuscode, 500}]),
-
- ok = http_status("GET /cgi-bin/"++ Script ++ "?Nisse:kkk?sss/lll ",
- Config,
- [{statuscode, 200}]),
-
- ok = http_status("POST /cgi-bin/"++ Script ++ " ", Config,
- [{statuscode, 200}]),
-
- ok = http_status("GET /htbin/"++ Script ++ " ", Config,
- [{statuscode, 200}]),
-
- ok = http_status("GET /htbin/not_there ", Config,
- [{statuscode, 404},{statuscode, 500}]),
-
- ok = http_status("GET /htbin/"++ Script ++ "?Nisse:kkk?sss/lll ", Config,
- [{statuscode, 200}]),
-
- ok = http_status("POST /htbin/"++ Script ++ " ", Config,
- [{statuscode, 200}]),
-
- ok = http_status("POST /htbin/"++ Script ++ " ", Config,
- [{statuscode, 200}]),
-
- %% Execute an existing, but bad CGI script..
- ok = http_status("POST /htbin/"++ Script2 ++ " ", Config,
- [{statuscode, 404}]),
-
- ok = http_status("POST /cgi-bin/"++ Script2 ++ " ", Config,
- [{statuscode, 404}]),
-
- %% Check "ScriptNoCache" directive (default: false)
- ok = http_status("GET /cgi-bin/" ++ Script ++ " ", Config,
- [{statuscode, 200},
- {no_header, "cache-control"}]).
-%%-------------------------------------------------------------------------
-cgi_chunked_encoding_test() ->
- [{doc, "Test chunked encoding together with mod_cgi "}].
-cgi_chunked_encoding_test(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Script =
- case test_server:os_type() of
- {win32, _} ->
- "/cgi-bin/printenv.bat";
- _ ->
- "/cgi-bin/printenv.sh"
- end,
- Requests =
- ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
- "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
- ++ Host ++"\r\n\r\n"],
- httpd_1_1:mod_cgi_chunked_encoding_test(?config(type, Config), ?config(port, Config),
- Host,
- ?config(node, Config),
- Requests).
-%%-------------------------------------------------------------------------
-alias_1_1() ->
- [{doc, "Test mod_alias"}].
-
-alias_1_1(Config) when is_list(Config) ->
- alias([{http_version, "HTTP/1.1"} | Config]).
-
-alias_1_0() ->
- [{doc, "Test mod_alias"}].
-
-alias_1_0(Config) when is_list(Config) ->
- alias([{http_version, "HTTP/1.0"} | Config]).
-
-alias_0_9() ->
- [{doc, "Test mod_alias"}].
-
-alias_0_9(Config) when is_list(Config) ->
- alias([{http_version, "HTTP/0.9"} | Config]).
-
-alias() ->
- [{doc, "Test mod_alias"}].
-
-alias(Config) when is_list(Config) ->
- ok = http_status("GET /pics/icon.sheet.gif ", Config,
- [{statuscode, 200},
- {header, "Content-Type","image/gif"},
- {header, "Server"},
- {header, "Date"}]),
-
- ok = http_status("GET / ", Config,
- [{statuscode, 200},
- {header, "Content-Type","text/html"},
- {header, "Server"},
- {header, "Date"}]),
-
- ok = http_status("GET /misc/ ", Config,
- [{statuscode, 200},
- {header, "Content-Type","text/html"},
- {header, "Server"},
- {header, "Date"}]),
-
- %% Check redirection if trailing slash is missing.
- ok = http_status("GET /misc ", Config,
- [{statuscode, 301},
- {header, "Location"},
- {header, "Content-Type","text/html"}]).
-%%-------------------------------------------------------------------------
-actions() ->
- [{doc, "Test mod_actions"}].
-
-actions(Config) when is_list(Config) ->
- ok = http_status("GET /", Config, [{statuscode, 200}]).
-
-%%-------------------------------------------------------------------------
-range() ->
- [{doc, "Test Range header"}].
-
-range(Config) when is_list(Config) ->
- httpd_1_1:range(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
-
-%%-------------------------------------------------------------------------
-if_modified_since() ->
- [{doc, "Test If-Modified-Since header"}].
-
-if_modified_since(Config) when is_list(Config) ->
- httpd_1_1:if_test(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config),
- ?config(doc_root, Config)).
-%%-------------------------------------------------------------------------
-trace() ->
- [{doc, "Test TRACE method"}].
-
-trace(Config) when is_list(Config) ->
- Cb = ?config(version_cb, Config),
- Cb:trace(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
-%%-------------------------------------------------------------------------
-light() ->
- ["Test light load"].
-light(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config), 10).
-%%-------------------------------------------------------------------------
-medium() ->
- ["Test medium load"].
-medium(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config), 100).
-%%-------------------------------------------------------------------------
-heavy() ->
- ["Test heavy load"].
-heavy(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config),
- 1000).
-%%-------------------------------------------------------------------------
-content_length() ->
- ["Tests that content-length is correct OTP-5775"].
-content_length(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET /cgi-bin/erl/httpd_example:get_bin ",
- Version, Host),
- [{statuscode, 200},
- {content_length, 274},
- {version, Version}]).
-%%-------------------------------------------------------------------------
-bad_hex() ->
- ["Tests that a URI with a bad hexadecimal code is handled OTP-6003"].
-bad_hex(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request("GET http://www.erlang.org/%skalle ",
- Version, Host),
- [{statuscode, 400},
- {version, Version}]).
-%%-------------------------------------------------------------------------
-missing_CR() ->
- ["Tests missing CR in delimiter OTP-7304"].
-missing_CR(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
- http_request_missing_CR("GET /index.html ", Version, Host),
- [{statuscode, 200},
- {version, Version}]).
-
-%%-------------------------------------------------------------------------
-max_header() ->
- ["Denial Of Service (DOS) attack, prevented by max_header"].
-max_header(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- case Version of
- "HTTP/0.9" ->
- {skip, not_implemented};
- _ ->
- dos_hostname(?config(type, Config), ?config(port, Config), Host,
- ?config(node, Config), Version, ?MAX_HEADER_SIZE)
- end.
-
-%%-------------------------------------------------------------------------
-max_content_length() ->
- ["Denial Of Service (DOS) attack, prevented by max_content_length"].
-max_content_length(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- garbage_content_length(?config(type, Config), ?config(port, Config), Host,
- ?config(node, Config), Version).
-
-%%-------------------------------------------------------------------------
-security_1_1(Config) when is_list(Config) ->
- security([{http_version, "HTTP/1.1"} | Config]).
-
-security_1_0(Config) when is_list(Config) ->
- security([{http_version, "HTTP/1.0"} | Config]).
-
-security() ->
- ["Test mod_security"].
-security(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
- ServerRoot = ?config(server_root, Config),
-
- global:register_name(mod_security_test, self()), % Receive events
-
- test_server:sleep(5000),
-
- OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
-
- %% Test blocking / unblocking of users.
-
- %% /open, require user one Aladdin
- remove_users(Node, ServerRoot, Host, Port, "", "open"),
-
- ok = auth_status(auth_request("/open/",
- "one", "onePassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- receive_security_event({event, auth_fail, Port, OpenDir,
- [{user, "one"}, {password, "onePassword"}]},
- Node, Port),
-
- ok = auth_status(auth_request("/open/",
- "two", "twoPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- receive_security_event({event, auth_fail, Port, OpenDir,
- [{user, "two"}, {password, "twoPassword"}]},
- Node, Port),
-
- ok = auth_status(auth_request("/open/",
- "Aladdin", "AladdinPassword", Version, Host),
- Config, [{statuscode, 401}]),
-
- receive_security_event({event, auth_fail, Port, OpenDir,
- [{user, "Aladdin"},
- {password, "AladdinPassword"}]},
- Node, Port),
-
- add_user(Node, ServerRoot, Port, "", "open", "one", "onePassword", []),
- add_user(Node, ServerRoot, Port, "", "open", "two", "twoPassword", []),
-
- ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- receive_security_event({event, auth_fail, Port, OpenDir,
- [{user, "one"}, {password, "WrongPassword"}]},
- Node, Port),
-
- ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- receive_security_event({event, auth_fail, Port, OpenDir,
- [{user, "one"}, {password, "WrongPassword"}]},
- Node, Port),
- receive_security_event({event, user_block, Port, OpenDir,
- [{user, "one"}]}, Node, Port),
-
- global:unregister_name(mod_security_test), % No more events.
-
- ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- %% User "one" should be blocked now..
- case list_blocked_users(Node, Port) of
- [{"one",_, Port, OpenDir,_}] ->
- ok;
- Blocked ->
- ct:fail({unexpected_blocked, Blocked})
- end,
-
- [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
-
- true = unblock_user(Node, "one", Port, OpenDir),
- %% User "one" should not be blocked any more.
-
- [] = list_blocked_users(Node, Port),
-
- ok = auth_status(auth_request("/open/", "one", "onePassword", Version, Host), Config,
- [{statuscode, 200}]),
-
- %% Test list_auth_users & auth_timeout
-
- ["one"] = list_auth_users(Node, Port),
-
- ok = auth_status(auth_request("/open/", "two", "onePassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- ["one"] = list_auth_users(Node, Port),
-
-
- ["one"] = list_auth_users(Node, Port, OpenDir),
-
-
- ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- ["one"] = list_auth_users(Node, Port),
-
-
- ["one"] = list_auth_users(Node, Port, OpenDir),
-
- %% Wait for successful auth to timeout.
- test_server:sleep(?AUTH_TIMEOUT*1001),
-
- [] = list_auth_users(Node, Port),
-
- [] = list_auth_users(Node, Port, OpenDir),
-
- %% "two" is blocked.
-
- true = unblock_user(Node, "two", Port, OpenDir),
-
-
- %% Test explicit blocking. Block user 'two'.
-
- [] = list_blocked_users(Node,Port,OpenDir),
-
- true = block_user(Node, "two", Port, OpenDir, 10),
-
- ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
- [{statuscode, 401}]),
-
- true = unblock_user(Node, "two", Port, OpenDir).
-
-%%-------------------------------------------------------------------------
-non_disturbing_reconfiger_dies(Config) when is_list(Config) ->
- do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], non_disturbing).
-disturbing_reconfiger_dies(Config) when is_list(Config) ->
- do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], disturbing).
-
-do_reconfiger_dies(Config, DisturbingType) ->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
-
- HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
- {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
- inets_test_lib:send(Type, Socket, BlockRequest),
- ct:sleep(100), %% Avoid possible timing issues
- Pid = spawn(fun() -> httpd:reload_config([{server_name, "httpd_kill_" ++ Version},
- {port, Port}|
- proplists:delete(server_name, HttpdConfig)], DisturbingType)
- end),
-
- monitor(process, Pid),
- exit(Pid, kill),
- receive
- {'DOWN', _, _, _, _} ->
- ok
- end,
- inets_test_lib:close(Type, Socket),
- [{server_name, "httpd_test"}] = httpd:info(Server, [server_name]).
-%%-------------------------------------------------------------------------
-disturbing_1_1(Config) when is_list(Config) ->
- disturbing([{http_version, "HTTP/1.1"} | Config]).
-
-disturbing_1_0(Config) when is_list(Config) ->
- disturbing([{http_version, "HTTP/1.0"} | Config]).
-
-disturbing_0_9(Config) when is_list(Config) ->
- disturbing([{http_version, "HTTP/0.9"} | Config]).
-
-disturbing(Config) when is_list(Config)->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
- HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
- {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
- inets_test_lib:send(Type, Socket, BlockRequest),
- ct:sleep(100), %% Avoid possible timing issues
- ok = httpd:reload_config([{server_name, "httpd_disturbing_" ++ Version}, {port, Port}|
- proplists:delete(server_name, HttpdConfig)], disturbing),
- Close = list_to_atom((typestr(Type)) ++ "_closed"),
- receive
- {Close, Socket} ->
- ok;
- Msg ->
- ct:fail({{expected, {Close, Socket}}, {got, Msg}})
- end,
- inets_test_lib:close(Type, Socket),
- [{server_name, "httpd_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
-%%-------------------------------------------------------------------------
-non_disturbing_1_1(Config) when is_list(Config) ->
- non_disturbing([{http_version, "HTTP/1.1"} | Config]).
-
-non_disturbing_1_0(Config) when is_list(Config) ->
- non_disturbing([{http_version, "HTTP/1.0"} | Config]).
-
-non_disturbing_0_9(Config) when is_list(Config) ->
- non_disturbing([{http_version, "HTTP/0.9"} | Config]).
-
-non_disturbing(Config) when is_list(Config)->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
-
- HttpdConfig = httpd:info(Server),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
- {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
- inets_test_lib:send(Type, Socket, BlockRequest),
- ct:sleep(100), %% Avoid possible timing issues
- ok = httpd:reload_config([{server_name, "httpd_non_disturbing_" ++ Version}, {port, Port}|
- proplists:delete(server_name, HttpdConfig)], non_disturbing),
- Transport = type(Type),
- receive
- {Transport, Socket, Msg} ->
- ct:pal("Received message ~p~n", [Msg]),
- ok
- after 2000 ->
- ct:fail(timeout)
- end,
- inets_test_lib:close(Type, Socket),
- [{server_name, "httpd_non_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
-
-
-%%--------------------------------------------------------------------
-%% Internal functions -----------------------------------
-%%--------------------------------------------------------------------
-url(http, End, Config) ->
- Port = ?config(port, Config),
- {ok,Host} = inet:gethostname(),
- ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
-
-do_max_clients(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
-
- Request = http_request("GET /index.html ", Version, Host),
- BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
- {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
- inets_test_lib:send(Type, Socket, BlockRequest),
- ct:sleep(100), %% Avoid possible timing issues
- ok = httpd_test_lib:verify_request(Type, Host,
- Port,
- transport_opts(Type, Config),
- ?config(node, Config),
- Request,
- [{statuscode, 503},
- {version, Version}]),
- receive
- {_, Socket, _Msg} ->
- ok
- end,
- inets_test_lib:close(Type, Socket),
- ct:sleep(100), %% Avoid possible timing issues
- ok = httpd_test_lib:verify_request(Type, Host,
- Port,
- transport_opts(Type, Config),
- ?config(node, Config),
- Request,
- [{statuscode, 200},
- {version, Version}]).
-
-setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
- CgiDir = filename:join(ServerRoot, "cgi-bin"),
- AuthDir = filename:join(ServerRoot, "auth"),
- PicsDir = filename:join(ServerRoot, "icons"),
- ConfigDir = filename:join(ServerRoot, "config"),
-
- ok = file:make_dir(ServerRoot),
- ok = file:make_dir(DocRoot),
- ok = file:make_dir(CgiDir),
- ok = file:make_dir(AuthDir),
- ok = file:make_dir(PicsDir),
- ok = file:make_dir(ConfigDir),
-
- DocSrc = filename:join(DataDir, "server_root/htdocs"),
- AuthSrc = filename:join(DataDir, "server_root/auth"),
- CgiSrc = filename:join(DataDir, "server_root/cgi-bin"),
- PicsSrc = filename:join(DataDir, "server_root/icons"),
- ConfigSrc = filename:join(DataDir, "server_root/config"),
-
- inets_test_lib:copy_dirs(DocSrc, DocRoot),
- inets_test_lib:copy_dirs(AuthSrc, AuthDir),
- inets_test_lib:copy_dirs(CgiSrc, CgiDir),
- inets_test_lib:copy_dirs(PicsSrc, PicsDir),
- inets_test_lib:copy_dirs(ConfigSrc, ConfigDir),
-
- Cgi = case test_server:os_type() of
- {win32, _} ->
- "cgi_echo.exe";
- _ ->
- "cgi_echo"
- end,
-
- inets_test_lib:copy_file(Cgi, DataDir, CgiDir),
- AbsCgi = filename:join([CgiDir, Cgi]),
- {ok, FileInfo} = file:read_file_info(AbsCgi),
- ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}),
-
- EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
- {ok, FileInfo1} = file:read_file_info(EnvCGI),
- ok = file:write_file_info(EnvCGI,
- FileInfo1#file_info{mode = 8#00755}).
-
-start_apps(Group) when Group == https_basic;
- Group == https_limit;
- Group == https_basic_auth;
- Group == https_auth_api;
- Group == https_auth_api_dets;
- Group == https_auth_api_mnesia;
- Group == http_htaccess;
- Group == http_security;
- Group == http_reload
- ->
- inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
-start_apps(Group) when Group == http_basic;
- Group == http_limit;
- Group == http_basic_auth;
- Group == http_auth_api;
- Group == http_auth_api_dets;
- Group == http_auth_api_mnesia;
- Group == https_htaccess;
- Group == https_security;
- Group == https_reload;
- Group == http_mime_types->
+ {custom, [], [customize]},
inets_test_lib:start_apps([inets]).
server_start(_, HttpdConfig) ->
@@ -1390,6 +148,10 @@ server_config(http_limit, Config) ->
[{max_clients, 1},
%% Make sure option checking code is run
{max_content_length, 100000002}] ++ server_config(http, Config);
+server_config(http_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(http, Config);
+server_config(https_custom, Config) ->
+ [{custom, ?MODULE}] ++ server_config(https, Config);
server_config(https_limit, Config) ->
[{max_clients, 1}] ++ server_config(https, Config);
server_config(http_basic_auth, Config) ->
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 579a3ae4a8..c77ee1e77a 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,33 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Gracefully terminate if sockets is unexpectedly closed.</p>
+ <p>
+ Own Id: OTP-12782</p>
+ </item>
+ <item>
+ <p>
+ Made Codenomicon Defensics test suite pass: <list>
+ <item>limit number of algorithms in kexinit
+ message</item> <item>check 'e' and 'f' parameters in
+ kexdh</item> <item>implement 'keyboard-interactive' user
+ authentication on server side</item> <item> return plain
+ text message to bad version exchange message</item>
+ </list></p>
+ <p>
+ Own Id: OTP-12784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.2.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 57f7ae8b5e..10b526ba28 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -360,7 +360,13 @@ handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+<<<<<<< HEAD
handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) ->
+=======
+handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) ->
+>>>>>>> maint
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
@@ -429,6 +435,13 @@ handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
+handle_ssh_option({auth_method_kb_interactive_data, {Name,Instruction,Prompt,Echo}} = Opt) when is_list(Name),
+ is_list(Instruction),
+ is_list(Prompt),
+ is_boolean(Echo) ->
+ Opt;
+handle_ssh_option({auth_method_kb_interactive_data, F} = Opt) when is_function(F,3) ->
+ Opt;
handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 197808754c..df9a97c8f8 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -243,6 +243,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
+ method = "keyboard-interactive",
+ data = _},
+ _, #ssh{opts = Opts} = Ssh) ->
+ %% RFC4256
+ %% The data field contains:
+ %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip
+ %% it for simplicity.
+ %% - submethods. "... the user can give a hint of which actual methods
+ %% he wants to use. ...". It's a "MAY use" so we skip
+ %% it. It also needs an understanding between the client
+ %% and the server.
+ %%
+ %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS,
+ %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message."
+ Default = {"SSH server",
+ "Enter password for \""++User++"\"",
+ "pwd: ",
+ false},
+
+ {Name, Instruction, Prompt, Echo} =
+ case proplists:get_value(auth_method_kb_interactive_data, Opts) of
+ undefined ->
+ Default;
+ {_,_,_,_}=V ->
+ V;
+ F when is_function(F) ->
+ {_,PeerName} = Ssh#ssh.peer,
+ F(PeerName, User, "ssh-connection")
+ end,
+ EchoEnc = case Echo of
+ true -> <<?TRUE>>;
+ false -> <<?FALSE>>
+ end,
+ Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name),
+ instruction = unicode:characters_to_list(Instruction),
+ language_tag = "",
+ num_prompts = 1,
+ data = <<?STRING(unicode:characters_to_binary(Prompt)),
+ EchoEnc/binary
+ >>
+ },
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
+ opts = [{max_kb_tries,3},{kb_userauth_info_msg,Msg}|Opts]
+ })};
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
method = Other}, _,
#ssh{userauth_supported_methods = Methods} = Ssh) ->
{not_authorized, {User, {authmethod, Other}},
@@ -264,6 +312,38 @@ handle_userauth_info_request(
#ssh_msg_userauth_info_response{num_responses = NumPrompts,
data = Responses}, Ssh)}.
+handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1,
+ data = <<?UINT32(Sz), Password:Sz/binary>>},
+ #ssh{opts = Opts0,
+ user = User} = Ssh) ->
+ NumTriesLeft = proplists:get_value(max_kb_tries, Opts0, 0) - 1,
+ Opts = lists:keydelete(max_kb_tries,1,Opts0),
+ case check_password(User, unicode:characters_to_list(Password), Opts) of
+ true ->
+ {authorized, User,
+ ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
+ false when NumTriesLeft > 0 ->
+ UserAuthInfoMsg =
+ (proplists:get_value(kb_userauth_info_msg,Opts))
+ #ssh_msg_userauth_info_request{name = "",
+ instruction =
+ lists:concat(
+ ["Bad user or password, try again. ",
+ integer_to_list(NumTriesLeft),
+ " tries left."])},
+ {not_authorized, {User, undefined},
+ ssh_transport:ssh_packet(UserAuthInfoMsg,
+ Ssh#ssh{opts = [{max_kb_tries,NumTriesLeft}|Opts]})};
+
+ false ->
+ {not_authorized, {User, {error,"Bad user or password"}},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = "",
+ partial_success = false},
+ Ssh#ssh{opts = lists:keydelete(kb_userauth_info_msg,1,Opts)}
+ )}
+ end;
+
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index ca63d2194f..3bdca4ba94 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -333,22 +333,25 @@ info(ConnectionHandler, ChannelProcess) ->
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]),
- inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
- {next_state, hello, State#state{recbuf = Size}};
+ case getopt(recbuf, Socket) of
+ {ok, Size} ->
+ inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
+ {next_state, hello, State#state{recbuf = Size}};
+ {error, Reason} ->
+ {stop, {shutdown, Reason}, State}
+ end;
hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
%% The server may send info lines before the version_exchange
inet:setopts(Socket, [{active, once}]),
{next_state, hello, State};
-hello({info_line, _Line},#state{role = server} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Did not receive expected protocol version exchange",
- language = "en"},
- handle_disconnect(DisconnectMsg, State);
+hello({info_line, _Line},#state{role = server,
+ socket = Socket,
+ transport_cb = Transport } = State) ->
+ %% as openssh
+ Transport:send(Socket, "Protocol mismatch."),
+ {stop, {shutdown,"Protocol mismatch in version exchange."}, State};
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
socket = Socket,
@@ -501,10 +504,21 @@ userauth(#ssh_msg_userauth_info_request{} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
userauth(#ssh_msg_userauth_info_response{} = Msg,
- #state{ssh_params = #ssh{role = server} = Ssh0} = State) ->
- {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response(Msg, Ssh0),
- send_msg(Reply, State),
- {next_state, userauth, next_packet(State#state{ssh_params = Ssh})};
+ #state{ssh_params = #ssh{role = server,
+ peer = {_, Address}} = Ssh0,
+ opts = Opts, starter = Pid} = State) ->
+ case ssh_auth:handle_userauth_info_response(Msg, Ssh0) of
+ {authorized, User, {Reply, Ssh}} ->
+ send_msg(Reply, State),
+ Pid ! ssh_connected,
+ connected_fun(User, Address, "keyboard-interactive", Opts),
+ {next_state, connected,
+ next_packet(State#state{auth_user = User, ssh_params = Ssh})};
+ {not_authorized, {User, Reason}, {Reply, Ssh}} ->
+ retry_fun(User, Address, Reason, Opts),
+ send_msg(Reply, State),
+ {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
+ end;
userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
starter = Pid} = State) ->
@@ -1763,3 +1777,12 @@ start_timeout(_,_, infinity) ->
ok;
start_timeout(Channel, From, Time) ->
erlang:send_after(Time, self(), {timeout, {Channel, From}}).
+
+getopt(Opt, Socket) ->
+ case inet:getopts(Socket, [Opt]) of
+ {ok, [{Opt, Value}]} ->
+ {ok, Value};
+ Other ->
+ {error, {unexpected_getopts_return, Other}}
+ end.
+
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 7162d18b19..ea9bca2390 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -585,10 +585,15 @@ alg_final(SSH0) ->
{ok,SSH6} = decompress_final(SSH5),
SSH6.
-select_all(CL, SL) ->
+select_all(CL, SL) when length(CL) + length(SL) < 50 ->
A = CL -- SL, %% algortihms only used by client
%% algorithms used by client and server (client pref)
- lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)).
+ lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A));
+select_all(_CL, _SL) ->
+ throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Too many algorithms",
+ language = "en"}).
+
select([], []) ->
none;
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 352563700b..fe0606b1a3 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,23 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 6.0</title>
+ <section><title>SSL 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Terminate gracefully when receving bad input to premaster
+ secret calculation</p>
+ <p>
+ Own Id: OTP-12783</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 6.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 1476336039..d100e41930 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,14 +1,16 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"6\\..*">>, [{restart_application, ssl}]},
- {<<"5\\..*">>, [{restart_application, ssl}]},
+ {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]},
+ {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]},
+ {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
]
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index b538fefe53..12a17cb6ac 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -476,19 +476,27 @@ update_handshake_history({Handshake0, _Prev}, Data) ->
%% end.
premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) ->
- public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params);
-
+ try
+ public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params)
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) ->
- crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]);
+ try
+ crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base])
+ catch
+ error:computation_failed ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end;
premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime,
verifier = Verifier}) ->
case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
-
premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public},
ClientKeys, {Username, Password}) ->
case ssl_srp_primes:check_srp_params(Generator, Prime) of
@@ -496,21 +504,19 @@ premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Sa
DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]),
case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of
error ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER));
PremasterSecret ->
PremasterSecret
end;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end;
-
premaster_secret(#client_rsa_psk_identity{
identity = PSKIdentity,
exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS}
}, #'RSAPrivateKey'{} = Key, PSKLookup) ->
PremasterSecret = premaster_secret(EncPMS, Key),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret);
-
premaster_secret(#server_dhe_psk_params{
hint = IdentityHint,
dh_params = #server_dh_params{dh_y = PublicDhKey} = Params},
@@ -518,7 +524,6 @@ premaster_secret(#server_dhe_psk_params{
LookupFun) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),
psk_secret(IdentityHint, LookupFun, PremasterSecret);
-
premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) ->
psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret).
@@ -527,13 +532,10 @@ premaster_secret(#client_dhe_psk_identity{
dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->
PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),
psk_secret(PSKIdentity, PSKLookup, PremasterSecret).
-
premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret({psk, PSKIdentity}, PSKLookup) ->
psk_secret(PSKIdentity, PSKLookup);
-
premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) ->
public_key:compute_key(ECPoint, ECDHKeys);
premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) ->
@@ -2036,7 +2038,7 @@ psk_secret(PSKIdentity, PSKLookup) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
@@ -2048,7 +2050,7 @@ psk_secret(PSKIdentity, PSKLookup, PremasterSecret) ->
#alert{} = Alert ->
Alert;
_ ->
- ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
end.
handle_psk_identity(_PSKIdentity, LookupFun)