]>
Commit | Line | Data |
---|---|---|
d81a31f1 | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 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. | |
d81a31f1 AR |
7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 93 Adaptation */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
d81a31f1 | 12 | #include "adaptation/ServiceConfig.h" |
582c2af2 FC |
13 | #include "ConfigParser.h" |
14 | #include "Debug.h" | |
15 | #include "globals.h" | |
3496b53a | 16 | #include "ip/tools.h" |
c25c2836 | 17 | #include <set> |
d81a31f1 | 18 | |
26ac0430 | 19 | Adaptation::ServiceConfig::ServiceConfig(): |
f53969cc SM |
20 | port(-1), method(methodNone), point(pointNone), |
21 | bypass(false), maxConn(-1), onOverload(srvWait), | |
22 | routing(false), ipv6(false) | |
d81a31f1 AR |
23 | {} |
24 | ||
25 | const char * | |
26 | Adaptation::ServiceConfig::methodStr() const | |
27 | { | |
28 | return Adaptation::methodStr(method); | |
29 | } | |
30 | ||
31 | const char * | |
32 | Adaptation::ServiceConfig::vectPointStr() const | |
33 | { | |
34 | return Adaptation::vectPointStr(point); | |
35 | } | |
36 | ||
37 | Adaptation::Method | |
38 | Adaptation::ServiceConfig::parseMethod(const char *str) const | |
39 | { | |
40 | if (!strncasecmp(str, "REQMOD", 6)) | |
41 | return Adaptation::methodReqmod; | |
42 | ||
43 | if (!strncasecmp(str, "RESPMOD", 7)) | |
44 | return Adaptation::methodRespmod; | |
45 | ||
46 | return Adaptation::methodNone; | |
47 | } | |
48 | ||
49 | Adaptation::VectPoint | |
50 | Adaptation::ServiceConfig::parseVectPoint(const char *service_configConfig) const | |
51 | { | |
52 | const char *t = service_configConfig; | |
53 | const char *q = strchr(t, '_'); | |
54 | ||
55 | if (q) | |
56 | t = q + 1; | |
57 | ||
a37d6070 | 58 | if (!strcmp(t, "precache")) |
d81a31f1 AR |
59 | return Adaptation::pointPreCache; |
60 | ||
a37d6070 | 61 | if (!strcmp(t, "postcache")) |
d81a31f1 AR |
62 | return Adaptation::pointPostCache; |
63 | ||
64 | return Adaptation::pointNone; | |
65 | } | |
66 | ||
67 | bool | |
68 | Adaptation::ServiceConfig::parse() | |
69 | { | |
2eceb328 CT |
70 | key = ConfigParser::NextToken(); |
71 | String method_point = ConfigParser::NextToken(); | |
29bf4910 CT |
72 | method = parseMethod(method_point.termedBuf()); |
73 | point = parseVectPoint(method_point.termedBuf()); | |
d81a31f1 | 74 | |
a22e6cd3 AR |
75 | // reset optional parameters in case we are reconfiguring |
76 | bypass = routing = false; | |
77 | ||
78 | // handle optional service name=value parameters | |
8e2fb579 | 79 | bool grokkedUri = false; |
2dba5b8e | 80 | bool onOverloadSet = false; |
c25c2836 | 81 | std::set<std::string> options; |
e29ccb57 | 82 | |
2eceb328 | 83 | while (char *option = ConfigParser::NextToken()) { |
c25c2836 CT |
84 | const char *name = option; |
85 | const char *value = ""; | |
a22e6cd3 | 86 | if (strcmp(option, "0") == 0) { // backward compatibility |
c25c2836 CT |
87 | name = "bypass"; |
88 | value = "off"; | |
aa7e2b35 | 89 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=off' option to disable service bypass"); |
c25c2836 CT |
90 | } else if (strcmp(option, "1") == 0) { // backward compatibility |
91 | name = "bypass"; | |
92 | value = "on"; | |
aa7e2b35 | 93 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=on' option to enable service bypass"); |
c25c2836 CT |
94 | } else { |
95 | char *eq = strstr(option, "="); | |
96 | const char *sffx = strstr(option, "://"); | |
97 | if (!eq || (sffx && sffx < eq)) { //no "=" or has the form "icap://host?arg=val" | |
98 | name = "uri"; | |
99 | value = option; | |
100 | } else { // a normal name=value option | |
101 | *eq = '\0'; // terminate option name | |
102 | value = eq + 1; // skip '=' | |
103 | } | |
a22e6cd3 AR |
104 | } |
105 | ||
c25c2836 CT |
106 | // Check if option is set twice |
107 | if (options.find(name) != options.end()) { | |
e29ccb57 | 108 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
c25c2836 CT |
109 | "Duplicate option \"" << name << "\" in adaptation service definition"); |
110 | return false; | |
a22e6cd3 | 111 | } |
c25c2836 | 112 | options.insert(name); |
a22e6cd3 | 113 | |
a22e6cd3 | 114 | bool grokked = false; |
2dba5b8e | 115 | if (strcmp(name, "bypass") == 0) { |
a22e6cd3 | 116 | grokked = grokBool(bypass, name, value); |
2dba5b8e | 117 | } else if (strcmp(name, "routing") == 0) |
a22e6cd3 | 118 | grokked = grokBool(routing, name, value); |
8e2fb579 AR |
119 | else if (strcmp(name, "uri") == 0) |
120 | grokked = grokkedUri = grokUri(value); | |
3496b53a | 121 | else if (strcmp(name, "ipv6") == 0) { |
e6713f4e | 122 | grokked = grokBool(ipv6, name, value); |
3496b53a | 123 | if (grokked && ipv6 && !Ip::EnableIpv6) |
aa7e2b35 | 124 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: IPv6 is disabled. ICAP service option ignored."); |
2dba5b8e CT |
125 | } else if (strcmp(name, "max-conn") == 0) |
126 | grokked = grokLong(maxConn, name, value); | |
127 | else if (strcmp(name, "on-overload") == 0) { | |
128 | grokked = grokOnOverload(onOverload, value); | |
129 | onOverloadSet = true; | |
88df846b CT |
130 | } else if (strcmp(name, "connection-encryption") == 0) { |
131 | bool encrypt; | |
132 | grokked = grokBool(encrypt, name, value); | |
133 | connectionEncryption.configure(encrypt); | |
1b091aec CT |
134 | } else if (strncmp(name, "ssl", 3) == 0 || strncmp(name, "tls-", 4) == 0) { |
135 | #if !USE_OPENSSL | |
2f32154e | 136 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: adaptation option '" << name << "' requires --with-openssl. ICAP service option ignored."); |
1b091aec CT |
137 | #else |
138 | // name prefix is "ssl" or "tls-" | |
139 | std::string tmp = name + (name[0] == 's' ? 3 : 4); | |
140 | tmp += "="; | |
141 | tmp += value; | |
142 | secure.parse(tmp.c_str()); | |
1b091aec CT |
143 | grokked = true; |
144 | #endif | |
e1e90d26 AR |
145 | } else |
146 | grokked = grokExtension(name, value); | |
147 | ||
a22e6cd3 AR |
148 | if (!grokked) |
149 | return false; | |
150 | } | |
151 | ||
2dba5b8e CT |
152 | // set default on-overload value if needed |
153 | if (!onOverloadSet) | |
154 | onOverload = bypass ? srvBypass : srvWait; | |
155 | ||
b05d749d AJ |
156 | // disable the TLS NPN extension if encrypted. |
157 | // Squid advertises "http/1.1", which is wrong for ICAPS. | |
158 | if (secure.encryptTransport) | |
159 | secure.parse("no-npn"); | |
160 | ||
c25c2836 CT |
161 | // is the service URI set? |
162 | if (!grokkedUri) { | |
e29ccb57 | 163 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
c25c2836 | 164 | "No \"uri\" option in adaptation service definition"); |
a22e6cd3 AR |
165 | return false; |
166 | } | |
d81a31f1 | 167 | |
a22e6cd3 | 168 | debugs(3,5, cfg_filename << ':' << config_lineno << ": " << |
e1381638 AJ |
169 | "adaptation_service " << key << ' ' << |
170 | methodStr() << "_" << vectPointStr() << ' ' << | |
171 | bypass << routing << ' ' << | |
172 | uri); | |
a22e6cd3 AR |
173 | |
174 | return true; | |
175 | } | |
176 | ||
177 | bool | |
178 | Adaptation::ServiceConfig::grokUri(const char *value) | |
179 | { | |
d81a31f1 | 180 | // TODO: find core code that parses URLs and extracts various parts |
5957a4c9 | 181 | // AYJ: most of this is duplicate of urlParse() in src/url.cc |
d81a31f1 | 182 | |
a22e6cd3 | 183 | if (!value || !*value) { |
fa84c01d | 184 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
a22e6cd3 AR |
185 | "empty adaptation service URI"); |
186 | return false; | |
187 | } | |
188 | ||
189 | uri = value; | |
190 | ||
d81a31f1 AR |
191 | // extract scheme and use it as the service_configConfig protocol |
192 | const char *schemeSuffix = "://"; | |
a22e6cd3 AR |
193 | const String::size_type schemeEnd = uri.find(schemeSuffix); |
194 | if (schemeEnd != String::npos) | |
b2f458b7 | 195 | protocol=uri.substr(0,schemeEnd); |
826a1fed | 196 | |
26ac0430 AJ |
197 | debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " << |
198 | "service protocol is " << protocol); | |
826a1fed FC |
199 | |
200 | if (protocol.size() == 0) | |
26ac0430 | 201 | return false; |
d81a31f1 AR |
202 | |
203 | // skip scheme | |
c1945e7d | 204 | const char *s = uri.termedBuf() + protocol.size() + strlen(schemeSuffix); |
d81a31f1 AR |
205 | |
206 | const char *e; | |
207 | ||
208 | bool have_port = false; | |
209 | ||
5957a4c9 AJ |
210 | int len = 0; |
211 | if (*s == '[') { | |
212 | const char *t; | |
213 | if ((t = strchr(s, ']')) == NULL) | |
214 | return false; | |
215 | ||
742a021b | 216 | ++s; |
5957a4c9 AJ |
217 | len = t - s; |
218 | if ((e = strchr(t, ':')) != NULL) { | |
219 | have_port = true; | |
220 | } else if ((e = strchr(t, '/')) != NULL) { | |
221 | have_port = false; | |
222 | } else { | |
223 | return false; | |
224 | } | |
792beadc | 225 | } else { |
5957a4c9 AJ |
226 | if ((e = strchr(s, ':')) != NULL) { |
227 | have_port = true; | |
228 | } else if ((e = strchr(s, '/')) != NULL) { | |
229 | have_port = false; | |
230 | } else { | |
231 | return false; | |
232 | } | |
233 | len = e - s; | |
d81a31f1 AR |
234 | } |
235 | ||
d81a31f1 | 236 | host.limitInit(s, len); |
1b091aec CT |
237 | #if USE_OPENSSL |
238 | if (secure.sslDomain.isEmpty()) | |
239 | secure.sslDomain.assign(host.rawBuf(), host.size()); | |
240 | #endif | |
d81a31f1 AR |
241 | s = e; |
242 | ||
26ac0430 | 243 | port = -1; |
d81a31f1 | 244 | if (have_port) { |
742a021b | 245 | ++s; |
d81a31f1 AR |
246 | |
247 | if ((e = strchr(s, '/')) != NULL) { | |
248 | char *t; | |
26ac0430 | 249 | const unsigned long p = strtoul(s, &t, 0); |
d81a31f1 | 250 | |
26ac0430 AJ |
251 | if (p > 65535) // port value is too high |
252 | return false; | |
d81a31f1 AR |
253 | |
254 | port = static_cast<int>(p); | |
255 | ||
256 | if (t != e) // extras after the port | |
257 | return false; | |
258 | ||
259 | s = e; | |
260 | ||
261 | if (s[0] != '/') | |
262 | return false; | |
263 | } | |
264 | } | |
265 | ||
266 | // if no port, the caller may use service_configConfigs or supply the default if neeeded | |
267 | ||
742a021b | 268 | ++s; |
d81a31f1 AR |
269 | e = strchr(s, '\0'); |
270 | len = e - s; | |
271 | ||
272 | if (len > 1024) { | |
fa84c01d | 273 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
26ac0430 | 274 | "long resource name (>1024), probably wrong"); |
d81a31f1 AR |
275 | } |
276 | ||
277 | resource.limitInit(s, len + 1); | |
a22e6cd3 AR |
278 | return true; |
279 | } | |
280 | ||
a22e6cd3 AR |
281 | bool |
282 | Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value) | |
283 | { | |
284 | if (!strcmp(value, "0") || !strcmp(value, "off")) | |
285 | var = false; | |
e1381638 | 286 | else if (!strcmp(value, "1") || !strcmp(value, "on")) |
a22e6cd3 AR |
287 | var = true; |
288 | else { | |
fa84c01d | 289 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
a22e6cd3 AR |
290 | "wrong value for boolean " << name << "; " << |
291 | "'0', '1', 'on', or 'off' expected but got: " << value); | |
d81a31f1 AR |
292 | return false; |
293 | } | |
294 | ||
295 | return true; | |
296 | } | |
e1e90d26 | 297 | |
2dba5b8e CT |
298 | bool |
299 | Adaptation::ServiceConfig::grokLong(long &var, const char *name, const char *value) | |
300 | { | |
301 | char *bad = NULL; | |
302 | const long p = strtol(value, &bad, 0); | |
303 | if (p < 0 || bad == value) { | |
304 | debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << | |
305 | config_lineno << ": " << "wrong value for " << name << "; " << | |
306 | "a non-negative integer expected but got: " << value); | |
307 | return false; | |
308 | } | |
309 | var = p; | |
310 | return true; | |
311 | } | |
312 | ||
313 | bool | |
314 | Adaptation::ServiceConfig::grokOnOverload(SrvBehaviour &var, const char *value) | |
315 | { | |
316 | if (strcmp(value, "block") == 0) | |
317 | var = srvBlock; | |
318 | else if (strcmp(value, "bypass") == 0) | |
319 | var = srvBypass; | |
320 | else if (strcmp(value, "wait") == 0) | |
321 | var = srvWait; | |
322 | else if (strcmp(value, "force") == 0) | |
323 | var = srvForce; | |
324 | else { | |
325 | debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << | |
326 | config_lineno << ": " << "wrong value for on-overload; " << | |
327 | "'block', 'bypass', 'wait' or 'force' expected but got: " << value); | |
328 | return false; | |
329 | } | |
330 | return true; | |
331 | } | |
332 | ||
e1e90d26 AR |
333 | bool |
334 | Adaptation::ServiceConfig::grokExtension(const char *name, const char *value) | |
335 | { | |
336 | // we do not accept extensions by default | |
6666da11 AR |
337 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
338 | "ERROR: unknown adaptation service option: " << | |
339 | name << '=' << value); | |
e1e90d26 AR |
340 | return false; |
341 | } | |
f53969cc | 342 |