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