Squid Web Cache master
Loading...
Searching...
No Matches
Parser.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9#include "squid.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"
16
17#include <algorithm>
18#include <limits>
19
20#if HAVE_SYS_SOCKET_H
21#include <sys/socket.h>
22#endif
23#if HAVE_NETINET_IN_H
24#include <netinet/in.h>
25#endif
26#if HAVE_NETINET_IP_H
27#include <netinet/ip.h>
28#endif
29
30namespace ProxyProtocol {
31namespace One {
33static const auto &
35{
36 static const auto magic = new SBuf("PROXY", 5);
37 return *magic;
38}
40static Parsed Parse(const SBuf &buf);
41
42static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr);
43static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace);
45}
46
47namespace Two {
49static const auto &
51{
52 static const auto magic = new SBuf("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
53 return *magic;
54}
56static Parsed Parse(const SBuf &buf);
57
58static void ParseAddresses(const uint8_t family, Parser::BinaryTokenizer &tok, Header::Pointer &header);
60}
61}
62
63void
65{
66 static const auto ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG;
67
68 SBuf ip;
69
70 if (!tok.prefix(ip, ipChars))
71 throw TexcHere("PROXY/1.0 error: malformed IP address");
72
73 if (!tok.skip(' '))
74 throw TexcHere("PROXY/1.0 error: garbage after IP address");
75
76 if (!addr.GetHostByName(ip.c_str()))
77 throw TexcHere("PROXY/1.0 error: invalid IP address");
78
79}
80
81void
83{
84 int64_t port = -1;
85
86 if (!tok.int64(port, 10, false))
87 throw TexcHere("PROXY/1.0 error: malformed port");
88
89 if (trailingSpace && !tok.skip(' '))
90 throw TexcHere("PROXY/1.0 error: garbage after port");
91
92 if (port > std::numeric_limits<uint16_t>::max())
93 throw TexcHere("PROXY/1.0 error: invalid port");
94
95 addr.port(static_cast<uint16_t>(port));
96}
97
98void
100{
101 static const CharacterSet addressFamilies("Address family", "46");
102 SBuf parsedAddressFamily;
103
104 if (!tok.prefix(parsedAddressFamily, addressFamilies, 1))
105 throw TexcHere("PROXY/1.0 error: missing or invalid IP address family");
106
107 if (!tok.skip(' '))
108 throw TexcHere("PROXY/1.0 error: missing SP after the IP address family");
109
110 // parse: src-IP SP dst-IP SP src-port SP dst-port
111 ExtractIp(tok, header->sourceAddress);
112 ExtractIp(tok, header->destinationAddress);
113
114 if (header->addressFamily() != parsedAddressFamily)
115 throw TexcHere("PROXY/1.0 error: declared and/or actual IP address families mismatch");
116
117 ExtractPort(tok, header->sourceAddress, true);
118 ExtractPort(tok, header->destinationAddress, false);
119}
120
124{
126
127 static const SBuf::size_type maxHeaderLength = 107; // including CRLF
128 static const auto maxInteriorLength = maxHeaderLength - Magic().length() - 2;
129 static const auto interiorChars = CharacterSet::CR.complement().rename("non-CR");
130 SBuf interior;
131
132 if (!(tok.prefix(interior, interiorChars, maxInteriorLength) &&
133 tok.skip('\r') &&
134 tok.skip('\n'))) {
135 if (tok.atEnd())
137 // "empty interior", "too-long interior", or "missing LF after CR"
138 throw TexcHere("PROXY/1.0 error: malformed header");
139 }
140 // extracted all PROXY protocol bytes
141
142 static const SBuf v1("1.0");
143 Header::Pointer header = new Header(v1, Two::cmdProxy);
144
145 Parser::Tokenizer interiorTok(interior);
146
147 if (!interiorTok.skip(' '))
148 throw TexcHere("PROXY/1.0 error: missing SP after the magic sequence");
149
150 static const SBuf protoUnknown("UNKNOWN");
151 static const SBuf protoTcp("TCP");
152
153 if (interiorTok.skip(protoTcp))
154 ParseAddresses(interiorTok, header);
155 else if (interiorTok.skip(protoUnknown))
156 header->ignoreAddresses();
157 else
158 throw TexcHere("PROXY/1.0 error: invalid INET protocol or family");
159
160 return Parsed(header, tok.parsedSize());
161}
162
163void
165{
166 switch (family) {
167
168 case afInet: {
169 header->sourceAddress = tok.inet4("src_addr IPv4");
170 header->destinationAddress = tok.inet4("dst_addr IPv4");
171 header->sourceAddress.port(tok.uint16("src_port"));
172 header->destinationAddress.port(tok.uint16("dst_port"));
173 break;
174 }
175
176 case afInet6: {
177 header->sourceAddress = tok.inet6("src_addr IPv6");
178 header->destinationAddress = tok.inet6("dst_addr IPv6");
179 header->sourceAddress.port(tok.uint16("src_port"));
180 header->destinationAddress.port(tok.uint16("dst_port"));
181 break;
182 }
183
184 case afUnix: { // TODO: add support
185 // the address block length is 216 bytes
186 tok.skip(216, "unix_addr");
187 break;
188 }
189
190 default: {
191 // unreachable code: we have checked family validity already
192 Must(false);
193 break;
194 }
195 }
196}
197
198void
200 while (!tok.atEnd()) {
201 const auto type = tok.uint8("pp2_tlv::type");
202 header->tlvs.emplace_back(type, tok.pstring16("pp2_tlv::value"));
203 }
204}
205
208{
209 Parser::BinaryTokenizer tokHeader(buf, true);
210
211 const auto versionAndCommand = tokHeader.uint8("version and command");
212
213 const auto version = (versionAndCommand & 0xF0) >> 4;
214 if (version != 2) // version == 2 is mandatory
215 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid version ", version));
216
217 const auto command = (versionAndCommand & 0x0F);
218 if (command > cmdProxy)
219 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid command ", command));
220
221 const auto familyAndProto = tokHeader.uint8("family and proto");
222
223 const auto family = (familyAndProto & 0xF0) >> 4;
224 if (family > afUnix)
225 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid address family ", family));
226
227 const auto proto = (familyAndProto & 0x0F);
228 if (proto > tpDgram)
229 throw TexcHere(ToSBuf("PROXY/2.0 error: invalid transport protocol ", proto));
230
231 const auto rawHeader = tokHeader.pstring16("header");
232
233 static const SBuf v2("2.0");
234 Header::Pointer header = new Header(v2, Two::Command(command));
235
236 if (proto == tpUnspecified || family == afUnspecified) {
237 header->ignoreAddresses();
238 // discard address block and TLVs because we cannot tell
239 // how to parse such addresses and where the TLVs start.
240 } else {
241 Parser::BinaryTokenizer leftoverTok(rawHeader);
242 ParseAddresses(family, leftoverTok, header);
243 // TODO: parse TLVs for LOCAL connections
244 if (header->hasForwardedAddresses())
245 ParseTLVs(leftoverTok, header);
246 }
247
248 return Parsed(header, tokHeader.parsed());
249}
250
253{
254 Parser::Tokenizer magicTok(buf);
255
256 const auto parser =
257 magicTok.skip(Two::Magic()) ? &Two::Parse :
258 magicTok.skip(One::Magic()) ? &One::Parse :
259 nullptr;
260
261 if (parser) {
262 const auto parsed = (parser)(magicTok.remaining());
263 return Parsed(parsed.header, magicTok.parsedSize() + parsed.size);
264 }
265
266 // detect and terminate other protocols
267 if (buf.length() >= Two::Magic().length()) {
268 // PROXY/1.0 magic is shorter, so we know that
269 // the input does not start with any PROXY magic
270 throw TexcHere("PROXY protocol error: invalid magic");
271 }
272
273 // TODO: detect short non-magic prefixes earlier to avoid
274 // waiting for more data which may never come
275
276 // not enough bytes to parse magic yet
278}
279
280ProxyProtocol::Parsed::Parsed(const Header::Pointer &parsedHeader, const size_t parsedSize):
281 header(parsedHeader),
282 size(parsedSize)
283{
284 assert(bool(parsedHeader));
285}
286
int size
Definition ModDevPoll.cc:70
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
#define Must(condition)
#define assert(EX)
Definition assert.h:17
static int version
optimized set of C chars, with quick membership test and merge support
CharacterSet complement(const char *complementLabel=nullptr) const
CharacterSet & rename(const char *label)
change name; handy in const declarations that use operators
static const CharacterSet HEXDIG
static const CharacterSet CR
bool GetHostByName(const char *s)
Definition Address.cc:392
unsigned short port() const
Definition Address.cc:790
uint64_t parsed() const
the number of already parsed bytes
uint8_t uint8(const char *description)
parse a single-byte unsigned integer
SBuf pstring16(const char *description)
up to 64 KiB-long p-string
::Parser::InsufficientInput InsufficientInput
SBuf::size_type parsedSize() const
number of parsed bytes, including skipped ones
Definition Tokenizer.h:38
const SBuf & remaining() const
the remaining unprocessed section of buffer
Definition Tokenizer.h:44
bool skip(const SBuf &tokenToSkip)
Definition Tokenizer.cc:189
PROXY protocol v1 or v2 header.
Definition Header.h:23
successful parsing result
Definition Parser.h:19
Parsed(const HeaderPointer &parsedHeader, const size_t parsedSize)
Definition Parser.cc:280
Definition SBuf.h:94
const char * c_str()
Definition SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition SBuf.h:419
MemBlob::size_type size_type
Definition SBuf.h:96
static int port
static Parsed Parse(const SBuf &buf)
extracts PROXY protocol v1 header from the given buffer
Definition Parser.cc:123
static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace)
Definition Parser.cc:82
static const auto & Magic()
magic octet prefix for PROXY protocol version 1
Definition Parser.cc:34
static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr)
Definition Parser.cc:64
static void ParseAddresses(Parser::Tokenizer &tok, Header::Pointer &header)
Definition Parser.cc:99
static void ParseTLVs(Parser::BinaryTokenizer &tok, Header::Pointer &header)
Definition Parser.cc:199
Command
PROXY protocol 'command' field value.
Definition Elements.h:48
static void ParseAddresses(const uint8_t family, Parser::BinaryTokenizer &tok, Header::Pointer &header)
Definition Parser.cc:164
static Parsed Parse(const SBuf &buf)
extracts PROXY protocol v2 header from the given buffer
Definition Parser.cc:207
static const auto & Magic()
magic octet prefix for PROXY protocol version 2
Definition Parser.cc:50
@ afUnspecified
corresponds to a local connection or an unsupported protocol family
Definition Elements.h:55
Parsed Parse(const SBuf &)
Definition Parser.cc:252
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
Definition parse.c:160