]> git.ipfire.org Git - thirdparty/squid.git/blob - src/helper/Reply.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / helper / Reply.cc
1 /*
2 * Copyright (C) 1996-2015 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(char *buf, size_t len) :
20 result(Helper::Unknown),
21 whichServer(NULL)
22 {
23 parse(buf,len);
24 }
25
26 void
27 Helper::Reply::parse(char *buf, size_t len)
28 {
29 debugs(84, 3, "Parsing helper buffer");
30 // check we have something to parse
31 if (!buf || len < 1) {
32 // empty line response was the old URL-rewriter interface ERR response.
33 result = Helper::Error;
34 // for now ensure that legacy handlers are not presented with NULL strings.
35 debugs(84, 3, "Reply length is smaller than 1 or none at all ");
36 other_.init(1,1);
37 other_.terminate();
38 return;
39 }
40
41 char *p = buf;
42 bool sawNA = false;
43
44 // optimization: do not consider parsing result code if the response is short.
45 // URL-rewriter may return relative URLs or empty response for a large portion
46 // of its replies.
47 if (len >= 2) {
48 debugs(84, 3, "Buff length is larger than 2");
49 // some helper formats (digest auth, URL-rewriter) just send a data string
50 // we must also check for the ' ' character after the response token (if anything)
51 if (!strncmp(p,"OK",2) && (len == 2 || p[2] == ' ')) {
52 debugs(84, 3, "helper Result = OK");
53 result = Helper::Okay;
54 p+=2;
55 } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) {
56 debugs(84, 3, "helper Result = ERR");
57 result = Helper::Error;
58 p+=3;
59 } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) {
60 debugs(84, 3, "helper Result = BH");
61 result = Helper::BrokenHelper;
62 p+=2;
63 } else if (!strncmp(p,"TT ",3)) {
64 // NTLM challenge token
65 result = Helper::TT;
66 p+=3;
67 // followed by an auth token
68 char *w1 = strwordtok(NULL, &p);
69 if (w1 != NULL) {
70 MemBuf authToken;
71 authToken.init();
72 authToken.append(w1, strlen(w1));
73 notes.add("token",authToken.content());
74 } else {
75 // token field is mandatory on this response code
76 result = Helper::BrokenHelper;
77 notes.add("message","Missing 'token' data");
78 }
79
80 } else if (!strncmp(p,"AF ",3)) {
81 // NTLM/Negotate OK response
82 result = Helper::Okay;
83 p+=3;
84 // followed by:
85 // an optional auth token and user field
86 // or, an optional username field
87 char *w1 = strwordtok(NULL, &p);
88 char *w2 = strwordtok(NULL, &p);
89 if (w2 != NULL) {
90 // Negotiate "token user"
91 MemBuf authToken;
92 authToken.init();
93 authToken.append(w1, strlen(w1));
94 notes.add("token",authToken.content());
95
96 MemBuf user;
97 user.init();
98 user.append(w2,strlen(w2));
99 notes.add("user",user.content());
100
101 } else if (w1 != NULL) {
102 // NTLM "user"
103 MemBuf user;
104 user.init();
105 user.append(w1,strlen(w1));
106 notes.add("user",user.content());
107 }
108 } else if (!strncmp(p,"NA ",3)) {
109 // NTLM fail-closed ERR response
110 result = Helper::Error;
111 p+=3;
112 sawNA=true;
113 }
114
115 for (; xisspace(*p); ++p); // skip whitespace
116 }
117
118 const mb_size_t blobSize = (buf+len-p);
119 other_.init(blobSize+1, blobSize+1);
120 other_.append(p, blobSize); // remainders of the line.
121
122 // NULL-terminate so the helper callback handlers do not buffer-overrun
123 other_.terminate();
124
125 // Hack for backward-compatibility: Do not parse for kv-pairs on NA response
126 if (!sawNA)
127 parseResponseKeys();
128
129 // Hack for backward-compatibility: BH and NA used to be a text message...
130 if (other().hasContent() && (sawNA || result == Helper::BrokenHelper)) {
131 notes.add("message",other().content());
132 modifiableOther().clean();
133 }
134 }
135
136 /// restrict key names to alphanumeric, hyphen, underscore characters
137 static bool
138 isKeyNameChar(char c)
139 {
140 if (c >= 'a' && c <= 'z')
141 return true;
142
143 if (c >= 'A' && c <= 'Z')
144 return true;
145
146 if (c >= '0' && c <= '9')
147 return true;
148
149 if (c == '-' || c == '_')
150 return true;
151
152 // prevent other characters matching the key=value
153 return false;
154 }
155
156 void
157 Helper::Reply::parseResponseKeys()
158 {
159 // parse a "key=value" pair off the 'other()' buffer.
160 while (other().hasContent()) {
161 char *p = modifiableOther().content();
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 const char *key = other().content();
175
176 // the value may be a quoted string or a token
177 const bool urlDecode = (*p != '"'); // check before moving p.
178 char *v = strwordtok(NULL, &p);
179 if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes
180 rfc1738_unescape(v);
181
182 notes.add(key, v ? v : ""); // value can be empty, but must not be NULL
183
184 modifiableOther().consume(p - other().content());
185 modifiableOther().consumeWhitespacePrefix();
186 }
187 }
188
189 std::ostream &
190 operator <<(std::ostream &os, const Helper::Reply &r)
191 {
192 os << "{result=";
193 switch (r.result) {
194 case Helper::Okay:
195 os << "OK";
196 break;
197 case Helper::Error:
198 os << "ERR";
199 break;
200 case Helper::BrokenHelper:
201 os << "BH";
202 break;
203 case Helper::TT:
204 os << "TT";
205 break;
206 case Helper::TimedOut:
207 os << "Timeout";
208 break;
209 case Helper::Unknown:
210 os << "Unknown";
211 break;
212 }
213
214 // dump the helper key=pair "notes" list
215 if (!r.notes.empty()) {
216 os << ", notes={";
217 os << r.notes.toString("; ");
218 os << "}";
219 }
220
221 if (r.other().hasContent())
222 os << ", other: \"" << r.other().content() << '\"';
223
224 os << '}';
225
226 return os;
227 }
228