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