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
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.

View File

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

View File

@ -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)

View File

@ -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,