]>
Commit | Line | Data |
---|---|---|
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 | 19 | Helper::Reply::Reply() : |
f53969cc SM |
20 | result(Helper::Unknown), |
21 | whichServer(NULL) | |
7bbefa01 | 22 | { |
ddc77a2e CT |
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; | |
7bbefa01 AJ |
36 | } |
37 | ||
38 | void | |
ddc77a2e | 39 | Helper::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 |
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 | ||
7bbefa01 | 155 | void |
24438ec5 | 156 | Helper::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 |
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 | ||
0272dd08 | 196 | std::ostream & |
24438ec5 | 197 | operator <<(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 |