]> git.ipfire.org Git - thirdparty/squid.git/blob - src/parser/BinaryTokenizer.cc
be16bdc548db82cfbaf82ecb8e7598f246490d2c
[thirdparty/squid.git] / src / parser / BinaryTokenizer.cc
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 /* DEBUG: section 24 SBuf */
10
11 #include "squid.h"
12 #include "base/Raw.h"
13 #include "ip/Address.h"
14 #include "parser/BinaryTokenizer.h"
15
16 Parser::BinaryTokenizer::BinaryTokenizer(): BinaryTokenizer(SBuf())
17 {
18 }
19
20 Parser::BinaryTokenizer::BinaryTokenizer(const SBuf &data, const bool expectMore):
21 context(nullptr),
22 data_(data),
23 parsed_(0),
24 syncPoint_(0),
25 expectMore_(expectMore)
26 {
27 }
28
29 static inline
30 std::ostream &
31 operator <<(std::ostream &os, const Parser::BinaryTokenizerContext *context)
32 {
33 if (context)
34 os << context->parent << context->name;
35 return os;
36 }
37
38 /// debugging helper that prints a "standard" debugs() trailer
39 #define BinaryTokenizer_tail(size, start) \
40 " occupying " << (size) << " bytes @" << (start) << " in " << this << \
41 (expectMore_ ? ';' : '.');
42
43 /// logs and throws if fewer than size octets remain; no other side effects
44 void
45 Parser::BinaryTokenizer::want(uint64_t size, const char *description) const
46 {
47 if (parsed_ + size > data_.length()) {
48 debugs(24, 5, (parsed_ + size - data_.length()) << " more bytes for " <<
49 context << description << BinaryTokenizer_tail(size, parsed_));
50 Must(expectMore_); // throw an error on premature input termination
51 throw InsufficientInput();
52 }
53 }
54
55 void
56 Parser::BinaryTokenizer::got(uint64_t size, const char *description) const
57 {
58 debugs(24, 7, context << description <<
59 BinaryTokenizer_tail(size, parsed_ - size));
60 }
61
62 /// debugging helper for parsed number fields
63 void
64 Parser::BinaryTokenizer::got(uint32_t value, uint64_t size, const char *description) const
65 {
66 debugs(24, 7, context << description << '=' << value <<
67 BinaryTokenizer_tail(size, parsed_ - size));
68 }
69
70 /// debugging helper for parsed areas/blobs
71 void
72 Parser::BinaryTokenizer::got(const SBuf &value, uint64_t size, const char *description) const
73 {
74 debugs(24, 7, context << description << '=' <<
75 Raw(nullptr, value.rawContent(), value.length()).hex() <<
76 BinaryTokenizer_tail(size, parsed_ - size));
77
78 }
79
80 /// debugging helper for parsed addresses
81 void
82 Parser::BinaryTokenizer::got(const Ip::Address &value, uint64_t size, const char *description) const
83 {
84 debugs(24, 7, context << description << '=' << value <<
85 BinaryTokenizer_tail(size, parsed_ - size));
86 }
87
88 /// debugging helper for skipped fields
89 void
90 Parser::BinaryTokenizer::skipped(uint64_t size, const char *description) const
91 {
92 debugs(24, 7, context << description << BinaryTokenizer_tail(size, parsed_ - size));
93
94 }
95
96 /// Returns the next ready-for-shift byte, adjusting the number of parsed bytes.
97 /// The larger 32-bit return type helps callers shift/merge octets into numbers.
98 /// This internal method does not perform out-of-bounds checks.
99 uint32_t
100 Parser::BinaryTokenizer::octet()
101 {
102 // While char may be signed, we view data characters as unsigned,
103 // which helps to arrive at the right 32-bit return value.
104 return static_cast<uint8_t>(data_[parsed_++]);
105 }
106
107 void
108 Parser::BinaryTokenizer::reset(const SBuf &data, const bool expectMore)
109 {
110 *this = BinaryTokenizer(data, expectMore);
111 }
112
113 void
114 Parser::BinaryTokenizer::rollback()
115 {
116 parsed_ = syncPoint_;
117 }
118
119 void
120 Parser::BinaryTokenizer::commit()
121 {
122 syncPoint_ = parsed_;
123 }
124
125 bool
126 Parser::BinaryTokenizer::atEnd() const
127 {
128 return parsed_ >= data_.length();
129 }
130
131 uint8_t
132 Parser::BinaryTokenizer::uint8(const char *description)
133 {
134 want(1, description);
135 const uint8_t result = octet();
136 got(result, 1, description);
137 return result;
138 }
139
140 uint16_t
141 Parser::BinaryTokenizer::uint16(const char *description)
142 {
143 want(2, description);
144 const uint16_t result = (octet() << 8) | octet();
145 got(result, 2, description);
146 return result;
147 }
148
149 uint32_t
150 Parser::BinaryTokenizer::uint24(const char *description)
151 {
152 want(3, description);
153 const uint32_t result = (octet() << 16) | (octet() << 8) | octet();
154 got(result, 3, description);
155 return result;
156 }
157
158 uint32_t
159 Parser::BinaryTokenizer::uint32(const char *description)
160 {
161 want(4, description);
162 const uint32_t result = (octet() << 24) | (octet() << 16) | (octet() << 8) | octet();
163 got(result, 4, description);
164 return result;
165 }
166
167 SBuf
168 Parser::BinaryTokenizer::area(uint64_t size, const char *description)
169 {
170 want(size, description);
171 const SBuf result = data_.substr(parsed_, size);
172 parsed_ += size;
173 got(result, size, description);
174 return result;
175 }
176
177 template <class InAddr>
178 Ip::Address
179 Parser::BinaryTokenizer::inetAny(const char *description)
180 {
181 InAddr addr;
182 const auto size = sizeof(addr);
183 want(size, description);
184 memcpy(&addr, data_.rawContent() + parsed_, size);
185 parsed_ += size;
186 const Ip::Address result(addr);
187 got(result, size, description);
188 return result;
189 }
190
191 Ip::Address
192 Parser::BinaryTokenizer::inet4(const char *description)
193 {
194 return inetAny<struct in_addr>(description);
195 }
196
197 Ip::Address
198 Parser::BinaryTokenizer::inet6(const char *description)
199 {
200 return inetAny<struct in6_addr>(description);
201 }
202
203 void
204 Parser::BinaryTokenizer::skip(uint64_t size, const char *description)
205 {
206 want(size, description);
207 parsed_ += size;
208 skipped(size, description);
209 }
210
211 /*
212 * BinaryTokenizer::pstringN() implementations below reduce debugging noise by
213 * not parsing empty areas and not summarizing parsing context.success().
214 */
215
216 SBuf
217 Parser::BinaryTokenizer::pstring8(const char *description)
218 {
219 BinaryTokenizerContext pstring(*this, description);
220 if (const uint8_t length = uint8(".length"))
221 return area(length, ".octets");
222 return SBuf();
223 }
224
225 SBuf
226 Parser::BinaryTokenizer::pstring16(const char *description)
227 {
228 BinaryTokenizerContext pstring(*this, description);
229 if (const uint16_t length = uint16(".length"))
230 return area(length, ".octets");
231 return SBuf();
232 }
233
234 SBuf
235 Parser::BinaryTokenizer::pstring24(const char *description)
236 {
237 BinaryTokenizerContext pstring(*this, description);
238 if (const uint32_t length = uint24(".length"))
239 return area(length, ".octets");
240 return SBuf();
241 }
242