From b69ff92008678bc0e0e45a4162359015d3c44972 Mon Sep 17 00:00:00 2001 From: silviu Date: Tue, 27 Feb 2024 20:19:57 +0000 Subject: [PATCH 1/2] Parse proxy protocol http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt --- src/gen_smtp_server_session.erl | 73 ++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/src/gen_smtp_server_session.erl b/src/gen_smtp_server_session.erl index 1090876..f04a38f 100644 --- a/src/gen_smtp_server_session.erl +++ b/src/gen_smtp_server_session.erl @@ -198,11 +198,22 @@ ranch_init({Ref, Transport, {Callback, Opts}}) -> -spec init(Args :: list()) -> {'ok', #state{}, ?TIMEOUT} | {'stop', any()} | 'ignore'. init([Ref, Transport, Socket, Module, Options]) -> Protocol = proplists:get_value(protocol, Options, smtp), - PeerName = - case Transport:peername(Socket) of - {ok, {IPaddr, _Port}} -> IPaddr; - {error, _} -> error - end, + + PeerName = case proplists:get_value(proxy_protocol, Options, false) of + false -> + case Transport:peername(Socket) of + {ok, {IPaddr, _Port}} -> IPaddr; + {error, _} -> error + end; + _ -> + case read_proxy_protocol(Transport, Socket, <<>>) of + {ok, IpAddr} -> + IpAddr; + _ -> + error + end + end, + case PeerName =/= error andalso Module:init( @@ -1405,6 +1416,57 @@ report_recipient(multiple, [{ResponseType, Value} | Rest], State) -> report_recipient(ResponseType, Value, State), report_recipient(multiple, Rest, State). +% parse proxy protocol +% http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + +read_proxy_protocol(Transport, Socket, Data1) -> + case Transport:recv(Socket, 0, 5000) of + {ok, Data0} -> + Data = <>, + case byte_size(Data) >=6 of + true -> + case Data of + <<"PROXY ", _/binary>> -> + case binary:split(Data, <<"\r\n">>) of + [ProxyBuffer, Rest] -> + case parse_proxy_protocol_buffer(ProxyBuffer) of + {ok, IpBin} -> + <<>> = Rest, + inet:parse_address(binary_to_list(IpBin)); + _ -> + ?LOG_ERROR("failed to parse proxy protocol.", []), + {error, invalid_protocol} + end; + _ -> + % a 108-byte buffer is always enough to store the entire proxy line + case byte_size(Data) >= 108 of + true -> + ?LOG_ERROR("failed to parse proxy protocol -> packet size exceedded", []), + {error, packet_size_exceeded}; + _ -> + read_proxy_protocol(Transport, Socket, Data) + end + end; + _ -> + % we don't have a proxy + ?LOG_WARNING("no expected proxy protocol found ...", []), + {error, no_proxy_protocol} + end; + _ -> + read_proxy_protocol(Transport, Socket, Data) + end; + Error -> + Error + end. + +parse_proxy_protocol_buffer(Buffer) -> + case binary:split(Buffer, <<" ">>, [global]) of + [<<"PROXY">>, InetFamily, SrcIp, _DstIp, _SrcPort, _DstPort] when InetFamily == <<"TCP4">> orelse InetFamily == <<"TCP6">> -> + {ok, SrcIp}; + _ -> + false + end. + -ifdef(TEST). parse_encoded_address_test_() -> [ @@ -3709,3 +3771,4 @@ smtp_session_nomaxsize_test_() -> ]}. -endif. + From 25e6e191c4466a85ccd47bbc9b9f1ca6867ac183 Mon Sep 17 00:00:00 2001 From: silviu Date: Thu, 29 Feb 2024 16:59:25 +0000 Subject: [PATCH 2/2] Improve proxy protocol parser --- src/gen_smtp_server_session.erl | 55 +++++++++------------------------ 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/src/gen_smtp_server_session.erl b/src/gen_smtp_server_session.erl index f04a38f..a75b109 100644 --- a/src/gen_smtp_server_session.erl +++ b/src/gen_smtp_server_session.erl @@ -206,7 +206,7 @@ init([Ref, Transport, Socket, Module, Options]) -> {error, _} -> error end; _ -> - case read_proxy_protocol(Transport, Socket, <<>>) of + case read_proxy_protocol(Transport, Socket) of {ok, IpAddr} -> IpAddr; _ -> @@ -1419,54 +1419,29 @@ report_recipient(multiple, [{ResponseType, Value} | Rest], State) -> % parse proxy protocol % http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt -read_proxy_protocol(Transport, Socket, Data1) -> +read_proxy_protocol(Transport, Socket) -> + ok = Transport:setopts(Socket, [{packet, line}, binary]), case Transport:recv(Socket, 0, 5000) of - {ok, Data0} -> - Data = <>, - case byte_size(Data) >=6 of - true -> - case Data of - <<"PROXY ", _/binary>> -> - case binary:split(Data, <<"\r\n">>) of - [ProxyBuffer, Rest] -> - case parse_proxy_protocol_buffer(ProxyBuffer) of - {ok, IpBin} -> - <<>> = Rest, - inet:parse_address(binary_to_list(IpBin)); - _ -> - ?LOG_ERROR("failed to parse proxy protocol.", []), - {error, invalid_protocol} - end; - _ -> - % a 108-byte buffer is always enough to store the entire proxy line - case byte_size(Data) >= 108 of - true -> - ?LOG_ERROR("failed to parse proxy protocol -> packet size exceedded", []), - {error, packet_size_exceeded}; - _ -> - read_proxy_protocol(Transport, Socket, Data) - end - end; + {ok, Data} -> + case Data of + <<"PROXY ", _/binary>> -> + case binary:split(Data, <<" ">>, [global]) of + [<<"PROXY">>, InetFamily, SrcIp, _DstIp, _SrcPort, _DstPort] when InetFamily == <<"TCP4">> orelse InetFamily == <<"TCP6">> -> + inet:parse_address(binary_to_list(SrcIp)); _ -> - % we don't have a proxy - ?LOG_WARNING("no expected proxy protocol found ...", []), - {error, no_proxy_protocol} + ?LOG_ERROR("unexpected proxy protocol found: ~p.", [Data]), + {error, invalid_proxy_protocol} end; _ -> - read_proxy_protocol(Transport, Socket, Data) + % misconfiguration. + ?LOG_ERROR("unexpected proxy protocol found: ~p.", [Data]), + {error, invalid_proxy_protocol} end; Error -> + ?LOG_ERROR("read_proxy_protocol receiving proxy data failed with ~p.", [Error]), Error end. -parse_proxy_protocol_buffer(Buffer) -> - case binary:split(Buffer, <<" ">>, [global]) of - [<<"PROXY">>, InetFamily, SrcIp, _DstIp, _SrcPort, _DstPort] when InetFamily == <<"TCP4">> orelse InetFamily == <<"TCP6">> -> - {ok, SrcIp}; - _ -> - false - end. - -ifdef(TEST). parse_encoded_address_test_() -> [