]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ServiceConfig.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / adaptation / ServiceConfig.cc
1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
7 */
8
9 /* DEBUG: section 93 Adaptation */
10
11 #include "squid.h"
12 #include "adaptation/ServiceConfig.h"
13 #include "ConfigParser.h"
14 #include "Debug.h"
15 #include "globals.h"
16 #include "ip/tools.h"
17 #include <set>
18
19 Adaptation::ServiceConfig::ServiceConfig():
20 port(-1), method(methodNone), point(pointNone),
21 bypass(false), maxConn(-1), onOverload(srvWait),
22 routing(false), ipv6(false)
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
58 if (!strcmp(t, "precache"))
59 return Adaptation::pointPreCache;
60
61 if (!strcmp(t, "postcache"))
62 return Adaptation::pointPostCache;
63
64 return Adaptation::pointNone;
65 }
66
67 bool
68 Adaptation::ServiceConfig::parse()
69 {
70 key = ConfigParser::NextToken();
71 String method_point = ConfigParser::NextToken();
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
78 method = parseMethod(method_point.termedBuf());
79 point = parseVectPoint(method_point.termedBuf());
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 }
85
86 // reset optional parameters in case we are reconfiguring
87 bypass = routing = false;
88
89 // handle optional service name=value parameters
90 bool grokkedUri = false;
91 bool onOverloadSet = false;
92 std::set<std::string> options;
93
94 while (char *option = ConfigParser::NextToken()) {
95 const char *name = option;
96 const char *value = "";
97 if (strcmp(option, "0") == 0) { // backward compatibility
98 name = "bypass";
99 value = "off";
100 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=off' option to disable service bypass");
101 } else if (strcmp(option, "1") == 0) { // backward compatibility
102 name = "bypass";
103 value = "on";
104 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=on' option to enable service bypass");
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 }
115 }
116
117 // Check if option is set twice
118 if (options.find(name) != options.end()) {
119 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << config_lineno << ": " <<
120 "Duplicate option \"" << name << "\" in adaptation service definition");
121 return false;
122 }
123 options.insert(name);
124
125 bool grokked = false;
126 if (strcmp(name, "bypass") == 0) {
127 grokked = grokBool(bypass, name, value);
128 } else if (strcmp(name, "routing") == 0)
129 grokked = grokBool(routing, name, value);
130 else if (strcmp(name, "uri") == 0)
131 grokked = grokkedUri = grokUri(value);
132 else if (strcmp(name, "ipv6") == 0) {
133 grokked = grokBool(ipv6, name, value);
134 if (grokked && ipv6 && !Ip::EnableIpv6)
135 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: IPv6 is disabled. ICAP service option ignored.");
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;
141 } else if (strcmp(name, "connection-encryption") == 0) {
142 bool encrypt = false;
143 grokked = grokBool(encrypt, name, value);
144 connectionEncryption.configure(encrypt);
145 } else if (strncmp(name, "ssl", 3) == 0 || strncmp(name, "tls-", 4) == 0) {
146 #if !USE_OPENSSL
147 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: adaptation option '" << name << "' requires --with-openssl. ICAP service option ignored.");
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());
154 grokked = true;
155 #endif
156 } else
157 grokked = grokExtension(name, value);
158
159 if (!grokked)
160 return false;
161 }
162
163 // set default on-overload value if needed
164 if (!onOverloadSet)
165 onOverload = bypass ? srvBypass : srvWait;
166
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
172 // is the service URI set?
173 if (!grokkedUri) {
174 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << config_lineno << ": " <<
175 "No \"uri\" option in adaptation service definition");
176 return false;
177 }
178
179 debugs(3,5, cfg_filename << ':' << config_lineno << ": " <<
180 "adaptation_service " << key << ' ' <<
181 methodStr() << "_" << vectPointStr() << ' ' <<
182 bypass << routing << ' ' <<
183 uri);
184
185 return true;
186 }
187
188 bool
189 Adaptation::ServiceConfig::grokUri(const char *value)
190 {
191 // TODO: find core code that parses URLs and extracts various parts
192 // AYJ: most of this is duplicate of AnyP::Uri::parse()
193
194 if (!value || !*value) {
195 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
196 "empty adaptation service URI");
197 return false;
198 }
199
200 uri = value;
201
202 // extract scheme and use it as the service_configConfig protocol
203 const char *schemeSuffix = "://";
204 const String::size_type schemeEnd = uri.find(schemeSuffix);
205 if (schemeEnd != String::npos)
206 protocol=uri.substr(0,schemeEnd);
207
208 debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
209 "service protocol is " << protocol);
210
211 if (protocol.size() == 0)
212 return false;
213
214 // skip scheme
215 const char *s = uri.termedBuf() + protocol.size() + strlen(schemeSuffix);
216
217 const char *e;
218
219 bool have_port = false;
220
221 int len = 0;
222 if (*s == '[') {
223 const char *t;
224 if ((t = strchr(s, ']')) == NULL)
225 return false;
226
227 ++s;
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 }
236 } else {
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;
245 }
246
247 host.assign(s, len);
248 #if USE_OPENSSL
249 if (secure.sslDomain.isEmpty())
250 secure.sslDomain.assign(host.rawBuf(), host.size());
251 #endif
252 s = e;
253
254 port = -1;
255 if (have_port) {
256 ++s;
257
258 if ((e = strchr(s, '/')) != NULL) {
259 char *t;
260 const unsigned long p = strtoul(s, &t, 0);
261
262 if (p > 65535) // port value is too high
263 return false;
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 needed
278
279 ++s;
280 e = strchr(s, '\0');
281 len = e - s;
282
283 if (len > 1024) {
284 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
285 "long resource name (>1024), probably wrong");
286 }
287
288 resource.assign(s, len + 1);
289 return true;
290 }
291
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;
297 else if (!strcmp(value, "1") || !strcmp(value, "on"))
298 var = true;
299 else {
300 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
301 "wrong value for boolean " << name << "; " <<
302 "'0', '1', 'on', or 'off' expected but got: " << value);
303 return false;
304 }
305
306 return true;
307 }
308
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
344 bool
345 Adaptation::ServiceConfig::grokExtension(const char *name, const char *value)
346 {
347 // we do not accept extensions by default
348 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
349 "ERROR: unknown adaptation service option: " <<
350 name << '=' << value);
351 return false;
352 }
353