]>
Commit | Line | Data |
---|---|---|
d81a31f1 | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 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" |
582c2af2 FC |
13 | #include "ConfigParser.h" |
14 | #include "Debug.h" | |
15 | #include "globals.h" | |
3496b53a | 16 | #include "ip/tools.h" |
c25c2836 | 17 | #include <set> |
d81a31f1 | 18 | |
26ac0430 | 19 | Adaptation::ServiceConfig::ServiceConfig(): |
f53969cc SM |
20 | port(-1), method(methodNone), point(pointNone), |
21 | bypass(false), maxConn(-1), onOverload(srvWait), | |
22 | routing(false), ipv6(false) | |
d81a31f1 AR |
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 | ||
a37d6070 | 58 | if (!strcmp(t, "precache")) |
d81a31f1 AR |
59 | return Adaptation::pointPreCache; |
60 | ||
a37d6070 | 61 | if (!strcmp(t, "postcache")) |
d81a31f1 AR |
62 | return Adaptation::pointPostCache; |
63 | ||
64 | return Adaptation::pointNone; | |
65 | } | |
66 | ||
67 | bool | |
68 | Adaptation::ServiceConfig::parse() | |
69 | { | |
2eceb328 CT |
70 | key = ConfigParser::NextToken(); |
71 | String method_point = ConfigParser::NextToken(); | |
29bf4910 CT |
72 | method = parseMethod(method_point.termedBuf()); |
73 | point = parseVectPoint(method_point.termedBuf()); | |
d81a31f1 | 74 | |
a22e6cd3 AR |
75 | // reset optional parameters in case we are reconfiguring |
76 | bypass = routing = false; | |
77 | ||
78 | // handle optional service name=value parameters | |
8e2fb579 | 79 | bool grokkedUri = false; |
2dba5b8e | 80 | bool onOverloadSet = false; |
c25c2836 | 81 | std::set<std::string> options; |
e29ccb57 | 82 | |
2eceb328 | 83 | while (char *option = ConfigParser::NextToken()) { |
c25c2836 CT |
84 | const char *name = option; |
85 | const char *value = ""; | |
a22e6cd3 | 86 | if (strcmp(option, "0") == 0) { // backward compatibility |
c25c2836 CT |
87 | name = "bypass"; |
88 | value = "off"; | |
aa7e2b35 | 89 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=off' option to disable service bypass"); |
c25c2836 CT |
90 | } else if (strcmp(option, "1") == 0) { // backward compatibility |
91 | name = "bypass"; | |
92 | value = "on"; | |
aa7e2b35 | 93 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: Please use 'bypass=on' option to enable service bypass"); |
c25c2836 CT |
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 | } | |
a22e6cd3 AR |
104 | } |
105 | ||
c25c2836 CT |
106 | // Check if option is set twice |
107 | if (options.find(name) != options.end()) { | |
e29ccb57 | 108 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
c25c2836 CT |
109 | "Duplicate option \"" << name << "\" in adaptation service definition"); |
110 | return false; | |
a22e6cd3 | 111 | } |
c25c2836 | 112 | options.insert(name); |
a22e6cd3 | 113 | |
a22e6cd3 | 114 | bool grokked = false; |
2dba5b8e | 115 | if (strcmp(name, "bypass") == 0) { |
a22e6cd3 | 116 | grokked = grokBool(bypass, name, value); |
2dba5b8e | 117 | } else if (strcmp(name, "routing") == 0) |
a22e6cd3 | 118 | grokked = grokBool(routing, name, value); |
8e2fb579 AR |
119 | else if (strcmp(name, "uri") == 0) |
120 | grokked = grokkedUri = grokUri(value); | |
3496b53a | 121 | else if (strcmp(name, "ipv6") == 0) { |
e6713f4e | 122 | grokked = grokBool(ipv6, name, value); |
3496b53a | 123 | if (grokked && ipv6 && !Ip::EnableIpv6) |
aa7e2b35 | 124 | debugs(3, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: IPv6 is disabled. ICAP service option ignored."); |
2dba5b8e CT |
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; | |
e1e90d26 AR |
130 | } else |
131 | grokked = grokExtension(name, value); | |
132 | ||
a22e6cd3 AR |
133 | if (!grokked) |
134 | return false; | |
135 | } | |
136 | ||
2dba5b8e CT |
137 | // set default on-overload value if needed |
138 | if (!onOverloadSet) | |
139 | onOverload = bypass ? srvBypass : srvWait; | |
140 | ||
c25c2836 CT |
141 | // is the service URI set? |
142 | if (!grokkedUri) { | |
e29ccb57 | 143 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
c25c2836 | 144 | "No \"uri\" option in adaptation service definition"); |
a22e6cd3 AR |
145 | return false; |
146 | } | |
d81a31f1 | 147 | |
a22e6cd3 | 148 | debugs(3,5, cfg_filename << ':' << config_lineno << ": " << |
e1381638 AJ |
149 | "adaptation_service " << key << ' ' << |
150 | methodStr() << "_" << vectPointStr() << ' ' << | |
151 | bypass << routing << ' ' << | |
152 | uri); | |
a22e6cd3 AR |
153 | |
154 | return true; | |
155 | } | |
156 | ||
157 | bool | |
158 | Adaptation::ServiceConfig::grokUri(const char *value) | |
159 | { | |
d81a31f1 | 160 | // TODO: find core code that parses URLs and extracts various parts |
5957a4c9 | 161 | // AYJ: most of this is duplicate of urlParse() in src/url.cc |
d81a31f1 | 162 | |
a22e6cd3 | 163 | if (!value || !*value) { |
fa84c01d | 164 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
a22e6cd3 AR |
165 | "empty adaptation service URI"); |
166 | return false; | |
167 | } | |
168 | ||
169 | uri = value; | |
170 | ||
d81a31f1 AR |
171 | // extract scheme and use it as the service_configConfig protocol |
172 | const char *schemeSuffix = "://"; | |
a22e6cd3 AR |
173 | const String::size_type schemeEnd = uri.find(schemeSuffix); |
174 | if (schemeEnd != String::npos) | |
b2f458b7 | 175 | protocol=uri.substr(0,schemeEnd); |
826a1fed | 176 | |
26ac0430 AJ |
177 | debugs(3, 5, HERE << cfg_filename << ':' << config_lineno << ": " << |
178 | "service protocol is " << protocol); | |
826a1fed FC |
179 | |
180 | if (protocol.size() == 0) | |
26ac0430 | 181 | return false; |
d81a31f1 AR |
182 | |
183 | // skip scheme | |
c1945e7d | 184 | const char *s = uri.termedBuf() + protocol.size() + strlen(schemeSuffix); |
d81a31f1 AR |
185 | |
186 | const char *e; | |
187 | ||
188 | bool have_port = false; | |
189 | ||
5957a4c9 AJ |
190 | int len = 0; |
191 | if (*s == '[') { | |
192 | const char *t; | |
193 | if ((t = strchr(s, ']')) == NULL) | |
194 | return false; | |
195 | ||
742a021b | 196 | ++s; |
5957a4c9 AJ |
197 | len = t - s; |
198 | if ((e = strchr(t, ':')) != NULL) { | |
199 | have_port = true; | |
200 | } else if ((e = strchr(t, '/')) != NULL) { | |
201 | have_port = false; | |
202 | } else { | |
203 | return false; | |
204 | } | |
792beadc | 205 | } else { |
5957a4c9 AJ |
206 | if ((e = strchr(s, ':')) != NULL) { |
207 | have_port = true; | |
208 | } else if ((e = strchr(s, '/')) != NULL) { | |
209 | have_port = false; | |
210 | } else { | |
211 | return false; | |
212 | } | |
213 | len = e - s; | |
d81a31f1 AR |
214 | } |
215 | ||
d81a31f1 AR |
216 | host.limitInit(s, len); |
217 | s = e; | |
218 | ||
26ac0430 | 219 | port = -1; |
d81a31f1 | 220 | if (have_port) { |
742a021b | 221 | ++s; |
d81a31f1 AR |
222 | |
223 | if ((e = strchr(s, '/')) != NULL) { | |
224 | char *t; | |
26ac0430 | 225 | const unsigned long p = strtoul(s, &t, 0); |
d81a31f1 | 226 | |
26ac0430 AJ |
227 | if (p > 65535) // port value is too high |
228 | return false; | |
d81a31f1 AR |
229 | |
230 | port = static_cast<int>(p); | |
231 | ||
232 | if (t != e) // extras after the port | |
233 | return false; | |
234 | ||
235 | s = e; | |
236 | ||
237 | if (s[0] != '/') | |
238 | return false; | |
239 | } | |
240 | } | |
241 | ||
242 | // if no port, the caller may use service_configConfigs or supply the default if neeeded | |
243 | ||
742a021b | 244 | ++s; |
d81a31f1 AR |
245 | e = strchr(s, '\0'); |
246 | len = e - s; | |
247 | ||
248 | if (len > 1024) { | |
fa84c01d | 249 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
26ac0430 | 250 | "long resource name (>1024), probably wrong"); |
d81a31f1 AR |
251 | } |
252 | ||
253 | resource.limitInit(s, len + 1); | |
a22e6cd3 AR |
254 | return true; |
255 | } | |
256 | ||
a22e6cd3 AR |
257 | bool |
258 | Adaptation::ServiceConfig::grokBool(bool &var, const char *name, const char *value) | |
259 | { | |
260 | if (!strcmp(value, "0") || !strcmp(value, "off")) | |
261 | var = false; | |
e1381638 | 262 | else if (!strcmp(value, "1") || !strcmp(value, "on")) |
a22e6cd3 AR |
263 | var = true; |
264 | else { | |
fa84c01d | 265 | debugs(3, DBG_CRITICAL, HERE << cfg_filename << ':' << config_lineno << ": " << |
a22e6cd3 AR |
266 | "wrong value for boolean " << name << "; " << |
267 | "'0', '1', 'on', or 'off' expected but got: " << value); | |
d81a31f1 AR |
268 | return false; |
269 | } | |
270 | ||
271 | return true; | |
272 | } | |
e1e90d26 | 273 | |
2dba5b8e CT |
274 | bool |
275 | Adaptation::ServiceConfig::grokLong(long &var, const char *name, const char *value) | |
276 | { | |
277 | char *bad = NULL; | |
278 | const long p = strtol(value, &bad, 0); | |
279 | if (p < 0 || bad == value) { | |
280 | debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << | |
281 | config_lineno << ": " << "wrong value for " << name << "; " << | |
282 | "a non-negative integer expected but got: " << value); | |
283 | return false; | |
284 | } | |
285 | var = p; | |
286 | return true; | |
287 | } | |
288 | ||
289 | bool | |
290 | Adaptation::ServiceConfig::grokOnOverload(SrvBehaviour &var, const char *value) | |
291 | { | |
292 | if (strcmp(value, "block") == 0) | |
293 | var = srvBlock; | |
294 | else if (strcmp(value, "bypass") == 0) | |
295 | var = srvBypass; | |
296 | else if (strcmp(value, "wait") == 0) | |
297 | var = srvWait; | |
298 | else if (strcmp(value, "force") == 0) | |
299 | var = srvForce; | |
300 | else { | |
301 | debugs(3, DBG_CRITICAL, "ERROR: " << cfg_filename << ':' << | |
302 | config_lineno << ": " << "wrong value for on-overload; " << | |
303 | "'block', 'bypass', 'wait' or 'force' expected but got: " << value); | |
304 | return false; | |
305 | } | |
306 | return true; | |
307 | } | |
308 | ||
e1e90d26 AR |
309 | bool |
310 | Adaptation::ServiceConfig::grokExtension(const char *name, const char *value) | |
311 | { | |
312 | // we do not accept extensions by default | |
6666da11 AR |
313 | debugs(3, DBG_CRITICAL, cfg_filename << ':' << config_lineno << ": " << |
314 | "ERROR: unknown adaptation service option: " << | |
315 | name << '=' << value); | |
e1e90d26 AR |
316 | return false; |
317 | } | |
f53969cc | 318 |