]>
Commit | Line | Data |
---|---|---|
22f3fd98 | 1 | |
2 | /* | |
190154cf | 3 | * $Id: cache_manager.cc,v 1.30 2003/08/10 11:00:42 robertc Exp $ |
22f3fd98 | 4 | * |
5 | * DEBUG: section 16 Cache Manager Objects | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
22f3fd98 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
22f3fd98 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
22f3fd98 | 34 | */ |
35 | ||
36 | #include "squid.h" | |
528b2c61 | 37 | #include "HttpReply.h" |
38 | #include "HttpRequest.h" | |
e6ccf245 | 39 | #include "Store.h" |
528b2c61 | 40 | #include "fde.h" |
22f3fd98 | 41 | |
42 | #define MGR_PASSWD_SZ 128 | |
43 | ||
62e76326 | 44 | typedef struct |
45 | { | |
22f3fd98 | 46 | StoreEntry *entry; |
47 | char *action; | |
63259c34 | 48 | char *user_name; |
22f3fd98 | 49 | char *passwd; |
62e76326 | 50 | } |
51 | ||
52 | cachemgrStateData; | |
22f3fd98 | 53 | |
62e76326 | 54 | typedef struct _action_table |
55 | { | |
22f3fd98 | 56 | char *action; |
57 | char *desc; | |
58 | OBJH *handler; | |
62e76326 | 59 | |
60 | struct | |
61 | { | |
62 | ||
63 | unsigned int pw_req: | |
64 | 1; | |
65 | ||
66 | unsigned int atomic: | |
67 | 1; | |
68 | } | |
69 | ||
70 | flags; | |
71 | ||
22f3fd98 | 72 | struct _action_table *next; |
62e76326 | 73 | } |
74 | ||
75 | action_table; | |
22f3fd98 | 76 | |
2ac76861 | 77 | static action_table *cachemgrFindAction(const char *action); |
63259c34 | 78 | static cachemgrStateData *cachemgrParseUrl(const char *url); |
190154cf | 79 | static void cachemgrParseHeaders(cachemgrStateData * mgr, const HttpRequest * request); |
22f3fd98 | 80 | static int cachemgrCheckPassword(cachemgrStateData *); |
2ac76861 | 81 | static void cachemgrStateFree(cachemgrStateData * mgr); |
22f3fd98 | 82 | static char *cachemgrPasswdGet(cachemgr_passwd *, const char *); |
2ac76861 | 83 | static const char *cachemgrActionProtection(const action_table * at); |
22f3fd98 | 84 | static OBJH cachemgrShutdown; |
85 | static OBJH cachemgrMenu; | |
d20b1cd0 | 86 | static OBJH cachemgrOfflineToggle; |
22f3fd98 | 87 | |
88 | action_table *ActionTable = NULL; | |
89 | ||
90 | void | |
1da3b90b | 91 | cachemgrRegister(const char *action, const char *desc, OBJH * handler, int pw_req_flag, int atomic) |
22f3fd98 | 92 | { |
93 | action_table *a; | |
94 | action_table **A; | |
62e76326 | 95 | |
e45867bd | 96 | if (cachemgrFindAction(action) != NULL) { |
62e76326 | 97 | debug(16, 3) ("cachemgrRegister: Duplicate '%s'\n", action); |
98 | return; | |
e45867bd | 99 | } |
62e76326 | 100 | |
528b2c61 | 101 | assert (strstr (" ", action) == NULL); |
e6ccf245 | 102 | a = (action_table *)xcalloc(1, sizeof(action_table)); |
22f3fd98 | 103 | a->action = xstrdup(action); |
104 | a->desc = xstrdup(desc); | |
105 | a->handler = handler; | |
1da3b90b | 106 | a->flags.pw_req = pw_req_flag; |
107 | a->flags.atomic = atomic; | |
62e76326 | 108 | |
109 | for (A = &ActionTable; *A; A = &(*A)->next) | |
110 | ||
111 | ; | |
22f3fd98 | 112 | *A = a; |
62e76326 | 113 | |
2ac76861 | 114 | debug(16, 3) ("cachemgrRegister: registered %s\n", action); |
22f3fd98 | 115 | } |
116 | ||
117 | static action_table * | |
118 | cachemgrFindAction(const char *action) | |
119 | { | |
120 | action_table *a; | |
62e76326 | 121 | |
22f3fd98 | 122 | for (a = ActionTable; a != NULL; a = a->next) { |
62e76326 | 123 | if (0 == strcmp(a->action, action)) |
124 | return a; | |
22f3fd98 | 125 | } |
62e76326 | 126 | |
22f3fd98 | 127 | return NULL; |
128 | } | |
129 | ||
130 | static cachemgrStateData * | |
63259c34 | 131 | cachemgrParseUrl(const char *url) |
22f3fd98 | 132 | { |
133 | int t; | |
134 | LOCAL_ARRAY(char, host, MAX_URL); | |
135 | LOCAL_ARRAY(char, request, MAX_URL); | |
136 | LOCAL_ARRAY(char, password, MAX_URL); | |
137 | action_table *a; | |
138 | cachemgrStateData *mgr = NULL; | |
7395afb8 | 139 | const char *prot; |
22f3fd98 | 140 | t = sscanf(url, "cache_object://%[^/]/%[^@]@%s", host, request, password); |
62e76326 | 141 | |
22f3fd98 | 142 | if (t < 2) { |
62e76326 | 143 | xstrncpy(request, "menu", MAX_URL); |
cd377065 | 144 | #ifdef _SQUID_OS2_ |
62e76326 | 145 | /* |
146 | * emx's sscanf insists of returning 2 because it sets request | |
147 | * to null | |
148 | */ | |
cd377065 | 149 | } else if (request[0] == '\0') { |
62e76326 | 150 | xstrncpy(request, "menu", MAX_URL); |
cd377065 | 151 | #endif |
62e76326 | 152 | |
22f3fd98 | 153 | } else if ((a = cachemgrFindAction(request)) == NULL) { |
62e76326 | 154 | debug(16, 1) ("cachemgrParseUrl: action '%s' not found\n", request); |
155 | return NULL; | |
7395afb8 | 156 | } else { |
62e76326 | 157 | prot = cachemgrActionProtection(a); |
158 | ||
159 | if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) { | |
160 | debug(16, 1) ("cachemgrParseUrl: action '%s' is %s\n", request, prot); | |
161 | return NULL; | |
162 | } | |
22f3fd98 | 163 | } |
62e76326 | 164 | |
63259c34 | 165 | /* set absent entries to NULL so we can test if they are present later */ |
e6ccf245 | 166 | mgr = (cachemgrStateData *)xcalloc(1, sizeof(cachemgrStateData)); |
62e76326 | 167 | |
63259c34 | 168 | mgr->user_name = NULL; |
62e76326 | 169 | |
63259c34 | 170 | mgr->passwd = t == 3 ? xstrdup(password) : NULL; |
62e76326 | 171 | |
22f3fd98 | 172 | mgr->action = xstrdup(request); |
62e76326 | 173 | |
22f3fd98 | 174 | return mgr; |
175 | } | |
176 | ||
63259c34 | 177 | static void |
190154cf | 178 | cachemgrParseHeaders(cachemgrStateData * mgr, const HttpRequest * request) |
63259c34 | 179 | { |
2ac76861 | 180 | const char *basic_cookie; /* base 64 _decoded_ user:passwd pair */ |
63259c34 | 181 | const char *passwd_del; |
182 | assert(mgr && request); | |
99edd1c3 | 183 | basic_cookie = httpHeaderGetAuth(&request->header, HDR_AUTHORIZATION, "Basic"); |
62e76326 | 184 | |
99edd1c3 | 185 | if (!basic_cookie) |
62e76326 | 186 | return; |
187 | ||
63259c34 | 188 | if (!(passwd_del = strchr(basic_cookie, ':'))) { |
62e76326 | 189 | debug(16, 1) ("cachemgrParseHeaders: unknown basic_cookie format '%s'\n", basic_cookie); |
190 | return; | |
63259c34 | 191 | } |
62e76326 | 192 | |
63259c34 | 193 | /* found user:password pair, reset old values */ |
194 | safe_free(mgr->user_name); | |
62e76326 | 195 | |
63259c34 | 196 | safe_free(mgr->passwd); |
62e76326 | 197 | |
2ac76861 | 198 | mgr->user_name = xstrdup(basic_cookie); |
62e76326 | 199 | |
63259c34 | 200 | mgr->user_name[passwd_del - basic_cookie] = '\0'; |
62e76326 | 201 | |
2ac76861 | 202 | mgr->passwd = xstrdup(passwd_del + 1); |
62e76326 | 203 | |
63259c34 | 204 | /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */ |
2ac76861 | 205 | debug(16, 9) ("cachemgrParseHeaders: got user: '%s' passwd: '%s'\n", mgr->user_name, mgr->passwd); |
63259c34 | 206 | } |
207 | ||
22f3fd98 | 208 | /* |
209 | * return 0 if mgr->password is good | |
210 | */ | |
211 | static int | |
212 | cachemgrCheckPassword(cachemgrStateData * mgr) | |
213 | { | |
214 | char *pwd = cachemgrPasswdGet(Config.passwd_list, mgr->action); | |
215 | action_table *a = cachemgrFindAction(mgr->action); | |
216 | assert(a != NULL); | |
62e76326 | 217 | |
22f3fd98 | 218 | if (pwd == NULL) |
62e76326 | 219 | return a->flags.pw_req; |
220 | ||
22f3fd98 | 221 | if (strcmp(pwd, "disable") == 0) |
62e76326 | 222 | return 1; |
223 | ||
22f3fd98 | 224 | if (strcmp(pwd, "none") == 0) |
62e76326 | 225 | return 0; |
226 | ||
63259c34 | 227 | if (!mgr->passwd) |
62e76326 | 228 | return 1; |
229 | ||
22f3fd98 | 230 | return strcmp(pwd, mgr->passwd); |
231 | } | |
232 | ||
233 | static void | |
2ac76861 | 234 | cachemgrStateFree(cachemgrStateData * mgr) |
22f3fd98 | 235 | { |
2ac76861 | 236 | safe_free(mgr->action); |
237 | safe_free(mgr->user_name); | |
238 | safe_free(mgr->passwd); | |
b771e69f | 239 | storeUnlockObject(mgr->entry); |
2ac76861 | 240 | xfree(mgr); |
22f3fd98 | 241 | } |
242 | ||
243 | void | |
190154cf | 244 | cachemgrStart(int fd, HttpRequest * request, StoreEntry * entry) |
22f3fd98 | 245 | { |
246 | cachemgrStateData *mgr = NULL; | |
247 | ErrorState *err = NULL; | |
22f3fd98 | 248 | action_table *a; |
249 | debug(16, 3) ("objectcacheStart: '%s'\n", storeUrl(entry)); | |
62e76326 | 250 | |
63259c34 | 251 | if ((mgr = cachemgrParseUrl(storeUrl(entry))) == NULL) { |
62e76326 | 252 | err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND); |
253 | err->url = xstrdup(storeUrl(entry)); | |
254 | errorAppendEntry(entry, err); | |
255 | entry->expires = squid_curtime; | |
256 | return; | |
22f3fd98 | 257 | } |
62e76326 | 258 | |
22f3fd98 | 259 | mgr->entry = entry; |
b771e69f | 260 | storeLockObject(entry); |
22f3fd98 | 261 | entry->expires = squid_curtime; |
63259c34 | 262 | debug(16, 5) ("CACHEMGR: %s requesting '%s'\n", |
62e76326 | 263 | fd_table[fd].ipaddr, mgr->action); |
63259c34 | 264 | /* get additional info from request headers */ |
265 | cachemgrParseHeaders(mgr, request); | |
22f3fd98 | 266 | /* Check password */ |
62e76326 | 267 | |
22f3fd98 | 268 | if (cachemgrCheckPassword(mgr) != 0) { |
62e76326 | 269 | /* build error message */ |
270 | ErrorState *err; | |
271 | HttpReply *rep; | |
272 | err = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED); | |
273 | /* warn if user specified incorrect password */ | |
274 | ||
275 | if (mgr->passwd) | |
276 | debug(16, 1) ("CACHEMGR: %s@%s: incorrect password for '%s'\n", | |
277 | mgr->user_name ? mgr->user_name : "<unknown>", | |
278 | fd_table[fd].ipaddr, mgr->action); | |
279 | else | |
280 | debug(16, 1) ("CACHEMGR: %s@%s: password needed for '%s'\n", | |
281 | mgr->user_name ? mgr->user_name : "<unknown>", | |
282 | fd_table[fd].ipaddr, mgr->action); | |
283 | ||
284 | err->request = requestLink(request); | |
285 | ||
286 | rep = errorBuildReply(err); | |
287 | ||
288 | errorStateFree(err); | |
289 | ||
290 | /* | |
291 | * add Authenticate header, use 'action' as a realm because | |
292 | * password depends on action | |
293 | */ | |
294 | httpHeaderPutAuth(&rep->header, "Basic", mgr->action); | |
295 | ||
296 | /* store the reply */ | |
297 | httpReplySwapOut(rep, entry); | |
298 | ||
299 | entry->expires = squid_curtime; | |
300 | ||
301 | entry->complete(); | |
302 | ||
303 | cachemgrStateFree(mgr); | |
304 | ||
305 | return; | |
22f3fd98 | 306 | } |
62e76326 | 307 | |
b771e69f | 308 | debug(16, 1) ("CACHEMGR: %s@%s requesting '%s'\n", |
62e76326 | 309 | mgr->user_name ? mgr->user_name : "<unknown>", |
310 | fd_table[fd].ipaddr, mgr->action); | |
22f3fd98 | 311 | /* retrieve object requested */ |
312 | a = cachemgrFindAction(mgr->action); | |
313 | assert(a != NULL); | |
62e76326 | 314 | |
1da3b90b | 315 | if (a->flags.atomic) |
62e76326 | 316 | storeBuffer(entry); |
317 | ||
cb69b4c7 | 318 | { |
62e76326 | 319 | http_version_t version; |
320 | HttpReply *rep = httpReplyCreate(); | |
321 | httpBuildVersion(&version, 1, 0); | |
322 | httpReplySetHeaders(rep, | |
323 | version, | |
324 | HTTP_OK, | |
325 | NULL, | |
326 | "text/plain", | |
327 | -1, /* C-Len */ | |
328 | squid_curtime, /* LMT */ | |
329 | squid_curtime); | |
330 | httpReplySwapOut(rep, entry); | |
cb69b4c7 | 331 | } |
62e76326 | 332 | |
22f3fd98 | 333 | a->handler(entry); |
62e76326 | 334 | |
1da3b90b | 335 | if (a->flags.atomic) { |
62e76326 | 336 | storeBufferFlush(entry); |
337 | entry->complete(); | |
1da3b90b | 338 | } |
62e76326 | 339 | |
22f3fd98 | 340 | cachemgrStateFree(mgr); |
341 | } | |
342 | ||
343 | static void | |
344 | cachemgrShutdown(StoreEntry * entryunused) | |
345 | { | |
346 | debug(16, 0) ("Shutdown by command.\n"); | |
347 | shut_down(0); | |
348 | } | |
349 | ||
d20b1cd0 | 350 | static void |
351 | cachemgrOfflineToggle(StoreEntry * sentry) | |
352 | { | |
353 | Config.onoff.offline = !Config.onoff.offline; | |
354 | debug(16, 0) ("offline_mode now %s.\n", | |
62e76326 | 355 | Config.onoff.offline ? "ON" : "OFF"); |
d20b1cd0 | 356 | storeAppendPrintf(sentry, "offline_mode is now %s\n", |
62e76326 | 357 | Config.onoff.offline ? "ON" : "OFF"); |
d20b1cd0 | 358 | } |
359 | ||
7395afb8 | 360 | static const char * |
2ac76861 | 361 | cachemgrActionProtection(const action_table * at) |
7395afb8 | 362 | { |
363 | char *pwd; | |
364 | assert(at); | |
365 | pwd = cachemgrPasswdGet(Config.passwd_list, at->action); | |
62e76326 | 366 | |
7395afb8 | 367 | if (!pwd) |
62e76326 | 368 | return at->flags.pw_req ? "hidden" : "public"; |
369 | ||
7395afb8 | 370 | if (!strcmp(pwd, "disable")) |
62e76326 | 371 | return "disabled"; |
372 | ||
7395afb8 | 373 | if (strcmp(pwd, "none") == 0) |
62e76326 | 374 | return "public"; |
375 | ||
7395afb8 | 376 | return "protected"; |
377 | } | |
378 | ||
22f3fd98 | 379 | static void |
2ac76861 | 380 | cachemgrMenu(StoreEntry * sentry) |
22f3fd98 | 381 | { |
7395afb8 | 382 | action_table *a; |
62e76326 | 383 | |
7395afb8 | 384 | for (a = ActionTable; a != NULL; a = a->next) { |
62e76326 | 385 | storeAppendPrintf(sentry, " %-22s\t%s\t%s\n", |
386 | a->action, a->desc, cachemgrActionProtection(a)); | |
7395afb8 | 387 | } |
22f3fd98 | 388 | } |
389 | ||
390 | static char * | |
391 | cachemgrPasswdGet(cachemgr_passwd * a, const char *action) | |
392 | { | |
393 | wordlist *w; | |
62e76326 | 394 | |
22f3fd98 | 395 | while (a != NULL) { |
62e76326 | 396 | for (w = a->actions; w != NULL; w = w->next) { |
397 | if (0 == strcmp(w->key, action)) | |
398 | return a->passwd; | |
399 | ||
400 | if (0 == strcmp(w->key, "all")) | |
401 | return a->passwd; | |
402 | } | |
403 | ||
404 | a = a->next; | |
22f3fd98 | 405 | } |
62e76326 | 406 | |
22f3fd98 | 407 | return NULL; |
408 | } | |
409 | ||
410 | void | |
411 | cachemgrInit(void) | |
412 | { | |
413 | cachemgrRegister("menu", | |
62e76326 | 414 | "This Cachemanager Menu", |
415 | cachemgrMenu, 0, 1); | |
22f3fd98 | 416 | cachemgrRegister("shutdown", |
62e76326 | 417 | "Shut Down the Squid Process", |
418 | cachemgrShutdown, 1, 1); | |
d20b1cd0 | 419 | cachemgrRegister("offline_toggle", |
62e76326 | 420 | "Toggle offline_mode setting", |
421 | cachemgrOfflineToggle, 1, 1); | |
22f3fd98 | 422 | } |