1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : ejabberd_router.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Main router |
5 |
|
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(ejabberd_router). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
-behaviour(gen_server). |
30 |
|
%% API |
31 |
|
-export([route/3, |
32 |
|
route/4, |
33 |
|
route_error/4, |
34 |
|
route_error_reply/4]). |
35 |
|
|
36 |
|
-export([start_link/0]). |
37 |
|
|
38 |
|
%% gen_server callbacks |
39 |
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). |
40 |
|
|
41 |
|
-ignore_xref([route_error/4, start_link/0]). |
42 |
|
|
43 |
|
-include("mongoose.hrl"). |
44 |
|
-include("jlib.hrl"). |
45 |
|
|
46 |
|
-record(state, {}). |
47 |
|
|
48 |
|
%%==================================================================== |
49 |
|
%% API |
50 |
|
%%==================================================================== |
51 |
|
|
52 |
|
-spec start_link() -> 'ignore' | {'error', _} | {'ok', pid()}. |
53 |
|
start_link() -> |
54 |
101 |
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). |
55 |
|
|
56 |
|
%% @doc The main routing function. It puts the message through a chain |
57 |
|
%% of filtering/routing modules, as defined in config 'routing_modules' |
58 |
|
%% setting (default is hardcoded in default_routing_modules function |
59 |
|
%% of this module). Each of those modules should use xmpp_router behaviour |
60 |
|
%% and implement two functions: |
61 |
|
%% filter/3 - should return either 'drop' atom or its args |
62 |
|
%% route/3, which should either: |
63 |
|
%% - deliver the message locally by calling mongoose_local_delivery:do_route/5 |
64 |
|
%% and return 'done' |
65 |
|
%% - deliver the message it its own way and return 'done' |
66 |
|
%% - return its args |
67 |
|
%% - return a tuple of {From, To, Packet} which might be modified |
68 |
|
%% For both functions, returning a 'drop' or 'done' atom terminates the procedure, |
69 |
|
%% while returning a tuple means 'proceed' and the tuple is passed to |
70 |
|
%% the next module in sequence. |
71 |
|
-spec route(From :: jid:jid(), |
72 |
|
To :: jid:jid(), |
73 |
|
Packet :: mongoose_acc:t()|exml:element()) -> mongoose_acc:t(). |
74 |
|
route(From, To, #xmlel{} = Packet) -> |
75 |
|
% ?LOG_ERROR("Deprecated - it should be Acc: ~p", [Packet]), |
76 |
2793 |
Acc = mongoose_acc:new(#{ location => ?LOCATION, |
77 |
|
lserver => From#jid.lserver, |
78 |
|
element => Packet, |
79 |
|
from_jid => From, |
80 |
|
to_jid => To }), |
81 |
|
% (called by broadcasting) |
82 |
2793 |
route(From, To, Acc); |
83 |
|
route(From, To, Acc) -> |
84 |
6151 |
?LOG_DEBUG(#{what => route, acc => Acc}), |
85 |
6151 |
El = mongoose_acc:element(Acc), |
86 |
6151 |
RoutingModules = mongoose_router:routing_modules_list(), |
87 |
6151 |
NewAcc = route(From, To, Acc, El, RoutingModules), |
88 |
6151 |
?LOG_DEBUG(#{what => routing_result, |
89 |
|
routing_result => mongoose_acc:get(router, result, {drop, undefined}, NewAcc), |
90 |
6151 |
routing_modules => RoutingModules, acc => Acc}), |
91 |
6151 |
NewAcc. |
92 |
|
|
93 |
|
-spec route(From :: jid:jid(), |
94 |
|
To :: jid:jid(), |
95 |
|
Acc :: mongoose_acc:t(), |
96 |
|
El :: exml:element() | {error, term()}) -> mongoose_acc:t(). |
97 |
|
route(_From, _To, Acc, {error, Reason} = Err) -> |
98 |
:-( |
?LOG_INFO(#{what => cannot_route_stanza, acc => Acc, reason => Reason}), |
99 |
:-( |
mongoose_acc:append(router, result, Err, Acc); |
100 |
|
route(From, To, Acc, El) -> |
101 |
17330 |
Acc1 = mongoose_acc:update_stanza(#{ from_jid => From, |
102 |
|
to_jid => To, |
103 |
|
element => El }, Acc), |
104 |
17330 |
?LOG_DEBUG(#{what => route, acc => Acc1}), |
105 |
17330 |
RoutingModules = mongoose_router:routing_modules_list(), |
106 |
17330 |
NewAcc = route(From, To, Acc1, El, RoutingModules), |
107 |
17330 |
?LOG_DEBUG(#{what => routing_result, |
108 |
|
routing_result => mongoose_acc:get(router, result, {drop, undefined}, NewAcc), |
109 |
17330 |
routing_modules => RoutingModules, acc => Acc}), |
110 |
17330 |
NewAcc. |
111 |
|
|
112 |
|
%% Route the error packet only if the originating packet is not an error itself. |
113 |
|
%% RFC3920 9.3.1 |
114 |
|
-spec route_error(From :: jid:jid(), |
115 |
|
To :: jid:jid(), |
116 |
|
Acc :: mongoose_acc:t(), |
117 |
|
ErrPacket :: exml:element()) -> mongoose_acc:t(). |
118 |
|
route_error(From, To, Acc, ErrPacket) -> |
119 |
:-( |
case mongoose_acc:stanza_type(Acc) of |
120 |
|
<<"error">> -> |
121 |
:-( |
Acc; |
122 |
|
_ -> |
123 |
:-( |
route(From, To, Acc, ErrPacket) |
124 |
|
end. |
125 |
|
|
126 |
|
-spec route_error_reply(jid:jid(), jid:jid(), mongoose_acc:t(), exml:element()) -> |
127 |
|
mongoose_acc:t(). |
128 |
|
route_error_reply(From, To, Acc, Error) -> |
129 |
:-( |
{Acc1, ErrorReply} = jlib:make_error_reply(Acc, Error), |
130 |
:-( |
route_error(From, To, Acc1, ErrorReply). |
131 |
|
|
132 |
|
%%==================================================================== |
133 |
|
%% gen_server callbacks |
134 |
|
%%==================================================================== |
135 |
|
|
136 |
|
init([]) -> |
137 |
101 |
mongoose_metrics:ensure_metric(global, routingErrors, spiral), |
138 |
101 |
mongoose_component:start(), |
139 |
101 |
{ok, #state{}}. |
140 |
|
|
141 |
|
handle_call(_Request, _From, State) -> |
142 |
:-( |
Reply = ok, |
143 |
:-( |
{reply, Reply, State}. |
144 |
|
|
145 |
|
handle_cast(_Msg, State) -> |
146 |
:-( |
{noreply, State}. |
147 |
|
|
148 |
|
handle_info(_Info, State) -> |
149 |
:-( |
{noreply, State}. |
150 |
|
|
151 |
|
terminate(_Reason, _State) -> |
152 |
:-( |
mongoose_component:stop(), |
153 |
:-( |
ok. |
154 |
|
|
155 |
|
code_change(_OldVsn, State, _Extra) -> |
156 |
:-( |
{ok, State}. |
157 |
|
|
158 |
|
%%-------------------------------------------------------------------- |
159 |
|
%%% Internal functions |
160 |
|
%%-------------------------------------------------------------------- |
161 |
|
|
162 |
|
-spec route(From :: jid:jid(), |
163 |
|
To :: jid:jid(), |
164 |
|
Acc :: mongoose_acc:t(), |
165 |
|
Packet :: exml:element(), |
166 |
|
[xmpp_router:t()]) -> mongoose_acc:t(). |
167 |
|
route(_From, _To, Acc, _Packet, []) -> |
168 |
:-( |
?LOG_ERROR(#{what => no_more_routing_modules, acc => Acc}), |
169 |
:-( |
mongoose_metrics:update(global, routingErrors, 1), |
170 |
:-( |
mongoose_acc:append(router, result, {error, out_of_modules}, Acc); |
171 |
|
route(OrigFrom, OrigTo, Acc0, OrigPacket, [M|Tail]) -> |
172 |
47457 |
try xmpp_router:call_filter(M, OrigFrom, OrigTo, Acc0, OrigPacket) of |
173 |
|
drop -> |
174 |
388 |
mongoose_acc:append(router, result, {drop, M}, Acc0); |
175 |
|
{OrigFrom, OrigTo, Acc1, OrigPacketFiltered} -> |
176 |
47069 |
try xmpp_router:call_route(M, OrigFrom, OrigTo, Acc1, OrigPacketFiltered) of |
177 |
|
{done, Acc2} -> |
178 |
23090 |
mongoose_acc:append(router, result, {done, M}, Acc2); |
179 |
|
{From, To, NAcc1, Packet} -> |
180 |
23976 |
route(From, To, NAcc1, Packet, Tail) |
181 |
|
catch Class:Reason:Stacktrace -> |
182 |
3 |
?LOG_WARNING(#{what => routing_failed, |
183 |
|
router_module => M, acc => Acc1, |
184 |
:-( |
class => Class, reason => Reason, stacktrace => Stacktrace}), |
185 |
3 |
mongoose_acc:append(router, result, {error, {M, Reason}}, Acc1) |
186 |
|
end |
187 |
|
catch Class:Reason:Stacktrace -> |
188 |
:-( |
?LOG_WARNING(#{what => route_filter_failed, |
189 |
|
text => <<"Error when filtering packet in router">>, |
190 |
|
router_module => M, acc => Acc0, |
191 |
:-( |
class => Class, reason => Reason, stacktrace => Stacktrace}), |
192 |
:-( |
mongoose_acc:append(router, result, {error, {M, Reason}}, Acc0) |
193 |
|
end. |