]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ServiceConfig.cc
Merged from trunk
[thirdparty/squid.git] / src / adaptation / ServiceConfig.cc
1 /*
2 * Copyright (C) 1996-2015 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 method = parseMethod(method_point.termedBuf());
73 point = parseVectPoint(method_point.termedBuf());
74
75 // reset optional parameters in case we are reconfiguring
76 bypass = routing = false;
77
78 // handle optional service name=value parameters
79 bool grokkedUri = false;
80 bool onOverloadSet = false;
81 std::set<std::string> options;
82
83 while (char *option = ConfigParser::NextToken()) {
84 const char *name = option;
85 const char *value = "";
86 if (strcmp(option, "0") == 0) { // backward compatibility
87 name = "bypass";
88 value = "off";
89 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=off' option to disable service bypass");
90 } else if (strcmp(option, "1") == 0) { // backward compatibility
91 name = "bypass";
92 value = "on";
93 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=on' option to enable service bypass");
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 }
104 }
105
106 // Check if option is set twice
107 if (options.find(name) != options.end()) {
108 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
109 "Duplicate option \"" << name << "\" in adaptation service definition");
110 return false;
111 }
112 options.insert(name);
113
114 bool grokked = false;
115 if (strcmp(name, "bypass") == 0) {
116 grokked = grokBool(bypass, name, value);
117 } else if (strcmp(name, "routing") == 0)
118 grokked = grokBool(routing, name, value);
119 else if (strcmp(name, "uri") == 0)
120 grokked = grokkedUri = grokUri(value);
121 else if (strcmp(name, "ipv6") == 0) {
122 grokked = grokBool(ipv6, name, value);
123 if (grokked && ipv6 && !Ip::EnableIpv6)
124 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: IPv6 is disabled. ICAP service option ignored.");
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;
130 } else if (strncmp(name, "ssl", 3) == 0 || strncmp(name, "tls-", 4) == 0) {
131 #if !USE_OPENSSL
132 debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: adaptation option '" << name << "' requires --with-openssl. ICAP service option ignored.");
133 #else
134 // name prefix is "ssl" or "tls-"
135 std::string tmp = name + (name[0] == 's' ? 3 : 4);
136 tmp += "=";
137 tmp += value;
138 secure.parse(tmp.c_str());
139 secure.encryptTransport = true;
140 grokked = true;
141 #endif
142 } else
143 grokked = grokExtension(name, value);
144
145 if (!grokked)
146 return false;
147 }
148
149 // set default on-overload value if needed
150 if (!onOverloadSet)
151 onOverload = bypass ? srvBypass : srvWait;
152
153 // is the service URI set?
154 if (!grokkedUri) {
155 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
156 "No \"uri\" option in adaptation service definition");
157 return false;
158 }
159
160 debugs(3,5, cfg_filename << ':' << config_lineno << ": " <<
161 "adaptation_service " << key << ' ' <<
162 methodStr() << "_" << vectPointStr() << ' ' <<
163 bypass << routing << ' ' <<
164 uri);
165
166 return true;
167 }
168
169 bool
170 Adaptation::ServiceConfig::grokUri(const char *value)
171 {
172 // TODO: find core code that parses URLs and extracts various parts
173 // AYJ: most of this is duplicate of urlParse() in src/url.cc
174
175 if (!value || !*value) {
176 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
177 "empty adaptation service URI");
178 return false;
179 }
180
181 uri = value;
182
183 // extract scheme and use it as the service_configConfig protocol
184 const char *schemeSuffix = "://";
185 const String::size_type schemeEnd = uri.find(schemeSuffix);
186 if (schemeEnd != String::npos)
187 protocol=uri.substr(0,schemeEnd);
188
189 debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
190 "service protocol is " << protocol);
191
192 if (protocol.size() == 0)
193 return false;
194
195 // skip scheme
196 const char *s = uri.termedBuf() + protocol.size() + strlen(schemeSuffix);
197
198 const char *e;
199
200 bool have_port = false;
201
202 int len = 0;
203 if (*s == '[') {
204 const char *t;
205 if ((t = strchr(s, ']')) == NULL)
206 return false;
207
208 ++s;
209 len = t - s;
210 if ((e = strchr(t, ':')) != NULL) {
211 have_port = true;
212 } else if ((e = strchr(t, '/')) != NULL) {
213 have_port = false;
214 } else {
215 return false;
216 }
217 } else {
218 if ((e = strchr(s, ':')) != NULL) {
219 have_port = true;
220 } else if ((e = strchr(s, '/')) != NULL) {
221 have_port = false;
222 } else {
223 return false;
224 }
225 len = e - s;
226 }
227
228 host.limitInit(s, len);
229 #if USE_OPENSSL
230 if (secure.sslDomain.isEmpty())
231 secure.sslDomain.assign(host.rawBuf(), host.size());
232 #endif
233 s = e;
234
235 port = -1;
236 if (have_port) {
237 ++s;
238
239 if ((e = strchr(s, '/')) != NULL) {
240 char *t;
241 const unsigned long p = strtoul(s, &t, 0);
242
243 if (p > 65535) // port value is too high
244 return false;
245
246 port = static_cast<int>(p);
247
248 if (t != e) // extras after the port
249 return false;
250
251 s = e;
252
253 if (s[0] != '/')
254 return false;
255 }
256 }
257
258 // if no port, the caller may use service_configConfigs or supply the default if neeeded
259
260 ++s;
261 e = strchr(s, '\0');
262 len = e - s;
263
264 if (len > 1024) {
265 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
266 "long resource name (>1024), probably wrong");
267 }
268
269 resource.limitInit(s, len + 1);
270 return true;
271 }
272
273 bool
274 Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value)
275 {
276 if (!strcmp(value, "0") || !strcmp(value, "off"))
277 var = false;
278 else if (!strcmp(value, "1") || !strcmp(value, "on"))
279 var = true;
280 else {
281 debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " <<
282 "wrong value for boolean " << name << "; " <<
283 "'0', '1', 'on', or 'off' expected but got: " << value);
284 return false;
285 }
286
287 return true;
288 }
289
290 bool
291 Adaptation::ServiceConfig::grokLong(long &var, const char *name, const char *value)
292 {
293 char *bad = NULL;
294 const long p = strtol(value, &bad, 0);
295 if (p < 0 || bad == value) {
296 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
297 config_lineno << ": " << "wrong value for " << name << "; " <<
298 "a non-negative integer expected but got: " << value);
299 return false;
300 }
301 var = p;
302 return true;
303 }
304
305 bool
306 Adaptation::ServiceConfig::grokOnOverload(SrvBehaviour &var, const char *value)
307 {
308 if (strcmp(value, "block") == 0)
309 var = srvBlock;
310 else if (strcmp(value, "bypass") == 0)
311 var = srvBypass;
312 else if (strcmp(value, "wait") == 0)
313 var = srvWait;
314 else if (strcmp(value, "force") == 0)
315 var = srvForce;
316 else {
317 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
318 config_lineno << ": " << "wrong value for on-overload; " <<
319 "'block', 'bypass', 'wait' or 'force' expected but got: " << value);
320 return false;
321 }
322 return true;
323 }
324
325 bool
326 Adaptation::ServiceConfig::grokExtension(const char *name, const char *value)
327 {
328 // we do not accept extensions by default
329 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
330 "ERROR: unknown adaptation service option: " <<
331 name << '=' << value);
332 return false;
333 }
334