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