aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_c2s.erl
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-27 10:45:08 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-27 10:45:08 +0300
commitc409ed2f2c09ae79a22745e2a253023787017893 (patch)
tree2a26aa019b06f37c39811d7e8fa9b1d77cc0b133 /src/ejabberd_c2s.erl
parentGet rid of "jlib.hrl" dependency in some files (diff)
Rewrite S2S and ejabberd_service code to use XML generator
Diffstat (limited to 'src/ejabberd_c2s.erl')
-rw-r--r--src/ejabberd_c2s.erl191
1 files changed, 96 insertions, 95 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 8d217a354..1ae9a7c29 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -320,42 +320,46 @@ get_subscribed(FsmRef) ->
(?GEN_FSM):sync_send_all_state_event(FsmRef,
get_subscribed, 1000).
-wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
- DefaultLang = ?MYLANG,
- case fxml:get_attr_s(<<"xmlns:stream">>, Attrs) of
- ?NS_STREAM ->
- Server =
- case StateData#state.server of
- <<"">> ->
- jid:nameprep(fxml:get_attr_s(<<"to">>, Attrs));
- S -> S
- end,
- Lang = case fxml:get_attr_s(<<"xml:lang">>, Attrs) of
- Lang1 when byte_size(Lang1) =< 35 ->
- %% As stated in BCP47, 4.4.1:
- %% Protocols or specifications that
- %% specify limited buffer sizes for
- %% language tags MUST allow for
- %% language tags of at least 35 characters.
- Lang1;
- _ ->
- %% Do not store long language tag to
- %% avoid possible DoS/flood attacks
- <<"">>
- end,
- StreamVersion = case fxml:get_attr_s(<<"version">>, Attrs) of
- <<"1.0">> ->
- <<"1.0">>;
- _ ->
- <<"">>
- end,
+wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
+ try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of
+ #stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM, lang = Lang}
+ when NS_CLIENT /= ?NS_CLIENT; NS_STREAM /= ?NS_STREAM ->
+ send_header(StateData, ?MYNAME, <<"">>, Lang),
+ send_element(StateData, xmpp:serr_invalid_namespace()),
+ {stop, normal, StateData};
+ #stream_start{lang = Lang} when byte_size(Lang) > 35 ->
+ %% As stated in BCP47, 4.4.1:
+ %% Protocols or specifications that specify limited buffer sizes for
+ %% language tags MUST allow for language tags of at least 35 characters.
+ %% Do not store long language tag to avoid possible DoS/flood attacks
+ send_header(StateData, ?MYNAME, <<"">>, ?MYLANG),
+ Txt = <<"Too long value of 'xml:lang' attribute">>,
+ send_element(StateData,
+ xmpp:serr_policy_violation(Txt, ?MYLANG)),
+ {stop, normal, StateData};
+ #stream_start{to = undefined, lang = Lang} ->
+ Txt = <<"Missing 'to' attribute">>,
+ send_header(StateData, ?MYNAME, <<"">>, Lang),
+ send_element(StateData,
+ xmpp:serr_improper_addressing(Txt, Lang)),
+ {stop, normal, StateData};
+ #stream_start{to = #jid{lserver = To}, lang = Lang,
+ version = Version} ->
+ Server = case StateData#state.server of
+ <<"">> -> To;
+ S -> S
+ end,
+ StreamVersion = case Version of
+ <<"1.0">> -> <<"1.0">>;
+ _ -> <<"">>
+ end,
IsBlacklistedIP = is_ip_blacklisted(StateData#state.ip, Lang),
case lists:member(Server, ?MYHOSTS) of
true when IsBlacklistedIP == false ->
change_shaper(StateData, jid:make(<<"">>, Server, <<"">>)),
case StreamVersion of
<<"1.0">> ->
- send_header(StateData, Server, <<"1.0">>, DefaultLang),
+ send_header(StateData, Server, <<"1.0">>, ?MYLANG),
case StateData#state.authenticated of
false ->
TLS = StateData#state.tls,
@@ -458,7 +462,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
end
end;
_ ->
- send_header(StateData, Server, <<"">>, DefaultLang),
+ send_header(StateData, Server, <<"">>, ?MYLANG),
if not StateData#state.tls_enabled and
StateData#state.tls_required ->
send_element(
@@ -477,17 +481,18 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{true, LogReason, ReasonT} = IsBlacklistedIP,
?INFO_MSG("Connection attempt from blacklisted IP ~s: ~s",
[jlib:ip_to_list(IP), LogReason]),
- send_header(StateData, Server, StreamVersion, DefaultLang),
+ send_header(StateData, Server, StreamVersion, ?MYLANG),
send_element(StateData, xmpp:serr_policy_violation(ReasonT, Lang)),
{stop, normal, StateData};
_ ->
- send_header(StateData, ?MYNAME, StreamVersion, DefaultLang),
+ send_header(StateData, ?MYNAME, StreamVersion, ?MYLANG),
send_element(StateData, xmpp:serr_host_unknown()),
{stop, normal, StateData}
- end;
- _ ->
- send_header(StateData, ?MYNAME, <<"">>, DefaultLang),
- send_element(StateData, xmpp:serr_invalid_namespace()),
+ end
+ catch _:{xmpp_codec, Why} ->
+ Txt = xmpp:format_error(Why),
+ send_header(StateData, ?MYNAME, <<"">>, ?MYLANG),
+ send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)),
{stop, normal, StateData}
end;
wait_for_stream(timeout, StateData) ->
@@ -854,38 +859,36 @@ resource_conflict_action(U, S, R) ->
{accept_resource, Rnew}
end.
--spec decode_subels(stanza()) -> stanza().
-decode_subels(#iq{sub_els = [El], type = T} = IQ) when T == set; T == get ->
- NewEl = case xmpp:get_ns(El) of
- ?NS_BIND when T == set -> xmpp:decode(El);
- ?NS_AUTH -> xmpp:decode(El);
- ?NS_PRIVACY -> xmpp:decode(El);
- ?NS_BLOCKING -> xmpp:decode(El);
- _ -> El
- end,
- IQ#iq{sub_els = [NewEl]};
-decode_subels(Pkt) ->
- Pkt.
-
--spec decode_element(xmlel(), state_name(), state()) -> fsm_next().
+-spec decode_element(xmlel(), state_name(), state()) -> fsm_transition().
decode_element(#xmlel{} = El, StateName, StateData) ->
- try
- Pkt0 = xmpp:decode(El, [ignore_els]),
- Pkt = decode_subels(Pkt0),
- ?MODULE:StateName(Pkt, StateData)
+ try case xmpp:decode(El, [ignore_els]) of
+ #iq{sub_els = [_], type = T} = Pkt when T == set; T == get ->
+ NewPkt = xmpp:decode_els(
+ Pkt,
+ fun(SubEl) when StateName == session_established ->
+ case xmpp:get_ns(SubEl) of
+ ?NS_PRIVACY -> true;
+ ?NS_BLOCKING -> true;
+ _ -> false
+ end;
+ (SubEl) ->
+ xmpp_codec:is_known_tag(SubEl)
+ end),
+ ?MODULE:StateName(NewPkt, StateData);
+ Pkt ->
+ ?MODULE:StateName(Pkt, StateData)
+ end
catch error:{xmpp_codec, Why} ->
- Type = xmpp:get_type(El),
NS = xmpp:get_ns(El),
case xmpp:is_stanza(El) of
- true when Type /= <<"result">>, Type /= <<"error">> ->
+ true ->
Lang = xmpp:get_lang(El),
Txt = xmpp:format_error(Why),
- Err = xmpp:make_error(El, xmpp:err_bad_request(Txt, Lang)),
- send_element(StateData, Err);
- _ when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
+ send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
+ false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
Err = #sm_failed{reason = 'bad-request', xmlns = NS},
send_element(StateData, Err);
- _ ->
+ false ->
ok
end,
fsm_next_state(StateName, StateData)
@@ -951,13 +954,7 @@ wait_for_bind(stop, StateData) ->
wait_for_bind(Pkt, StateData) ->
case xmpp:is_stanza(Pkt) of
true ->
- Type = xmpp:get_type(Pkt),
- if Type /= error, Type /= result ->
- Err = xmpp:make_error(Pkt, xmpp:err_not_acceptable()),
- send_element(StateData, Err);
- true ->
- ok
- end;
+ send_error(StateData, Pkt, xmpp:err_not_acceptable());
false ->
ok
end,
@@ -1046,7 +1043,7 @@ session_established(closed, StateData) ->
{stop, normal, StateData};
session_established(stop, StateData) ->
{stop, normal, StateData};
-session_established(Pkt, StateData) ->
+session_established(Pkt, StateData) when ?is_stanza(Pkt) ->
FromJID = StateData#state.jid,
case check_from(Pkt, FromJID) of
'invalid-from' ->
@@ -1055,11 +1052,13 @@ session_established(Pkt, StateData) ->
_ ->
NewStateData = update_num_stanzas_in(StateData, Pkt),
session_established2(Pkt, NewStateData)
- end.
+ end;
+session_established(_Pkt, StateData) ->
+ fsm_next_state(session_established, StateData).
-spec session_established2(xmpp_element(), state()) -> fsm_next().
%% Process packets sent by user (coming from user on c2s XMPP connection)
-session_established2(Pkt, StateData) when ?is_stanza(Pkt) ->
+session_established2(Pkt, StateData) ->
User = StateData#state.user,
Server = StateData#state.server,
FromJID = StateData#state.jid,
@@ -1116,11 +1115,7 @@ session_established2(Pkt, StateData) when ?is_stanza(Pkt) ->
end,
ejabberd_hooks:run(c2s_loop_debug,
[{xmlstreamelement, Pkt}]),
- fsm_next_state(session_established, NewState);
-session_established2(Pkt, StateData) ->
- ejabberd_hooks:run(c2s_loop_debug,
- [{xmlstreamelement, Pkt}]),
- fsm_next_state(session_established, StateData).
+ fsm_next_state(session_established, NewState).
wait_for_resume({xmlstreamelement, _El} = Event, StateData) ->
Result = session_established(Event, StateData),
@@ -1573,6 +1568,16 @@ send_element(StateData, #xmlel{} = El) ->
send_element(StateData, Pkt) ->
send_element(StateData, xmpp:encode(Pkt)).
+-spec send_error(state(), xmlel() | stanza(), error()) -> ok.
+send_error(StateData, Stanza, Error) ->
+ Type = xmpp:get_type(Stanza),
+ if Type == error; Type == result;
+ Type == <<"error">>; Type == <<"result">> ->
+ ok;
+ true ->
+ send_element(StateData, xmpp:make_error(Stanza, Error))
+ end.
+
-spec send_stanza(state(), xmpp_element()) -> state().
send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive ->
csi_filter_stanza(StateData, Stanza);
@@ -2136,28 +2141,24 @@ is_ip_blacklisted({IP, _Port}, Lang) ->
%% Check from attributes
%% returns invalid-from|NewElement
+-spec check_from(stanza(), jid()) -> 'invalid-from' | stanza().
check_from(Pkt, FromJID) ->
- case xmpp:is_stanza(Pkt) of
- false ->
+ JID = xmpp:get_from(Pkt),
+ case JID of
+ undefined ->
Pkt;
- true ->
- JID = xmpp:get_from(Pkt),
- case JID of
- undefined ->
+ #jid{} ->
+ if
+ (JID#jid.luser == FromJID#jid.luser) and
+ (JID#jid.lserver == FromJID#jid.lserver) and
+ (JID#jid.lresource == FromJID#jid.lresource) ->
Pkt;
- #jid{} ->
- if
- (JID#jid.luser == FromJID#jid.luser) and
- (JID#jid.lserver == FromJID#jid.lserver) and
- (JID#jid.lresource == FromJID#jid.lresource) ->
- Pkt;
- (JID#jid.luser == FromJID#jid.luser) and
- (JID#jid.lserver == FromJID#jid.lserver) and
- (JID#jid.lresource == <<"">>) ->
- Pkt;
- true ->
- 'invalid-from'
- end
+ (JID#jid.luser == FromJID#jid.luser) and
+ (JID#jid.lserver == FromJID#jid.lserver) and
+ (JID#jid.lresource == <<"">>) ->
+ Pkt;
+ true ->
+ 'invalid-from'
end
end.