2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "parser/BinaryTokenizer.h"
11 #include "parser/Tokenizer.h"
12 #include "proxyp/Elements.h"
13 #include "proxyp/Header.h"
14 #include "proxyp/Parser.h"
15 #include "sbuf/Stream.h"
20 #include <sys/socket.h>
23 #include <netinet/in.h>
26 #include <netinet/ip.h>
29 namespace ProxyProtocol
{
31 /// magic octet prefix for PROXY protocol version 1
32 static const SBuf
Magic("PROXY", 5);
33 /// extracts PROXY protocol v1 header from the given buffer
34 static Parsed
Parse(const SBuf
&buf
);
36 static void ExtractIp(Parser::Tokenizer
&tok
, Ip::Address
&addr
);
37 static void ExtractPort(Parser::Tokenizer
&tok
, Ip::Address
&addr
, const bool trailingSpace
);
38 static void ParseAddresses(Parser::Tokenizer
&tok
, Header::Pointer
&header
);
42 /// magic octet prefix for PROXY protocol version 2
43 static const SBuf
Magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
44 /// extracts PROXY protocol v2 header from the given buffer
45 static Parsed
Parse(const SBuf
&buf
);
47 static void ParseAddresses(const uint8_t family
, Parser::BinaryTokenizer
&tok
, Header::Pointer
&header
);
48 static void ParseTLVs(Parser::BinaryTokenizer
&tok
, Header::Pointer
&header
);
53 ProxyProtocol::One::ExtractIp(Parser::Tokenizer
&tok
, Ip::Address
&addr
)
55 static const auto ipChars
= CharacterSet("IP Address",".:") + CharacterSet::HEXDIG
;
59 if (!tok
.prefix(ip
, ipChars
))
60 throw TexcHere("PROXY/1.0 error: malformed IP address");
63 throw TexcHere("PROXY/1.0 error: garbage after IP address");
65 if (!addr
.GetHostByName(ip
.c_str()))
66 throw TexcHere("PROXY/1.0 error: invalid IP address");
71 ProxyProtocol::One::ExtractPort(Parser::Tokenizer
&tok
, Ip::Address
&addr
, const bool trailingSpace
)
75 if (!tok
.int64(port
, 10, false))
76 throw TexcHere("PROXY/1.0 error: malformed port");
78 if (trailingSpace
&& !tok
.skip(' '))
79 throw TexcHere("PROXY/1.0 error: garbage after port");
81 if (port
> std::numeric_limits
<uint16_t>::max())
82 throw TexcHere("PROXY/1.0 error: invalid port");
84 addr
.port(static_cast<uint16_t>(port
));
88 ProxyProtocol::One::ParseAddresses(Parser::Tokenizer
&tok
, Header::Pointer
&header
)
90 static const CharacterSet
addressFamilies("Address family", "46");
91 SBuf parsedAddressFamily
;
93 if (!tok
.prefix(parsedAddressFamily
, addressFamilies
, 1))
94 throw TexcHere("PROXY/1.0 error: missing or invalid IP address family");
97 throw TexcHere("PROXY/1.0 error: missing SP after the IP address family");
99 // parse: src-IP SP dst-IP SP src-port SP dst-port
100 ExtractIp(tok
, header
->sourceAddress
);
101 ExtractIp(tok
, header
->destinationAddress
);
103 if (header
->addressFamily() != parsedAddressFamily
)
104 throw TexcHere("PROXY/1.0 error: declared and/or actual IP address families mismatch");
106 ExtractPort(tok
, header
->sourceAddress
, true);
107 ExtractPort(tok
, header
->destinationAddress
, false);
110 /// parses PROXY protocol v1 header from the buffer
111 ProxyProtocol::Parsed
112 ProxyProtocol::One::Parse(const SBuf
&buf
)
114 Parser::Tokenizer
tok(buf
);
116 static const SBuf::size_type maxHeaderLength
= 107; // including CRLF
117 static const auto maxInteriorLength
= maxHeaderLength
- Magic
.length() - 2;
118 static const auto interiorChars
= CharacterSet::CR
.complement().rename("non-CR");
121 if (!(tok
.prefix(interior
, interiorChars
, maxInteriorLength
) &&
125 throw Parser::BinaryTokenizer::InsufficientInput();
126 // "empty interior", "too-long interior", or "missing LF after CR"
127 throw TexcHere("PROXY/1.0 error: malformed header");
129 // extracted all PROXY protocol bytes
131 static const SBuf
v1("1.0");
132 Header::Pointer header
= new Header(v1
, Two::cmdProxy
);
134 Parser::Tokenizer
interiorTok(interior
);
136 if (!interiorTok
.skip(' '))
137 throw TexcHere("PROXY/1.0 error: missing SP after the magic sequence");
139 static const SBuf
protoUnknown("UNKNOWN");
140 static const SBuf
protoTcp("TCP");
142 if (interiorTok
.skip(protoTcp
))
143 ParseAddresses(interiorTok
, header
);
144 else if (interiorTok
.skip(protoUnknown
))
145 header
->ignoreAddresses();
147 throw TexcHere("PROXY/1.0 error: invalid INET protocol or family");
149 return Parsed(header
, tok
.parsedSize());
153 ProxyProtocol::Two::ParseAddresses(const uint8_t family
, Parser::BinaryTokenizer
&tok
, Header::Pointer
&header
)
158 header
->sourceAddress
= tok
.inet4("src_addr IPv4");
159 header
->destinationAddress
= tok
.inet4("dst_addr IPv4");
160 header
->sourceAddress
.port(tok
.uint16("src_port"));
161 header
->destinationAddress
.port(tok
.uint16("dst_port"));
166 header
->sourceAddress
= tok
.inet6("src_addr IPv6");
167 header
->destinationAddress
= tok
.inet6("dst_addr IPv6");
168 header
->sourceAddress
.port(tok
.uint16("src_port"));
169 header
->destinationAddress
.port(tok
.uint16("dst_port"));
173 case afUnix
: { // TODO: add support
174 // the address block length is 216 bytes
175 tok
.skip(216, "unix_addr");
180 // unreachable code: we have checked family validity already
188 ProxyProtocol::Two::ParseTLVs(Parser::BinaryTokenizer
&tok
, Header::Pointer
&header
) {
189 while (!tok
.atEnd()) {
190 const auto type
= tok
.uint8("pp2_tlv::type");
191 header
->tlvs
.emplace_back(type
, tok
.pstring16("pp2_tlv::value"));
195 ProxyProtocol::Parsed
196 ProxyProtocol::Two::Parse(const SBuf
&buf
)
198 Parser::BinaryTokenizer
tokHeader(buf
, true);
200 const auto versionAndCommand
= tokHeader
.uint8("version and command");
202 const auto version
= (versionAndCommand
& 0xF0) >> 4;
203 if (version
!= 2) // version == 2 is mandatory
204 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid version ", version
));
206 const auto command
= (versionAndCommand
& 0x0F);
207 if (command
> cmdProxy
)
208 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid command ", command
));
210 const auto familyAndProto
= tokHeader
.uint8("family and proto");
212 const auto family
= (familyAndProto
& 0xF0) >> 4;
214 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid address family ", family
));
216 const auto proto
= (familyAndProto
& 0x0F);
218 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid transport protocol ", proto
));
220 const auto rawHeader
= tokHeader
.pstring16("header");
222 static const SBuf
v2("2.0");
223 Header::Pointer header
= new Header(v2
, Two::Command(command
));
225 if (proto
== tpUnspecified
|| family
== afUnspecified
) {
226 header
->ignoreAddresses();
227 // discard address block and TLVs because we cannot tell
228 // how to parse such addresses and where the TLVs start.
230 Parser::BinaryTokenizer
leftoverTok(rawHeader
);
231 ParseAddresses(family
, leftoverTok
, header
);
232 // TODO: parse TLVs for LOCAL connections
233 if (header
->hasForwardedAddresses())
234 ParseTLVs(leftoverTok
, header
);
237 return Parsed(header
, tokHeader
.parsed());
240 ProxyProtocol::Parsed
241 ProxyProtocol::Parse(const SBuf
&buf
)
243 Parser::Tokenizer
magicTok(buf
);
246 magicTok
.skip(Two::Magic
) ? &Two::Parse
:
247 magicTok
.skip(One::Magic
) ? &One::Parse
:
251 const auto parsed
= (parser
)(magicTok
.remaining());
252 return Parsed(parsed
.header
, magicTok
.parsedSize() + parsed
.size
);
255 // detect and terminate other protocols
256 if (buf
.length() >= Two::Magic
.length()) {
257 // PROXY/1.0 magic is shorter, so we know that
258 // the input does not start with any PROXY magic
259 throw TexcHere("PROXY protocol error: invalid magic");
262 // TODO: detect short non-magic prefixes earlier to avoid
263 // waiting for more data which may never come
265 // not enough bytes to parse magic yet
266 throw Parser::BinaryTokenizer::InsufficientInput();
269 ProxyProtocol::Parsed::Parsed(const Header::Pointer
&parsedHeader
, const size_t parsedSize
):
270 header(parsedHeader
),
273 assert(bool(parsedHeader
));