]> git.ipfire.org Git - thirdparty/squid.git/blame - src/helper/Reply.cc
Source Format Enforcement (#963)
[thirdparty/squid.git] / src / helper / Reply.cc
CommitLineData
ab332e27 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
bbc27441
AJ
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.
ab332e27 7 */
bbc27441
AJ
8
9/* DEBUG: section 84 Helper process maintenance */
10
0272dd08 11#include "squid.h"
7bbefa01 12#include "ConfigParser.h"
602d9612 13#include "Debug.h"
0272dd08 14#include "helper.h"
24438ec5 15#include "helper/Reply.h"
7bbefa01 16#include "rfc1738.h"
ab332e27 17#include "SquidString.h"
0272dd08 18
ddc77a2e 19Helper::Reply::Reply() :
a56fcf0b 20 result(Helper::Unknown)
7bbefa01 21{
ddc77a2e
CT
22}
23
24bool
25Helper::Reply::accumulate(const char *buf, size_t len)
26{
27 if (other_.isNull())
28 other_.init(4*1024, 1*1024*1024);
29
30 if (other_.potentialSpaceSize() < static_cast<mb_size_t>(len))
31 return false; // no space left
32
33 other_.append(buf, len);
34 return true;
7bbefa01
AJ
35}
36
37void
ddc77a2e 38Helper::Reply::finalize()
0272dd08 39{
602fab5b 40 debugs(84, 3, "Parsing helper buffer");
0272dd08 41 // check we have something to parse
ddc77a2e 42 if (!other_.hasContent()) {
aef3208d 43 // empty line response was the old URL-rewriter interface ERR response.
2428ce02 44 result = Helper::Error;
e166785a 45 // for now ensure that legacy handlers are not presented with NULL strings.
ddc77a2e 46 debugs(84, 3, "Zero length reply");
0272dd08 47 return;
e166785a 48 }
0272dd08 49
ddc77a2e
CT
50 char *p = other_.content();
51 size_t len = other_.contentSize();
723fd4c1 52 bool sawNA = false;
0272dd08 53
e166785a
AJ
54 // optimization: do not consider parsing result code if the response is short.
55 // URL-rewriter may return relative URLs or empty response for a large portion
56 // of its replies.
0272dd08 57 if (len >= 2) {
602fab5b 58 debugs(84, 3, "Buff length is larger than 2");
0272dd08 59 // some helper formats (digest auth, URL-rewriter) just send a data string
e166785a
AJ
60 // we must also check for the ' ' character after the response token (if anything)
61 if (!strncmp(p,"OK",2) && (len == 2 || p[2] == ' ')) {
602fab5b 62 debugs(84, 3, "helper Result = OK");
2428ce02 63 result = Helper::Okay;
0272dd08 64 p+=2;
e166785a 65 } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) {
602fab5b 66 debugs(84, 3, "helper Result = ERR");
2428ce02 67 result = Helper::Error;
0272dd08 68 p+=3;
e166785a 69 } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) {
602fab5b 70 debugs(84, 3, "helper Result = BH");
2428ce02 71 result = Helper::BrokenHelper;
0272dd08
AJ
72 p+=2;
73 } else if (!strncmp(p,"TT ",3)) {
74 // NTLM challenge token
2428ce02 75 result = Helper::TT;
e166785a 76 p+=3;
d850d8ee 77 // followed by an auth token
7bbefa01 78 char *w1 = strwordtok(NULL, &p);
b7719af4 79 if (w1 != NULL) {
ddc77a2e
CT
80 const char *authToken = w1;
81 notes.add("token",authToken);
b7719af4
AJ
82 } else {
83 // token field is mandatory on this response code
2428ce02 84 result = Helper::BrokenHelper;
fd7f26ea 85 notes.add("message","Missing 'token' data");
b7719af4 86 }
7bbefa01 87
0272dd08 88 } else if (!strncmp(p,"AF ",3)) {
2f8abb64 89 // NTLM/Negotiate OK response
2428ce02 90 result = Helper::Okay;
e082eaba 91 p+=3;
0b7a812f 92 // followed by:
7bbefa01 93 // an optional auth token and user field
0b7a812f 94 // or, an optional username field
7bbefa01
AJ
95 char *w1 = strwordtok(NULL, &p);
96 char *w2 = strwordtok(NULL, &p);
97 if (w2 != NULL) {
98 // Negotiate "token user"
ddc77a2e
CT
99 const char *authToken = w1;
100 notes.add("token",authToken);
7bbefa01 101
ddc77a2e
CT
102 const char *user = w2;
103 notes.add("user",user);
7bbefa01
AJ
104
105 } else if (w1 != NULL) {
106 // NTLM "user"
ddc77a2e
CT
107 const char *user = w1;
108 notes.add("user",user);
e082eaba 109 }
0272dd08
AJ
110 } else if (!strncmp(p,"NA ",3)) {
111 // NTLM fail-closed ERR response
2428ce02 112 result = Helper::Error;
e166785a 113 p+=3;
723fd4c1 114 sawNA=true;
0272dd08
AJ
115 }
116
dacb64b9 117 for (; xisspace(*p); ++p); // skip whitespace
0272dd08
AJ
118 }
119
ddc77a2e
CT
120 other_.consume(p - other_.content());
121 other_.consumeWhitespacePrefix();
ab332e27 122
723fd4c1
AJ
123 // Hack for backward-compatibility: Do not parse for kv-pairs on NA response
124 if (!sawNA)
125 parseResponseKeys();
7bbefa01 126
723fd4c1 127 // Hack for backward-compatibility: BH and NA used to be a text message...
ddc77a2e
CT
128 if (other_.hasContent() && (sawNA || result == Helper::BrokenHelper)) {
129 notes.add("message", other_.content());
130 other_.clean();
ab332e27
AJ
131 }
132}
133
acf2ce75
AJ
134/// restrict key names to alphanumeric, hyphen, underscore characters
135static bool
136isKeyNameChar(char c)
137{
138 if (c >= 'a' && c <= 'z')
139 return true;
140
141 if (c >= 'A' && c <= 'Z')
142 return true;
143
144 if (c >= '0' && c <= '9')
145 return true;
146
147 if (c == '-' || c == '_')
148 return true;
149
150 // prevent other characters matching the key=value
151 return false;
152}
153
7bbefa01 154void
24438ec5 155Helper::Reply::parseResponseKeys()
ab332e27 156{
7bbefa01 157 // parse a "key=value" pair off the 'other()' buffer.
ddc77a2e
CT
158 while (other_.hasContent()) {
159 char *p = other_.content();
160 const char *key = p;
acf2ce75 161 while (*p && isKeyNameChar(*p)) ++p;
7bbefa01
AJ
162 if (*p != '=')
163 return; // done. Not a key.
164
24eac830
AJ
165 // whitespace between key and value is prohibited.
166 // workaround strwordtok() which skips whitespace prefix.
167 if (xisspace(*(p+1)))
168 return; // done. Not a key.
169
7bbefa01
AJ
170 *p = '\0';
171 ++p;
172
7bbefa01 173 // the value may be a quoted string or a token
05e52854 174 const bool urlDecode = (*p != '"'); // check before moving p.
7bbefa01 175 char *v = strwordtok(NULL, &p);
05e52854 176 if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
7bbefa01 177 rfc1738_unescape(v);
7bbefa01 178
cf9f0261 179 notes.add(key, v ? v : ""); // value can be empty, but must not be NULL
7bbefa01 180
ddc77a2e
CT
181 other_.consume(p - other_.content());
182 other_.consumeWhitespacePrefix();
ab332e27 183 }
0272dd08
AJ
184}
185
ddc77a2e
CT
186const MemBuf &
187Helper::Reply::emptyBuf() const
188{
189 static MemBuf empty;
190 if (empty.isNull())
191 empty.init(1, 1);
192 return empty;
193}
194
0272dd08 195std::ostream &
24438ec5 196operator <<(std::ostream &os, const Helper::Reply &r)
0272dd08
AJ
197{
198 os << "{result=";
154be258 199 switch (r.result) {
2428ce02 200 case Helper::Okay:
0272dd08
AJ
201 os << "OK";
202 break;
2428ce02 203 case Helper::Error:
0272dd08 204 os << "ERR";
ab332e27 205 break;
2428ce02 206 case Helper::BrokenHelper:
0272dd08
AJ
207 os << "BH";
208 break;
2428ce02 209 case Helper::TT:
0272dd08
AJ
210 os << "TT";
211 break;
32fd6d8a
CT
212 case Helper::TimedOut:
213 os << "Timeout";
214 break;
2428ce02 215 case Helper::Unknown:
0272dd08
AJ
216 os << "Unknown";
217 break;
218 }
219
7bbefa01 220 // dump the helper key=pair "notes" list
cf9f0261 221 if (!r.notes.empty()) {
7bbefa01 222 os << ", notes={";
7e6ef752 223 os << r.notes.toString("; ");
7bbefa01
AJ
224 os << "}";
225 }
226
ddc77a2e
CT
227 MemBuf const &o = r.other();
228 if (o.hasContent())
229 os << ", other: \"" << o.content() << '\"';
0272dd08
AJ
230
231 os << '}';
232
233 return os;
234}
f53969cc 235