aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_s2s_in.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/ejabberd_s2s_in.erl')
-rw-r--r--src/ejabberd_s2s_in.erl1166
1 files changed, 581 insertions, 585 deletions
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 6ae4f3446..2dc7c86b2 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -25,119 +25,110 @@
%%%----------------------------------------------------------------------
-module(ejabberd_s2s_in).
+
-author('alexey@process-one.net').
-behaviour(p1_fsm).
%% External exports
--export([start/2,
- start_link/2,
- match_domain/2,
+-export([start/2, start_link/2, match_domain/2,
socket_type/0]).
%% gen_fsm callbacks
--export([init/1,
- wait_for_stream/2,
- wait_for_feature_request/2,
- stream_established/2,
- handle_event/3,
- handle_sync_event/4,
- code_change/4,
- handle_info/3,
- print_state/1,
- terminate/3]).
+-export([init/1, wait_for_stream/2,
+ wait_for_feature_request/2, stream_established/2,
+ handle_event/3, handle_sync_event/4, code_change/4,
+ handle_info/3, print_state/1, terminate/3]).
-include("ejabberd.hrl").
+
-include("jlib.hrl").
--ifdef(SSL40).
--include_lib("public_key/include/public_key.hrl").
+
+-include_lib("public_key/include/public_key.hrl").
+
-define(PKIXEXPLICIT, 'OTP-PUB-KEY').
+
-define(PKIXIMPLICIT, 'OTP-PUB-KEY').
--else.
--ifdef(SSL39).
--include_lib("ssl/include/ssl_pkix.hrl").
--define(PKIXEXPLICIT, 'OTP-PKIX').
--define(PKIXIMPLICIT, 'OTP-PKIX').
--else.
--include_lib("ssl/include/PKIX1Explicit88.hrl").
--include_lib("ssl/include/PKIX1Implicit88.hrl").
--define(PKIXEXPLICIT, 'PKIX1Explicit88').
--define(PKIXIMPLICIT, 'PKIX1Implicit88').
--endif.
--endif.
+
-include("XmppAddr.hrl").
-define(DICT, dict).
--record(state, {socket,
- sockmod,
- streamid,
- shaper,
- tls = false,
- tls_enabled = false,
- tls_required = false,
- tls_certverify = false,
- tls_options = [],
- server,
- authenticated = false,
- auth_domain,
- connections = ?DICT:new(),
- timer}).
-
+-record(state,
+ {socket :: ejabberd_socket:socket_state(),
+ sockmod = ejabberd_socket :: ejabberd_socket | ejabberd_frontend_socket,
+ streamid = <<"">> :: binary(),
+ shaper = none :: shaper:shaper(),
+ tls = false :: boolean(),
+ tls_enabled = false :: boolean(),
+ tls_required = false :: boolean(),
+ tls_certverify = false :: boolean(),
+ tls_options = [] :: list(),
+ server = <<"">> :: binary(),
+ authenticated = false :: boolean(),
+ auth_domain = <<"">> :: binary(),
+ connections = (?DICT):new() :: dict(),
+ timer = make_ref() :: reference()}).
%-define(DBGFSM, true).
-ifdef(DBGFSM).
+
-define(FSMOPTS, [{debug, [trace]}]).
+
-else.
+
-define(FSMOPTS, []).
--endif.
--define(FSMLIMITS, [{max_queue, 2000}]). %% if queue grows more than this, we shutdown this connection.
+-endif.
%% Module start with or without supervisor:
-ifdef(NO_TRANSIENT_SUPERVISORS).
--define(SUPERVISOR_START, p1_fsm:start(ejabberd_s2s_in, [SockData, Opts],
- ?FSMOPTS ++ ?FSMLIMITS)).
+
+-define(SUPERVISOR_START,
+ p1_fsm:start(ejabberd_s2s_in, [SockData, Opts],
+ ?FSMOPTS ++ fsm_limit_opts(Opts)).
+
-else.
--define(SUPERVISOR_START, supervisor:start_child(ejabberd_s2s_in_sup,
- [SockData, Opts])).
+
+-define(SUPERVISOR_START,
+ supervisor:start_child(ejabberd_s2s_in_sup,
+ [SockData, Opts])).
+
-endif.
-define(STREAM_HEADER(Version),
- ("<?xml version='1.0'?>"
- "<stream:stream "
- "xmlns:stream='http://etherx.jabber.org/streams' "
- "xmlns='jabber:server' "
- "xmlns:db='jabber:server:dialback' "
- "id='" ++ StateData#state.streamid ++ "'" ++ Version ++ ">")
- ).
+ <<"<?xml version='1.0'?><stream:stream "
+ "xmlns:stream='http://etherx.jabber.org/stream"
+ "s' xmlns='jabber:server' xmlns:db='jabber:ser"
+ "ver:dialback' id='",
+ (StateData#state.streamid)/binary, "'", Version/binary,
+ ">">>).
--define(STREAM_TRAILER, "</stream:stream>").
+-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(INVALID_NAMESPACE_ERR,
- xml:element_to_string(?SERR_INVALID_NAMESPACE)).
+ xml:element_to_binary(?SERR_INVALID_NAMESPACE)).
-define(HOST_UNKNOWN_ERR,
- xml:element_to_string(?SERR_HOST_UNKNOWN)).
+ xml:element_to_binary(?SERR_HOST_UNKNOWN)).
-define(INVALID_FROM_ERR,
- xml:element_to_string(?SERR_INVALID_FROM)).
+ xml:element_to_binary(?SERR_INVALID_FROM)).
-define(INVALID_XML_ERR,
- xml:element_to_string(?SERR_XML_NOT_WELL_FORMED)).
+ xml:element_to_binary(?SERR_XML_NOT_WELL_FORMED)).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
-start(SockData, Opts) ->
- ?SUPERVISOR_START.
+start(SockData, Opts) -> ?SUPERVISOR_START.
start_link(SockData, Opts) ->
- p1_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS ++ ?FSMLIMITS).
+ p1_fsm:start_link(ejabberd_s2s_in, [SockData, Opts],
+ ?FSMOPTS ++ fsm_limit_opts(Opts)).
-socket_type() ->
- xml_stream.
+socket_type() -> xml_stream.
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
@@ -153,36 +144,44 @@ socket_type() ->
init([{SockMod, Socket}, Opts]) ->
?DEBUG("started: ~p", [{SockMod, Socket}]),
Shaper = case lists:keysearch(shaper, 1, Opts) of
- {value, {_, S}} -> S;
- _ -> none
+ {value, {_, S}} -> S;
+ _ -> none
end,
- {StartTLS, TLSRequired, TLSCertverify} = case ejabberd_config:get_local_option(s2s_use_starttls) of
- UseTls when (UseTls==undefined) or (UseTls==false) ->
- {false, false, false};
- UseTls when (UseTls==true) or (UseTls==optional) ->
- {true, false, false};
- required ->
- {true, true, false};
- required_trusted ->
- {true, true, true}
- end,
- TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
- undefined ->
- [];
- CertFile ->
- [{certfile, CertFile}]
+ {StartTLS, TLSRequired, TLSCertverify} =
+ case ejabberd_config:get_local_option(
+ s2s_use_starttls,
+ fun(false) -> false;
+ (true) -> true;
+ (optional) -> optional;
+ (required) -> required;
+ (required_trusted) -> required_trusted
+ end,
+ false) of
+ UseTls
+ when (UseTls == undefined) or
+ (UseTls == false) ->
+ {false, false, false};
+ UseTls
+ when (UseTls == true) or
+ (UseTls ==
+ optional) ->
+ {true, false, false};
+ required -> {true, true, false};
+ required_trusted ->
+ {true, true, true}
+ end,
+ TLSOpts = case ejabberd_config:get_local_option(
+ s2s_certfile,
+ fun iolist_to_binary/1) of
+ undefined -> [];
+ CertFile -> [{certfile, CertFile}]
end,
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
{ok, wait_for_stream,
- #state{socket = Socket,
- sockmod = SockMod,
- streamid = new_id(),
- shaper = Shaper,
- tls = StartTLS,
- tls_enabled = false,
- tls_required = TLSRequired,
- tls_certverify = TLSCertverify,
- tls_options = TLSOpts,
+ #state{socket = Socket, sockmod = SockMod,
+ streamid = new_id(), shaper = Shaper, tls = StartTLS,
+ tls_enabled = false, tls_required = TLSRequired,
+ tls_certverify = TLSCertverify, tls_options = TLSOpts,
timer = Timer}}.
%%----------------------------------------------------------------------
@@ -192,373 +191,371 @@ init([{SockMod, Socket}, Opts]) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
-wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
- case {xml:get_attr_s("xmlns", Attrs),
- xml:get_attr_s("xmlns:db", Attrs),
- xml:get_attr_s("to", Attrs),
- xml:get_attr_s("version", Attrs) == "1.0"} of
- {"jabber:server", _, Server, true} when
- StateData#state.tls and (not StateData#state.authenticated) ->
- send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
- SASL =
- if
- StateData#state.tls_enabled ->
- case (StateData#state.sockmod):get_peer_certificate(
- StateData#state.socket) of
- {ok, Cert} ->
- case (StateData#state.sockmod):get_verify_result(StateData#state.socket) of
- 0 ->
- [{xmlelement, "mechanisms",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, "mechanism", [],
- [{xmlcdata, "EXTERNAL"}]}]}];
- CertVerifyRes ->
- case StateData#state.tls_certverify of
- true -> {error_cert_verif, CertVerifyRes, Cert};
- false -> []
- end
- end;
- error ->
- []
+wait_for_stream({xmlstreamstart, _Name, Attrs},
+ StateData) ->
+ case {xml:get_attr_s(<<"xmlns">>, Attrs),
+ xml:get_attr_s(<<"xmlns:db">>, Attrs),
+ xml:get_attr_s(<<"to">>, Attrs),
+ xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
+ of
+ {<<"jabber:server">>, _, Server, true}
+ when StateData#state.tls and
+ not StateData#state.authenticated ->
+ send_text(StateData,
+ ?STREAM_HEADER(<<" version='1.0'">>)),
+ SASL = if StateData#state.tls_enabled ->
+ case
+ (StateData#state.sockmod):get_peer_certificate(StateData#state.socket)
+ of
+ {ok, Cert} ->
+ case
+ (StateData#state.sockmod):get_verify_result(StateData#state.socket)
+ of
+ 0 ->
+ [#xmlel{name = <<"mechanisms">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = <<"mechanism">>,
+ attrs = [],
+ children =
+ [{xmlcdata,
+ <<"EXTERNAL">>}]}]}];
+ CertVerifyRes ->
+ case StateData#state.tls_certverify of
+ true ->
+ {error_cert_verif, CertVerifyRes,
+ Cert};
+ false -> []
+ end
+ end;
+ error -> []
end;
- true ->
- []
- end,
- StartTLS = if
- StateData#state.tls_enabled ->
- [];
- (not StateData#state.tls_enabled) and (not StateData#state.tls_required) ->
- [{xmlelement, "starttls", [{"xmlns", ?NS_TLS}], []}];
- (not StateData#state.tls_enabled) and StateData#state.tls_required ->
- [{xmlelement, "starttls", [{"xmlns", ?NS_TLS}],
- [{xmlelement, "required", [], []}]
- }]
- end,
- case SASL of
- {error_cert_verif, CertVerifyResult, Certificate} ->
- CertError = tls:get_cert_verify_string(CertVerifyResult, Certificate),
- RemoteServer = xml:get_attr_s("from", Attrs),
- ?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)", [StateData#state.server, RemoteServer, CertError]),
- send_text(StateData, xml:element_to_string(?SERRT_POLICY_VIOLATION("en", CertError))),
- {atomic, Pid} = ejabberd_s2s:find_connection(jlib:make_jid("", Server, ""), jlib:make_jid("", RemoteServer, "")),
- ejabberd_s2s_out:stop_connection(Pid),
-
- {stop, normal, StateData};
- _ ->
- send_element(StateData,
- {xmlelement, "stream:features", [],
- SASL ++ StartTLS ++
- ejabberd_hooks:run_fold(
- s2s_stream_features,
- Server,
- [], [Server])}),
- {next_state, wait_for_feature_request, StateData#state{server = Server}}
- end;
- {"jabber:server", _, Server, true} when
- StateData#state.authenticated ->
- send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
- send_element(StateData,
- {xmlelement, "stream:features", [],
- ejabberd_hooks:run_fold(
- s2s_stream_features,
- Server,
- [], [Server])}),
- {next_state, stream_established, StateData};
- {"jabber:server", "jabber:server:dialback", _Server, _} ->
- send_text(StateData, ?STREAM_HEADER("")),
- {next_state, stream_established, StateData};
- _ ->
- send_text(StateData, ?INVALID_NAMESPACE_ERR),
- {stop, normal, StateData}
+ true -> []
+ end,
+ StartTLS = if StateData#state.tls_enabled -> [];
+ not StateData#state.tls_enabled and
+ not StateData#state.tls_required ->
+ [#xmlel{name = <<"starttls">>,
+ attrs = [{<<"xmlns">>, ?NS_TLS}],
+ children = []}];
+ not StateData#state.tls_enabled and
+ StateData#state.tls_required ->
+ [#xmlel{name = <<"starttls">>,
+ attrs = [{<<"xmlns">>, ?NS_TLS}],
+ children =
+ [#xmlel{name = <<"required">>,
+ attrs = [], children = []}]}]
+ end,
+ case SASL of
+ {error_cert_verif, CertVerifyResult, Certificate} ->
+ CertError = tls:get_cert_verify_string(CertVerifyResult,
+ Certificate),
+ RemoteServer = xml:get_attr_s(<<"from">>, Attrs),
+ ?INFO_MSG("Closing s2s connection: ~s <--> ~s (~s)",
+ [StateData#state.server, RemoteServer, CertError]),
+ send_text(StateData,
+ xml:element_to_binary(?SERRT_POLICY_VIOLATION(<<"en">>,
+ CertError))),
+ {atomic, Pid} =
+ ejabberd_s2s:find_connection(jlib:make_jid(<<"">>,
+ Server, <<"">>),
+ jlib:make_jid(<<"">>,
+ RemoteServer,
+ <<"">>)),
+ ejabberd_s2s_out:stop_connection(Pid),
+ {stop, normal, StateData};
+ _ ->
+ send_element(StateData,
+ #xmlel{name = <<"stream:features">>, attrs = [],
+ children =
+ SASL ++
+ StartTLS ++
+ ejabberd_hooks:run_fold(s2s_stream_features,
+ Server, [],
+ [Server])}),
+ {next_state, wait_for_feature_request,
+ StateData#state{server = Server}}
+ end;
+ {<<"jabber:server">>, _, Server, true}
+ when StateData#state.authenticated ->
+ send_text(StateData,
+ ?STREAM_HEADER(<<" version='1.0'">>)),
+ send_element(StateData,
+ #xmlel{name = <<"stream:features">>, attrs = [],
+ children =
+ ejabberd_hooks:run_fold(s2s_stream_features,
+ Server, [],
+ [Server])}),
+ {next_state, stream_established, StateData};
+ {<<"jabber:server">>, <<"jabber:server:dialback">>,
+ _Server, _} ->
+ send_text(StateData, ?STREAM_HEADER(<<"">>)),
+ {next_state, stream_established, StateData};
+ _ ->
+ send_text(StateData, ?INVALID_NAMESPACE_ERR),
+ {stop, normal, StateData}
end;
-
wait_for_stream({xmlstreamerror, _}, StateData) ->
send_text(StateData,
- ?STREAM_HEADER("") ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
+ <<(?STREAM_HEADER(<<"">>))/binary,
+ (?INVALID_XML_ERR)/binary, (?STREAM_TRAILER)/binary>>),
{stop, normal, StateData};
-
wait_for_stream(timeout, StateData) ->
{stop, normal, StateData};
-
wait_for_stream(closed, StateData) ->
{stop, normal, StateData}.
-
-wait_for_feature_request({xmlstreamelement, El}, StateData) ->
- {xmlelement, Name, Attrs, Els} = El,
+wait_for_feature_request({xmlstreamelement, El},
+ StateData) ->
+ #xmlel{name = Name, attrs = Attrs, children = Els} = El,
TLS = StateData#state.tls,
TLSEnabled = StateData#state.tls_enabled,
- SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
- case {xml:get_attr_s("xmlns", Attrs), Name} of
- {?NS_TLS, "starttls"} when TLS == true,
- TLSEnabled == false,
- SockMod == gen_tcp ->
- ?DEBUG("starttls", []),
- Socket = StateData#state.socket,
- TLSOpts = case ejabberd_config:get_local_option(
- {domain_certfile,
- StateData#state.server}) of
- undefined ->
- StateData#state.tls_options;
- CertFile ->
- [{certfile, CertFile} |
- lists:keydelete(
- certfile, 1,
- StateData#state.tls_options)]
- end,
- TLSSocket = (StateData#state.sockmod):starttls(
- Socket, TLSOpts,
- xml:element_to_binary(
- {xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
- {next_state, wait_for_stream,
- StateData#state{socket = TLSSocket,
- streamid = new_id(),
- tls_enabled = true,
- tls_options = TLSOpts
- }};
- {?NS_SASL, "auth"} when TLSEnabled ->
- Mech = xml:get_attr_s("mechanism", Attrs),
- case Mech of
- "EXTERNAL" ->
- Auth = jlib:decode_base64(xml:get_cdata(Els)),
- AuthDomain = jlib:nameprep(Auth),
- AuthRes =
- case (StateData#state.sockmod):get_peer_certificate(
- StateData#state.socket) of
+ SockMod =
+ (StateData#state.sockmod):get_sockmod(StateData#state.socket),
+ case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
+ {?NS_TLS, <<"starttls">>}
+ when TLS == true, TLSEnabled == false,
+ SockMod == gen_tcp ->
+ ?DEBUG("starttls", []),
+ Socket = StateData#state.socket,
+ TLSOpts = case
+ ejabberd_config:get_local_option(
+ {domain_certfile, StateData#state.server},
+ fun iolist_to_binary/1) of
+ undefined -> StateData#state.tls_options;
+ CertFile ->
+ [{certfile, CertFile} | lists:keydelete(certfile, 1,
+ StateData#state.tls_options)]
+ end,
+ TLSSocket = (StateData#state.sockmod):starttls(Socket,
+ TLSOpts,
+ xml:element_to_binary(#xmlel{name
+ =
+ <<"proceed">>,
+ attrs
+ =
+ [{<<"xmlns">>,
+ ?NS_TLS}],
+ children
+ =
+ []})),
+ {next_state, wait_for_stream,
+ StateData#state{socket = TLSSocket, streamid = new_id(),
+ tls_enabled = true, tls_options = TLSOpts}};
+ {?NS_SASL, <<"auth">>} when TLSEnabled ->
+ Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
+ case Mech of
+ <<"EXTERNAL">> ->
+ Auth = jlib:decode_base64(xml:get_cdata(Els)),
+ AuthDomain = jlib:nameprep(Auth),
+ AuthRes = case
+ (StateData#state.sockmod):get_peer_certificate(StateData#state.socket)
+ of
{ok, Cert} ->
- case (StateData#state.sockmod):get_verify_result(
- StateData#state.socket) of
- 0 ->
- case AuthDomain of
- error ->
- false;
- _ ->
- case idna:domain_utf8_to_ascii(AuthDomain) of
- false ->
- false;
- PCAuthDomain ->
- lists:any(
- fun(D) ->
- match_domain(
- PCAuthDomain, D)
- end, get_cert_domains(Cert))
- end
- end;
- _ ->
- false
+ case
+ (StateData#state.sockmod):get_verify_result(StateData#state.socket)
+ of
+ 0 ->
+ case AuthDomain of
+ error -> false;
+ _ ->
+ case
+ idna:domain_utf8_to_ascii(AuthDomain)
+ of
+ false -> false;
+ PCAuthDomain ->
+ lists:any(fun (D) ->
+ match_domain(PCAuthDomain,
+ D)
+ end,
+ get_cert_domains(Cert))
+ end
+ end;
+ _ -> false
end;
- error ->
- false
- end,
- AllowRemoteHost = ejabberd_s2s:allow_host("", AuthDomain),
- if
- AuthRes andalso AllowRemoteHost ->
- (StateData#state.sockmod):reset_stream(
- StateData#state.socket),
- send_element(StateData,
- {xmlelement, "success",
- [{"xmlns", ?NS_SASL}], []}),
- ?DEBUG("(~w) Accepted s2s authentication for ~s",
- [StateData#state.socket, AuthDomain]),
-
- %% acess rules are first checked against the globally defined ones, that have precedence over
- %% domain-specific ones.. http://www.process-one.net/docs/ejabberd/guide_en.html#AccessRights
- %% since there is allways a shaper defined globally for s2s, it doesn't matter the actual
- %% local host, since the globall one will be used, even if this domain has a special rule
- change_shaper(StateData, "", jlib:make_jid("", AuthDomain, "")),
- {next_state, wait_for_stream,
- StateData#state{streamid = new_id(),
- authenticated = true,
- auth_domain = AuthDomain
- }};
- true ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}], []}),
- send_text(StateData, ?STREAM_TRAILER),
- {stop, normal, StateData}
- end;
- _ ->
- send_element(StateData,
- {xmlelement, "failure",
- [{"xmlns", ?NS_SASL}],
- [{xmlelement, "invalid-mechanism", [], []}]}),
- {stop, normal, StateData}
- end;
- _ ->
- stream_established({xmlstreamelement, El}, StateData)
+ error -> false
+ end,
+ AllowRemoteHost = ejabberd_s2s:allow_host(<<"">>,
+ AuthDomain),
+ if AuthRes andalso AllowRemoteHost ->
+ (StateData#state.sockmod):reset_stream(StateData#state.socket),
+ send_element(StateData,
+ #xmlel{name = <<"success">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children = []}),
+ ?DEBUG("(~w) Accepted s2s authentication for ~s",
+ [StateData#state.socket, AuthDomain]),
+ change_shaper(StateData, <<"">>,
+ jlib:make_jid(<<"">>, AuthDomain, <<"">>)),
+ {next_state, wait_for_stream,
+ StateData#state{streamid = new_id(),
+ authenticated = true,
+ auth_domain = AuthDomain}};
+ true ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children = []}),
+ send_text(StateData, ?STREAM_TRAILER),
+ {stop, normal, StateData}
+ end;
+ _ ->
+ send_element(StateData,
+ #xmlel{name = <<"failure">>,
+ attrs = [{<<"xmlns">>, ?NS_SASL}],
+ children =
+ [#xmlel{name = <<"invalid-mechanism">>,
+ attrs = [], children = []}]}),
+ {stop, normal, StateData}
+ end;
+ _ ->
+ stream_established({xmlstreamelement, El}, StateData)
end;
-
-wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
+wait_for_feature_request({xmlstreamend, _Name},
+ StateData) ->
send_text(StateData, ?STREAM_TRAILER),
{stop, normal, StateData};
-
-wait_for_feature_request({xmlstreamerror, _}, StateData) ->
- send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
+wait_for_feature_request({xmlstreamerror, _},
+ StateData) ->
+ send_text(StateData,
+ <<(?INVALID_XML_ERR)/binary,
+ (?STREAM_TRAILER)/binary>>),
{stop, normal, StateData};
-
wait_for_feature_request(closed, StateData) ->
{stop, normal, StateData}.
-
stream_established({xmlstreamelement, El}, StateData) ->
cancel_timer(StateData#state.timer),
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
case is_key_packet(El) of
- {key, To, From, Id, Key} ->
- ?DEBUG("GET KEY: ~p", [{To, From, Id, Key}]),
- LTo = jlib:nameprep(To),
- LFrom = jlib:nameprep(From),
- %% Checks if the from domain is allowed and if the to
- %% domain is handled by this server:
- case {ejabberd_s2s:allow_host(LTo, LFrom),
- lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
- {true, true} ->
- ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
- ejabberd_s2s_out:start(LTo, LFrom,
- {verify, self(),
- Key, StateData#state.streamid}),
- Conns = ?DICT:store({LFrom, LTo}, wait_for_verification,
- StateData#state.connections),
- change_shaper(StateData, LTo, jlib:make_jid("", LFrom, "")),
- {next_state,
- stream_established,
- StateData#state{connections = Conns,
- timer = Timer}};
- {_, false} ->
- send_text(StateData, ?HOST_UNKNOWN_ERR),
- {stop, normal, StateData};
- {false, _} ->
- send_text(StateData, ?INVALID_FROM_ERR),
- {stop, normal, StateData}
- end;
- {verify, To, From, Id, Key} ->
- ?DEBUG("VERIFY KEY: ~p", [{To, From, Id, Key}]),
- LTo = jlib:nameprep(To),
- LFrom = jlib:nameprep(From),
- Type = case ejabberd_s2s:has_key({LTo, LFrom}, Key) of
- true -> "valid";
- _ -> "invalid"
- end,
- %Type = if Key == Key1 -> "valid";
- % true -> "invalid"
- % end,
- send_element(StateData,
- {xmlelement,
- "db:verify",
- [{"from", To},
- {"to", From},
- {"id", Id},
- {"type", Type}],
- []}),
- {next_state, stream_established, StateData#state{timer = Timer}};
- _ ->
- NewEl = jlib:remove_attr("xmlns", El),
- {xmlelement, Name, Attrs, _Els} = NewEl,
- From_s = xml:get_attr_s("from", Attrs),
- From = jlib:string_to_jid(From_s),
- To_s = xml:get_attr_s("to", Attrs),
- To = jlib:string_to_jid(To_s),
- if
- (To /= error) and (From /= error) ->
- LFrom = From#jid.lserver,
- LTo = To#jid.lserver,
- if
- StateData#state.authenticated ->
- case (LFrom == StateData#state.auth_domain)
- andalso
- lists:member(
- LTo,
- ejabberd_router:dirty_get_all_domains()) of
- true ->
- if ((Name == "iq") or
- (Name == "message") or
- (Name == "presence")) ->
- ejabberd_hooks:run(
- s2s_receive_packet,
- LTo,
- [From, To, NewEl]),
- ejabberd_router:route(
- From, To, NewEl);
- true ->
- error
- end;
- false ->
- error
- end;
- true ->
- case ?DICT:find({LFrom, LTo},
- StateData#state.connections) of
- {ok, established} ->
- if ((Name == "iq") or
- (Name == "message") or
- (Name == "presence")) ->
- ejabberd_hooks:run(
- s2s_receive_packet,
- LTo,
- [From, To, NewEl]),
- ejabberd_router:route(
- From, To, NewEl);
- true ->
- error
- end;
- _ ->
- error
- end
- end;
- true ->
- error
- end,
- ejabberd_hooks:run(s2s_loop_debug, [{xmlstreamelement, El}]),
- {next_state, stream_established, StateData#state{timer = Timer}}
+ {key, To, From, Id, Key} ->
+ ?DEBUG("GET KEY: ~p", [{To, From, Id, Key}]),
+ LTo = jlib:nameprep(To),
+ LFrom = jlib:nameprep(From),
+ case {ejabberd_s2s:allow_host(LTo, LFrom),
+ lists:member(LTo,
+ ejabberd_router:dirty_get_all_domains())}
+ of
+ {true, true} ->
+ ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
+ ejabberd_s2s_out:start(LTo, LFrom,
+ {verify, self(), Key,
+ StateData#state.streamid}),
+ Conns = (?DICT):store({LFrom, LTo},
+ wait_for_verification,
+ StateData#state.connections),
+ change_shaper(StateData, LTo,
+ jlib:make_jid(<<"">>, LFrom, <<"">>)),
+ {next_state, stream_established,
+ StateData#state{connections = Conns, timer = Timer}};
+ {_, false} ->
+ send_text(StateData, ?HOST_UNKNOWN_ERR),
+ {stop, normal, StateData};
+ {false, _} ->
+ send_text(StateData, ?INVALID_FROM_ERR),
+ {stop, normal, StateData}
+ end;
+ {verify, To, From, Id, Key} ->
+ ?DEBUG("VERIFY KEY: ~p", [{To, From, Id, Key}]),
+ LTo = jlib:nameprep(To),
+ LFrom = jlib:nameprep(From),
+ Type = case ejabberd_s2s:has_key({LTo, LFrom}, Key) of
+ true -> <<"valid">>;
+ _ -> <<"invalid">>
+ end,
+ send_element(StateData,
+ #xmlel{name = <<"db:verify">>,
+ attrs =
+ [{<<"from">>, To}, {<<"to">>, From},
+ {<<"id">>, Id}, {<<"type">>, Type}],
+ children = []}),
+ {next_state, stream_established,
+ StateData#state{timer = Timer}};
+ _ ->
+ NewEl = jlib:remove_attr(<<"xmlns">>, El),
+ #xmlel{name = Name, attrs = Attrs} = NewEl,
+ From_s = xml:get_attr_s(<<"from">>, Attrs),
+ From = jlib:string_to_jid(From_s),
+ To_s = xml:get_attr_s(<<"to">>, Attrs),
+ To = jlib:string_to_jid(To_s),
+ if (To /= error) and (From /= error) ->
+ LFrom = From#jid.lserver,
+ LTo = To#jid.lserver,
+ if StateData#state.authenticated ->
+ case LFrom == StateData#state.auth_domain andalso
+ lists:member(LTo,
+ ejabberd_router:dirty_get_all_domains())
+ of
+ true ->
+ if (Name == <<"iq">>) or (Name == <<"message">>)
+ or (Name == <<"presence">>) ->
+ ejabberd_hooks:run(s2s_receive_packet, LTo,
+ [From, To, NewEl]),
+ ejabberd_router:route(From, To, NewEl);
+ true -> error
+ end;
+ false -> error
+ end;
+ true ->
+ case (?DICT):find({LFrom, LTo},
+ StateData#state.connections)
+ of
+ {ok, established} ->
+ if (Name == <<"iq">>) or (Name == <<"message">>)
+ or (Name == <<"presence">>) ->
+ ejabberd_hooks:run(s2s_receive_packet, LTo,
+ [From, To, NewEl]),
+ ejabberd_router:route(From, To, NewEl);
+ true -> error
+ end;
+ _ -> error
+ end
+ end;
+ true -> error
+ end,
+ ejabberd_hooks:run(s2s_loop_debug,
+ [{xmlstreamelement, El}]),
+ {next_state, stream_established,
+ StateData#state{timer = Timer}}
end;
-
stream_established({valid, From, To}, StateData) ->
send_element(StateData,
- {xmlelement,
- "db:result",
- [{"from", To},
- {"to", From},
- {"type", "valid"}],
- []}),
+ #xmlel{name = <<"db:result">>,
+ attrs =
+ [{<<"from">>, To}, {<<"to">>, From},
+ {<<"type">>, <<"valid">>}],
+ children = []}),
LFrom = jlib:nameprep(From),
LTo = jlib:nameprep(To),
- NSD = StateData#state{
- connections = ?DICT:store({LFrom, LTo}, established,
- StateData#state.connections)},
+ NSD = StateData#state{connections =
+ (?DICT):store({LFrom, LTo}, established,
+ StateData#state.connections)},
{next_state, stream_established, NSD};
-
stream_established({invalid, From, To}, StateData) ->
send_element(StateData,
- {xmlelement,
- "db:result",
- [{"from", To},
- {"to", From},
- {"type", "invalid"}],
- []}),
+ #xmlel{name = <<"db:result">>,
+ attrs =
+ [{<<"from">>, To}, {<<"to">>, From},
+ {<<"type">>, <<"invalid">>}],
+ children = []}),
LFrom = jlib:nameprep(From),
LTo = jlib:nameprep(To),
- NSD = StateData#state{
- connections = ?DICT:erase({LFrom, LTo},
- StateData#state.connections)},
+ NSD = StateData#state{connections =
+ (?DICT):erase({LFrom, LTo},
+ StateData#state.connections)},
{next_state, stream_established, NSD};
-
stream_established({xmlstreamend, _Name}, StateData) ->
{stop, normal, StateData};
-
stream_established({xmlstreamerror, _}, StateData) ->
send_text(StateData,
- ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
+ <<(?INVALID_XML_ERR)/binary,
+ (?STREAM_TRAILER)/binary>>),
{stop, normal, StateData};
-
stream_established(timeout, StateData) ->
{stop, normal, StateData};
-
stream_established(closed, StateData) ->
{stop, normal, StateData}.
-
-
%%----------------------------------------------------------------------
%% Func: StateName/3
%% Returns: {next_state, NextStateName, NextStateData} |
@@ -586,32 +583,30 @@ handle_event(_Event, StateName, StateData) ->
%% {reply, Reply, NextStateName, NextStateData}
%% Reply = {state_infos, [{InfoName::atom(), InfoValue::any()]
%%----------------------------------------------------------------------
-handle_sync_event(get_state_infos, _From, StateName, StateData) ->
+
+handle_sync_event(get_state_infos, _From, StateName,
+ StateData) ->
SockMod = StateData#state.sockmod,
- {Addr,Port} = try SockMod:peername(StateData#state.socket) of
- {ok, {A,P}} -> {A,P};
- {error, _} -> {unknown,unknown}
- catch
- _:_ -> {unknown,unknown}
- end,
- Domains = get_external_hosts(StateData),
- Infos = [
- {direction, in},
- {statename, StateName},
- {addr, Addr},
- {port, Port},
+ {Addr, Port} = try
+ SockMod:peername(StateData#state.socket)
+ of
+ {ok, {A, P}} -> {A, P};
+ {error, _} -> {unknown, unknown}
+ catch
+ _:_ -> {unknown, unknown}
+ end,
+ Domains = get_external_hosts(StateData),
+ Infos = [{direction, in}, {statename, StateName},
+ {addr, Addr}, {port, Port},
{streamid, StateData#state.streamid},
{tls, StateData#state.tls},
{tls_enabled, StateData#state.tls_enabled},
{tls_options, StateData#state.tls_options},
{authenticated, StateData#state.authenticated},
- {shaper, StateData#state.shaper},
- {sockmod, SockMod},
- {domains, Domains}
- ],
+ {shaper, StateData#state.shaper}, {sockmod, SockMod},
+ {domains, Domains}],
Reply = {state_infos, Infos},
- {reply,Reply,StateName,StateData};
-
+ {reply, Reply, StateName, StateData};
%%----------------------------------------------------------------------
%% Func: handle_sync_event/4
%% Returns: {next_state, NextStateName, NextStateData} |
@@ -621,9 +616,9 @@ handle_sync_event(get_state_infos, _From, StateName, StateData) ->
%% {stop, Reason, NewStateData} |
%% {stop, Reason, Reply, NewStateData}
%%----------------------------------------------------------------------
-handle_sync_event(_Event, _From, StateName, StateData) ->
- Reply = ok,
- {reply, Reply, StateName, StateData}.
+handle_sync_event(_Event, _From, StateName,
+ StateData) ->
+ Reply = ok, {reply, Reply, StateName, StateData}.
code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}.
@@ -637,15 +632,12 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text),
{next_state, StateName, StateData};
-
handle_info({timeout, Timer, _}, _StateName,
#state{timer = Timer} = StateData) ->
{stop, normal, StateData};
-
handle_info(_, StateName, StateData) ->
{next_state, StateName, StateData}.
-
%%----------------------------------------------------------------------
%% Func: terminate/3
%% Purpose: Shutdown the fsm
@@ -654,21 +646,21 @@ handle_info(_, StateName, StateData) ->
terminate(Reason, _StateName, StateData) ->
?DEBUG("terminated: ~p", [Reason]),
case Reason of
- {process_limit, _} ->
- [ejabberd_s2s:external_host_overloaded(Host) || Host <- get_external_hosts(StateData)];
- _ ->
- ok
+ {process_limit, _} ->
+ [ejabberd_s2s:external_host_overloaded(Host)
+ || Host <- get_external_hosts(StateData)];
+ _ -> ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ok.
get_external_hosts(StateData) ->
case StateData#state.authenticated of
- true ->
- [StateData#state.auth_domain];
- false ->
- Connections = StateData#state.connections,
- [D || {{D, _}, established} <- dict:to_list(Connections)]
+ true -> [StateData#state.auth_domain];
+ false ->
+ Connections = StateData#state.connections,
+ [D
+ || {{D, _}, established} <- dict:to_list(Connections)]
end.
%%----------------------------------------------------------------------
@@ -676,168 +668,172 @@ get_external_hosts(StateData) ->
%% Purpose: Prepare the state to be printed on error log
%% Returns: State to print
%%----------------------------------------------------------------------
-print_state(State) ->
- State.
+print_state(State) -> State.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
send_text(StateData, Text) ->
- (StateData#state.sockmod):send(StateData#state.socket, Text).
+ (StateData#state.sockmod):send(StateData#state.socket,
+ Text).
send_element(StateData, El) ->
send_text(StateData, xml:element_to_binary(El)).
-
change_shaper(StateData, Host, JID) ->
- Shaper = acl:match_rule(Host, StateData#state.shaper, JID),
- (StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
-
+ Shaper = acl:match_rule(Host, StateData#state.shaper,
+ JID),
+ (StateData#state.sockmod):change_shaper(StateData#state.socket,
+ Shaper).
-new_id() ->
- randoms:get_string().
+new_id() -> randoms:get_string().
cancel_timer(Timer) ->
erlang:cancel_timer(Timer),
- receive
- {timeout, Timer, _} ->
- ok
- after 0 ->
- ok
- end.
-
-
-is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
- {key,
- xml:get_attr_s("to", Attrs),
- xml:get_attr_s("from", Attrs),
- xml:get_attr_s("id", Attrs),
- xml:get_cdata(Els)};
-is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
- {verify,
- xml:get_attr_s("to", Attrs),
- xml:get_attr_s("from", Attrs),
- xml:get_attr_s("id", Attrs),
- xml:get_cdata(Els)};
-is_key_packet(_) ->
- false.
-
+ receive {timeout, Timer, _} -> ok after 0 -> ok end.
+
+is_key_packet(#xmlel{name = Name, attrs = Attrs,
+ children = Els})
+ when Name == <<"db:result">> ->
+ {key, xml:get_attr_s(<<"to">>, Attrs),
+ xml:get_attr_s(<<"from">>, Attrs),
+ xml:get_attr_s(<<"id">>, Attrs), xml:get_cdata(Els)};
+is_key_packet(#xmlel{name = Name, attrs = Attrs,
+ children = Els})
+ when Name == <<"db:verify">> ->
+ {verify, xml:get_attr_s(<<"to">>, Attrs),
+ xml:get_attr_s(<<"from">>, Attrs),
+ xml:get_attr_s(<<"id">>, Attrs), xml:get_cdata(Els)};
+is_key_packet(_) -> false.
get_cert_domains(Cert) ->
{rdnSequence, Subject} =
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject,
Extensions =
(Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.extensions,
- lists:flatmap(
- fun(#'AttributeTypeAndValue'{type = ?'id-at-commonName',
- value = Val}) ->
- case ?PKIXEXPLICIT:decode('X520CommonName', Val) of
- {ok, {_, D1}} ->
- D = if
- is_list(D1) -> D1;
- is_binary(D1) -> binary_to_list(D1);
- true -> error
- end,
- if
- D /= error ->
- case jlib:string_to_jid(D) of
- #jid{luser = "",
- lserver = LD,
- lresource = ""} ->
- [LD];
- _ ->
- []
- end;
- true ->
- []
- end;
- _ ->
- []
- end;
- (_) ->
- []
- end, lists:flatten(Subject)) ++
- lists:flatmap(
- fun(#'Extension'{extnID = ?'id-ce-subjectAltName',
- extnValue = Val}) ->
- BVal = if
- is_list(Val) -> list_to_binary(Val);
- is_binary(Val) -> Val;
- true -> Val
- end,
- case ?PKIXIMPLICIT:decode('SubjectAltName', BVal) of
- {ok, SANs} ->
- lists:flatmap(
- fun({otherName,
- #'AnotherName'{'type-id' = ?'id-on-xmppAddr',
- value = XmppAddr
- }}) ->
- case 'XmppAddr':decode(
- 'XmppAddr', XmppAddr) of
- {ok, D} when is_binary(D) ->
- case jlib:string_to_jid(
- binary_to_list(D)) of
- #jid{luser = "",
- lserver = LD,
- lresource = ""} ->
- case idna:domain_utf8_to_ascii(LD) of
- false ->
- [];
- PCLD ->
- [PCLD]
- end;
- _ ->
- []
- end;
- _ ->
- []
- end;
- ({dNSName, D}) when is_list(D) ->
- case jlib:string_to_jid(D) of
- #jid{luser = "",
- lserver = LD,
- lresource = ""} ->
- [LD];
- _ ->
- []
- end;
- (_) ->
- []
- end, SANs);
- _ ->
- []
- end;
- (_) ->
- []
- end, Extensions).
-
-match_domain(Domain, Domain) ->
- true;
+ lists:flatmap(fun (#'AttributeTypeAndValue'{type =
+ ?'id-at-commonName',
+ value = Val}) ->
+ case 'OTP-PUB-KEY':decode('X520CommonName', Val) of
+ {ok, {_, D1}} ->
+ D = if is_binary(D1) -> D1;
+ is_binary(D1) -> (D1);
+ true -> error
+ end,
+ if D /= error ->
+ case jlib:string_to_jid(D) of
+ #jid{luser = <<"">>, lserver = LD,
+ lresource = <<"">>} ->
+ [LD];
+ _ -> []
+ end;
+ true -> []
+ end;
+ _ -> []
+ end;
+ (_) -> []
+ end,
+ lists:flatten(Subject))
+ ++
+ lists:flatmap(fun (#'Extension'{extnID =
+ ?'id-ce-subjectAltName',
+ extnValue = Val}) ->
+ BVal = if is_binary(Val) -> iolist_to_binary(Val);
+ is_binary(Val) -> Val;
+ true -> Val
+ end,
+ case 'OTP-PUB-KEY':decode('SubjectAltName', BVal)
+ of
+ {ok, SANs} ->
+ lists:flatmap(fun ({otherName,
+ #'AnotherName'{'type-id' =
+ ?'id-on-xmppAddr',
+ value =
+ XmppAddr}}) ->
+ case
+ 'XmppAddr':decode('XmppAddr',
+ XmppAddr)
+ of
+ {ok, D}
+ when
+ is_binary(D) ->
+ case
+ jlib:string_to_jid((D))
+ of
+ #jid{luser =
+ <<"">>,
+ lserver =
+ LD,
+ lresource =
+ <<"">>} ->
+ case
+ idna:domain_utf8_to_ascii(LD)
+ of
+ false ->
+ [];
+ PCLD ->
+ [PCLD]
+ end;
+ _ -> []
+ end;
+ _ -> []
+ end;
+ ({dNSName, D})
+ when is_binary(D) ->
+ case
+ jlib:string_to_jid(D)
+ of
+ #jid{luser = <<"">>,
+ lserver = LD,
+ lresource =
+ <<"">>} ->
+ [LD];
+ _ -> []
+ end;
+ (_) -> []
+ end,
+ SANs);
+ _ -> []
+ end;
+ (_) -> []
+ end,
+ Extensions).
+
+match_domain(Domain, Domain) -> true;
match_domain(Domain, Pattern) ->
- DLabels = string:tokens(Domain, "."),
- PLabels = string:tokens(Pattern, "."),
+ DLabels = str:tokens(Domain, <<".">>),
+ PLabels = str:tokens(Pattern, <<".">>),
match_labels(DLabels, PLabels).
-match_labels([], []) ->
- true;
-match_labels([], [_ | _]) ->
- false;
-match_labels([_ | _], []) ->
- false;
+match_labels([], []) -> true;
+match_labels([], [_ | _]) -> false;
+match_labels([_ | _], []) -> false;
match_labels([DL | DLabels], [PL | PLabels]) ->
- case lists:all(fun(C) -> (($a =< C) andalso (C =< $z))
- orelse (($0 =< C) andalso (C =< $9))
- orelse (C == $-) orelse (C == $*)
- end, PL) of
- true ->
- Regexp = ejabberd_regexp:sh_to_awk(PL),
- case ejabberd_regexp:run(DL, Regexp) of
- match ->
- match_labels(DLabels, PLabels);
- nomatch ->
- false
- end;
- false ->
- false
+ case lists:all(fun (C) ->
+ $a =< C andalso C =< $z orelse
+ $0 =< C andalso C =< $9 orelse
+ C == $- orelse C == $*
+ end,
+ binary_to_list(PL))
+ of
+ true ->
+ Regexp = ejabberd_regexp:sh_to_awk(PL),
+ case ejabberd_regexp:run(DL, Regexp) of
+ match -> match_labels(DLabels, PLabels);
+ nomatch -> false
+ end;
+ false -> false
+ end.
+
+fsm_limit_opts(Opts) ->
+ case lists:keysearch(max_fsm_queue, 1, Opts) of
+ {value, {_, N}} when is_integer(N) -> [{max_queue, N}];
+ _ ->
+ case ejabberd_config:get_local_option(
+ max_fsm_queue,
+ fun(I) when is_integer(I), I > 0 -> I end) of
+ undefined -> [];
+ N -> [{max_queue, N}]
+ end
end.