]>
Commit | Line | Data |
---|---|---|
ab332e27 AJ |
1 | /* |
2 | * DEBUG: section 84 Helper process maintenance | |
3 | * AUTHOR: Amos Jeffries | |
4 | */ | |
0272dd08 | 5 | #include "squid.h" |
7bbefa01 | 6 | #include "ConfigParser.h" |
602d9612 | 7 | #include "Debug.h" |
0272dd08 | 8 | #include "helper.h" |
602d9612 | 9 | #include "HelperReply.h" |
7bbefa01 | 10 | #include "rfc1738.h" |
ab332e27 | 11 | #include "SquidString.h" |
0272dd08 | 12 | |
05e52854 | 13 | HelperReply::HelperReply(char *buf, size_t len) : |
0272dd08 AJ |
14 | result(HelperReply::Unknown), |
15 | whichServer(NULL) | |
7bbefa01 | 16 | { |
05e52854 | 17 | parse(buf,len); |
7bbefa01 AJ |
18 | } |
19 | ||
20 | void | |
05e52854 | 21 | HelperReply::parse(char *buf, size_t len) |
0272dd08 | 22 | { |
602fab5b | 23 | debugs(84, 3, "Parsing helper buffer"); |
0272dd08 | 24 | // check we have something to parse |
e166785a | 25 | if (!buf || len < 1) { |
aef3208d AJ |
26 | // empty line response was the old URL-rewriter interface ERR response. |
27 | result = HelperReply::Error; | |
e166785a | 28 | // for now ensure that legacy handlers are not presented with NULL strings. |
602fab5b | 29 | debugs(84, 3, "Reply length is smaller than 1 or none at all "); |
e166785a AJ |
30 | other_.init(1,1); |
31 | other_.terminate(); | |
0272dd08 | 32 | return; |
e166785a | 33 | } |
0272dd08 | 34 | |
7bbefa01 | 35 | char *p = buf; |
723fd4c1 | 36 | bool sawNA = false; |
0272dd08 | 37 | |
e166785a AJ |
38 | // optimization: do not consider parsing result code if the response is short. |
39 | // URL-rewriter may return relative URLs or empty response for a large portion | |
40 | // of its replies. | |
0272dd08 | 41 | if (len >= 2) { |
602fab5b | 42 | debugs(84, 3, "Buff length is larger than 2"); |
0272dd08 | 43 | // some helper formats (digest auth, URL-rewriter) just send a data string |
e166785a AJ |
44 | // we must also check for the ' ' character after the response token (if anything) |
45 | if (!strncmp(p,"OK",2) && (len == 2 || p[2] == ' ')) { | |
602fab5b | 46 | debugs(84, 3, "helper Result = OK"); |
0272dd08 AJ |
47 | result = HelperReply::Okay; |
48 | p+=2; | |
e166785a | 49 | } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) { |
602fab5b | 50 | debugs(84, 3, "helper Result = ERR"); |
0272dd08 AJ |
51 | result = HelperReply::Error; |
52 | p+=3; | |
e166785a | 53 | } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) { |
602fab5b | 54 | debugs(84, 3, "helper Result = BH"); |
0272dd08 AJ |
55 | result = HelperReply::BrokenHelper; |
56 | p+=2; | |
57 | } else if (!strncmp(p,"TT ",3)) { | |
58 | // NTLM challenge token | |
59 | result = HelperReply::TT; | |
e166785a | 60 | p+=3; |
d850d8ee | 61 | // followed by an auth token |
7bbefa01 | 62 | char *w1 = strwordtok(NULL, &p); |
b7719af4 AJ |
63 | if (w1 != NULL) { |
64 | MemBuf authToken; | |
65 | authToken.init(); | |
66 | authToken.append(w1, strlen(w1)); | |
fd7f26ea | 67 | notes.add("token",authToken.content()); |
b7719af4 AJ |
68 | } else { |
69 | // token field is mandatory on this response code | |
70 | result = HelperReply::BrokenHelper; | |
fd7f26ea | 71 | notes.add("message","Missing 'token' data"); |
b7719af4 | 72 | } |
7bbefa01 | 73 | |
0272dd08 | 74 | } else if (!strncmp(p,"AF ",3)) { |
e082eaba | 75 | // NTLM/Negotate OK response |
7bbefa01 | 76 | result = HelperReply::Okay; |
e082eaba | 77 | p+=3; |
0b7a812f | 78 | // followed by: |
7bbefa01 | 79 | // an optional auth token and user field |
0b7a812f | 80 | // or, an optional username field |
7bbefa01 AJ |
81 | char *w1 = strwordtok(NULL, &p); |
82 | char *w2 = strwordtok(NULL, &p); | |
83 | if (w2 != NULL) { | |
84 | // Negotiate "token user" | |
85 | MemBuf authToken; | |
0b7a812f | 86 | authToken.init(); |
7bbefa01 | 87 | authToken.append(w1, strlen(w1)); |
fd7f26ea | 88 | notes.add("token",authToken.content()); |
7bbefa01 AJ |
89 | |
90 | MemBuf user; | |
0b7a812f | 91 | user.init(); |
7bbefa01 | 92 | user.append(w2,strlen(w2)); |
fd7f26ea | 93 | notes.add("user",user.content()); |
7bbefa01 AJ |
94 | |
95 | } else if (w1 != NULL) { | |
96 | // NTLM "user" | |
97 | MemBuf user; | |
0b7a812f | 98 | user.init(); |
7bbefa01 | 99 | user.append(w1,strlen(w1)); |
fd7f26ea | 100 | notes.add("user",user.content()); |
e082eaba | 101 | } |
0272dd08 AJ |
102 | } else if (!strncmp(p,"NA ",3)) { |
103 | // NTLM fail-closed ERR response | |
7bbefa01 | 104 | result = HelperReply::Error; |
e166785a | 105 | p+=3; |
723fd4c1 | 106 | sawNA=true; |
0272dd08 AJ |
107 | } |
108 | ||
dacb64b9 | 109 | for (; xisspace(*p); ++p); // skip whitespace |
0272dd08 AJ |
110 | } |
111 | ||
112 | const mb_size_t blobSize = (buf+len-p); | |
e166785a | 113 | other_.init(blobSize+1, blobSize+1); |
0272dd08 AJ |
114 | other_.append(p, blobSize); // remainders of the line. |
115 | ||
116 | // NULL-terminate so the helper callback handlers do not buffer-overrun | |
117 | other_.terminate(); | |
ab332e27 | 118 | |
723fd4c1 AJ |
119 | // Hack for backward-compatibility: Do not parse for kv-pairs on NA response |
120 | if (!sawNA) | |
121 | parseResponseKeys(); | |
7bbefa01 | 122 | |
723fd4c1 AJ |
123 | // Hack for backward-compatibility: BH and NA used to be a text message... |
124 | if (other().hasContent() && (sawNA || result == HelperReply::BrokenHelper)) { | |
fd7f26ea | 125 | notes.add("message",other().content()); |
7bbefa01 | 126 | modifiableOther().clean(); |
ab332e27 AJ |
127 | } |
128 | } | |
129 | ||
7bbefa01 | 130 | void |
05e52854 | 131 | HelperReply::parseResponseKeys() |
ab332e27 | 132 | { |
7bbefa01 | 133 | // parse a "key=value" pair off the 'other()' buffer. |
1d972b9a | 134 | while (other().hasContent()) { |
7bbefa01 | 135 | char *p = modifiableOther().content(); |
1d972b9a | 136 | while (*p && *p != '=' && *p != ' ') ++p; |
7bbefa01 AJ |
137 | if (*p != '=') |
138 | return; // done. Not a key. | |
139 | ||
24eac830 AJ |
140 | // whitespace between key and value is prohibited. |
141 | // workaround strwordtok() which skips whitespace prefix. | |
142 | if (xisspace(*(p+1))) | |
143 | return; // done. Not a key. | |
144 | ||
7bbefa01 AJ |
145 | *p = '\0'; |
146 | ++p; | |
147 | ||
cf9f0261 | 148 | const char *key = other().content(); |
7bbefa01 AJ |
149 | |
150 | // the value may be a quoted string or a token | |
05e52854 | 151 | const bool urlDecode = (*p != '"'); // check before moving p. |
7bbefa01 | 152 | char *v = strwordtok(NULL, &p); |
05e52854 | 153 | if (v != NULL && urlDecode && (p-v) > 2) // 1-octet %-escaped requires 3 bytes |
7bbefa01 | 154 | rfc1738_unescape(v); |
7bbefa01 | 155 | |
cf9f0261 | 156 | notes.add(key, v ? v : ""); // value can be empty, but must not be NULL |
7bbefa01 AJ |
157 | |
158 | modifiableOther().consume(p - other().content()); | |
24eac830 | 159 | modifiableOther().consumeWhitespacePrefix(); |
ab332e27 | 160 | } |
0272dd08 AJ |
161 | } |
162 | ||
163 | std::ostream & | |
164 | operator <<(std::ostream &os, const HelperReply &r) | |
165 | { | |
166 | os << "{result="; | |
154be258 | 167 | switch (r.result) { |
0272dd08 AJ |
168 | case HelperReply::Okay: |
169 | os << "OK"; | |
170 | break; | |
171 | case HelperReply::Error: | |
172 | os << "ERR"; | |
ab332e27 | 173 | break; |
0272dd08 AJ |
174 | case HelperReply::BrokenHelper: |
175 | os << "BH"; | |
176 | break; | |
177 | case HelperReply::TT: | |
178 | os << "TT"; | |
179 | break; | |
0272dd08 AJ |
180 | case HelperReply::Unknown: |
181 | os << "Unknown"; | |
182 | break; | |
183 | } | |
184 | ||
7bbefa01 | 185 | // dump the helper key=pair "notes" list |
cf9f0261 | 186 | if (!r.notes.empty()) { |
7bbefa01 | 187 | os << ", notes={"; |
7e6ef752 | 188 | os << r.notes.toString("; "); |
7bbefa01 AJ |
189 | os << "}"; |
190 | } | |
191 | ||
0272dd08 AJ |
192 | if (r.other().hasContent()) |
193 | os << ", other: \"" << r.other().content() << '\"'; | |
194 | ||
195 | os << '}'; | |
196 | ||
197 | return os; | |
198 | } |