Skip to content

Commit 531a1fd

Browse files
committed
Add options to have separate parser limits before authentication
This also add new option that allows to limit max number of elements that stanza can have.
1 parent 8fe48cc commit 531a1fd

3 files changed

Lines changed: 51 additions & 19 deletions

File tree

rebar.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
{"priv/lib/xmpp_lang.so", ["c_src/xmpp_lang.c"]}]}.
3838

3939
{deps, [{p1_utils, "~> 1.0.29", {git, "https://github.com/processone/p1_utils", {tag, "1.0.29"}}},
40-
{fast_xml, "~> 1.1.58", {git, "https://github.com/processone/fast_xml", {tag, "1.1.58"}}},
40+
{fast_xml, "~> 1.1.59", {git, "https://github.com/processone/fast_xml", {tag, "1.1.59"}}},
4141
{fast_tls, "~> 1.1.26", {git, "https://github.com/processone/fast_tls", {tag, "1.1.26"}}},
4242
{ezlib, "~> 1.0.16", {git, "https://github.com/processone/ezlib", {tag, "1.0.16"}}},
4343
{idna, "~> 7.1", {git, "https://github.com/benoitc/erlang-idna", {tag, "7.1.0"}}},

src/xmpp_socket.erl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
release/1,
5252
get_tls_cb_exporter/1,
5353
get_tls_cert_hash/1,
54-
finish_tls_handshake/1]).
54+
finish_tls_handshake/1,
55+
change_limits/3]).
5556

5657
-include("xmpp.hrl").
5758
-include_lib("public_key/include/public_key.hrl").
@@ -71,6 +72,7 @@
7172
-record(socket_state, {sockmod :: sockmod(),
7273
socket :: socket(),
7374
max_stanza_size :: timeout(),
75+
max_stanza_elements :: timeout(),
7476
xml_stream :: undefined | fxml_stream:xml_stream_state(),
7577
shaper = none :: none | p1_shaper:state(),
7678
sock_peer_name = none :: none | {endpoint(), endpoint()},
@@ -108,17 +110,19 @@
108110
-spec new(sockmod(), socket(), [proplists:property()]) -> socket_state().
109111
new(SockMod, Socket, Opts) ->
110112
MaxStanzaSize = proplists:get_value(max_stanza_size, Opts, infinity),
113+
MaxStanzaElements = proplists:get_value(max_stanza_elements, Opts, infinity),
111114
SockPeer = proplists:get_value(sock_peer_name, Opts, none),
112115
XMLStream = case get_owner(SockMod, Socket) of
113116
Pid when Pid == self() ->
114-
fxml_stream:new(self(), MaxStanzaSize);
117+
fxml_stream:new(self(), {MaxStanzaSize, MaxStanzaElements});
115118
_ ->
116119
undefined
117120
end,
118121
#socket_state{sockmod = SockMod,
119122
socket = Socket,
120123
xml_stream = XMLStream,
121124
max_stanza_size = MaxStanzaSize,
125+
max_stanza_elements = MaxStanzaElements,
122126
sock_peer_name = SockPeer}.
123127

124128
connect(Addr, Port, Opts) ->
@@ -227,19 +231,28 @@ compress(_, _) ->
227231

228232
reset_stream(#socket_state{xml_stream = XMLStream,
229233
sockmod = SockMod, socket = Socket,
230-
max_stanza_size = MaxStanzaSize} = SocketData) ->
234+
max_stanza_size = MaxStanzaSize,
235+
max_stanza_elements = MaxStanzaElements} = SocketData) ->
231236
if XMLStream /= undefined ->
232237
XMLStream1 = try fxml_stream:reset(XMLStream)
233238
catch error:_ ->
234239
fxml_stream:close(XMLStream),
235-
fxml_stream:new(self(), MaxStanzaSize)
240+
fxml_stream:new(self(), {MaxStanzaSize, MaxStanzaElements})
236241
end,
237242
SocketData#socket_state{xml_stream = XMLStream1};
238243
true ->
239244
Socket1 = SockMod:reset_stream(Socket),
240245
SocketData#socket_state{socket = Socket1}
241246
end.
242247

