2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "proxy-protocol.hh"
25 // TODO: maybe use structs instead of explicitly working byte by byte, like https://github.com/dovecot/core/blob/master/src/lib-master/master-service-haproxy.c
27 #define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
28 #define PROXYMAGICLEN sizeof(PROXYMAGIC)-1
30 static const string
proxymagic(PROXYMAGIC
, PROXYMAGICLEN
);
32 static void makeSimpleHeader(uint8_t command
, uint8_t protocol
, uint16_t contentLen
, size_t additionalSize
, std::string
& out
)
34 const uint8_t versioncommand
= (0x20 | command
);
35 const size_t totalSize
= proxymagic
.size() + sizeof(versioncommand
) + sizeof(protocol
) + sizeof(contentLen
) + additionalSize
;
36 if (out
.capacity() < totalSize
) {
37 out
.reserve(totalSize
);
40 out
.append(proxymagic
);
42 out
.append(reinterpret_cast<const char*>(&versioncommand
), sizeof(versioncommand
));
43 out
.append(reinterpret_cast<const char*>(&protocol
), sizeof(protocol
));
45 out
.append(reinterpret_cast<const char*>(&contentLen
), sizeof(contentLen
));
48 std::string
makeLocalProxyHeader()
51 makeSimpleHeader(0x00, 0, 0, 0, out
);
55 std::string
makeProxyHeader(bool tcp
, const ComboAddress
& source
, const ComboAddress
& destination
, const std::vector
<ProxyProtocolValue
>& values
)
57 if (source
.sin4
.sin_family
!= destination
.sin4
.sin_family
) {
58 throw std::runtime_error("The PROXY destination and source addresses must be of the same family");
61 const uint8_t command
= 0x01;
62 const uint8_t protocol
= (source
.isIPv4() ? 0x10 : 0x20) | (tcp
? 0x01 : 0x02);
63 const size_t addrSize
= source
.isIPv4() ? sizeof(source
.sin4
.sin_addr
.s_addr
) : sizeof(source
.sin6
.sin6_addr
.s6_addr
);
64 const uint16_t sourcePort
= source
.sin4
.sin_port
;
65 const uint16_t destinationPort
= destination
.sin4
.sin_port
;
67 size_t valuesSize
= 0;
68 for (const auto& value
: values
) {
69 if (value
.content
.size() > std::numeric_limits
<uint16_t>::max()) {
70 throw std::runtime_error("The size of proxy protocol values is limited to " + std::to_string(std::numeric_limits
<uint16_t>::max()) + ", trying to add a value of size " + std::to_string(value
.content
.size()));
72 valuesSize
+= sizeof(uint8_t) + sizeof(uint8_t) * 2 + value
.content
.size();
73 if (valuesSize
> std::numeric_limits
<uint16_t>::max()) {
74 throw std::runtime_error("The total size of proxy protocol values is limited to " + std::to_string(std::numeric_limits
<uint16_t>::max()));
78 /* size of the data that will come _after_ the minimal proxy protocol header */
79 size_t additionalDataSize
= (addrSize
* 2) + sizeof(sourcePort
) + sizeof(destinationPort
) + valuesSize
;
80 if (additionalDataSize
> std::numeric_limits
<uint16_t>::max()) {
81 throw std::runtime_error("The size of a proxy protocol header is limited to " + std::to_string(std::numeric_limits
<uint16_t>::max()) + ", trying to send one of size " + std::to_string(additionalDataSize
));
84 const uint16_t contentlen
= htons(static_cast<uint16_t>(additionalDataSize
));
86 makeSimpleHeader(command
, protocol
, contentlen
, additionalDataSize
, ret
);
88 // We already established source and destination sin_family equivalence
89 if (source
.isIPv4()) {
90 assert(addrSize
== sizeof(source
.sin4
.sin_addr
.s_addr
));
91 ret
.append(reinterpret_cast<const char*>(&source
.sin4
.sin_addr
.s_addr
), addrSize
);
92 assert(addrSize
== sizeof(destination
.sin4
.sin_addr
.s_addr
));
93 ret
.append(reinterpret_cast<const char*>(&destination
.sin4
.sin_addr
.s_addr
), addrSize
);
96 assert(addrSize
== sizeof(source
.sin6
.sin6_addr
.s6_addr
));
97 ret
.append(reinterpret_cast<const char*>(&source
.sin6
.sin6_addr
.s6_addr
), addrSize
);
98 assert(addrSize
== sizeof(destination
.sin6
.sin6_addr
.s6_addr
));
99 ret
.append(reinterpret_cast<const char*>(&destination
.sin6
.sin6_addr
.s6_addr
), addrSize
);
102 ret
.append(reinterpret_cast<const char*>(&sourcePort
), sizeof(sourcePort
));
103 ret
.append(reinterpret_cast<const char*>(&destinationPort
), sizeof(destinationPort
));
105 for (const auto& value
: values
) {
106 uint16_t contentSize
= htons(static_cast<uint16_t>(value
.content
.size()));
107 ret
.append(reinterpret_cast<const char*>(&value
.type
), sizeof(value
.type
));
108 ret
.append(reinterpret_cast<const char*>(&contentSize
), sizeof(contentSize
));
109 ret
.append(reinterpret_cast<const char*>(value
.content
.data()), value
.content
.size());
115 /* returns: number of bytes consumed (positive) after successful parse
116 or number of bytes missing (negative)
117 or unfixable parse error (0)*/
118 template<typename Container
> ssize_t
isProxyHeaderComplete(const Container
& header
, bool* proxy
, bool* tcp
, size_t* addrSizeOut
, uint8_t* protocolOut
)
120 static const size_t addr4Size
= sizeof(ComboAddress::sin4
.sin_addr
.s_addr
);
121 static const size_t addr6Size
= sizeof(ComboAddress::sin6
.sin6_addr
.s6_addr
);
123 uint8_t versioncommand
;
126 if (header
.size() < s_proxyProtocolMinimumHeaderSize
) {
127 // this is too short to be a complete proxy header
128 return -(s_proxyProtocolMinimumHeaderSize
- header
.size());
131 if (std::memcmp(&header
.at(0), &proxymagic
.at(0), proxymagic
.size()) != 0) {
132 // wrong magic, can not be a proxy header
136 versioncommand
= header
.at(12);
138 if (!(versioncommand
& 0x20)) {
142 /* remove the version to get the command */
143 uint8_t command
= versioncommand
& ~0x20;
145 if (command
== 0x01) {
146 protocol
= header
.at(13);
147 if ((protocol
& 0xf) == 1) {
151 } else if ((protocol
& 0xf) == 2) {
159 protocol
= protocol
>> 4;
165 addrSize
= addr4Size
; // IPv4
166 } else if (protocol
== 2) {
170 addrSize
= addr6Size
; // IPv6
177 *addrSizeOut
= addrSize
;
184 else if (command
== 0x00) {
190 /* unsupported command */
194 uint16_t contentlen
= (static_cast<uint8_t>(header
.at(14)) << 8) + static_cast<uint8_t>(header
.at(15));
195 uint16_t expectedlen
= 0;
196 if (command
!= 0x00) {
197 expectedlen
= (addrSize
* 2) + sizeof(ComboAddress::sin4
.sin_port
) + sizeof(ComboAddress::sin4
.sin_port
);
200 if (contentlen
< expectedlen
) {
204 if (header
.size() < s_proxyProtocolMinimumHeaderSize
+ contentlen
) {
205 return -((s_proxyProtocolMinimumHeaderSize
+ contentlen
) - header
.size());
208 return s_proxyProtocolMinimumHeaderSize
+ contentlen
;
211 /* returns: number of bytes consumed (positive) after successful parse
212 or number of bytes missing (negative)
213 or unfixable parse error (0)*/
214 template<typename Container
> ssize_t
parseProxyHeader(const Container
& header
, bool& proxy
, ComboAddress
& source
, ComboAddress
& destination
, bool& tcp
, std::vector
<ProxyProtocolValue
>& values
)
217 uint8_t protocol
= 0;
218 ssize_t got
= isProxyHeaderComplete(header
, &proxy
, &tcp
, &addrSize
, &protocol
);
223 size_t pos
= s_proxyProtocolMinimumHeaderSize
;
226 source
= makeComboAddressFromRaw(protocol
, reinterpret_cast<const char*>(&header
.at(pos
)), addrSize
);
227 pos
= pos
+ addrSize
;
228 destination
= makeComboAddressFromRaw(protocol
, reinterpret_cast<const char*>(&header
.at(pos
)), addrSize
);
229 pos
= pos
+ addrSize
;
230 source
.setPort((static_cast<uint8_t>(header
.at(pos
)) << 8) + static_cast<uint8_t>(header
.at(pos
+1)));
231 pos
= pos
+ sizeof(uint16_t);
232 destination
.setPort((static_cast<uint8_t>(header
.at(pos
)) << 8) + static_cast<uint8_t>(header
.at(pos
+1)));
233 pos
= pos
+ sizeof(uint16_t);
236 size_t remaining
= got
- pos
;
237 while (remaining
>= (sizeof(uint8_t) + sizeof(uint16_t))) {
238 /* we still have TLV values to parse */
239 uint8_t type
= static_cast<uint8_t>(header
.at(pos
));
240 pos
+= sizeof(uint8_t);
241 uint16_t len
= (static_cast<uint8_t>(header
.at(pos
)) << 8) + static_cast<uint8_t>(header
.at(pos
+ 1));
242 pos
+= sizeof(uint16_t);
245 if (len
> (got
- pos
)) {
249 values
.push_back({std::string(reinterpret_cast<const char*>(&header
.at(pos
)), len
), type
});
253 values
.push_back({"", type
});
256 remaining
= got
- pos
;
262 #include "noinitvector.hh"
263 template ssize_t isProxyHeaderComplete
<std::string
>(const std::string
& header
, bool* proxy
, bool* tcp
, size_t* addrSizeOut
, uint8_t* protocolOut
);
264 template ssize_t isProxyHeaderComplete
<PacketBuffer
>(const PacketBuffer
& header
, bool* proxy
, bool* tcp
, size_t* addrSizeOut
, uint8_t* protocolOut
);
265 template ssize_t parseProxyHeader
<std::string
>(const std::string
& header
, bool& proxy
, ComboAddress
& source
, ComboAddress
& destination
, bool& tcp
, std::vector
<ProxyProtocolValue
>& values
);
266 template ssize_t parseProxyHeader
<PacketBuffer
>(const PacketBuffer
& header
, bool& proxy
, ComboAddress
& source
, ComboAddress
& destination
, bool& tcp
, std::vector
<ProxyProtocolValue
>& values
);