mitigate SSL and emfile related conditions per #138
Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/f4e2daaf Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/f4e2daaf Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/f4e2daaf Branch: refs/heads/upstream Commit: f4e2daafb8a98363d1b9d63e90edfff7dc5d1c82 Parents: 0dccaaa Author: Bob Ippolito <[email protected]> Authored: Mon Jan 12 15:55:44 2015 +1300 Committer: Bob Ippolito <[email protected]> Committed: Mon Jan 12 15:55:44 2015 +1300 ---------------------------------------------------------------------- CHANGES.md | 9 +++++++++ src/mochiweb.app.src | 2 +- src/mochiweb_acceptor.erl | 40 ++++++++++++++++++++++++++-------------- src/mochiweb_socket.erl | 40 +++++++++++++++++++++++++--------------- 4 files changed, 61 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f4e2daaf/CHANGES.md ---------------------------------------------------------------------- diff --git a/CHANGES.md b/CHANGES.md index 673d759..19f96d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +Version 2.11.0 released 2015-01-12 + +* Perform SSL handshake after releasing acceptor back into the pool, + and slow accept rate when file descriptors are not available, + to mitigate a potential DoS attack. Adds new mochiweb_socket + functions transport_accept/1 and finish_accept/1 which should be + used in preference to the now deprecated accept/1 function. + https://github.com/mochi/mochiweb/issues/138 + Version 2.10.1 released 2015-01-11 * Fixes issue with SSL and mochiweb_websocket. Note that http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f4e2daaf/src/mochiweb.app.src ---------------------------------------------------------------------- diff --git a/src/mochiweb.app.src b/src/mochiweb.app.src index ccad5bc..4754266 100644 --- a/src/mochiweb.app.src +++ b/src/mochiweb.app.src @@ -1,7 +1,7 @@ %% This is generated from src/mochiweb.app.src {application, mochiweb, [{description, "MochiMedia Web Server"}, - {vsn, "2.10.1"}, + {vsn, "2.11.0"}, {modules, []}, {registered, []}, {env, []}, http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f4e2daaf/src/mochiweb_acceptor.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_acceptor.erl b/src/mochiweb_acceptor.erl index 208f861..8a58fcf 100644 --- a/src/mochiweb_acceptor.erl +++ b/src/mochiweb_acceptor.erl @@ -10,25 +10,44 @@ -export([start_link/3, start_link/4, init/4]). +-define(EMFILE_SLEEP_MSEC, 100). + start_link(Server, Listen, Loop) -> start_link(Server, Listen, Loop, []). start_link(Server, Listen, Loop, Opts) -> proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop, Opts]). -init(Server, Listen, Loop, Opts) -> +do_accept(Server, Listen) -> T1 = os:timestamp(), - case catch mochiweb_socket:accept(Listen) of + case mochiweb_socket:transport_accept(Listen) of {ok, Socket} -> gen_server:cast(Server, {accepted, self(), timer:now_diff(os:timestamp(), T1)}), + mochiweb_socket:finish_accept(Socket); + Other -> + Other + end. + +init(Server, Listen, Loop, Opts) -> + case catch do_accept(Server, Listen) of + {ok, Socket} -> call_loop(Loop, Socket, Opts); - {error, closed} -> - exit(normal); - {error, timeout} -> - init(Server, Listen, Loop, Opts); - {error, esslaccept} -> + {error, Err} when Err =:= closed orelse + Err =:= esslaccept orelse + Err =:= timeout -> exit(normal); Other -> + %% Mitigate out of file descriptor scenario by sleeping for a + %% short time to slow error rate + case Other of + {error, emfile} -> + receive + after ?EMFILE_SLEEP_MSEC -> + ok + end; + _ -> + ok + end, error_logger:error_report( [{application, mochiweb}, "Accept failed error", @@ -44,10 +63,3 @@ call_loop({M, F, A}, Socket, Opts) -> erlang:apply(M, F, [Socket, Opts | A]); call_loop(Loop, Socket, Opts) -> Loop(Socket, Opts). - -%% -%% Tests -%% --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --endif. http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/f4e2daaf/src/mochiweb_socket.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_socket.erl b/src/mochiweb_socket.erl index f4b8bfb..1e35e15 100644 --- a/src/mochiweb_socket.erl +++ b/src/mochiweb_socket.erl @@ -4,7 +4,9 @@ -module(mochiweb_socket). --export([listen/4, accept/1, recv/3, send/2, close/1, port/1, peername/1, +-export([listen/4, + accept/1, transport_accept/1, finish_accept/1, + recv/3, send/2, close/1, port/1, peername/1, setopts/2, getopts/2, type/1]). -define(ACCEPT_TIMEOUT, 2000). @@ -66,27 +68,35 @@ filter_unsafe_protcol_versions(Versions) -> end, Versions). +%% Provided for backwards compatibility only +accept(ListenSocket) -> + case transport_accept(ListenSocket) of + {ok, Socket} -> + finish_accept(Socket); + {error, _} = Err -> + Err + end. -accept({ssl, ListenSocket}) -> - % There's a bug in ssl:transport_accept/2 at the moment, which is the - % reason for the try...catch block. Should be fixed in OTP R14. - try ssl:transport_accept(ListenSocket, ?SSL_TIMEOUT) of +transport_accept({ssl, ListenSocket}) -> + case ssl:transport_accept(ListenSocket, ?SSL_TIMEOUT) of {ok, Socket} -> - case ssl:ssl_accept(Socket, ?SSL_HANDSHAKE_TIMEOUT) of - ok -> - {ok, {ssl, Socket}}; - {error, _} = Err -> - Err - end; + {ok, {ssl, Socket}}; {error, _} = Err -> Err - catch - error:{badmatch, {error, Reason}} -> - {error, Reason} end; -accept(ListenSocket) -> +transport_accept(ListenSocket) -> gen_tcp:accept(ListenSocket, ?ACCEPT_TIMEOUT). +finish_accept({ssl, Socket}) -> + case ssl:ssl_accept(Socket, ?SSL_HANDSHAKE_TIMEOUT) of + ok -> + {ok, {ssl, Socket}}; + {error, _} = Err -> + Err + end; +finish_accept(Socket) -> + {ok, Socket}. + recv({ssl, Socket}, Length, Timeout) -> ssl:recv(Socket, Length, Timeout); recv(Socket, Length, Timeout) ->
