25
1
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:
Evgeniy Khramtsov 2011-05-09 13:43:54 +10:00
parent 1922bf21f0
commit 8806fdc1c2
4 changed files with 176 additions and 120 deletions

View File

@ -231,6 +231,12 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
(_) -> false (_) -> false
end, Opts), end, Opts),
TLSOpts = [verify_none | TLSOpts1], 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 IP = case lists:keysearch(frontend_ip, 1, Opts) of
{value, {_, IP1}} -> {value, {_, IP1}} ->
IP1; IP1;
@ -265,6 +271,7 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
access = Access, access = Access,
shaper = Shaper, shaper = Shaper,
ip = IP, ip = IP,
redirect = Redirect,
fsm_limit_opts = FSMLimitOpts}, fsm_limit_opts = FSMLimitOpts},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT} {ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end; end;
@ -585,45 +592,55 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
"(~w) Accepted legacy authentication for ~s by ~p", "(~w) Accepted legacy authentication for ~s by ~p",
[StateData#state.socket, [StateData#state.socket,
jlib:jid_to_string(JID), AuthModule]), jlib:jid_to_string(JID), AuthModule]),
SID = {now(), self()}, case need_redirect(StateData#state{user = U}) of
Conn = get_conn_type(StateData), {true, Host} ->
%% Info = [{ip, StateData#state.ip}, {conn, Conn}, ?INFO_MSG("(~w) Redirecting ~s to ~s",
%% {auth_module, AuthModule}], [StateData#state.socket,
Res1 = jlib:make_result_iq_reply(El), jlib:jid_to_string(JID), Host]),
Res = setelement(4, Res1, []), send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
send_element(StateData, Res), send_trailer(StateData),
%% ejabberd_sm:open_session( {stop, normal, StateData};
%% SID, U, StateData#state.server, R, Info), false ->
change_shaper(StateData, JID), SID = {now(), self()},
{Fs, Ts} = ejabberd_hooks:run_fold( Conn = get_conn_type(StateData),
roster_get_subscription_lists, Res1 = jlib:make_result_iq_reply(El),
StateData#state.server, Res = setelement(4, Res1, []),
{[], []}, send_element(StateData, Res),
[U, StateData#state.server]), change_shaper(StateData, JID),
LJID = jlib:jid_tolower( {Fs, Ts} = ejabberd_hooks:run_fold(
jlib:jid_remove_resource(JID)), roster_get_subscription_lists,
Fs1 = [LJID | Fs], StateData#state.server,
Ts1 = [LJID | Ts], {[], []},
PrivList = [U, StateData#state.server]),
ejabberd_hooks:run_fold( LJID = jlib:jid_tolower(
privacy_get_user_list, StateData#state.server, jlib:jid_remove_resource(JID)),
#userlist{}, Fs1 = [LJID | Fs],
[U, StateData#state.server]), Ts1 = [LJID | Ts],
NewStateData = StateData#state{ PrivList =
user = U, ejabberd_hooks:run_fold(
resource = R, privacy_get_user_list,
jid = JID, StateData#state.server,
sid = SID, #userlist{},
conn = Conn, [U, StateData#state.server]),
auth_module = AuthModule, NewStateData =
pres_f = ?SETS:from_list(Fs1), StateData#state{
pres_t = ?SETS:from_list(Ts1), user = U,
privacy_list = PrivList}, resource = R,
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook, jid = JID,
NewStateData#state.server, sid = SID,
false, conn = Conn,
[self(), NewStateData]), auth_module = AuthModule,
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag}); 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( ?INFO_MSG(
"(~w) Failed legacy authentication for ~s", "(~w) Failed legacy authentication for ~s",
@ -715,21 +732,30 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Mech, Mech,
ClientIn) of ClientIn) of
{ok, Props} -> {ok, Props} ->
catch (StateData#state.sockmod):reset_stream( catch (StateData#state.sockmod):reset_stream(
StateData#state.socket), StateData#state.socket),
send_element(StateData, U = xml:get_attr_s(username, Props),
{xmlelement, "success", AuthModule = xml:get_attr_s(auth_module, Props),
[{"xmlns", ?NS_SASL}], []}), ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
U = xml:get_attr_s(username, Props), [StateData#state.socket, U, AuthModule]),
AuthModule = xml:get_attr_s(auth_module, Props), case need_redirect(StateData#state{user = U}) of
?INFO_MSG("(~w) Accepted authentication for ~s by ~p", {true, Host} ->
[StateData#state.socket, U, AuthModule]), ?INFO_MSG("(~w) Redirecting ~s to ~s",
fsm_next_state(wait_for_stream, [StateData#state.socket, U, Host]),
StateData#state{ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
streamid = new_id(), send_trailer(StateData),
authenticated = true, {stop, normal, StateData};
auth_module = AuthModule, false ->
user = U }); 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} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
@ -869,20 +895,29 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
ClientIn) of ClientIn) of
{ok, Props} -> {ok, Props} ->
catch (StateData#state.sockmod):reset_stream( catch (StateData#state.sockmod):reset_stream(
StateData#state.socket), StateData#state.socket),
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
U = xml:get_attr_s(username, Props), U = xml:get_attr_s(username, Props),
AuthModule = xml:get_attr_s(auth_module, Props), AuthModule = xml:get_attr_s(auth_module, Props),
?INFO_MSG("(~w) Accepted authentication for ~s by ~p", ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
[StateData#state.socket, U, AuthModule]), [StateData#state.socket, U, AuthModule]),
fsm_next_state(wait_for_stream, case need_redirect(StateData#state{user = U}) of
StateData#state{ {true, Host} ->
streamid = new_id(), ?INFO_MSG("(~w) Redirecting ~s to ~s",
authenticated = true, [StateData#state.socket, U, Host]),
auth_module = AuthModule, send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
user = U}); 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} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
@ -3394,3 +3429,21 @@ flash_policy_string() ->
++ ToPortsString ++ ++ ToPortsString ++
"\"/>\n" "\"/>\n"
"</cross-domain-policy>\n\0". "</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.

View File

@ -57,6 +57,7 @@
conn = unknown, conn = unknown,
auth_module = unknown, auth_module = unknown,
ip, ip,
redirect = false,
aux_fields = [], aux_fields = [],
fsm_limit_opts, fsm_limit_opts,
lang, lang,

View File

@ -440,6 +440,8 @@ process_term(Term, State) ->
State; State;
{max_fsm_queue, N} -> {max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State); add_option(max_fsm_queue, N, State);
{hostname, Host} ->
add_option(hostname, Host, State);
{_Opt, _Val} -> {_Opt, _Val} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts) State, State#state.hosts)

View File

@ -206,117 +206,117 @@
?ERRT_CONFLICT(Lang, "Resource conflict")). ?ERRT_CONFLICT(Lang, "Resource conflict")).
-define(STREAM_ERROR(Condition), -define(STREAM_ERROR(Condition, Cdata),
{xmlelement, "stream:error", {xmlelement, "stream:error",
[], [],
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}). [{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
[{xmlcdata, Cdata}]}]}).
-define(SERR_BAD_FORMAT, -define(SERR_BAD_FORMAT,
?STREAM_ERROR("bad-format")). ?STREAM_ERROR("bad-format", "")).
-define(SERR_BAD_NAMESPACE_PREFIX, -define(SERR_BAD_NAMESPACE_PREFIX,
?STREAM_ERROR("bad-namespace-prefix")). ?STREAM_ERROR("bad-namespace-prefix", "")).
-define(SERR_CONFLICT, -define(SERR_CONFLICT,
?STREAM_ERROR("conflict")). ?STREAM_ERROR("conflict", "")).
-define(SERR_CONNECTION_TIMEOUT, -define(SERR_CONNECTION_TIMEOUT,
?STREAM_ERROR("connection-timeout")). ?STREAM_ERROR("connection-timeout", "")).
-define(SERR_HOST_GONE, -define(SERR_HOST_GONE,
?STREAM_ERROR("host-gone")). ?STREAM_ERROR("host-gone", "")).
-define(SERR_HOST_UNKNOWN, -define(SERR_HOST_UNKNOWN,
?STREAM_ERROR("host-unknown")). ?STREAM_ERROR("host-unknown", "")).
-define(SERR_IMPROPER_ADDRESSING, -define(SERR_IMPROPER_ADDRESSING,
?STREAM_ERROR("improper-addressing")). ?STREAM_ERROR("improper-addressing", "")).
-define(SERR_INTERNAL_SERVER_ERROR, -define(SERR_INTERNAL_SERVER_ERROR,
?STREAM_ERROR("internal-server-error")). ?STREAM_ERROR("internal-server-error", "")).
-define(SERR_INVALID_FROM, -define(SERR_INVALID_FROM,
?STREAM_ERROR("invalid-from")). ?STREAM_ERROR("invalid-from", "")).
-define(SERR_INVALID_ID, -define(SERR_INVALID_ID,
?STREAM_ERROR("invalid-id")). ?STREAM_ERROR("invalid-id", "")).
-define(SERR_INVALID_NAMESPACE, -define(SERR_INVALID_NAMESPACE,
?STREAM_ERROR("invalid-namespace")). ?STREAM_ERROR("invalid-namespace", "")).
-define(SERR_INVALID_XML, -define(SERR_INVALID_XML,
?STREAM_ERROR("invalid-xml")). ?STREAM_ERROR("invalid-xml", "")).
-define(SERR_NOT_AUTHORIZED, -define(SERR_NOT_AUTHORIZED,
?STREAM_ERROR("not-authorized")). ?STREAM_ERROR("not-authorized", "")).
-define(SERR_POLICY_VIOLATION, -define(SERR_POLICY_VIOLATION,
?STREAM_ERROR("policy-violation")). ?STREAM_ERROR("policy-violation", "")).
-define(SERR_REMOTE_CONNECTION_FAILED, -define(SERR_REMOTE_CONNECTION_FAILED,
?STREAM_ERROR("remote-connection-failed")). ?STREAM_ERROR("remote-connection-failed", "")).
-define(SERR_RESOURSE_CONSTRAINT, -define(SERR_RESOURSE_CONSTRAINT,
?STREAM_ERROR("resource-constraint")). ?STREAM_ERROR("resource-constraint", "")).
-define(SERR_RESTRICTED_XML, -define(SERR_RESTRICTED_XML,
?STREAM_ERROR("restricted-xml")). ?STREAM_ERROR("restricted-xml", "")).
% TODO: include hostname or IP -define(SERR_SEE_OTHER_HOST(Host),
-define(SERR_SEE_OTHER_HOST, ?STREAM_ERROR("see-other-host", Host)).
?STREAM_ERROR("see-other-host")).
-define(SERR_SYSTEM_SHUTDOWN, -define(SERR_SYSTEM_SHUTDOWN,
?STREAM_ERROR("system-shutdown")). ?STREAM_ERROR("system-shutdown", "")).
-define(SERR_UNSUPPORTED_ENCODING, -define(SERR_UNSUPPORTED_ENCODING,
?STREAM_ERROR("unsupported-encoding")). ?STREAM_ERROR("unsupported-encoding", "")).
-define(SERR_UNSUPPORTED_STANZA_TYPE, -define(SERR_UNSUPPORTED_STANZA_TYPE,
?STREAM_ERROR("unsupported-stanza-type")). ?STREAM_ERROR("unsupported-stanza-type", "")).
-define(SERR_UNSUPPORTED_VERSION, -define(SERR_UNSUPPORTED_VERSION,
?STREAM_ERROR("unsupported-version")). ?STREAM_ERROR("unsupported-version", "")).
-define(SERR_XML_NOT_WELL_FORMED, -define(SERR_XML_NOT_WELL_FORMED,
?STREAM_ERROR("xml-not-well-formed")). ?STREAM_ERROR("xml-not-well-formed", "")).
%-define(SERR_, %-define(SERR_,
% ?STREAM_ERROR("")). % ?STREAM_ERROR("", "")).
-define(STREAM_ERRORT(Condition, Lang, Text), -define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
{xmlelement, "stream:error", {xmlelement, "stream:error",
[], [],
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}, [{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
[{xmlcdata, Cdata}]},
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}], {xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
[{xmlcdata, translate:translate(Lang, Text)}]}]}). [{xmlcdata, translate:translate(Lang, Text)}]}]}).
-define(SERRT_BAD_FORMAT(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), -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), -define(SERRT_CONFLICT(Lang, Text),
?STREAM_ERRORT("conflict", Lang, Text)). ?STREAM_ERRORT("conflict", "", Lang, Text)).
-define(SERRT_CONNECTION_TIMEOUT(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), -define(SERRT_HOST_GONE(Lang, Text),
?STREAM_ERRORT("host-gone", Lang, Text)). ?STREAM_ERRORT("host-gone", "", Lang, Text)).
-define(SERRT_HOST_UNKNOWN(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), -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), -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), -define(SERRT_INVALID_FROM(Lang, Text),
?STREAM_ERRORT("invalid-from", Lang, Text)). ?STREAM_ERRORT("invalid-from", "", Lang, Text)).
-define(SERRT_INVALID_ID(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), -define(SERRT_INVALID_NAMESPACE(Lang, Text),
?STREAM_ERRORT("invalid-namespace", Lang, Text)). ?STREAM_ERRORT("invalid-namespace", "", Lang, Text)).
-define(SERRT_INVALID_XML(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), -define(SERRT_NOT_AUTHORIZED(Lang, Text),
?STREAM_ERRORT("not-authorized", Lang, Text)). ?STREAM_ERRORT("not-authorized", "", Lang, Text)).
-define(SERRT_POLICY_VIOLATION(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), -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), -define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
?STREAM_ERRORT("resource-constraint", Lang, Text)). ?STREAM_ERRORT("resource-constraint", "", Lang, Text)).
-define(SERRT_RESTRICTED_XML(Lang, Text), -define(SERRT_RESTRICTED_XML(Lang, Text),
?STREAM_ERRORT("restricted-xml", Lang, Text)). ?STREAM_ERRORT("restricted-xml", "", Lang, Text)).
% TODO: include hostname or IP -define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
-define(SERRT_SEE_OTHER_HOST(Lang, Text), ?STREAM_ERRORT("see-other-host", Host, Lang, Text)).
?STREAM_ERRORT("see-other-host", Lang, Text)).
-define(SERRT_SYSTEM_SHUTDOWN(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), -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), -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), -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), -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), %-define(SERRT_(Lang, Text),
% ?STREAM_ERRORT("", Lang, Text)). % ?STREAM_ERRORT("", "", Lang, Text)).
-record(jid, {user, server, resource, -record(jid, {user, server, resource,