]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/proxy-protocol.cc
rec: mention rust compiler in compiling docs
[thirdparty/pdns.git] / pdns / proxy-protocol.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22
23 #include "proxy-protocol.hh"
24
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
26
27 #define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
28 #define PROXYMAGICLEN sizeof(PROXYMAGIC)-1
29
30 static const string proxymagic(PROXYMAGIC, PROXYMAGICLEN);
31
32 static void makeSimpleHeader(uint8_t command, uint8_t protocol, uint16_t contentLen, size_t additionalSize, std::string& out)
33 {
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);
38 }
39
40 out.append(proxymagic);
41
42 out.append(reinterpret_cast<const char*>(&versioncommand), sizeof(versioncommand));
43 out.append(reinterpret_cast<const char*>(&protocol), sizeof(protocol));
44
45 out.append(reinterpret_cast<const char*>(&contentLen), sizeof(contentLen));
46 }
47
48 std::string makeLocalProxyHeader()
49 {
50 std::string out;
51 makeSimpleHeader(0x00, 0, 0, 0, out);
52 return out;
53 }
54
55 std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values)
56 {
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");
59 }
60
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;
66
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()));
71 }
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()));
75 }
76 }
77
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));
82 }
83
84 const uint16_t contentlen = htons(static_cast<uint16_t>(additionalDataSize));
85 std::string ret;
86 makeSimpleHeader(command, protocol, contentlen, additionalDataSize, ret);
87
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);
94 }
95 else {
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);
100 }
101
102 ret.append(reinterpret_cast<const char*>(&sourcePort), sizeof(sourcePort));
103 ret.append(reinterpret_cast<const char*>(&destinationPort), sizeof(destinationPort));
104
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());
110 }
111
112 return ret;
113 }
114
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)
119 {
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);
122 size_t addrSize = 0;
123 uint8_t versioncommand;
124 uint8_t protocol;
125
126 if (header.size() < s_proxyProtocolMinimumHeaderSize) {
127 // this is too short to be a complete proxy header
128 return -(s_proxyProtocolMinimumHeaderSize - header.size());
129 }
130
131 if (std::memcmp(&header.at(0), &proxymagic.at(0), proxymagic.size()) != 0) {
132 // wrong magic, can not be a proxy header
133 return 0;
134 }
135
136 versioncommand = header.at(12);
137 /* check version */
138 if (!(versioncommand & 0x20)) {
139 return 0;
140 }
141
142 /* remove the version to get the command */
143 uint8_t command = versioncommand & ~0x20;
144
145 if (command == 0x01) {
146 protocol = header.at(13);
147 if ((protocol & 0xf) == 1) {
148 if (tcp) {
149 *tcp = true;
150 }
151 } else if ((protocol & 0xf) == 2) {
152 if (tcp) {
153 *tcp = false;
154 }
155 } else {
156 return 0;
157 }
158
159 protocol = protocol >> 4;
160
161 if (protocol == 1) {
162 if (protocolOut) {
163 *protocolOut = 4;
164 }
165 addrSize = addr4Size; // IPv4
166 } else if (protocol == 2) {
167 if (protocolOut) {
168 *protocolOut = 6;
169 }
170 addrSize = addr6Size; // IPv6
171 } else {
172 // invalid protocol
173 return 0;
174 }
175
176 if (addrSizeOut) {
177 *addrSizeOut = addrSize;
178 }
179
180 if (proxy) {
181 *proxy = true;
182 }
183 }
184 else if (command == 0x00) {
185 if (proxy) {
186 *proxy = false;
187 }
188 }
189 else {
190 /* unsupported command */
191 return 0;
192 }
193
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);
198 }
199
200 if (contentlen < expectedlen) {
201 return 0;
202 }
203
204 if (header.size() < s_proxyProtocolMinimumHeaderSize + contentlen) {
205 return -((s_proxyProtocolMinimumHeaderSize + contentlen) - header.size());
206 }
207
208 return s_proxyProtocolMinimumHeaderSize + contentlen;
209 }
210
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)
215 {
216 size_t addrSize = 0;
217 uint8_t protocol = 0;
218 ssize_t got = isProxyHeaderComplete(header, &proxy, &tcp, &addrSize, &protocol);
219 if (got <= 0) {
220 return got;
221 }
222
223 size_t pos = s_proxyProtocolMinimumHeaderSize;
224
225 if (proxy) {
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);
234 }
235
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);
243
244 if (len > 0) {
245 if (len > (got - pos)) {
246 return 0;
247 }
248
249 values.push_back({std::string(reinterpret_cast<const char*>(&header.at(pos)), len), type});
250 pos += len;
251 }
252 else {
253 values.push_back({"", type});
254 }
255
256 remaining = got - pos;
257 }
258
259 return pos;
260 }
261
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);