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"
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"
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 
30 namespace ProxyProtocol {
31 namespace One {
33 static const auto &
35 {
36  static const auto magic = new SBuf("PROXY", 5);
37  return *magic;
38 }
40 static Parsed Parse(const SBuf &buf);
41 
42 static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr);
43 static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace);
45 }
46 
47 namespace Two {
49 static 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 }
56 static Parsed Parse(const SBuf &buf);
57 
58 static void ParseAddresses(const uint8_t family, Parser::BinaryTokenizer &tok, Header::Pointer &header);
60 }
61 }
62 
63 void
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 
81 void
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 
93  throw TexcHere("PROXY/1.0 error: invalid port");
94 
95  addr.port(static_cast<uint16_t>(port));
96 }
97 
98 void
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 {
125  Parser::Tokenizer tok(buf);
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 
163 void
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 
198 void
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 
280 ProxyProtocol::Parsed::Parsed(const Header::Pointer &parsedHeader, const size_t parsedSize):
281  header(parsedHeader),
282  size(parsedSize)
283 {
284  assert(bool(parsedHeader));
285 }
286 
uint64_t parsed() const
the number of already parsed bytes
static Parsed Parse(const SBuf &buf)
extracts PROXY protocol v2 header from the given buffer
Definition: Parser.cc:207
bool GetHostByName(const char *s)
Definition: Address.cc:392
::Parser::InsufficientInput InsufficientInput
Definition: SBuf.h:93
static void ParseAddresses(Parser::Tokenizer &tok, Header::Pointer &header)
Definition: Parser.cc:99
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
const A & max(A const &lhs, A const &rhs)
bool skip(const SBuf &tokenToSkip)
Definition: Tokenizer.cc:189
PROXY protocol v1 or v2 header.
Definition: Header.h:22
static int port
Definition: ldap_backend.cc:70
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:63
static const CharacterSet CR
Definition: CharacterSet.h:80
successful parsing result
Definition: Parser.h:18
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
int size
Definition: ModDevPoll.cc:69
uint8_t uint8(const char *description)
parse a single-byte unsigned integer
MemBlob::size_type size_type
Definition: SBuf.h:96
static const auto & Magic()
magic octet prefix for PROXY protocol version 1
Definition: Parser.cc:34
unsigned short port() const
Definition: Address.cc:798
static const CharacterSet HEXDIG
Definition: CharacterSet.h:88
#define assert(EX)
Definition: assert.h:17
static int version
const char * c_str()
Definition: SBuf.cc:516
static void ParseAddresses(const uint8_t family, Parser::BinaryTokenizer &tok, Header::Pointer &header)
Definition: Parser.cc:164
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
CharacterSet & rename(const char *label)
change name; handy in const declarations that use operators
Definition: CharacterSet.h:61
static void ParseTLVs(Parser::BinaryTokenizer &tok, Header::Pointer &header)
Definition: Parser.cc:199
static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace)
Definition: Parser.cc:82
Definition: parse.c:160
Parsed Parse(const SBuf &)
Definition: Parser.cc:252
Parsed(const HeaderPointer &parsedHeader, const size_t parsedSize)
Definition: Parser.cc:280
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define Must(condition)
Definition: TextException.h:75
static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr)
Definition: Parser.cc:64
Command
PROXY protocol 'command' field value.
Definition: Elements.h:48
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
static Parsed Parse(const SBuf &buf)
extracts PROXY protocol v1 header from the given buffer
Definition: Parser.cc:123
static const auto & Magic()
magic octet prefix for PROXY protocol version 2
Definition: Parser.cc:50
SBuf pstring16(const char *description)
up to 64 KiB-long p-string
@ afUnspecified
corresponds to a local connection or an unsupported protocol family
Definition: Elements.h:55

 

Introduction

Documentation

Support

Miscellaneous