%%%---------------------------------------------------------------------- %%% File : ejabberd_http.erl %%% Author : Paweł Chmielowski %%% Purpose : %%% Created : 27 Nov 2018 by Paweł Chmielowski %%% %%% %%% ejabberd, Copyright (C) 2002-2020 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License along %%% with this program; if not, write to the Free Software Foundation, Inc., %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- -module(proxy_protocol). -author("pawel@process-one.net"). %% API -export([decode/3]). decode(SockMod, Socket, Timeout) -> V = SockMod:recv(Socket, 6, Timeout), case V of {ok, <<"PROXY ">>} -> decode_v1(SockMod, Socket, Timeout); {ok, <<16#0d, 16#0a, 16#0d, 16#0a, 16#00, 16#0d>>} -> decode_v2(SockMod, Socket, Timeout); _ -> {error, eproto} end. decode_v1(SockMod, Socket, Timeout) -> case read_until_rn(SockMod, Socket, <<>>, false, Timeout) of {error, _} = Err -> Err; Val -> case binary:split(Val, <<" ">>, [global]) of [<<"TCP4">>, SAddr, DAddr, SPort, DPort] -> try {inet_parse:ipv4strict_address(binary_to_list(SAddr)), inet_parse:ipv4strict_address(binary_to_list(DAddr)), binary_to_integer(SPort), binary_to_integer(DPort)} of {{ok, DA}, {ok, SA}, DP, SP} -> {{SA, SP}, {DA, DP}}; _ -> {error, eproto} catch error:badarg -> {error, eproto} end; [<<"TCP6">>, SAddr, DAddr, SPort, DPort] -> try {inet_parse:ipv6strict_address(binary_to_list(SAddr)), inet_parse:ipv6strict_address(binary_to_list(DAddr)), binary_to_integer(SPort), binary_to_integer(DPort)} of {{ok, DA}, {ok, SA}, DP, SP} -> {{SA, SP}, {DA, DP}}; _ -> {error, eproto} catch error:badarg -> {error, eproto} end; [<<"UNKNOWN">> | _] -> {undefined, undefined} end end. decode_v2(SockMod, Socket, Timeout) -> case SockMod:recv(Socket, 10, Timeout) of {error, _} = Err -> Err; {ok, <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a, 2:4, Command:4, Transport:8, AddrLen:16/big-unsigned-integer>>} -> case SockMod:recv(Socket, AddrLen, Timeout) of {error, _} = Err -> Err; {ok, Data} -> case Command of 0 -> case {inet:sockname(Socket), inet:peername(Socket)} of {{ok, SA}, {ok, DA}} -> {SA, DA}; {{error, _} = E, _} -> E; {_, {error, _} = E} -> E end; 1 -> case Transport of % UNSPEC or UNIX V when V == 0; V == 16#31; V == 16#32 -> {{unknown, unknown}, {unknown, unknown}}; % IPV4 over TCP or UDP V when V == 16#11; V == 16#12 -> case Data of <> -> {{{S1, S2, S3, S4}, SP}, {{D1, D2, D3, D4}, DP}}; _ -> {error, eproto} end; % IPV6 over TCP or UDP V when V == 16#21; V == 16#22 -> case Data of <> -> {{{S1, S2, S3, S4, S5, S6, S7, S8}, SP}, {{D1, D2, D3, D4, D5, D6, D7, D8}, DP}}; _ -> {error, eproto} end end; _ -> {error, eproto} end end; <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a, _/binary>> -> {error, eproto}; _ -> {error, eproto} end. read_until_rn(_SockMod, _Socket, Data, _, _) when size(Data) > 107 -> {error, eproto}; read_until_rn(SockMod, Socket, Data, true, Timeout) -> case SockMod:recv(Socket, 1, Timeout) of {ok, <<"\n">>} -> Data; {ok, <<"\r">>} -> read_until_rn(SockMod, Socket, <>, true, Timeout); {ok, Other} -> read_until_rn(SockMod, Socket, <>, false, Timeout); {error, _} = Err -> Err end; read_until_rn(SockMod, Socket, Data, false, Timeout) -> case SockMod:recv(Socket, 2, Timeout) of {ok, <<"\r\n">>} -> Data; {ok, <>} -> read_until_rn(SockMod, Socket, <>, true, Timeout); {ok, Other} -> read_until_rn(SockMod, Socket, <>, false, Timeout); {error, _} = Err -> Err end.