248+
change_limits(#socket_state{xml_stream = undefined} = SocketData, _NewMaxStanzaSize, _NewMaxStanzaElements) ->
249+
SocketData;
250+
change_limits(#socket_state{xml_stream = XMLStream} = SocketData, NewMaxStanzaSize, NewMaxStanzaElements) ->
251+
fxml_stream:change_limits(XMLStream, NewMaxStanzaSize, NewMaxStanzaElements),
252+
SocketData#socket_state{max_stanza_size = NewMaxStanzaSize, max_stanza_elements = NewMaxStanzaElements};
253+
change_limits(SocketData, _, _) ->
254+
SocketData.
255+
243256
-spec send_element(socket_state(), fxml:xmlel()) -> ok | {error, inet:posix()}.
244257
send_element(#socket_state{xml_stream = undefined} = SocketData, El) ->
245258
send_xml(SocketData, {xmlstreamelement, El});

src/xmpp_stream_in.erl

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,12 @@ init([Mod, {SockMod, Socket}, Opts]) ->
302302
handle_cast(accept, #{socket := Socket,
303303
socket_mod := SockMod,
304304
socket_opts := Opts} = State) ->
305-
XMPPSocket = xmpp_socket:new(SockMod, Socket, Opts),
305+
MaxStanzaSize = proplists:get_value(max_stanza_size, Opts, infinity),
306+
MaxStanzaElements = proplists:get_value(max_stanza_elements, Opts, infinity),
307+
PreAuthMaxStanzaSize = proplists:get_value(pre_auth_max_stanza_size, Opts, MaxStanzaSize),
308+
PreAuthMaxStanzaElements = proplists:get_value(pre_auth_max_stanza_elements, Opts, MaxStanzaElements),
309+
Opts2 = [{max_stanza_size, PreAuthMaxStanzaSize}, {max_stanza_elements, PreAuthMaxStanzaElements} | Opts],
310+
XMPPSocket = xmpp_socket:new(SockMod, Socket, Opts2),
306311
SocketMonitor = xmpp_socket:monitor(XMPPSocket),
307312
case xmpp_socket:peername(XMPPSocket) of
308313
{ok, IP} ->
@@ -314,9 +319,14 @@ handle_cast(accept, #{socket := Socket,
314319
{stop, State4} ->
315320
{stop, normal, State4};
316321
State4 ->
317-
case is_disconnected(State4) of
318-
true -> noreply(State4);
319-
false -> handle_info({tcp, Socket, <<>>}, State4)
322+
State5 = if MaxStanzaSize /= PreAuthMaxStanzaSize orelse MaxStanzaElements /= PreAuthMaxStanzaElements ->
323+
State4#{post_auth_stanza_limits => {MaxStanzaSize, MaxStanzaElements}};
324+
true ->
325+
State4
326+
end,
327+
case is_disconnected(State5) of
328+
true -> noreply(State5);
329+
false -> handle_info({tcp, Socket, <<>>}, State5)
320330
end
321331
end;
322332
{error, _} ->
@@ -862,10 +872,17 @@ process_stream_established(State) ->
862872
State1 = State#{stream_authenticated => true,
863873
stream_state => established,
864874
stream_timeout => infinity},
865-
try callback(handle_stream_established, State1)
866-
catch _:{?MODULE, undef} -> State1
875+
State2 = update_parser_limits(State1),
876+
try callback(handle_stream_established, State2)
877+
catch _:{?MODULE, undef} -> State2
867878
end.
868879

880+
update_parser_limits(#{socket := Socket, post_auth_stanza_limits := {MaxStanzaSize, MaxElementsCount}} = State) ->
881+
Socket2 = xmpp_socket:change_limits(Socket, MaxStanzaSize, MaxElementsCount),
882+
maps:remove(post_auth_stanza_limits, State#{socket => Socket2});
883+
update_parser_limits(State) ->
884+
State.
885+
869886
-spec process_compress(compress(), state()) -> state().
870887
process_compress(#compress{},
871888
#{stream_compressed := Compressed,
@@ -1032,8 +1049,9 @@ process_sasl_success(Props, ServerOut,
10321049
case is_disconnected(State2) of
10331050
true -> State2;
10341051
false ->
1035-
State3 = map_remove_keys(State2, [sasl_state, sasl_mech, sasl_channel_bindings]),
1036-
State3#{stream_id => xmpp_stream:new_id(),
1052+
State3 = update_parser_limits(State2),
1053+
State4 = map_remove_keys(State3, [sasl_state, sasl_mech, sasl_channel_bindings]),
1054+
State4#{stream_id => xmpp_stream:new_id(),
10371055
stream_authenticated => true,
10381056
stream_header_sent => false,
10391057
stream_restarted => true,
@@ -1161,13 +1179,14 @@ process_sasl2_success(Props, ServerOut,
11611179
case is_disconnected(State1) of
11621180
true -> State1;
11631181
false ->
1164-
State2 = State1#{stream_id => xmpp_stream:new_id(),
1165-
stream_authenticated => true,
1166-
user => User},
1182+
State2 = update_parser_limits(
1183+
State1#{stream_id => xmpp_stream:new_id(),
1184+
stream_authenticated => true,
1185+
user => User}),
11671186
{State3, NewEls, Results} =
1168-
try callback(handle_sasl2_inline, InlineEls, State2)
1169-
catch _:{?MODULE, undef} -> {State2, InlineEls, []}
1170-
end,
1187+
try callback(handle_sasl2_inline, InlineEls, State2)
1188+
catch _:{?MODULE, undef} -> {State2, InlineEls, []}
1189+
end,
11711190

11721191
case NewEls of
11731192
{continue, Tasks} ->

0 commit comments

Comments
 (0)