Skip to content

Commit 7336140

Browse files
committed
fix: improve receiving proxy protocol information
* handle proxy protocol timeout correctly * handle recv errors while receiving proxy protocol info
1 parent 70ea563 commit 7336140

File tree

4 files changed

+148
-22
lines changed

4 files changed

+148
-22
lines changed

src/esockd.app.src

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{application, esockd,
22
[{description, "General Non-blocking TCP/SSL and UDP/DTLS Server"},
33
{id, "esockd"},
4-
{vsn, "5.9.6"},
4+
{vsn, "5.9.7"},
55
{modules, []},
66
{registered, []},
77
{applications, [kernel, stdlib, sasl, ssl]},

src/esockd.appup.src

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
%%-*- mode: erlang; -*-
22

3-
{"5.9.6",
4-
[{"5.9.5",
3+
{"5.9.7",
4+
[{"5.9.6",
5+
[ {load_module,esockd_proxy_protocol,brutal_purge,soft_purge,[]}
6+
]
7+
},
8+
{"5.9.5",
59
[ {load_module,esockd_transport,brutal_purge,soft_purge,[]}
610
, {load_module,esockd_proxy_protocol,brutal_purge,soft_purge,[]}
711
, {load_module,esockd_acceptor,brutal_purge,soft_purge,[]}
@@ -46,7 +50,17 @@
4650
},
4751
{<<".*">>, []}
4852
],
49-
[{"5.9.4",
53+
[{"5.9.6",
54+
[ {load_module,esockd_proxy_protocol,brutal_purge,soft_purge,[]}
55+
]
56+
},
57+
{"5.9.5",
58+
[ {load_module,esockd_transport,brutal_purge,soft_purge,[]}
59+
, {load_module,esockd_proxy_protocol,brutal_purge,soft_purge,[]}
60+
, {load_module,esockd_acceptor,brutal_purge,soft_purge,[]}
61+
]
62+
},
63+
{"5.9.4",
5064
[ {load_module,esockd_transport,brutal_purge,soft_purge,[]}
5165
, {load_module,esockd_proxy_protocol,brutal_purge,soft_purge,[]}
5266
, {load_module,esockd_acceptor,brutal_purge,soft_purge,[]}

src/esockd_proxy_protocol.erl

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
-define(SPACE, 16#20).
4242

43-
-define(TIMEOUT, 5000).
43+
-define(MAX_TIMEOUT, 17000).
4444

4545
%% Proxy Protocol Additional Fields
4646
-define(PP2_TYPE_ALPN, 16#01).
@@ -62,6 +62,7 @@
6262
-spec(recv(module(), inet:socket() | #ssl_socket{}, timeout()) ->
6363
{ok, #proxy_socket{}} | {error, term()}).
6464
recv(Transport, Sock, Timeout) ->
65+
Deadline = deadline(Timeout),
6566
{ok, OriginOpts} = Transport:getopts(Sock, [mode, active, packet]),
6667
ok = Transport:setopts(Sock, [binary, {active, once}, {packet, line}]),
6768
receive
@@ -75,16 +76,7 @@ recv(Transport, Sock, Timeout) ->
7576
{ok, Sock};
7677
%% V2 TCP
7778
{_, _Sock, <<"\r\n">>} ->
78-
Transport:setopts(Sock, [{active, false}, {packet, raw}]),
79-
{ok, Header} = Transport:recv(Sock, 14, 1000),
80-
<<?SIG, 2:4, Cmd:4, AF:4, Trans:4, Len:16>> = Header,
81-
case Transport:recv(Sock, Len, 1000) of
82-
{ok, ProxyInfo} ->
83-
Transport:setopts(Sock, OriginOpts),
84-
parse_v2(Cmd, Trans, ProxyInfo, #proxy_socket{inet = inet_family(AF), socket = Sock});
85-
{error, Reason} ->
86-
{error, {recv_proxy_info_error, Reason}}
87-
end;
79+
recv_v2(Transport, Sock, OriginOpts, Deadline);
8880
{tcp_error, _Sock, Reason} ->
8981
{error, {recv_proxy_info_error, Reason}};
9082
{tcp_closed, _Sock} ->
@@ -98,6 +90,31 @@ recv(Transport, Sock, Timeout) ->
9890
{error, proxy_proto_timeout}
9991
end.
10092

93+
recv_v2(Transport, Sock, OriginOpts, Deadline) ->
94+
Transport:setopts(Sock, [{active, false}, {packet, raw}]),
95+
with_remaining_timeout(Deadline, fun(HeaderTimeout) ->
96+
case Transport:recv(Sock, 14, HeaderTimeout) of
97+
{ok, <<?SIG, 2:4, Cmd:4, AF:4, Trans:4, Len:16>>} ->
98+
with_remaining_timeout(Deadline, fun(ProxyInfoTimeout) ->
99+
case Transport:recv(Sock, Len, ProxyInfoTimeout) of
100+
{ok, ProxyInfo} ->
101+
Transport:setopts(Sock, OriginOpts),
102+
parse_v2(Cmd, Trans, ProxyInfo, #proxy_socket{inet = inet_family(AF), socket = Sock});
103+
{error, closed} ->
104+
{error, proxy_proto_close};
105+
{error, Reason} ->
106+
{error, {recv_proxy_info_error, Reason}}
107+
end
108+
end);
109+
{ok, UnknownHeader} ->
110+
{error, {invalid_proxy_info, UnknownHeader}};
111+
{error, closed} ->
112+
{error, proxy_proto_close};
113+
{error, Reason} ->
114+
{error, {recv_proxy_info_error, Reason}}
115+
end
116+
end).
117+
101118
parse_v1(ProxyInfo, ProxySock) ->
102119
[SrcAddrBin, DstAddrBin, SrcPortBin, DstPortBin]
103120
= binary:split(ProxyInfo, [<<" ">>, <<"\r\n">>], [global, trim]),
@@ -210,3 +227,22 @@ inet_family(?UNIX) -> unix.
210227
bool(1) -> true;
211228
bool(_) -> false.
212229

230+
maybe_limit_timeout(infinity) -> ?MAX_TIMEOUT;
231+
maybe_limit_timeout(Timeout) when is_integer(Timeout) andalso Timeout > ?MAX_TIMEOUT ->
232+
?MAX_TIMEOUT;
233+
maybe_limit_timeout(Timeout) when is_integer(Timeout) andalso Timeout > 0 ->
234+
Timeout.
235+
236+
deadline(Timeout) ->
237+
erlang:monotonic_time(millisecond) + maybe_limit_timeout(Timeout).
238+
239+
timeout_left(Deadline) ->
240+
Deadline - erlang:monotonic_time(millisecond).
241+
242+
with_remaining_timeout(Timer, Fun) ->
243+
case timeout_left(Timer) of
244+
Timeout when Timeout > 0 ->
245+
Fun(Timeout);
246+
_ ->
247+
{error, proxy_proto_timeout}
248+
end.

test/esockd_proxy_protocol_SUITE.erl

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,79 @@ t_ppv2_connect(_) ->
182182
12,127,50,210,1,210,21,16,142,250,32,1,181>>),
183183
ok = gen_tcp:send(Sock, <<"Hello">>),
184184
{ok, <<"Hello">>} = gen_tcp:recv(Sock, 0)
185-
end).
185+
end),
186+
with_pp_server(fun(Sock) ->
187+
ok = gen_tcp:send(Sock, <<13,10,13,10,0,13,10,81,85,73,84,10,33,17,0,
188+
12,127,50,210,1,210,21,16,142,250,32,1,181>>),
189+
ok = gen_tcp:send(Sock, <<"Hello">>),
190+
{ok, <<"Hello">>} = gen_tcp:recv(Sock, 0)
191+
end,
192+
[{proxy_protocol_timeout, infinity}]).
193+
194+
195+
t_ppv2_connect_first_marker_timeout(_) ->
196+
with_pp_server(
197+
fun(Sock) ->
198+
timer:sleep(200),
199+
{error, closed} = gen_tcp:recv(Sock, 0)
200+
end,
201+
[{proxy_protocol_timeout, 100}]),
202+
with_pp_server(
203+
fun(Sock) ->
204+
timer:sleep(400),
205+
ok = gen_tcp:send(Sock, <<13,10,13,10,0,13,10,81,85,73,84,10,33,17,0,
206+
12,127,50,210,1,210,21,16,142,250,32,1,181>>),
207+
ok = gen_tcp:send(Sock, <<"Hello">>),
208+
{ok, <<"Hello">>} = gen_tcp:recv(Sock, 0)
209+
end,
210+
[{proxy_protocol_timeout, 500}]).
211+
212+
t_ppv2_connect_header_timeout(_) ->
213+
with_pp_server(
214+
fun(Sock) ->
215+
timer:sleep(200),
216+
ok = gen_tcp:send(Sock, <<13,10>>),
217+
timer:sleep(200),
218+
ok = gen_tcp:send(Sock, <<13,10,0,13,10,81,85,73,84,10,33,17,0,
219+
12,127,50,210,1,210,21,16,142,250,32,1,181>>),
220+
ok = gen_tcp:send(Sock, <<"Hello">>),
221+
{ok, <<"Hello">>} = gen_tcp:recv(Sock, 0)
222+
end,
223+
[{proxy_protocol_timeout, 500}]),
224+
with_pp_server(
225+
fun(Sock) ->
226+
timer:sleep(200),
227+
ok = gen_tcp:send(Sock, <<13,10>>),
228+
timer:sleep(400),
229+
{error, closed} = gen_tcp:recv(Sock, 0)
230+
end,
231+
[{proxy_protocol_timeout, 500}]).
232+
233+
t_ppv2_connect_proxy_info_timeout(_) ->
234+
with_pp_server(
235+
fun(Sock) ->
236+
timer:sleep(100),
237+
ok = gen_tcp:send(Sock, <<13,10>>),
238+
timer:sleep(100),
239+
ok = gen_tcp:send(Sock, <<13,10,0,13,10,81,85,73,84,10,33,17,0,
240+
12,127,50,210,1,210,21,16,142>>),
241+
timer:sleep(200),
242+
ok = gen_tcp:send(Sock, <<250,32,1,181>>),
243+
ok = gen_tcp:send(Sock, <<"Hello">>),
244+
{ok, <<"Hello">>} = gen_tcp:recv(Sock, 0)
245+
end,
246+
[{proxy_protocol_timeout, 500}]),
247+
with_pp_server(
248+
fun(Sock) ->
249+
timer:sleep(100),
250+
ok = gen_tcp:send(Sock, <<13,10>>),
251+
timer:sleep(100),
252+
ok = gen_tcp:send(Sock, <<13,10,0,13,10,81,85,73,84,10,33,17,0,
253+
12,127,50,210,1,210,21,16,142>>),
254+
timer:sleep(400),
255+
{error, closed} = gen_tcp:recv(Sock, 0)
256+
end,
257+
[{proxy_protocol_timeout, 500}]).
186258

187259
t_ppv1_unknown(_) ->
188260
with_pp_server(fun(Sock) ->
@@ -198,11 +270,15 @@ t_ppv1_garbage_data(_) ->
198270
end).
199271

200272
with_pp_server(TestFun) ->
201-
{ok, _} = esockd:open(echo, 5000, [{tcp_options, [binary]},
202-
proxy_protocol,
203-
{proxy_protocol_timeout, 3000}
204-
],
205-
{echo_server, start_link, []}),
273+
PPOpts = [{proxy_protocol_timeout, 3000}],
274+
with_pp_server(TestFun, PPOpts).
275+
276+
with_pp_server(TestFun, PPOpts) ->
277+
Opts = PPOpts ++
278+
[
279+
{tcp_options, [binary]},
280+
proxy_protocol
281+
],
282+
{ok, _} = esockd:open(echo, 5000, Opts, {echo_server, start_link, []}),
206283
{ok, Sock} = gen_tcp:connect({127,0,0,1}, 5000, [binary, {active, false}]),
207284
try TestFun(Sock) after ok = esockd:close(echo, 5000) end.
208-

0 commit comments

Comments
 (0)