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