]>
Commit | Line | Data |
---|---|---|
4a9a952e | 1 | |
30a4f2a8 | 2 | /* |
34266cde | 3 | * $Id: errorpage.cc,v 1.214 2006/05/19 17:19:09 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 4 Error Generation | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 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. | |
30a4f2a8 | 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 | * |
30a4f2a8 | 34 | */ |
1e74c110 | 35 | |
fe40a877 | 36 | /* |
4e3f29eb | 37 | * Abstract: These routines are used to generate error messages to be |
38 | * sent to clients. The error type is used to select between | |
39 | * the various message formats. (formats are stored in the | |
40 | * Config.errorDirectory) | |
fe40a877 | 41 | */ |
42 | ||
44a47c6e | 43 | #include "squid.h" |
f5691f9c | 44 | #include "AuthUserRequest.h" |
985c86bc | 45 | #include "SquidTime.h" |
e6ccf245 | 46 | #include "Store.h" |
528b2c61 | 47 | #include "HttpReply.h" |
48 | #include "HttpRequest.h" | |
49 | #include "MemObject.h" | |
50 | #include "fde.h" | |
0eb49b6d | 51 | #include "MemBuf.h" |
985c86bc | 52 | #include "URLScheme.h" |
d295d770 | 53 | #include "wordlist.h" |
02922e76 | 54 | |
55 | /* local types */ | |
56 | ||
62e76326 | 57 | typedef struct |
58 | { | |
02922e76 | 59 | int id; |
60 | char *page_name; | |
62e76326 | 61 | } |
62 | ||
63 | ErrorDynamicPageInfo; | |
02922e76 | 64 | |
65 | /* local constant and vars */ | |
66 | ||
1d803566 | 67 | /* |
68 | * note: hard coded error messages are not appended with %S automagically | |
69 | * to give you more control on the format | |
70 | */ | |
62e76326 | 71 | |
72 | static const struct | |
73 | { | |
1afe05c5 | 74 | int type; /* and page_id */ |
2ac76861 | 75 | const char *text; |
62e76326 | 76 | } |
77 | ||
78 | error_hard_text[] = { | |
79 | ||
80 | { | |
81 | ERR_SQUID_SIGNATURE, | |
82 | "\n<BR clear=\"all\">\n" | |
83 | "<HR noshade size=\"1px\">\n" | |
84 | "<ADDRESS>\n" | |
85 | "Generated %T by %h (%s)\n" | |
86 | "</ADDRESS>\n" | |
87 | "</BODY></HTML>\n" | |
88 | }, | |
89 | { | |
90 | TCP_RESET, | |
91 | "reset" | |
92 | } | |
93 | }; | |
02922e76 | 94 | |
4ff59fc7 | 95 | static Vector<ErrorDynamicPageInfo *> ErrorDynamicPages; |
02922e76 | 96 | |
97 | /* local prototypes */ | |
98 | ||
2ac76861 | 99 | static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text); |
02922e76 | 100 | static char **error_text = NULL; |
101 | static int error_page_count = 0; | |
9b312a19 | 102 | |
02922e76 | 103 | static char *errorTryLoadText(const char *page_name, const char *dir); |
104 | static char *errorLoadText(const char *page_name); | |
1d803566 | 105 | static const char *errorFindHardText(err_type type); |
c68e9c6b | 106 | static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name); |
107 | static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info); | |
032785bf | 108 | static MemBuf *errorBuildContent(ErrorState * err); |
b5fb34f1 | 109 | static int errorDump(ErrorState * err, MemBuf * mb); |
fe40a877 | 110 | static const char *errorConvert(char token, ErrorState * err); |
9b312a19 | 111 | static CWCB errorSendComplete; |
1e74c110 | 112 | |
e6ccf245 | 113 | |
114 | err_type &operator++ (err_type &anErr) | |
115 | { | |
1f1ae50a | 116 | int tmp = (int)anErr; |
117 | anErr = (err_type)(++tmp); | |
e6ccf245 | 118 | return anErr; |
119 | } | |
4ff59fc7 | 120 | |
62e76326 | 121 | int operator - (err_type const &anErr, err_type const &anErr2) |
122 | { | |
4ff59fc7 | 123 | return (int)anErr - (int)anErr2; |
124 | } | |
125 | ||
fe40a877 | 126 | /* |
127 | * Function: errorInitialize | |
128 | * | |
1d803566 | 129 | * Abstract: This function finds the error messages formats, and stores |
fe40a877 | 130 | * them in error_text[]; |
131 | * | |
132 | * Global effects: | |
133 | * error_text[] - is modified | |
134 | */ | |
b8d8561b | 135 | void |
0673c0ba | 136 | errorInitialize(void) |
fa966b74 | 137 | { |
728da2ee | 138 | err_type i; |
1d803566 | 139 | const char *text; |
4ff59fc7 | 140 | error_page_count = ERR_MAX + ErrorDynamicPages.size(); |
e6ccf245 | 141 | error_text = static_cast<char **>(xcalloc(error_page_count, sizeof(char *))); |
62e76326 | 142 | |
e6ccf245 | 143 | for (i = ERR_NONE, ++i; i < error_page_count; ++i) { |
62e76326 | 144 | safe_free(error_text[i]); |
145 | /* hard-coded ? */ | |
146 | ||
147 | if ((text = errorFindHardText(i))) | |
148 | error_text[i] = xstrdup(text); | |
149 | else if (i < ERR_MAX) { | |
150 | /* precompiled ? */ | |
151 | error_text[i] = errorLoadText(err_type_str[i]); | |
152 | } else { | |
153 | /* dynamic */ | |
154 | ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX]; | |
155 | assert(info && info->id == i && info->page_name); | |
156 | ||
157 | if (strchr(info->page_name, ':') == NULL) { | |
158 | /* Not on redirected errors... */ | |
159 | error_text[i] = errorLoadText(info->page_name); | |
160 | } | |
161 | } | |
1d803566 | 162 | } |
163 | } | |
164 | ||
c68e9c6b | 165 | void |
166 | errorClean(void) | |
167 | { | |
168 | if (error_text) { | |
62e76326 | 169 | int i; |
170 | ||
171 | for (i = ERR_NONE + 1; i < error_page_count; i++) | |
172 | safe_free(error_text[i]); | |
173 | ||
174 | safe_free(error_text); | |
c68e9c6b | 175 | } |
62e76326 | 176 | |
4ff59fc7 | 177 | while (ErrorDynamicPages.size()) |
62e76326 | 178 | errorDynamicPageInfoDestroy(ErrorDynamicPages.pop_back()); |
179 | ||
c68e9c6b | 180 | error_page_count = 0; |
181 | } | |
182 | ||
1d803566 | 183 | static const char * |
184 | errorFindHardText(err_type type) | |
185 | { | |
186 | int i; | |
62e76326 | 187 | |
1d803566 | 188 | for (i = 0; i < error_hard_text_count; i++) |
62e76326 | 189 | if (error_hard_text[i].type == type) |
190 | return error_hard_text[i].text; | |
191 | ||
1d803566 | 192 | return NULL; |
193 | } | |
194 | ||
195 | ||
196 | static char * | |
02922e76 | 197 | errorLoadText(const char *page_name) |
1d803566 | 198 | { |
199 | /* test configured location */ | |
02922e76 | 200 | char *text = errorTryLoadText(page_name, Config.errorDirectory); |
1d803566 | 201 | /* test default location if failed */ |
62e76326 | 202 | |
1d803566 | 203 | if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR)) |
62e76326 | 204 | text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR); |
205 | ||
1d803566 | 206 | /* giving up if failed */ |
207 | if (!text) | |
62e76326 | 208 | fatal("failed to find or read error text file."); |
209 | ||
1d803566 | 210 | return text; |
211 | } | |
212 | ||
213 | static char * | |
02922e76 | 214 | errorTryLoadText(const char *page_name, const char *dir) |
1d803566 | 215 | { |
9b312a19 | 216 | int fd; |
217 | char path[MAXPATHLEN]; | |
e8f6c5c7 | 218 | char buf[4096]; |
1d803566 | 219 | char *text; |
e8f6c5c7 | 220 | ssize_t len; |
221 | MemBuf textbuf; | |
1d803566 | 222 | |
137ee196 | 223 | snprintf(path, sizeof(path), "%s/%s", dir, page_name); |
c4aefe96 | 224 | fd = file_open(path, O_RDONLY | O_TEXT); |
e8f6c5c7 | 225 | |
226 | if (fd < 0) { | |
62e76326 | 227 | debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror()); |
228 | return NULL; | |
1d803566 | 229 | } |
62e76326 | 230 | |
2fe7eff9 | 231 | textbuf.init(); |
62e76326 | 232 | |
e8f6c5c7 | 233 | while((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) { |
2fe7eff9 | 234 | textbuf.append(buf, len); |
e8f6c5c7 | 235 | } |
62e76326 | 236 | |
e8f6c5c7 | 237 | if (len < 0) { |
62e76326 | 238 | debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n", |
239 | path, xstrerror()); | |
1e74c110 | 240 | } |
62e76326 | 241 | |
1d803566 | 242 | file_close(fd); |
e8f6c5c7 | 243 | |
244 | if (strstr(textbuf.buf, "%s") == NULL) | |
2fe7eff9 | 245 | textbuf.append("%S", 2); /* add signature */ |
e8f6c5c7 | 246 | |
247 | /* Shrink memory size down to exact size. MemBuf has a tencendy | |
248 | * to be rather large.. | |
249 | */ | |
250 | text = xstrdup(textbuf.buf); | |
62e76326 | 251 | |
2fe7eff9 | 252 | textbuf.clean(); |
62e76326 | 253 | |
1d803566 | 254 | return text; |
6eb42cae | 255 | } |
256 | ||
02922e76 | 257 | static ErrorDynamicPageInfo * |
258 | errorDynamicPageInfoCreate(int id, const char *page_name) | |
259 | { | |
4ff59fc7 | 260 | ErrorDynamicPageInfo *info = new ErrorDynamicPageInfo; |
02922e76 | 261 | info->id = id; |
262 | info->page_name = xstrdup(page_name); | |
263 | return info; | |
264 | } | |
265 | ||
266 | static void | |
1afe05c5 | 267 | errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info) |
02922e76 | 268 | { |
269 | assert(info); | |
270 | xfree(info->page_name); | |
4ff59fc7 | 271 | delete info; |
02922e76 | 272 | } |
273 | ||
76cdc28d | 274 | static int |
275 | errorPageId(const char *page_name) | |
276 | { | |
528b2c61 | 277 | for (int i = 0; i < ERR_MAX; i++) { |
62e76326 | 278 | if (strcmp(err_type_str[i], page_name) == 0) |
279 | return i; | |
76cdc28d | 280 | } |
62e76326 | 281 | |
190154cf | 282 | for (size_t j = 0; j < ErrorDynamicPages.size(); j++) { |
283 | if (strcmp(ErrorDynamicPages.items[j]->page_name, page_name) == 0) | |
284 | return j + ERR_MAX; | |
76cdc28d | 285 | } |
62e76326 | 286 | |
76cdc28d | 287 | return ERR_NONE; |
288 | } | |
289 | ||
e6ccf245 | 290 | err_type |
02922e76 | 291 | errorReservePageId(const char *page_name) |
292 | { | |
76cdc28d | 293 | ErrorDynamicPageInfo *info; |
294 | int id = errorPageId(page_name); | |
62e76326 | 295 | |
76cdc28d | 296 | if (id == ERR_NONE) { |
62e76326 | 297 | info = errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.size(), page_name); |
298 | ErrorDynamicPages.push_back(info); | |
299 | id = info->id; | |
76cdc28d | 300 | } |
62e76326 | 301 | |
e6ccf245 | 302 | return (err_type)id; |
02922e76 | 303 | } |
304 | ||
c68e9c6b | 305 | static const char * |
306 | errorPageName(int pageId) | |
53ad48e6 | 307 | { |
c68e9c6b | 308 | if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */ |
62e76326 | 309 | return err_type_str[pageId]; |
310 | ||
4ff59fc7 | 311 | if (pageId >= ERR_MAX && pageId - ERR_MAX < (ssize_t)ErrorDynamicPages.size()) |
62e76326 | 312 | return ErrorDynamicPages.items[pageId - ERR_MAX]->page_name; |
313 | ||
c68e9c6b | 314 | return "ERR_UNKNOWN"; /* should not happen */ |
53ad48e6 | 315 | } |
316 | ||
fe40a877 | 317 | /* |
318 | * Function: errorCon | |
319 | * | |
320 | * Abstract: This function creates a ErrorState object. | |
321 | */ | |
322 | ErrorState * | |
728da2ee | 323 | errorCon(err_type type, http_status status) |
fe40a877 | 324 | { |
28c60158 | 325 | ErrorState *err; |
72711e31 | 326 | err = cbdataAlloc(ErrorState); |
1afe05c5 | 327 | err->page_id = type; /* has to be reset manually if needed */ |
fe40a877 | 328 | err->type = type; |
29b8d8d6 | 329 | err->httpStatus = status; |
fe40a877 | 330 | return err; |
331 | } | |
332 | ||
333 | /* | |
334 | * Function: errorAppendEntry | |
335 | * | |
336 | * Arguments: err - This object is destroyed after use in this function. | |
337 | * | |
338 | * Abstract: This function generates a error page from the info contained | |
4e3f29eb | 339 | * by 'err' and then stores the text in the specified store |
340 | * entry. This function should only be called by ``server | |
341 | * side routines'' which need to communicate errors to the | |
79e0dc20 | 342 | * client side. It should also be called from client_side.c |
343 | * because we now support persistent connections, and | |
344 | * cannot assume that we can immediately write to the socket | |
345 | * for an error. | |
fe40a877 | 346 | */ |
347 | void | |
348 | errorAppendEntry(StoreEntry * entry, ErrorState * err) | |
349 | { | |
cb69b4c7 | 350 | HttpReply *rep; |
e4a67a80 | 351 | assert(entry->mem_obj != NULL); |
528b2c61 | 352 | assert (entry->isEmpty()); |
62e76326 | 353 | |
1ea80e98 | 354 | if (entry->store_status != STORE_PENDING) { |
62e76326 | 355 | /* |
356 | * If the entry is not STORE_PENDING, then no clients | |
357 | * care about it, and we don't need to generate an | |
358 | * error message | |
359 | */ | |
360 | assert(EBIT_TEST(entry->flags, ENTRY_ABORTED)); | |
6ea35247 | 361 | assert(entry->mem_obj->nclients == 0); |
62e76326 | 362 | errorStateFree(err); |
363 | return; | |
1ea80e98 | 364 | } |
62e76326 | 365 | |
76cdc28d | 366 | if (err->page_id == TCP_RESET) { |
62e76326 | 367 | if (err->request) { |
368 | debug(4, 2) ("RSTing this reply\n"); | |
369 | err->request->flags.setResetTCP(); | |
370 | } | |
98264874 | 371 | } |
62e76326 | 372 | |
34266cde | 373 | entry->lock() |
374 | ||
375 | ; | |
429cf70b | 376 | storeBuffer(entry); |
34266cde | 377 | |
cb69b4c7 | 378 | rep = errorBuildReply(err); |
34266cde | 379 | |
1cfdbcf0 | 380 | /* Add authentication header */ |
94439e4e | 381 | /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line |
382 | * depends on authenticate behaviour: all schemes to date send no extra data | |
383 | * on 407/401 responses, and do not check the accel state on 401/407 responses | |
384 | */ | |
721b0310 | 385 | authenticateFixHeader(rep, err->auth_user_request, err->request, 0, 1); |
34266cde | 386 | |
db237875 | 387 | entry->replaceHttpReply(rep); |
34266cde | 388 | |
ee08bdf5 | 389 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); |
34266cde | 390 | |
429cf70b | 391 | storeBufferFlush(entry); |
34266cde | 392 | |
528b2c61 | 393 | entry->complete(); |
34266cde | 394 | |
b50179a6 | 395 | storeNegativeCache(entry); |
34266cde | 396 | |
b50179a6 | 397 | storeReleaseRequest(entry); |
34266cde | 398 | |
97b5e68f | 399 | entry->unlock(); |
34266cde | 400 | |
fe40a877 | 401 | errorStateFree(err); |
fe40a877 | 402 | } |
403 | ||
404 | /* | |
405 | * Function: errorSend | |
406 | * | |
407 | * Arguments: err - This object is destroyed after use in this function. | |
408 | * | |
409 | * Abstract: This function generates a error page from the info contained | |
410 | * by 'err' and then sends it to the client. | |
4e3f29eb | 411 | * The callback function errorSendComplete() is called after |
412 | * the page has been written to the client socket (fd). | |
413 | * errorSendComplete() deallocates 'err'. We need to add | |
414 | * 'err' to the cbdata because comm_write() requires it | |
415 | * for all callback data pointers. | |
79e0dc20 | 416 | * |
417 | * Note, normally errorSend() should only be called from | |
418 | * routines in ssl.c and pass.c, where we don't have any | |
419 | * StoreEntry's. In client_side.c we must allocate a StoreEntry | |
420 | * for errors and use errorAppendEntry() to account for | |
421 | * persistent/pipeline connections. | |
fe40a877 | 422 | */ |
423 | void | |
424 | errorSend(int fd, ErrorState * err) | |
425 | { | |
cb69b4c7 | 426 | HttpReply *rep; |
fe40a877 | 427 | debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err); |
428 | assert(fd >= 0); | |
88aad2e5 | 429 | /* |
430 | * ugh, this is how we make sure error codes get back to | |
431 | * the client side for logging and error tracking. | |
432 | */ | |
62e76326 | 433 | |
88aad2e5 | 434 | if (err->request) |
62e76326 | 435 | err->request->errType = err->type; |
436 | ||
cb69b4c7 | 437 | /* moved in front of errorBuildBuf @?@ */ |
b515fc11 | 438 | err->flags.flag_cbdata = 1; |
62e76326 | 439 | |
cb69b4c7 | 440 | rep = errorBuildReply(err); |
62e76326 | 441 | |
06a5ae20 | 442 | comm_old_write_mbuf(fd, rep->pack(), errorSendComplete, err); |
62e76326 | 443 | |
06a5ae20 | 444 | delete rep; |
fe40a877 | 445 | } |
446 | ||
447 | /* | |
448 | * Function: errorSendComplete | |
449 | * | |
4e3f29eb | 450 | * Abstract: Called by commHandleWrite() after data has been written |
451 | * to the client socket. | |
fe40a877 | 452 | * |
453 | * Note: If there is a callback, the callback is responsible for | |
454 | * closeing the FD, otherwise we do it ourseves. | |
455 | */ | |
456 | static void | |
3d7e9d7c | 457 | errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data) |
fe40a877 | 458 | { |
e6ccf245 | 459 | ErrorState *err = static_cast<ErrorState *>(data); |
32754419 | 460 | debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd, (long int) size); |
62e76326 | 461 | |
fe40a877 | 462 | if (errflag != COMM_ERR_CLOSING) { |
62e76326 | 463 | if (err->callback) { |
464 | debug(4, 3) ("errorSendComplete: callback\n"); | |
465 | err->callback(fd, err->callback_data, size); | |
466 | } else { | |
467 | comm_close(fd); | |
468 | debug(4, 3) ("errorSendComplete: comm_close\n"); | |
469 | } | |
fe40a877 | 470 | } |
62e76326 | 471 | |
fe40a877 | 472 | errorStateFree(err); |
fe40a877 | 473 | } |
474 | ||
cb69b4c7 | 475 | void |
9b312a19 | 476 | errorStateFree(ErrorState * err) |
6eb42cae | 477 | { |
6dd9f4bd | 478 | HTTPMSGUNLOCK(err->request); |
9b312a19 | 479 | safe_free(err->redirect_url); |
480 | safe_free(err->url); | |
5f3c4e9a | 481 | safe_free(err->dnsserver_msg); |
b5af8569 | 482 | safe_free(err->request_hdrs); |
9bc73deb | 483 | wordlistDestroy(&err->ftp.server_msg); |
484 | safe_free(err->ftp.request); | |
485 | safe_free(err->ftp.reply); | |
62e76326 | 486 | |
94439e4e | 487 | if (err->auth_user_request) |
f5691f9c | 488 | err->auth_user_request->unlock(); |
62e76326 | 489 | |
5d146f7d | 490 | err->auth_user_request = NULL; |
62e76326 | 491 | |
43ae1d95 | 492 | safe_free(err->err_msg); |
493 | ||
28c60158 | 494 | cbdataFree(err); |
1e74c110 | 495 | } |
8213067d | 496 | |
b5fb34f1 | 497 | static int |
498 | errorDump(ErrorState * err, MemBuf * mb) | |
499 | { | |
190154cf | 500 | HttpRequest *r = err->request; |
032785bf | 501 | MemBuf str; |
b5fb34f1 | 502 | const char *p = NULL; /* takes priority over mb if set */ |
2fe7eff9 | 503 | str.reset(); |
b5fb34f1 | 504 | /* email subject line */ |
2fe7eff9 | 505 | str.Printf("CacheErrorInfo - %s", errorPageName(err->type)); |
506 | mb->Printf("?subject=%s", rfc1738_escape_part(str.buf)); | |
507 | str.reset(); | |
b5fb34f1 | 508 | /* email body */ |
2fe7eff9 | 509 | str.Printf("CacheHost: %s\r\n", getMyHostname()); |
b5fb34f1 | 510 | /* - Err Msgs */ |
2fe7eff9 | 511 | str.Printf("ErrPage: %s\r\n", errorPageName(err->type)); |
62e76326 | 512 | |
b5fb34f1 | 513 | if (err->xerrno) { |
2fe7eff9 | 514 | str.Printf("Err: (%d) %s\r\n", err->xerrno, strerror(err->xerrno)); |
b5fb34f1 | 515 | } else { |
2fe7eff9 | 516 | str.Printf("Err: [none]\r\n"); |
b5fb34f1 | 517 | } |
62e76326 | 518 | |
f5691f9c | 519 | if (err->auth_user_request->denyMessage()) |
2fe7eff9 | 520 | str.Printf("Auth ErrMsg: %s\r\n", err->auth_user_request->denyMessage()); |
62e76326 | 521 | |
b5fb34f1 | 522 | if (err->dnsserver_msg) { |
2fe7eff9 | 523 | str.Printf("DNS Server ErrMsg: %s\r\n", err->dnsserver_msg); |
b5fb34f1 | 524 | } |
62e76326 | 525 | |
b5fb34f1 | 526 | /* - TimeStamp */ |
2fe7eff9 | 527 | str.Printf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime)); |
62e76326 | 528 | |
b5fb34f1 | 529 | /* - IP stuff */ |
2fe7eff9 | 530 | str.Printf("ClientIP: %s\r\n", inet_ntoa(err->src_addr)); |
62e76326 | 531 | |
beed27a2 | 532 | if (r && r->hier.host) { |
533 | str.Printf("ServerIP: %s\r\n", r->hier.host); | |
b5fb34f1 | 534 | } |
62e76326 | 535 | |
2fe7eff9 | 536 | str.Printf("\r\n"); |
b5fb34f1 | 537 | /* - HTTP stuff */ |
2fe7eff9 | 538 | str.Printf("HTTP Request:\r\n"); |
62e76326 | 539 | |
b5fb34f1 | 540 | if (NULL != r) { |
62e76326 | 541 | Packer p; |
2fe7eff9 | 542 | str.Printf("%s %s HTTP/%d.%d\n", |
543 | RequestMethodStr[r->method], | |
544 | r->urlpath.size() ? r->urlpath.buf() : "/", | |
545 | r->http_ver.major, r->http_ver.minor); | |
62e76326 | 546 | packerToMemInit(&p, &str); |
a9925b40 | 547 | r->header.packInto(&p); |
62e76326 | 548 | packerClean(&p); |
b5fb34f1 | 549 | } else if (err->request_hdrs) { |
62e76326 | 550 | p = err->request_hdrs; |
b5fb34f1 | 551 | } else { |
62e76326 | 552 | p = "[none]"; |
b5fb34f1 | 553 | } |
62e76326 | 554 | |
2fe7eff9 | 555 | str.Printf("\r\n"); |
b5fb34f1 | 556 | /* - FTP stuff */ |
62e76326 | 557 | |
b5fb34f1 | 558 | if (err->ftp.request) { |
2fe7eff9 | 559 | str.Printf("FTP Request: %s\r\n", err->ftp.request); |
560 | str.Printf("FTP Reply: %s\r\n", err->ftp.reply); | |
561 | str.Printf("FTP Msg: "); | |
62e76326 | 562 | wordlistCat(err->ftp.server_msg, &str); |
2fe7eff9 | 563 | str.Printf("\r\n"); |
b5fb34f1 | 564 | } |
62e76326 | 565 | |
2fe7eff9 | 566 | str.Printf("\r\n"); |
567 | mb->Printf("&body=%s", rfc1738_escape_part(str.buf)); | |
568 | str.clean(); | |
b5fb34f1 | 569 | return 0; |
570 | } | |
571 | ||
2658f489 | 572 | #define CVT_BUF_SZ 512 |
fe40a877 | 573 | |
574 | /* | |
523f44f5 | 575 | * a - User identity x |
1d803566 | 576 | * B - URL with FTP %2f hack x |
577 | * c - Squid error code x | |
578 | * d - seconds elapsed since request received x | |
fe40a877 | 579 | * e - errno x |
580 | * E - strerror() x | |
fe40a877 | 581 | * f - FTP request line x |
969c39b9 | 582 | * F - FTP reply line x |
7131112f | 583 | * g - FTP server message x |
fe40a877 | 584 | * h - cache hostname x |
969c39b9 | 585 | * H - server host name x |
fe40a877 | 586 | * i - client IP address x |
587 | * I - server IP address x | |
588 | * L - HREF link for more info/contact x | |
589 | * M - Request Method x | |
bdf7e1b4 | 590 | * m - Error message returned by auth helper x |
591 | * o - Message returned external acl helper x | |
fe40a877 | 592 | * p - URL port # x |
593 | * P - Protocol x | |
b5af8569 | 594 | * R - Full HTTP Request x |
1d803566 | 595 | * S - squid signature from ERR_SIGNATURE x |
596 | * s - caching proxy software with version x | |
fe40a877 | 597 | * t - local time x |
598 | * T - UTC x | |
969c39b9 | 599 | * U - URL without password x |
76cdc28d | 600 | * u - URL with password x |
fe40a877 | 601 | * w - cachemgr email address x |
b5fb34f1 | 602 | * W - error data (to be included in the mailto links) |
fe40a877 | 603 | * z - dns server error message x |
43ae1d95 | 604 | * Z - Preformatted error message x |
fe40a877 | 605 | */ |
606 | ||
607 | static const char * | |
9b312a19 | 608 | errorConvert(char token, ErrorState * err) |
8213067d | 609 | { |
190154cf | 610 | HttpRequest *r = err->request; |
032785bf | 611 | static MemBuf mb; |
eeb423fb | 612 | const char *p = NULL; /* takes priority over mb if set */ |
10270faa | 613 | int do_quote = 1; |
eeb423fb | 614 | |
2fe7eff9 | 615 | mb.reset(); |
62e76326 | 616 | |
9b312a19 | 617 | switch (token) { |
62e76326 | 618 | |
523f44f5 | 619 | case 'a': |
620 | ||
621 | if (r->auth_user_request) | |
622 | p = r->auth_user_request->username(); | |
623 | ||
624 | if (!p) | |
625 | p = "-"; | |
626 | ||
627 | break; | |
628 | ||
8f872bb6 | 629 | case 'B': |
62e76326 | 630 | p = r ? ftpUrlWith2f(r) : "[no URL]"; |
523f44f5 | 631 | |
62e76326 | 632 | break; |
633 | ||
42b51993 | 634 | case 'c': |
62e76326 | 635 | p = errorPageName(err->type); |
523f44f5 | 636 | |
62e76326 | 637 | break; |
638 | ||
042461c3 | 639 | case 'e': |
2fe7eff9 | 640 | mb.Printf("%d", err->xerrno); |
523f44f5 | 641 | |
62e76326 | 642 | break; |
643 | ||
042461c3 | 644 | case 'E': |
62e76326 | 645 | |
646 | if (err->xerrno) | |
2fe7eff9 | 647 | mb.Printf("(%d) %s", err->xerrno, strerror(err->xerrno)); |
62e76326 | 648 | else |
2fe7eff9 | 649 | mb.Printf("[No Error]"); |
62e76326 | 650 | |
651 | break; | |
652 | ||
fe40a877 | 653 | case 'f': |
62e76326 | 654 | /* FTP REQUEST LINE */ |
655 | if (err->ftp.request) | |
656 | p = err->ftp.request; | |
657 | else | |
658 | p = "nothing"; | |
659 | ||
660 | break; | |
661 | ||
fe40a877 | 662 | case 'F': |
62e76326 | 663 | /* FTP REPLY LINE */ |
664 | if (err->ftp.request) | |
665 | p = err->ftp.reply; | |
666 | else | |
667 | p = "nothing"; | |
668 | ||
669 | break; | |
670 | ||
7131112f | 671 | case 'g': |
62e76326 | 672 | /* FTP SERVER MESSAGE */ |
673 | wordlistCat(err->ftp.server_msg, &mb); | |
674 | ||
675 | break; | |
676 | ||
03d7b07f | 677 | case 'h': |
2fe7eff9 | 678 | mb.Printf("%s", getMyHostname()); |
62e76326 | 679 | |
680 | break; | |
681 | ||
fe40a877 | 682 | case 'H': |
beed27a2 | 683 | if (r) { |
684 | if (r->hier.host) | |
685 | p = r->hier.host; | |
686 | else | |
687 | p = r->host; | |
688 | } else | |
689 | p = "[unknown host]"; | |
62e76326 | 690 | |
691 | break; | |
692 | ||
f787fb1e | 693 | case 'i': |
2fe7eff9 | 694 | mb.Printf("%s", inet_ntoa(err->src_addr)); |
62e76326 | 695 | |
696 | break; | |
697 | ||
f787fb1e | 698 | case 'I': |
beed27a2 | 699 | if (r && r->hier.host) { |
700 | mb.Printf("%s", r->hier.host); | |
62e76326 | 701 | } else |
702 | p = "[unknown]"; | |
703 | ||
704 | break; | |
705 | ||
fe40a877 | 706 | case 'L': |
62e76326 | 707 | if (Config.errHtmlText) { |
2fe7eff9 | 708 | mb.Printf("%s", Config.errHtmlText); |
62e76326 | 709 | do_quote = 0; |
710 | } else | |
711 | p = "[not available]"; | |
712 | ||
713 | break; | |
714 | ||
066ed5c1 | 715 | case 'm': |
f5691f9c | 716 | p = err->auth_user_request->denyMessage("[not available]"); |
62e76326 | 717 | |
718 | break; | |
719 | ||
fe40a877 | 720 | case 'M': |
985c86bc | 721 | p = r ? RequestMethodStr[r->method] : "[unknown method]"; |
62e76326 | 722 | |
723 | break; | |
724 | ||
4a972fa2 | 725 | case 'o': |
726 | p = external_acl_message ? external_acl_message : "[not available]"; | |
727 | ||
728 | break; | |
729 | ||
fe40a877 | 730 | case 'p': |
62e76326 | 731 | if (r) { |
2fe7eff9 | 732 | mb.Printf("%d", (int) r->port); |
62e76326 | 733 | } else { |
734 | p = "[unknown port]"; | |
735 | } | |
736 | ||
737 | break; | |
738 | ||
fe40a877 | 739 | case 'P': |
985c86bc | 740 | p = r ? ProtocolStr[r->protocol] : "[unknown protocol]"; |
62e76326 | 741 | break; |
742 | ||
b5af8569 | 743 | case 'R': |
62e76326 | 744 | |
745 | if (NULL != r) { | |
746 | Packer p; | |
2fe7eff9 | 747 | mb.Printf("%s %s HTTP/%d.%d\n", |
748 | RequestMethodStr[r->method], | |
749 | r->urlpath.size() ? r->urlpath.buf() : "/", | |
750 | r->http_ver.major, r->http_ver.minor); | |
62e76326 | 751 | packerToMemInit(&p, &mb); |
a9925b40 | 752 | r->header.packInto(&p); |
62e76326 | 753 | packerClean(&p); |
754 | } else if (err->request_hdrs) { | |
755 | p = err->request_hdrs; | |
756 | } else { | |
757 | p = "[no request]"; | |
758 | } | |
759 | ||
760 | break; | |
761 | ||
1d803566 | 762 | case 's': |
d3caee79 | 763 | p = visible_appname_string; |
62e76326 | 764 | break; |
765 | ||
1d803566 | 766 | case 'S': |
62e76326 | 767 | /* signature may contain %-escapes, recursion */ |
768 | ||
769 | if (err->page_id != ERR_SQUID_SIGNATURE) { | |
770 | const int saved_id = err->page_id; | |
62e76326 | 771 | err->page_id = ERR_SQUID_SIGNATURE; |
032785bf | 772 | MemBuf *sign_mb = errorBuildContent(err); |
2fe7eff9 | 773 | mb.Printf("%s", sign_mb->content()); |
774 | sign_mb->clean(); | |
032785bf | 775 | delete sign_mb; |
62e76326 | 776 | err->page_id = saved_id; |
777 | do_quote = 0; | |
778 | } else { | |
779 | /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */ | |
780 | p = "[%S]"; | |
781 | } | |
782 | ||
783 | break; | |
784 | ||
fe40a877 | 785 | case 't': |
2fe7eff9 | 786 | mb.Printf("%s", mkhttpdlogtime(&squid_curtime)); |
62e76326 | 787 | break; |
788 | ||
f8291f8f | 789 | case 'T': |
2fe7eff9 | 790 | mb.Printf("%s", mkrfc1123(squid_curtime)); |
62e76326 | 791 | break; |
792 | ||
fe40a877 | 793 | case 'U': |
62e76326 | 794 | p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]"; |
795 | break; | |
796 | ||
76cdc28d | 797 | case 'u': |
62e76326 | 798 | p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]"; |
799 | break; | |
800 | ||
fe40a877 | 801 | case 'w': |
62e76326 | 802 | |
803 | if (Config.adminEmail) | |
2fe7eff9 | 804 | mb.Printf("%s", Config.adminEmail); |
62e76326 | 805 | else |
806 | p = "[unknown]"; | |
807 | ||
808 | break; | |
809 | ||
b5fb34f1 | 810 | case 'W': |
62e76326 | 811 | if (Config.adminEmail && Config.onoff.emailErrData) |
812 | errorDump(err, &mb); | |
813 | ||
814 | break; | |
815 | ||
fe40a877 | 816 | case 'z': |
62e76326 | 817 | if (err->dnsserver_msg) |
818 | p = err->dnsserver_msg; | |
819 | else | |
820 | p = "[unknown]"; | |
821 | ||
822 | break; | |
823 | ||
43ae1d95 | 824 | case 'Z': |
825 | if (err->err_msg) | |
826 | p = err->err_msg; | |
827 | else | |
828 | p = "[unknown]"; | |
829 | ||
830 | break; | |
831 | ||
e347f8e5 | 832 | case '%': |
62e76326 | 833 | p = "%"; |
834 | ||
835 | break; | |
836 | ||
9b312a19 | 837 | default: |
2fe7eff9 | 838 | mb.Printf("%%%c", token); |
62e76326 | 839 | |
cbba2ba2 | 840 | do_quote = 0; |
841 | ||
62e76326 | 842 | break; |
9b312a19 | 843 | } |
62e76326 | 844 | |
137ee196 | 845 | if (!p) |
62e76326 | 846 | p = mb.buf; /* do not use mb after this assignment! */ |
847 | ||
137ee196 | 848 | assert(p); |
62e76326 | 849 | |
e102ebda | 850 | debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p); |
62e76326 | 851 | |
10270faa | 852 | if (do_quote) |
62e76326 | 853 | p = html_quote(p); |
854 | ||
9b312a19 | 855 | return p; |
8213067d | 856 | } |
e381a13d | 857 | |
cb69b4c7 | 858 | /* allocates and initializes an error response */ |
859 | HttpReply * | |
2ac76861 | 860 | errorBuildReply(ErrorState * err) |
cb69b4c7 | 861 | { |
06a5ae20 | 862 | HttpReply *rep = new HttpReply; |
76cdc28d | 863 | const char *name = errorPageName(err->page_id); |
cb69b4c7 | 864 | /* no LMT for error pages; error pages expire immediately */ |
450e0c10 | 865 | HttpVersion version(1, 0); |
62e76326 | 866 | |
76cdc28d | 867 | if (strchr(name, ':')) { |
62e76326 | 868 | /* Redirection */ |
06a5ae20 | 869 | rep->setHeaders(version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, squid_curtime); |
c44950c4 | 870 | |
871 | if (err->request) { | |
872 | char *quoted_url = rfc1738_escape_part(urlCanonical(err->request)); | |
873 | httpHeaderPutStrf(&rep->header, HDR_LOCATION, name, quoted_url); | |
874 | } | |
875 | ||
62e76326 | 876 | httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s\n", err->httpStatus, "Access Denied"); |
76cdc28d | 877 | } else { |
032785bf | 878 | MemBuf *content = errorBuildContent(err); |
06a5ae20 | 879 | rep->setHeaders(version, err->httpStatus, NULL, "text/html", content->contentSize(), 0, squid_curtime); |
62e76326 | 880 | /* |
881 | * include some information for downstream caches. Implicit | |
882 | * replaceable content. This isn't quite sufficient. xerrno is not | |
883 | * necessarily meaningful to another system, so we really should | |
884 | * expand it. Additionally, we should identify ourselves. Someone | |
885 | * might want to know. Someone _will_ want to know OTOH, the first | |
886 | * X-CACHE-MISS entry should tell us who. | |
887 | */ | |
888 | httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d", | |
889 | name, err->xerrno); | |
032785bf | 890 | httpBodySet(&rep->body, content); |
891 | /* do not memBufClean() or delete the content, it was absorbed by httpBody */ | |
76cdc28d | 892 | } |
62e76326 | 893 | |
cb69b4c7 | 894 | return rep; |
895 | } | |
896 | ||
032785bf | 897 | static MemBuf * |
1d803566 | 898 | errorBuildContent(ErrorState * err) |
cb69b4c7 | 899 | { |
032785bf | 900 | MemBuf *content = new MemBuf; |
1d803566 | 901 | const char *m; |
902 | const char *p; | |
cb69b4c7 | 903 | const char *t; |
904 | assert(err != NULL); | |
02922e76 | 905 | assert(err->page_id > ERR_NONE && err->page_id < error_page_count); |
2fe7eff9 | 906 | content->init(); |
02922e76 | 907 | m = error_text[err->page_id]; |
1d803566 | 908 | assert(m); |
62e76326 | 909 | |
cb69b4c7 | 910 | while ((p = strchr(m, '%'))) { |
2fe7eff9 | 911 | content->append(m, p - m); /* copy */ |
62e76326 | 912 | t = errorConvert(*++p, err); /* convert */ |
2fe7eff9 | 913 | content->Printf("%s", t); /* copy */ |
62e76326 | 914 | m = p + 1; /* advance */ |
cb69b4c7 | 915 | } |
62e76326 | 916 | |
1d803566 | 917 | if (*m) |
2fe7eff9 | 918 | content->Printf("%s", m); /* copy tail */ |
62e76326 | 919 | |
032785bf | 920 | assert((size_t)content->contentSize() == strlen(content->content())); |
62e76326 | 921 | |
cb69b4c7 | 922 | return content; |
923 | } |