]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/ServiceConfig.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / adaptation / ServiceConfig.cc
CommitLineData
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 20Adaptation::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
26const char *
27Adaptation::ServiceConfig::methodStr() const
28{
29 return Adaptation::methodStr(method);
30}
31
32const char *
33Adaptation::ServiceConfig::vectPointStr() const
34{
35 return Adaptation::vectPointStr(point);
36}
37
38Adaptation::Method
39Adaptation::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
50Adaptation::VectPoint
51Adaptation::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
68bool
69Adaptation::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
189bool
190Adaptation::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
293bool
294Adaptation::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
310bool
311Adaptation::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
325bool
326Adaptation::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
345bool
346Adaptation::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