]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/helper/Reply.cc
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 84 Helper process maintenance */
12 #include "ConfigParser.h"
13 #include "debug/Messages.h"
14 #include "debug/Stream.h"
16 #include "helper/Reply.h"
18 #include "SquidString.h"
20 Helper::Reply::Reply() :
21 result(Helper::Unknown
)
26 Helper::Reply::accumulate(const char *buf
, size_t len
)
29 other_
.init(4*1024, 1*1024*1024);
31 if (other_
.potentialSpaceSize() < static_cast<mb_size_t
>(len
))
32 return false; // no space left
34 other_
.append(buf
, len
);
39 Helper::Reply::finalize()
41 debugs(84, 3, "Parsing helper buffer");
42 // check we have something to parse
43 if (!other_
.hasContent()) {
44 // empty line response was the old URL-rewriter interface ERR response.
45 result
= Helper::Error
;
46 // for now ensure that legacy handlers are not presented with NULL strings.
47 debugs(84, 3, "Zero length reply");
51 char *p
= other_
.content();
52 size_t len
= other_
.contentSize();
55 // optimization: do not consider parsing result code if the response is short.
56 // URL-rewriter may return relative URLs or empty response for a large portion
59 debugs(84, 3, "Buff length is larger than 2");
60 // some helper formats (digest auth, URL-rewriter) just send a data string
61 // we must also check for the ' ' character after the response token (if anything)
62 if (!strncmp(p
,"OK",2) && (len
== 2 || p
[2] == ' ')) {
63 debugs(84, 3, "helper Result = OK");
64 result
= Helper::Okay
;
66 } else if (!strncmp(p
,"ERR",3) && (len
== 3 || p
[3] == ' ')) {
67 debugs(84, 3, "helper Result = ERR");
68 result
= Helper::Error
;
70 } else if (!strncmp(p
,"BH",2) && (len
== 2 || p
[2] == ' ')) {
71 debugs(84, 3, "helper Result = BH");
72 result
= Helper::BrokenHelper
;
74 } else if (!strncmp(p
,"TT ",3)) {
75 // NTLM challenge token
78 // followed by an auth token
79 char *w1
= strwordtok(nullptr, &p
);
81 const char *authToken
= w1
;
82 notes
.add("token",authToken
);
84 // token field is mandatory on this response code
85 result
= Helper::BrokenHelper
;
86 notes
.add("message","Missing 'token' data");
89 } else if (!strncmp(p
,"AF ",3)) {
90 // NTLM/Negotiate OK response
91 result
= Helper::Okay
;
94 // an optional auth token and user field
95 // or, an optional username field
96 char *w1
= strwordtok(nullptr, &p
);
97 char *w2
= strwordtok(nullptr, &p
);
99 // Negotiate "token user"
100 const char *authToken
= w1
;
101 notes
.add("token",authToken
);
103 const char *user
= w2
;
104 notes
.add("user",user
);
106 } else if (w1
!= nullptr) {
108 const char *user
= w1
;
109 notes
.add("user",user
);
111 } else if (!strncmp(p
,"NA ",3)) {
112 // NTLM fail-closed ERR response
113 result
= Helper::Error
;
118 for (; xisspace(*p
); ++p
); // skip whitespace
121 other_
.consume(p
- other_
.content());
122 other_
.consumeWhitespacePrefix();
124 // Hack for backward-compatibility: Do not parse for kv-pairs on NA response
128 // Hack for backward-compatibility: BH and NA used to be a text message...
129 if (other_
.hasContent() && (sawNA
|| result
== Helper::BrokenHelper
)) {
130 notes
.add("message", other_
.content());
135 /// restrict key names to alphanumeric, hyphen, underscore characters
137 isKeyNameChar(char c
)
139 if (c
>= 'a' && c
<= 'z')
142 if (c
>= 'A' && c
<= 'Z')
145 if (c
>= '0' && c
<= '9')
148 if (c
== '-' || c
== '_')
151 // prevent other characters matching the key=value
155 /// warns admin about problematic key=value pairs
157 Helper::Reply::CheckReceivedKey(const SBuf
&key
, const SBuf
&value
)
159 // Squid recognizes these keys (by name) in some helper responses
160 static const std::vector
<SBuf
> recognized
= {
161 SBuf("clt_conn_tag"),
177 // TODO: Merge with Notes::ReservedKeys(). That list has an entry that Squid
178 // sources do _not_ recognize today ("ttl"), and it is missing some
179 // recognized entries ("clt_conn_tag", "nonce", store-id", and "token").
182 debugs(84, DBG_IMPORTANT
, "WARNING: Deprecated from-helper annotation without a name: " <<
183 key
<< '=' << value
<<
184 Debug::Extra
<< "advice: Name or remove this annotation");
185 // TODO: Skip/ignore these annotations.
189 // We do not check custom keys for repetitions because Squid supports them:
190 // The "note" ACL checks all of them and %note prints all of them.
191 if (*key
.rbegin() == '_')
192 return; // a custom key
194 // To simplify, we allow all recognized keys, even though some of them are
195 // only expected from certain helpers or even only in certain reply types.
196 // To simplify and optimize, we do not check recognized keys for repetitions
197 // because _some_ of them (e.g., "message") do support repetitions.
198 if (std::find(recognized
.begin(), recognized
.end(), key
) != recognized
.end())
199 return; // a Squid-recognized key
201 debugs(84, Important(69), "WARNING: Unsupported or unexpected from-helper annotation with a name reserved for Squid use: " <<
202 key
<< '=' << value
<<
203 Debug::Extra
<< "advice: If this is a custom annotation, rename it to add a trailing underscore: " <<
208 Helper::Reply::parseResponseKeys()
210 // parse a "key=value" pair off the 'other()' buffer.
211 while (other_
.hasContent()) {
212 char *p
= other_
.content();
214 while (*p
&& isKeyNameChar(*p
)) ++p
;
216 return; // done. Not a key.
218 // whitespace between key and value is prohibited.
219 // workaround strwordtok() which skips whitespace prefix.
220 if (xisspace(*(p
+1)))
221 return; // done. Not a key.
226 // the value may be a quoted string or a token
227 const bool urlDecode
= (*p
!= '"'); // check before moving p.
228 char *v
= strwordtok(nullptr, &p
);
229 if (v
!= nullptr && urlDecode
&& (p
-v
) > 2) // 1-octet %-escaped requires 3 bytes
232 // TODO: Convert the above code to use Tokenizer and SBuf
233 const SBuf
parsedKey(key
);
234 const SBuf
parsedValue(v
); // allow empty values (!v or !*v)
235 CheckReceivedKey(parsedKey
, parsedValue
);
236 notes
.add(parsedKey
, parsedValue
);
238 other_
.consume(p
- other_
.content());
239 other_
.consumeWhitespacePrefix();
244 Helper::Reply::emptyBuf() const
253 Helper::operator <<(std::ostream
&os
, const Reply
&r
)
277 // dump the helper key=pair "notes" list
278 if (!r
.notes
.empty()) {
280 // This simple format matches what most helpers use and is sufficient
281 // for debugging nearly any helper response, but the result differs from
282 // raw helper responses when the helper quotes values or escapes special
283 // characters. See also: Helper::Reply::parseResponseKeys().
284 r
.notes
.print(os
, "=", " ");
288 MemBuf
const &o
= r
.other();
290 os
<< ", other: \"" << o
.content() << '\"';