diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ejabberd_c2s.erl | 65 | ||||
-rw-r--r-- | src/ejabberd_s2s.erl | 18 | ||||
-rw-r--r-- | src/ejabberd_s2s_in.erl | 101 | ||||
-rw-r--r-- | src/ejabberd_s2s_out.erl | 48 | ||||
-rw-r--r-- | src/ejabberd_service.erl | 17 | ||||
-rw-r--r-- | src/xmpp_codec.erl | 23 |
6 files changed, 157 insertions, 115 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 7dc9960e6..858e285a6 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -352,16 +352,16 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> S -> S end, StreamVersion = case Version of - <<"1.0">> -> <<"1.0">>; - _ -> <<"">> + {1,0} -> {1,0}; + _ -> undefined end, IsBlacklistedIP = is_ip_blacklisted(StateData#state.ip, Lang), case lists:member(Server, ?MYHOSTS) of true when IsBlacklistedIP == false -> change_shaper(StateData, jid:make(<<"">>, Server, <<"">>)), case StreamVersion of - <<"1.0">> -> - send_header(StateData, Server, <<"1.0">>, ?MYLANG), + {1,0} -> + send_header(StateData, Server, {1,0}, ?MYLANG), case StateData#state.authenticated of false -> TLS = StateData#state.tls, @@ -490,10 +490,14 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> send_header(StateData, ?MYNAME, StreamVersion, ?MYLANG), send_element(StateData, xmpp:serr_host_unknown()), {stop, normal, StateData} - end + end; + _ -> + send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, ?MYNAME, <<"1.0">>, ?MYLANG), + send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; @@ -506,7 +510,7 @@ wait_for_stream({xmlstreamend, _}, StateData) -> send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream({xmlstreamerror, _}, StateData) -> - send_header(StateData, ?MYNAME, <<"1.0">>, <<"">>), + send_header(StateData, ?MYNAME, {1,0}, <<"">>), send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream(closed, StateData) -> @@ -1374,7 +1378,7 @@ handle_info({'DOWN', Monitor, _Type, _Object, _Info}, handle_info(system_shutdown, StateName, StateData) -> case StateName of wait_for_stream -> - send_header(StateData, ?MYNAME, <<"1.0">>, <<"en">>), + send_header(StateData, ?MYNAME, {1,0}, <<"en">>), send_element(StateData, xmpp:serr_system_shutdown()), ok; _ -> @@ -1597,39 +1601,20 @@ send_packet(StateData, Packet) -> end. -spec send_header(state(), binary(), binary(), binary()) -> ok | {error, any()}. -send_header(StateData, Server, Version, Lang) - when StateData#state.xml_socket -> - VersionAttr = case Version of - <<"">> -> []; - _ -> [{<<"version">>, Version}] - end, - LangAttr = case Lang of - <<"">> -> []; - _ -> [{<<"xml:lang">>, Lang}] - end, - Header = {xmlstreamstart, <<"stream:stream">>, - VersionAttr ++ - LangAttr ++ - [{<<"xmlns">>, <<"jabber:client">>}, - {<<"xmlns:stream">>, - <<"http://etherx.jabber.org/streams">>}, - {<<"id">>, StateData#state.streamid}, - {<<"from">>, Server}]}, - (StateData#state.sockmod):send_xml(StateData#state.socket, - Header); send_header(StateData, Server, Version, Lang) -> - VersionStr = case Version of - <<"">> -> <<"">>; - _ -> [<<" version='">>, Version, <<"'">>] - end, - LangStr = case Lang of - <<"">> -> <<"">>; - _ -> [<<" xml:lang='">>, Lang, <<"'">>] - end, - Header = io_lib:format(?STREAM_HEADER, - [StateData#state.streamid, Server, VersionStr, - LangStr]), - send_text(StateData, iolist_to_binary(Header)). + Header = #xmlel{name = Name, attrs = Attrs} = + xmpp:encode(#stream_start{version = Version, + lang = Lang, + xmlns = ?NS_CLIENT, + stream_xmlns = ?NS_STREAM, + id = StateData#state.streamid, + from = jid:make(Server)}), + if StateData#state.xml_socket -> + (StateData#state.sockmod):send_xml(StateData#state.socket, + {xmlstreamstart, Name, Attrs}); + true -> + send_text(StateData, fxml:element_to_header(Header)) + end. -spec send_trailer(state()) -> ok | {error, any()}. send_trailer(StateData) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index e585257e8..3c3e698ad 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -39,6 +39,7 @@ remove_connection/2, find_connection/2, dirty_get_connections/0, allow_host/2, incoming_s2s_number/0, outgoing_s2s_number/0, + stop_all_connections/0, clean_temporarily_blocked_table/0, list_temporarily_blocked_hosts/0, external_host_overloaded/1, is_temporarly_blocked/1, @@ -480,7 +481,13 @@ get_commands_spec() -> "the node", policy = admin, module = ?MODULE, function = outgoing_s2s_number, - args = [], result = {s2s_outgoing, integer}}]. + args = [], result = {s2s_outgoing, integer}}, + #ejabberd_commands{name = stop_all_connections, + tags = [s2s], + desc = "Stop all outgoing and incoming connections", + policy = admin, + module = ?MODULE, function = stop_all_connections, + args = [], result = {res, rescode}}]. incoming_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_in_sup)). @@ -488,6 +495,15 @@ incoming_s2s_number() -> outgoing_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_out_sup)). +stop_all_connections() -> + lists:foreach( + fun({_Id, Pid, _Type, _Module}) -> + exit(Pid, kill) + end, + supervisor:which_children(ejabberd_s2s_in_sup) ++ + supervisor:which_children(ejabberd_s2s_out_sup)), + mnesia:clear_table(s2s). + %%%---------------------------------------------------------------------- %%% Update Mnesia tables diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index fd560a451..6d1791d0b 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -168,21 +168,26 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of #stream_start{xmlns = NS_SERVER, stream_xmlns = NS_STREAM} when NS_SERVER /= ?NS_SERVER; NS_STREAM /= ?NS_STREAM -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_invalid_namespace()), {stop, normal, StateData}; #stream_start{to = #jid{lserver = Server}, - from = #jid{lserver = From}, - version = <<"1.0">>} + from = From, version = {1,0}} when StateData#state.tls and not StateData#state.authenticated -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), Auth = if StateData#state.tls_enabled -> - {Result, Message} = - ejabberd_s2s:check_peer_certificate( - StateData#state.sockmod, - StateData#state.socket, - From), - {Result, From, Message}; + case From of + #jid{} -> + {Result, Message} = + ejabberd_s2s:check_peer_certificate( + StateData#state.sockmod, + StateData#state.socket, + From#jid.lserver), + {Result, From#jid.lserver, Message}; + undefined -> + {error, <<"(unknown)">>, + <<"Got no valid 'from' attribute">>} + end; true -> {no_verify, <<"(unknown)">>, <<"TLS not (yet) enabled">>} end, @@ -225,8 +230,8 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> NewStateData#state{server = Server}} end; #stream_start{to = #jid{lserver = Server}, - version = <<"1.0">>} when StateData#state.authenticated -> - send_header(StateData, <<" version='1.0'">>), + version = {1,0}} when StateData#state.authenticated -> + send_header(StateData, {1,0}), send_element(StateData, #stream_features{ sub_els = ejabberd_hooks:run_fold( @@ -236,24 +241,28 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> #stream_start{db_xmlns = ?NS_SERVER_DIALBACK} when (StateData#state.tls_required and StateData#state.tls_enabled) or (not StateData#state.tls_required) -> - send_header(StateData, <<"">>), + send_header(StateData, undefined), {next_state, stream_established, StateData}; #stream_start{} -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_undefined_condition()), - {stop, normal, StateData} + {stop, normal, StateData}; + _ -> + send_header(StateData, {1,0}), + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, <<" version='1.0'">>), - send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)), + send_header(StateData, {1,0}), + send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; wait_for_stream({xmlstreamerror, _}, StateData) -> - send_header(StateData, <<"">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream(timeout, StateData) -> - send_header(StateData, <<"">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_connection_timeout()), {stop, normal, StateData}; wait_for_stream(closed, StateData) -> @@ -277,13 +286,21 @@ wait_for_feature_request(#starttls{}, StateData#state.tls_options, {certfile, CertFile}) end, + TLSOpts2 = case ejabberd_config:get_option( + {s2s_cafile, StateData#state.server}, + fun iolist_to_binary/1) of + undefined -> TLSOpts1; + CAFile -> + lists:keystore(cafile, 1, TLSOpts1, + {cafile, CAFile}) + end, TLSOpts = case ejabberd_config:get_option( {s2s_tls_compression, StateData#state.server}, fun(true) -> true; (false) -> false end, false) of - true -> lists:delete(compression_none, TLSOpts1); - false -> [compression_none | TLSOpts1] + true -> lists:delete(compression_none, TLSOpts2); + false -> [compression_none | TLSOpts2] end, TLSSocket = (StateData#state.sockmod):starttls( Socket, TLSOpts, @@ -293,8 +310,7 @@ wait_for_feature_request(#starttls{}, StateData#state{socket = TLSSocket, streamid = new_id(), tls_enabled = true, tls_options = TLSOpts}}; _ -> - Txt = <<"Unsupported TLS transport">>, - send_element(StateData, xmpp:serr_policy_violation(Txt, ?MYLANG)), + send_element(StateData, #starttls_failure{}), {stop, normal, StateData} end; wait_for_feature_request(#sasl_auth{mechanism = Mech}, @@ -313,7 +329,10 @@ wait_for_feature_request(#sasl_auth{mechanism = Mech}, StateData#state{streamid = new_id(), authenticated = true}}; true -> - send_element(StateData, #sasl_failure{}), + Txt = xmpp:mk_text(<<"Denied by ACL">>, ?MYLANG), + send_element(StateData, + #sasl_failure{reason = 'not-authorized', + text = Txt}), {stop, normal, StateData} end; _ -> @@ -495,7 +514,7 @@ handle_info({send_text, Text}, StateName, StateData) -> handle_info({timeout, Timer, _}, StateName, #state{timer = Timer} = StateData) -> if StateName == wait_for_stream -> - send_header(StateData, <<"">>); + send_header(StateData, undefined); true -> ok end, @@ -555,15 +574,15 @@ send_error(StateData, Stanza, Error) -> send_trailer(StateData) -> send_text(StateData, <<"</stream:stream>">>). --spec send_header(state(), binary()) -> ok. +-spec send_header(state(), undefined | {integer(), integer()}) -> ok. send_header(StateData, Version) -> - send_text(StateData, - <<"<?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, - ">">>). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_SERVER, + stream_xmlns = ?NS_STREAM, + db_xmlns = ?NS_SERVER_DIALBACK, + id = StateData#state.streamid, + version = Version}), + send_text(StateData, fxml:element_to_header(Header)). -spec change_shaper(state(), binary(), jid()) -> ok. change_shaper(StateData, Host, JID) -> @@ -606,9 +625,14 @@ fsm_limit_opts(Opts) -> end end. --spec decode_element(xmlel(), state_name(), state()) -> fsm_transition(). +-spec decode_element(xmlel() | xmpp_element(), state_name(), state()) -> fsm_transition(). decode_element(#xmlel{} = El, StateName, StateData) -> - try xmpp:decode(El) of + Opts = if StateName == stream_established -> + [ignore_els]; + true -> + [] + end, + try xmpp:decode(El, Opts) of Pkt -> ?MODULE:StateName(Pkt, StateData) catch error:{xmpp_codec, Why} -> case xmpp:is_stanza(El) of @@ -620,12 +644,15 @@ decode_element(#xmlel{} = El, StateName, StateData) -> ok end, {next_state, StateName, StateData} - end. + end; +decode_element(Pkt, StateName, StateData) -> + ?MODULE:StateName(Pkt, StateData). opt_type(domain_certfile) -> fun iolist_to_binary/1; opt_type(max_fsm_queue) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(s2s_certfile) -> fun iolist_to_binary/1; +opt_type(s2s_cafile) -> fun iolist_to_binary/1; opt_type(s2s_ciphers) -> fun iolist_to_binary/1; opt_type(s2s_dhfile) -> fun iolist_to_binary/1; opt_type(s2s_protocol_options) -> @@ -647,6 +674,6 @@ opt_type(s2s_use_starttls) -> (required_trusted) -> required_trusted end; opt_type(_) -> - [domain_certfile, max_fsm_queue, s2s_certfile, + [domain_certfile, max_fsm_queue, s2s_certfile, s2s_cafile, s2s_ciphers, s2s_dhfile, s2s_protocol_options, s2s_tls_compression, s2s_use_starttls]. diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index dd37445d7..06ba16863 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -106,12 +106,6 @@ %% Specified in miliseconds. Default value is 5 minutes. -define(MAX_RETRY_DELAY, 300000). --define(STREAM_HEADER, - <<"<?xml version='1.0'?><stream:stream " - "xmlns:stream='http://etherx.jabber.org/stream" - "s' xmlns='jabber:server' xmlns:db='jabber:ser" - "ver:dialback' from='~s' to='~s'~s>">>). - -define(SOCKET_DEFAULT_RESULT, {error, badarg}). %%%---------------------------------------------------------------------- @@ -228,9 +222,8 @@ open_socket(init, StateData) -> ?SOCKET_DEFAULT_RESULT, AddrList) of {ok, Socket} -> - Version = if StateData#state.use_v10 -> - <<" version='1.0'">>; - true -> <<"">> + Version = if StateData#state.use_v10 -> {1,0}; + true -> undefined end, NewStateData = StateData#state{socket = Socket, tls_enabled = false, @@ -318,11 +311,10 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData0) -> {stop, normal, StateData}; #stream_start{xmlns = NS_SERVER, stream_xmlns = NS_STREAM} when NS_SERVER /= ?NS_SERVER; NS_STREAM /= ?NS_STREAM -> - send_header(StateData, <<" version='1.0'">>), send_element(StateData, xmpp:serr_invalid_namespace()), {stop, normal, StateData}; #stream_start{db_xmlns = ?NS_SERVER_DIALBACK, id = ID, - version = V} when V /= <<"1.0">> -> + version = V} when V /= {1,0} -> send_db_request(StateData#state{remote_streamid = ID}); #stream_start{db_xmlns = ?NS_SERVER_DIALBACK, id = ID} when StateData#state.use_v10 -> @@ -337,13 +329,14 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData0) -> StateData#state{db_enabled = false, remote_streamid = ID}, ?FSMTIMEOUT}; #stream_start{} -> - send_header(StateData, <<"">>), send_element(StateData, xmpp:serr_invalid_namespace()), - {stop, normal, StateData} + {stop, normal, StateData}; + _ -> + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, <<" version='1.0'">>), - send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)), + send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; wait_for_stream(Event, StateData) -> @@ -469,7 +462,7 @@ wait_for_auth_result({xmlstreamelement, El}, StateData) -> wait_for_auth_result(#sasl_success{}, StateData) -> ?DEBUG("auth: ~p", [{StateData#state.myname, StateData#state.server}]), ejabberd_socket:reset_stream(StateData#state.socket), - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), {next_state, wait_for_stream, StateData#state{streamid = new_id(), authenticated = true}, ?FSMTIMEOUT}; @@ -500,7 +493,7 @@ wait_for_starttls_proceed(#starttls_proceed{}, StateData) -> streamid = new_id(), tls_enabled = true, tls_options = TLSOpts}, - send_header(NewStateData, <<" version='1.0'">>), + send_header(NewStateData, {1,0}), {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT}; wait_for_starttls_proceed(Event, StateData) -> handle_unexpected_event(Event, wait_for_starttls_proceed, StateData). @@ -567,7 +560,8 @@ handle_unexpected_event(Event, StateName, StateData) -> {xmlstreamend, _} -> ?INFO_MSG("Closing s2s connection ~s -> ~s in state ~s: " "XML stream closed by peer", - [StateData#state.myname, StateData#state.server]), + [StateData#state.myname, StateData#state.server, + StateName]), {stop, normal, StateData}; timeout -> send_element(StateData, xmpp:serr_connection_timeout()), @@ -741,6 +735,7 @@ print_state(State) -> State. -spec send_text(state(), iodata()) -> ok. send_text(StateData, Text) -> + ?DEBUG("Send Text on stream = ~s", [Text]), ejabberd_socket:send(StateData#state.socket, Text). -spec send_element(state(), xmpp_element()) -> ok. @@ -748,15 +743,16 @@ send_element(StateData, El) -> El1 = fix_ns(xmpp:encode(El)), send_text(StateData, fxml:element_to_binary(El1)). --spec send_header(state(), binary()) -> ok. +-spec send_header(state(), undefined | {integer(), integer()}) -> ok. send_header(StateData, Version) -> - Txt = io_lib:format( - "<?xml version='1.0'?><stream:stream " - "xmlns:stream='http://etherx.jabber.org/stream" - "s' xmlns='jabber:server' xmlns:db='jabber:ser" - "ver:dialback' from='~s' to='~s'~s>", - [StateData#state.myname, StateData#state.server, Version]), - send_text(StateData, Txt). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_SERVER, + stream_xmlns = ?NS_STREAM, + db_xmlns = ?NS_SERVER_DIALBACK, + from = jid:make(StateData#state.myname), + to = jid:make(StateData#state.server), + version = Version}), + send_text(StateData, fxml:element_to_header(Header)). -spec send_trailer(state()) -> ok. send_trailer(StateData) -> diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index f4338593d..46d32e4fd 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -149,6 +149,10 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> #stream_start{} -> send_header(StateData, ?MYNAME), send_element(StateData, xmpp:serr_improper_addressing()), + {stop, normal, StateData}; + _ -> + send_header(StateData, ?MYNAME), + send_element(StateData, xmpp:serr_invalid_xml()), {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), @@ -319,13 +323,12 @@ send_error(StateData, Stanza, Error) -> -spec send_header(state(), binary()) -> ok. send_header(StateData, Host) -> - send_text(StateData, - io_lib:format( - <<"<?xml version='1.0'?><stream:stream " - "xmlns:stream='http://etherx.jabber.org/stream" - "s' xmlns='jabber:component:accept' id='~s' " - "from='~s'>">>, - [StateData#state.streamid, fxml:crypt(Host)])). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_COMPONENT, + stream_xmlns = ?NS_STREAM, + from = jid:make(Host), + id = StateData#state.streamid}), + send_text(StateData, fxml:element_to_header(Header)). -spec send_trailer(state()) -> ok. send_trailer(StateData) -> diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index 00ee53aaf..0a9258195 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -3955,6 +3955,14 @@ pp(upload_slot, 3) -> [get, put, xmlns]; pp(thumbnail, 4) -> [uri, 'media-type', width, height]; pp(_, _) -> no. +enc_version({Maj, Min}) -> + <<(integer_to_binary(Maj))/binary, $., + (integer_to_binary(Min))/binary>>. + +dec_version(S) -> + [Major, Minor] = binary:split(S, <<$.>>), + {binary_to_integer(Major), binary_to_integer(Minor)}. + enc_host_port(Host) when is_binary(Host) -> Host; enc_host_port({{_, _, _, _, _, _, _, _} = IPv6, Port}) -> @@ -5284,13 +5292,20 @@ encode_stream_start_attr_xmlns(_val, _acc) -> decode_stream_start_attr_version(__TopXMLNS, undefined) -> - <<>>; + undefined; decode_stream_start_attr_version(__TopXMLNS, _val) -> - _val. + case catch dec_version(_val) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"version">>, <<"stream:stream">>, + __TopXMLNS}}); + _res -> _res + end. -encode_stream_start_attr_version(<<>>, _acc) -> _acc; +encode_stream_start_attr_version(undefined, _acc) -> + _acc; encode_stream_start_attr_version(_val, _acc) -> - [{<<"version">>, _val} | _acc]. + [{<<"version">>, enc_version(_val)} | _acc]. decode_stream_start_attr_id(__TopXMLNS, undefined) -> <<>>; |