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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors