diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 2566dec1b..bc8b087db 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -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" "\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. diff --git a/src/ejabberd_c2s.hrl b/src/ejabberd_c2s.hrl index 9af6b02d5..7889c0de5 100644 --- a/src/ejabberd_c2s.hrl +++ b/src/ejabberd_c2s.hrl @@ -57,6 +57,7 @@ conn = unknown, auth_module = unknown, ip, + redirect = false, aux_fields = [], fsm_limit_opts, lang, diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 1609b447d..848bcf7ca 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -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) diff --git a/src/jlib.hrl b/src/jlib.hrl index a4f8fd0da..7ac220cc0 100644 --- a/src/jlib.hrl +++ b/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,