diff options
Diffstat (limited to 'src/ejabberd_router.erl')
-rw-r--r-- | src/ejabberd_router.erl | 131 |
1 files changed, 87 insertions, 44 deletions
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index e29d6acfb..c6d919097 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -39,6 +39,7 @@ register_route/3, register_routes/1, host_of_route/1, + process_iq/3, unregister_route/1, unregister_routes/1, dirty_get_all_routes/0, @@ -53,7 +54,7 @@ -include("ejabberd.hrl"). -include("logger.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -type local_hint() :: undefined | integer() | {apply, atom(), atom()}. @@ -71,7 +72,7 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec route(jid(), jid(), xmlel()) -> ok. +-spec route(jid(), jid(), xmlel() | stanza()) -> ok. route(From, To, Packet) -> case catch do_route(From, To, Packet) of @@ -84,13 +85,21 @@ route(From, To, Packet) -> %% Route the error packet only if the originating packet is not an error itself. %% RFC3920 9.3.1 --spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok. +-spec route_error(jid(), jid(), xmlel(), xmlel()) -> ok; + (jid(), jid(), stanza(), stanza_error()) -> ok. -route_error(From, To, ErrPacket, OrigPacket) -> +route_error(From, To, #xmlel{} = ErrPacket, #xmlel{} = OrigPacket) -> #xmlel{attrs = Attrs} = OrigPacket, case <<"error">> == fxml:get_attr_s(<<"type">>, Attrs) of false -> route(From, To, ErrPacket); true -> ok + end; +route_error(From, To, Packet, #stanza_error{} = Err) -> + Type = xmpp:get_type(Packet), + if Type == error; Type == result -> + ok; + true -> + ejabberd_router:route(From, To, xmpp:make_error(Packet, Err)) end. -spec register_route(binary()) -> term(). @@ -236,6 +245,28 @@ host_of_route(Domain) -> end end. +-spec process_iq(jid(), jid(), iq() | xmlel()) -> any(). +process_iq(From, To, #iq{} = IQ) -> + if To#jid.luser == <<"">> -> + ejabberd_local:process_iq(From, To, IQ); + true -> + ejabberd_sm:process_iq(From, To, IQ) + end; +process_iq(From, To, El) -> + try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of + IQ -> process_iq(From, To, IQ) + catch _:{xmpp_codec, Why} -> + Type = xmpp:get_type(El), + if Type == <<"get">>; Type == <<"set">> -> + Txt = xmpp:format_error(Why), + Lang = xmpp:get_lang(El), + Err = xmpp:make_error(El, xmpp:err_bad_request(Txt, Lang)), + ejabberd_router:route(To, From, Err); + true -> + ok + end + end. + %%==================================================================== %% gen_server callbacks %%==================================================================== @@ -347,6 +378,7 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +-spec do_route(jid(), jid(), xmlel() | xmpp_element()) -> any(). do_route(OrigFrom, OrigTo, OrigPacket) -> ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket " "~p~n", @@ -357,69 +389,80 @@ do_route(OrigFrom, OrigTo, OrigPacket) -> {From, To, Packet} -> LDstDomain = To#jid.lserver, case mnesia:dirty_read(route, LDstDomain) of - [] -> ejabberd_s2s:route(From, To, Packet); + [] -> + try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of + Pkt -> + ejabberd_s2s:route(From, To, Pkt) + catch _:{xmpp_codec, Why} -> + log_decoding_error(From, To, Packet, Why) + end; [R] -> - Pid = R#route.pid, - if node(Pid) == node() -> - case R#route.local_hint of - {apply, Module, Function} -> - Module:Function(From, To, Packet); - _ -> Pid ! {route, From, To, Packet} - end; - is_pid(Pid) -> Pid ! {route, From, To, Packet}; - true -> drop - end; + do_route(From, To, Packet, R); Rs -> - Value = case - ejabberd_config:get_option({domain_balancing, - LDstDomain}, fun(D) when is_atom(D) -> D end) - of - undefined -> p1_time_compat:monotonic_time(); - random -> p1_time_compat:monotonic_time(); - source -> jid:tolower(From); - destination -> jid:tolower(To); - bare_source -> - jid:remove_resource(jid:tolower(From)); - bare_destination -> - jid:remove_resource(jid:tolower(To)) - end, + Value = get_domain_balancing(From, To, LDstDomain), case get_component_number(LDstDomain) of undefined -> case [R || R <- Rs, node(R#route.pid) == node()] of [] -> R = lists:nth(erlang:phash(Value, length(Rs)), Rs), - Pid = R#route.pid, - if is_pid(Pid) -> Pid ! {route, From, To, Packet}; - true -> drop - end; + do_route(From, To, Packet, R); LRs -> - R = lists:nth(erlang:phash(Value, length(LRs)), - LRs), - Pid = R#route.pid, - case R#route.local_hint of - {apply, Module, Function} -> - Module:Function(From, To, Packet); - _ -> Pid ! {route, From, To, Packet} - end + R = lists:nth(erlang:phash(Value, length(LRs)), LRs), + do_route(From, To, Packet, R) end; _ -> SRs = lists:ukeysort(#route.local_hint, Rs), R = lists:nth(erlang:phash(Value, length(SRs)), SRs), - Pid = R#route.pid, - if is_pid(Pid) -> Pid ! {route, From, To, Packet}; - true -> drop - end + do_route(From, To, Packet, R) end end; drop -> ok end. +-spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any(). +do_route(From, To, Packet, #route{local_hint = LocalHint, + pid = Pid}) when is_pid(Pid) -> + try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of + Pkt -> + case LocalHint of + {apply, Module, Function} when node(Pid) == node() -> + Module:Function(From, To, Pkt); + _ -> + Pid ! {route, From, To, Pkt} + end + catch error:{xmpp_codec, Why} -> + log_decoding_error(From, To, Packet, Why) + end; +do_route(_From, _To, _Packet, _Route) -> + drop. + +-spec log_decoding_error(jid(), jid(), xmlel() | xmpp_element(), term()) -> ok. +log_decoding_error(From, To, Packet, Reason) -> + ?ERROR_MSG("failed to decode xml element ~p when " + "routing from ~s to ~s: ~s", + [Packet, jid:to_string(From), jid:to_string(To), + xmpp:format_error(Reason)]). + +-spec get_component_number(binary()) -> pos_integer() | undefined. get_component_number(LDomain) -> ejabberd_config:get_option( {domain_balancing_component_number, LDomain}, fun(N) when is_integer(N), N > 1 -> N end, undefined). +-spec get_domain_balancing(jid(), jid(), binary()) -> any(). +get_domain_balancing(From, To, LDomain) -> + case ejabberd_config:get_option( + {domain_balancing, LDomain}, fun(D) when is_atom(D) -> D end) of + undefined -> p1_time_compat:monotonic_time(); + random -> p1_time_compat:monotonic_time(); + source -> jid:tolower(From); + destination -> jid:tolower(To); + bare_source -> jid:remove_resource(jid:tolower(From)); + bare_destination -> jid:remove_resource(jid:tolower(To)) + end. + +-spec update_tables() -> ok. update_tables() -> try mnesia:transform_table(route, ignore, record_info(fields, route)) |