Parser.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 SBuf Magic("PROXY", 5);
35 static Parsed Parse(const SBuf &buf);
36 
37 static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr);
38 static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace);
40 }
41 
42 namespace Two {
44 static const SBuf Magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
46 static Parsed Parse(const SBuf &buf);
47 
48 static void ParseAddresses(const uint8_t family, Parser::BinaryTokenizer &tok, Header::Pointer &header);
50 }
51 }
52 
53 void
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 
71 void
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 
88 void
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 {
115  Parser::Tokenizer tok(buf);
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 
153 void
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 
188 void
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 
270 ProxyProtocol::Parsed::Parsed(const Header::Pointer &parsedHeader, const size_t parsedSize):
271  header(parsedHeader),
272  size(parsedSize)
273 {
274  assert(bool(parsedHeader));
275 }
276 
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:197
bool GetHostByName(const char *s)
Definition: Address.cc:372
::Parser::InsufficientInput InsufficientInput
Definition: SBuf.h:94
static void ParseAddresses(Parser::Tokenizer &tok, Header::Pointer &header)
Definition: Parser.cc:89
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
int type
Definition: errorpage.cc:152
bool skip(const SBuf &tokenToSkip)
Definition: Tokenizer.cc:179
PROXY protocol v1 or v2 header.
Definition: Header.h:23
static int port
Definition: ldap_backend.cc:70
A const & max(A const &lhs, A const &rhs)
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:59
static const CharacterSet CR
Definition: CharacterSet.h:80
successful parsing result
Definition: Parser.h:19
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:75
static const SBuf Magic("PROXY", 5)
magic octet prefix for PROXY protocol version 1
uint8_t uint8(const char *description)
parse a single-byte unsigned integer
MemBlob::size_type size_type
Definition: SBuf.h:96
unsigned short port() const
Definition: Address.cc:778
static const CharacterSet HEXDIG
Definition: CharacterSet.h:88
#define assert(EX)
Definition: assert.h:19
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:154
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
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:189
static void ExtractPort(Parser::Tokenizer &tok, Ip::Address &addr, const bool trailingSpace)
Definition: Parser.cc:72
Definition: parse.c:160
Parsed Parse(const SBuf &)
Definition: Parser.cc:242
Parsed(const HeaderPointer &parsedHeader, const size_t parsedSize)
Definition: Parser.cc:270
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define Must(condition)
Definition: TextException.h:71
static void ExtractIp(Parser::Tokenizer &tok, Ip::Address &addr)
Definition: Parser.cc:54
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
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:18
static Parsed Parse(const SBuf &buf)
extracts PROXY protocol v1 header from the given buffer
Definition: Parser.cc:113
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

Web Site Translations

Mirrors