]> git.ipfire.org Git - thirdparty/squid.git/blob - src/helper/Reply.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / helper / Reply.cc
1 /*
2 * Copyright (C) 1996-2018 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 84 Helper process maintenance */
10
11 #include "squid.h"
12 #include "ConfigParser.h"
13 #include "Debug.h"
14 #include "helper.h"
15 #include "helper/Reply.h"
16 #include "rfc1738.h"
17 #include "SquidString.h"
18
19 Helper::Reply::Reply() :
20 result(Helper::Unknown),
21 whichServer(NULL)
22 {
23 }
24
25 bool
26 Helper::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;
36 }
37
38 void
39 Helper::Reply::finalize()
40 {
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");
48 return;
49 }
50
51 char *p = other_.content();
52 size_t len = other_.contentSize();
53 bool sawNA = false;
54
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.
58 if (len >= 2) {
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;
65 p+=2;
66 } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) {
67 debugs(84, 3, "helper Result = ERR");
68 result = Helper::Error;
69 p+=3;
70 } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) {
71 debugs(84, 3, "helper Result = BH");
72 result = Helper::BrokenHelper;
73 p+=2;
74 } else if (!strncmp(p,"TT ",3)) {
75 // NTLM challenge token
76 result = Helper::TT;
77 p+=3;
78 // followed by an auth token
79 char *w1 = strwordtok(NULL, &p);
80 if (w1 != NULL) {
81 const char *authToken = w1;
82 notes.add("token",authToken);
83 } else {
84 // token field is mandatory on this response code
85 result = Helper::BrokenHelper;
86 notes.add("message","Missing 'token' data");
87 }
88
89 } else if (!strncmp(p,"AF ",3)) {
90 // NTLM/Negotate OK response
91 result = Helper::Okay;
92 p+=3;
93 // followed by:
94 // an optional auth token and user field
95 // or, an optional username field
96 char *w1 = strwordtok(NULL, &p);
97 char *w2 = strwordtok(NULL, &p);
98 if (w2 != NULL) {
99 // Negotiate "token user"
100 const char *authToken = w1;
101 notes.add("token",authToken);
102
103 const char *user = w2;
104 notes.add("user",user);
105
106 } else if (w1 != NULL) {
107 // NTLM "user"
108 const char *user = w1;
109 notes.add("user",user);
110 }
111 } else if (!strncmp(p,"NA ",3)) {
112 // NTLM fail-closed ERR response
113 result = Helper::Error;
114 p+=3;
115 sawNA=true;
116 }
117
118 for (; xisspace(*p); ++p); // skip whitespace
119 }
120
121 other_.consume(p - other_.content());
122 other_.consumeWhitespacePrefix();
123
124 // Hack for backward-compatibility: Do not parse for kv-pairs on NA response
125 if (!sawNA)
126 parseResponseKeys();
127
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());
131 other_.clean();
132 }
133 }
134
135 /// restrict key names to alphanumeric, hyphen, underscore characters
136 static bool
137 isKeyNameChar(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
155 void
156 Helper::Reply::parseResponseKeys()
157 {
158 // parse a "key=value" pair off the 'other()' buffer.
159 while (other_.hasContent()) {
160 char *p = other_.content();
161 const char *key = p;
162 while (*p && isKeyNameChar(*p)) ++p;
163 if (*p != '=')
164 return; // done. Not a key.
165
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
171 *p = '\0';
172 ++p;
173
174 // the value may be a quoted string or a token
175 const bool urlDecode = (*p != '"'); // check before moving p.
176 char *v = strwordtok(NULL, &p);
177 if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
178 rfc1738_unescape(v);
179
180 notes.add(key, v ? v : ""); // value can be empty, but must not be NULL
181
182 other_.consume(p - other_.content());
183 other_.consumeWhitespacePrefix();
184 }
185 }
186
187 const MemBuf &
188 Helper::Reply::emptyBuf() const
189 {
190 static MemBuf empty;
191 if (empty.isNull())
192 empty.init(1, 1);
193 return empty;
194 }
195
196 std::ostream &
197 operator <<(std::ostream &os, const Helper::Reply &r)
198 {
199 os << "{result=";
200 switch (r.result) {
201 case Helper::Okay:
202 os << "OK";
203 break;
204 case Helper::Error:
205 os << "ERR";
206 break;
207 case Helper::BrokenHelper:
208 os << "BH";
209 break;
210 case Helper::TT:
211 os << "TT";
212 break;
213 case Helper::TimedOut:
214 os << "Timeout";
215 break;
216 case Helper::Unknown:
217 os << "Unknown";
218 break;
219 }
220
221 // dump the helper key=pair "notes" list
222 if (!r.notes.empty()) {
223 os << ", notes={";
224 os << r.notes.toString("; ");
225 os << "}";
226 }
227
228 MemBuf const &o = r.other();
229 if (o.hasContent())
230 os << ", other: \"" << o.content() << '\"';
231
232 os << '}';
233
234 return os;
235 }
236