mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +01:00
Implement C2S redirection
- The feature is based on <see-other-host/> stream error, see RFC6120, 4.9.3.19 - To enable the feature you must set {redirect, true} in C2S listener section and set global "hostname" option on all nodes in cluster. The hostname must be string in the form as described in the RFC, for example: "foo.org", "foo.org:5222", "1.2.3.4", "[2001:41D0:1:A49b::1]:9222" and so on
This commit is contained in:
parent
1922bf21f0
commit
8806fdc1c2
@ -231,6 +231,12 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
TLSOpts = [verify_none | TLSOpts1],
|
||||
Redirect = case lists:keysearch(redirect, 1, Opts) of
|
||||
{value, {_, true}} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
IP = case lists:keysearch(frontend_ip, 1, Opts) of
|
||||
{value, {_, IP1}} ->
|
||||
IP1;
|
||||
@ -265,6 +271,7 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
|
||||
access = Access,
|
||||
shaper = Shaper,
|
||||
ip = IP,
|
||||
redirect = Redirect,
|
||||
fsm_limit_opts = FSMLimitOpts},
|
||||
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
|
||||
end;
|
||||
@ -585,45 +592,55 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
"(~w) Accepted legacy authentication for ~s by ~p",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID), AuthModule]),
|
||||
SID = {now(), self()},
|
||||
Conn = get_conn_type(StateData),
|
||||
%% Info = [{ip, StateData#state.ip}, {conn, Conn},
|
||||
%% {auth_module, AuthModule}],
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
%% ejabberd_sm:open_session(
|
||||
%% SID, U, StateData#state.server, R, Info),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
StateData#state.server,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
ejabberd_hooks:run_fold(
|
||||
privacy_get_user_list, StateData#state.server,
|
||||
#userlist{},
|
||||
[U, StateData#state.server]),
|
||||
NewStateData = StateData#state{
|
||||
user = U,
|
||||
resource = R,
|
||||
jid = JID,
|
||||
sid = SID,
|
||||
conn = Conn,
|
||||
auth_module = AuthModule,
|
||||
pres_f = ?SETS:from_list(Fs1),
|
||||
pres_t = ?SETS:from_list(Ts1),
|
||||
privacy_list = PrivList},
|
||||
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
|
||||
NewStateData#state.server,
|
||||
false,
|
||||
[self(), NewStateData]),
|
||||
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID), Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
send_trailer(StateData),
|
||||
{stop, normal, StateData};
|
||||
false ->
|
||||
SID = {now(), self()},
|
||||
Conn = get_conn_type(StateData),
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
StateData#state.server,
|
||||
{[], []},
|
||||
[U, StateData#state.server]),
|
||||
LJID = jlib:jid_tolower(
|
||||
jlib:jid_remove_resource(JID)),
|
||||
Fs1 = [LJID | Fs],
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
ejabberd_hooks:run_fold(
|
||||
privacy_get_user_list,
|
||||
StateData#state.server,
|
||||
#userlist{},
|
||||
[U, StateData#state.server]),
|
||||
NewStateData =
|
||||
StateData#state{
|
||||
user = U,
|
||||
resource = R,
|
||||
jid = JID,
|
||||
sid = SID,
|
||||
conn = Conn,
|
||||
auth_module = AuthModule,
|
||||
pres_f = ?SETS:from_list(Fs1),
|
||||
pres_t = ?SETS:from_list(Ts1),
|
||||
privacy_list = PrivList},
|
||||
DebugFlag = ejabberd_hooks:run_fold(
|
||||
c2s_debug_start_hook,
|
||||
NewStateData#state.server,
|
||||
false,
|
||||
[self(), NewStateData]),
|
||||
maybe_migrate(session_established,
|
||||
NewStateData#state{debug=DebugFlag})
|
||||
end;
|
||||
_ ->
|
||||
?INFO_MSG(
|
||||
"(~w) Failed legacy authentication for ~s",
|
||||
@ -715,21 +732,30 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
Mech,
|
||||
ClientIn) of
|
||||
{ok, Props} ->
|
||||
catch (StateData#state.sockmod):reset_stream(
|
||||
StateData#state.socket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
|
||||
[StateData#state.socket, U, AuthModule]),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{
|
||||
streamid = new_id(),
|
||||
authenticated = true,
|
||||
auth_module = AuthModule,
|
||||
user = U });
|
||||
catch (StateData#state.sockmod):reset_stream(
|
||||
StateData#state.socket),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
|
||||
[StateData#state.socket, U, AuthModule]),
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket, U, Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
send_trailer(StateData),
|
||||
{stop, normal, StateData};
|
||||
false ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{
|
||||
streamid = new_id(),
|
||||
authenticated = true,
|
||||
auth_module = AuthModule,
|
||||
user = U })
|
||||
end;
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "challenge",
|
||||
@ -869,20 +895,29 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
ClientIn) of
|
||||
{ok, Props} ->
|
||||
catch (StateData#state.sockmod):reset_stream(
|
||||
StateData#state.socket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
StateData#state.socket),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
|
||||
[StateData#state.socket, U, AuthModule]),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{
|
||||
streamid = new_id(),
|
||||
authenticated = true,
|
||||
auth_module = AuthModule,
|
||||
user = U});
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket, U, Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
send_trailer(StateData),
|
||||
{stop, normal, StateData};
|
||||
false ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{
|
||||
streamid = new_id(),
|
||||
authenticated = true,
|
||||
auth_module = AuthModule,
|
||||
user = U})
|
||||
end;
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "challenge",
|
||||
@ -3394,3 +3429,21 @@ flash_policy_string() ->
|
||||
++ ToPortsString ++
|
||||
"\"/>\n"
|
||||
"</cross-domain-policy>\n\0".
|
||||
|
||||
need_redirect(#state{redirect = true, user = User, server = Server}) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
case ejabberd_cluster:get_node({LUser, LServer}) of
|
||||
Node when node() == Node ->
|
||||
false;
|
||||
Node ->
|
||||
case rpc:call(Node, ejabberd_config,
|
||||
get_local_option, [hostname], 5000) of
|
||||
Host when is_list(Host) ->
|
||||
{true, Host};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end;
|
||||
need_redirect(_) ->
|
||||
false.
|
||||
|
@ -57,6 +57,7 @@
|
||||
conn = unknown,
|
||||
auth_module = unknown,
|
||||
ip,
|
||||
redirect = false,
|
||||
aux_fields = [],
|
||||
fsm_limit_opts,
|
||||
lang,
|
||||
|
@ -440,6 +440,8 @@ process_term(Term, State) ->
|
||||
State;
|
||||
{max_fsm_queue, N} ->
|
||||
add_option(max_fsm_queue, N, State);
|
||||
{hostname, Host} ->
|
||||
add_option(hostname, Host, State);
|
||||
{_Opt, _Val} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts)
|
||||
|
112
src/jlib.hrl
112
src/jlib.hrl
@ -206,117 +206,117 @@
|
||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
-define(STREAM_ERROR(Condition, Cdata),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, Cdata}]}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR("bad-format")).
|
||||
?STREAM_ERROR("bad-format", "")).
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR("bad-namespace-prefix")).
|
||||
?STREAM_ERROR("bad-namespace-prefix", "")).
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR("conflict")).
|
||||
?STREAM_ERROR("conflict", "")).
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR("connection-timeout")).
|
||||
?STREAM_ERROR("connection-timeout", "")).
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR("host-gone")).
|
||||
?STREAM_ERROR("host-gone", "")).
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR("host-unknown")).
|
||||
?STREAM_ERROR("host-unknown", "")).
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR("improper-addressing")).
|
||||
?STREAM_ERROR("improper-addressing", "")).
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR("internal-server-error")).
|
||||
?STREAM_ERROR("internal-server-error", "")).
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR("invalid-from")).
|
||||
?STREAM_ERROR("invalid-from", "")).
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR("invalid-id")).
|
||||
?STREAM_ERROR("invalid-id", "")).
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR("invalid-namespace")).
|
||||
?STREAM_ERROR("invalid-namespace", "")).
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR("invalid-xml")).
|
||||
?STREAM_ERROR("invalid-xml", "")).
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR("not-authorized")).
|
||||
?STREAM_ERROR("not-authorized", "")).
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR("policy-violation")).
|
||||
?STREAM_ERROR("policy-violation", "")).
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR("remote-connection-failed")).
|
||||
?STREAM_ERROR("remote-connection-failed", "")).
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR("resource-constraint")).
|
||||
?STREAM_ERROR("resource-constraint", "")).
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR("restricted-xml")).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERR_SEE_OTHER_HOST,
|
||||
?STREAM_ERROR("see-other-host")).
|
||||
?STREAM_ERROR("restricted-xml", "")).
|
||||
-define(SERR_SEE_OTHER_HOST(Host),
|
||||
?STREAM_ERROR("see-other-host", Host)).
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR("system-shutdown")).
|
||||
?STREAM_ERROR("system-shutdown", "")).
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR("unsupported-encoding")).
|
||||
?STREAM_ERROR("unsupported-encoding", "")).
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR("unsupported-stanza-type")).
|
||||
?STREAM_ERROR("unsupported-stanza-type", "")).
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR("unsupported-version")).
|
||||
?STREAM_ERROR("unsupported-version", "")).
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR("xml-not-well-formed")).
|
||||
?STREAM_ERROR("xml-not-well-formed", "")).
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("")).
|
||||
% ?STREAM_ERROR("", "")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
||||
-define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, Cdata}]},
|
||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
||||
?STREAM_ERRORT("bad-format", "", Lang, Text)).
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
||||
?STREAM_ERRORT("bad-namespace-prefix", "", Lang, Text)).
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
||||
?STREAM_ERRORT("conflict", "", Lang, Text)).
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
||||
?STREAM_ERRORT("connection-timeout", "", Lang, Text)).
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
||||
?STREAM_ERRORT("host-gone", "", Lang, Text)).
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
||||
?STREAM_ERRORT("host-unknown", "", Lang, Text)).
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
||||
?STREAM_ERRORT("improper-addressing", "", Lang, Text)).
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
||||
?STREAM_ERRORT("internal-server-error", "", Lang, Text)).
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-from", "", Lang, Text)).
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-id", "", Lang, Text)).
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-namespace", "", Lang, Text)).
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-xml", "", Lang, Text)).
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
||||
?STREAM_ERRORT("not-authorized", "", Lang, Text)).
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
||||
?STREAM_ERRORT("policy-violation", "", Lang, Text)).
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
||||
?STREAM_ERRORT("remote-connection-failed", "", Lang, Text)).
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
||||
?STREAM_ERRORT("resource-constraint", "", Lang, Text)).
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
||||
?STREAM_ERRORT("restricted-xml", "", Lang, Text)).
|
||||
-define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Host, Lang, Text)).
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
||||
?STREAM_ERRORT("system-shutdown", "", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
||||
?STREAM_ERRORT("unsupported-encoding", "", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
||||
?STREAM_ERRORT("unsupported-stanza-type", "", Lang, Text)).
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
||||
?STREAM_ERRORT("unsupported-version", "", Lang, Text)).
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
||||
?STREAM_ERRORT("xml-not-well-formed", "", Lang, Text)).
|
||||
%-define(SERRT_(Lang, Text),
|
||||
% ?STREAM_ERRORT("", Lang, Text)).
|
||||
% ?STREAM_ERRORT("", "", Lang, Text)).
|
||||
|
||||
|
||||
-record(jid, {user, server, resource,
|
||||
|
Loading…
Reference in New Issue
Block a user