]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/ServiceConfig.cc
Bug 3349: Bad support for adaptation service URIs with '='
[thirdparty/squid.git] / src / adaptation / ServiceConfig.cc
1 /*
2 * DEBUG: section 93 Adaptation
3 */
4
5 #include "squid.h"
6 #include "ConfigParser.h"
7 #include "adaptation/ServiceConfig.h"
8 #include "ip/tools.h"
9 #include <set>
10
11 Adaptation::ServiceConfig::ServiceConfig():
12 port(-1), method(methodNone), point(pointNone),
13 bypass(false), maxConn(-1), onOverload(srvWait),
14 routing(false), ipv6(false)
15 {}
16
17 const char *
18 Adaptation::ServiceConfig::methodStr() const
19 {
20 return Adaptation::methodStr(method);
21 }
22
23 const char *
24 Adaptation::ServiceConfig::vectPointStr() const
25 {
26 return Adaptation::vectPointStr(point);
27 }
28
29 Adaptation::Method
30 Adaptation::ServiceConfig::parseMethod(const char *str) const
31 {
32 if (!strncasecmp(str, "REQMOD", 6))
33 return Adaptation::methodReqmod;
34
35 if (!strncasecmp(str, "RESPMOD", 7))
36 return Adaptation::methodRespmod;
37
38 return Adaptation::methodNone;
39 }
40
41 Adaptation::VectPoint
42 Adaptation::ServiceConfig::parseVectPoint(const char *service_configConfig) const
43 {
44 const char *t = service_configConfig;
45 const char *q = strchr(t, '_');
46
47 if (q)
48 t = q + 1;
49
50 if (!strcasecmp(t, "precache"))
51 return Adaptation::pointPreCache;
52
53 if (!strcasecmp(t, "postcache"))
54 return Adaptation::pointPostCache;
55
56 return Adaptation::pointNone;
57 }
58
59 bool
60 Adaptation::ServiceConfig::parse()
61 {
62 String method_point;
63
64 ConfigParser::ParseString(&key);
65 ConfigParser::ParseString(&method_point);
66 method = parseMethod(method_point.termedBuf());
67 point = parseVectPoint(method_point.termedBuf());
68
69 // reset optional parameters in case we are reconfiguring
70 bypass = routing = false;
71
72 // handle optional service name=value parameters
73 bool grokkedUri = false;
74 bool onOverloadSet = false;
75 std::set<std::string> options;
76
77 while (char *option = strtok(NULL, w_space)) {
78 const char *name = option;
79 const char *value = "";
80 if (strcmp(option, "0") == 0) { // backward compatibility
81 name = "bypass";
82 value = "off";
83 debugs(3, opt_parse_cfg_only?0:1, "UPGRADE: Please use 'bypass=off' option to disable service bypass");
84 } else if (strcmp(option, "1") == 0) { // backward compatibility
85 name = "bypass";
86 value = "on";
87 debugs(3, opt_parse_cfg_only?0:1, "UPGRADE: Please use 'bypass=on' option to enable service bypass");
88 } else {
89 char *eq = strstr(option, "=");
90 const char *sffx = strstr(option, "://");
91 if (!eq || (sffx && sffx < eq)) { //no "=" or has the form "icap://host?arg=val"
92 name = "uri";
93 value = option;
94 } else { // a normal name=value option
95 *eq = '\0'; // terminate option name
96 value = eq + 1; // skip '='
97 }
98 }
99
100 // Check if option is set twice
101 if (options.find(name) != options.end()) {
102 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
103 "Duplicate option \"" << name << "\" in adaptation service definition");
104 return false;
105 }
106 options.insert(name);
107
108 bool grokked = false;
109 if (strcmp(name, "bypass") == 0) {
110 grokked = grokBool(bypass, name, value);
111 } else if (strcmp(name, "routing") == 0)
112 grokked = grokBool(routing, name, value);
113 else if (strcmp(name, "uri") == 0)
114 grokked = grokkedUri = grokUri(value);
115 else if (strcmp(name, "ipv6") == 0) {
116 grokked = grokBool(ipv6, name, value);
117 if (grokked && ipv6 && !Ip::EnableIpv6)
118 debugs(3, DBG_IMPORTANT, "WARNING: IPv6 is disabled. ICAP service option ignored.");
119 } else if (strcmp(name, "max-conn") == 0)
120 grokked = grokLong(maxConn, name, value);
121 else if (strcmp(name, "on-overload") == 0) {
122 grokked = grokOnOverload(onOverload, value);
123 onOverloadSet = true;
124 } else
125 grokked = grokExtension(name, value);
126
127 if (!grokked)
128 return false;
129 }
130
131 // set default on-overload value if needed
132 if (!onOverloadSet)
133 onOverload = bypass ? srvBypass : srvWait;
134
135 // is the service URI set?
136 if (!grokkedUri) {
137 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
138 "No \"uri\" option in adaptation service definition");
139 return false;
140 }
141
142 debugs(3,5, cfg_filename << ':' << config_lineno << ": " <<
143 "adaptation_service " << key << ' ' <<
144 methodStr() << "_" << vectPointStr() << ' ' <<
145 bypass << routing << ' ' <<
146 uri);
147
148 return true;
149 }
150
151 bool
152 Adaptation::ServiceConfig::grokUri(const char *value)
153 {
154 // TODO: find core code that parses URLs and extracts various parts
155 // AYJ: most of this is duplicate of urlParse() in src/url.cc
156
157 if (!value || !*value) {
158 debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
159 "empty adaptation service URI");
160 return false;
161 }
162
163 uri = value;
164
165 // extract scheme and use it as the service_configConfig protocol
166 const char *schemeSuffix = "://";
167 const String::size_type schemeEnd = uri.find(schemeSuffix);
168 if (schemeEnd != String::npos)
169 protocol=uri.substr(0,schemeEnd);
170
171 debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " <<
172 "service protocol is " << protocol);
173
174 if (protocol.size() == 0)
175 return false;
176
177 // skip scheme
178 const char *s = uri.termedBuf() + protocol.size() + strlen(schemeSuffix);
179
180 const char *e;
181
182 bool have_port = false;
183
184 int len = 0;
185 if (*s == '[') {
186 const char *t;
187 if ((t = strchr(s, ']')) == NULL)
188 return false;
189
190 s++;
191 len = t - s;
192 if ((e = strchr(t, ':')) != NULL) {
193 have_port = true;
194 } else if ((e = strchr(t, '/')) != NULL) {
195 have_port = false;
196 } else {
197 return false;
198 }
199 } else {
200 if ((e = strchr(s, ':')) != NULL) {
201 have_port = true;
202 } else if ((e = strchr(s, '/')) != NULL) {
203 have_port = false;
204 } else {
205 return false;
206 }
207 len = e - s;
208 }
209
210 host.limitInit(s, len);
211 s = e;
212
213 port = -1;
214 if (have_port) {
215 s++;
216
217 if ((e = strchr(s, '/')) != NULL) {
218 char *t;
219 const unsigned long p = strtoul(s, &t, 0);
220
221 if (p > 65535) // port value is too high
222 return false;
223
224 port = static_cast<int>(p);
225
226 if (t != e) // extras after the port
227 return false;
228
229 s = e;
230
231 if (s[0] != '/')
232 return false;
233 }
234 }
235
236 // if no port, the caller may use service_configConfigs or supply the default if neeeded
237
238 s++;
239 e = strchr(s, '\0');
240 len = e - s;
241
242 if (len > 1024) {
243 debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
244 "long resource name (>1024), probably wrong");
245 }
246
247 resource.limitInit(s, len + 1);
248 return true;
249 }
250
251
252 bool
253 Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value)
254 {
255 if (!strcmp(value, "0") || !strcmp(value, "off"))
256 var = false;
257 else if (!strcmp(value, "1") || !strcmp(value, "on"))
258 var = true;
259 else {
260 debugs(3, 0, HERE << cfg_filename << ':' << config_lineno << ": " <<
261 "wrong value for boolean " << name << "; " <<
262 "'0', '1', 'on', or 'off' expected but got: " << value);
263 return false;
264 }
265
266 return true;
267 }
268
269 bool
270 Adaptation::ServiceConfig::grokLong(long &var, const char *name, const char *value)
271 {
272 char *bad = NULL;
273 const long p = strtol(value, &bad, 0);
274 if (p < 0 || bad == value) {
275 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
276 config_lineno << ": " << "wrong value for " << name << "; " <<
277 "a non-negative integer expected but got: " << value);
278 return false;
279 }
280 var = p;
281 return true;
282 }
283
284 bool
285 Adaptation::ServiceConfig::grokOnOverload(SrvBehaviour &var, const char *value)
286 {
287 if (strcmp(value, "block") == 0)
288 var = srvBlock;
289 else if (strcmp(value, "bypass") == 0)
290 var = srvBypass;
291 else if (strcmp(value, "wait") == 0)
292 var = srvWait;
293 else if (strcmp(value, "force") == 0)
294 var = srvForce;
295 else {
296 debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' <<
297 config_lineno << ": " << "wrong value for on-overload; " <<
298 "'block', 'bypass', 'wait' or 'force' expected but got: " << value);
299 return false;
300 }
301 return true;
302 }
303
304 bool
305 Adaptation::ServiceConfig::grokExtension(const char *name, const char *value)
306 {
307 // we do not accept extensions by default
308 debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " <<
309 "ERROR: unknown adaptation service option: " <<
310 name << '=' << value);
311 return false;
312 }