]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
Fix request processing CONNECT requests to immediately stop trying to
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
4ff59fc7 3 * $Id: errorpage.cc,v 1.183 2003/01/27 22:33:25 robertc 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"
e6ccf245 44#include "authenticate.h"
45#include "Store.h"
528b2c61 46#include "HttpReply.h"
47#include "HttpRequest.h"
48#include "MemObject.h"
49#include "fde.h"
02922e76 50
51/* local types */
52
53typedef struct {
54 int id;
55 char *page_name;
56} ErrorDynamicPageInfo;
57
58/* local constant and vars */
59
1d803566 60/*
61 * note: hard coded error messages are not appended with %S automagically
62 * to give you more control on the format
63 */
2ac76861 64static const struct {
1afe05c5 65 int type; /* and page_id */
2ac76861 66 const char *text;
67} error_hard_text[] = {
1afe05c5 68
2ac76861 69 {
70 ERR_SQUID_SIGNATURE,
df339671 71 "\n<BR clear=\"all\">\n"
72 "<HR noshade size=\"1px\">\n"
b5fb34f1 73 "<ADDRESS>\n"
9bc73deb 74 "Generated %T by %h (%s)\n"
b5fb34f1 75 "</ADDRESS>\n"
e32b11fc 76 "</BODY></HTML>\n"
76cdc28d 77 },
78 {
79 TCP_RESET,
80 "reset"
1d803566 81 }
82};
02922e76 83
4ff59fc7 84static Vector<ErrorDynamicPageInfo *> ErrorDynamicPages;
02922e76 85
86/* local prototypes */
87
2ac76861 88static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
02922e76 89static char **error_text = NULL;
90static int error_page_count = 0;
9b312a19 91
02922e76 92static char *errorTryLoadText(const char *page_name, const char *dir);
93static char *errorLoadText(const char *page_name);
1d803566 94static const char *errorFindHardText(err_type type);
c68e9c6b 95static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
96static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
1d803566 97static MemBuf errorBuildContent(ErrorState * err);
b5fb34f1 98static int errorDump(ErrorState * err, MemBuf * mb);
fe40a877 99static const char *errorConvert(char token, ErrorState * err);
9b312a19 100static CWCB errorSendComplete;
1e74c110 101
e6ccf245 102
103err_type &operator++ (err_type &anErr)
104{
105 anErr = (err_type)(++(int)anErr);
106 return anErr;
107}
4ff59fc7 108
109int operator - (err_type const &anErr, err_type const &anErr2) {
110 return (int)anErr - (int)anErr2;
111}
112
fe40a877 113/*
114 * Function: errorInitialize
115 *
1d803566 116 * Abstract: This function finds the error messages formats, and stores
fe40a877 117 * them in error_text[];
118 *
119 * Global effects:
120 * error_text[] - is modified
121 */
b8d8561b 122void
0673c0ba 123errorInitialize(void)
fa966b74 124{
728da2ee 125 err_type i;
1d803566 126 const char *text;
4ff59fc7 127 error_page_count = ERR_MAX + ErrorDynamicPages.size();
e6ccf245 128 error_text = static_cast<char **>(xcalloc(error_page_count, sizeof(char *)));
129 for (i = ERR_NONE, ++i; i < error_page_count; ++i) {
1d803566 130 safe_free(error_text[i]);
131 /* hard-coded ? */
132 if ((text = errorFindHardText(i)))
133 error_text[i] = xstrdup(text);
c68e9c6b 134 else if (i < ERR_MAX) {
1afe05c5 135 /* precompiled ? */
02922e76 136 error_text[i] = errorLoadText(err_type_str[i]);
c68e9c6b 137 } else {
138 /* dynamic */
4ff59fc7 139 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
02922e76 140 assert(info && info->id == i && info->page_name);
76cdc28d 141 if (strchr(info->page_name, ':') == NULL) {
142 /* Not on redirected errors... */
143 error_text[i] = errorLoadText(info->page_name);
144 }
02922e76 145 }
1d803566 146 }
147}
148
c68e9c6b 149void
150errorClean(void)
151{
152 if (error_text) {
153 int i;
154 for (i = ERR_NONE + 1; i < error_page_count; i++)
155 safe_free(error_text[i]);
156 safe_free(error_text);
157 }
4ff59fc7 158 while (ErrorDynamicPages.size())
159 errorDynamicPageInfoDestroy(ErrorDynamicPages.pop_back());
c68e9c6b 160 error_page_count = 0;
161}
162
1d803566 163static const char *
164errorFindHardText(err_type type)
165{
166 int i;
167 for (i = 0; i < error_hard_text_count; i++)
168 if (error_hard_text[i].type == type)
169 return error_hard_text[i].text;
170 return NULL;
171}
172
173
174static char *
02922e76 175errorLoadText(const char *page_name)
1d803566 176{
177 /* test configured location */
02922e76 178 char *text = errorTryLoadText(page_name, Config.errorDirectory);
1d803566 179 /* test default location if failed */
180 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
02922e76 181 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
1d803566 182 /* giving up if failed */
183 if (!text)
184 fatal("failed to find or read error text file.");
185 return text;
186}
187
188static char *
02922e76 189errorTryLoadText(const char *page_name, const char *dir)
1d803566 190{
9b312a19 191 int fd;
192 char path[MAXPATHLEN];
193 struct stat sb;
1d803566 194 char *text;
195
137ee196 196 snprintf(path, sizeof(path), "%s/%s", dir, page_name);
c4aefe96 197 fd = file_open(path, O_RDONLY | O_TEXT);
1d803566 198 if (fd < 0 || fstat(fd, &sb) < 0) {
199 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
200 if (fd >= 0)
201 file_close(fd);
202 return NULL;
203 }
e6ccf245 204 text = (char *)xcalloc(sb.st_size + 2 + 1, 1); /* 2 == space for %S */
1f7c9178 205 if (FD_READ_METHOD(fd, text, sb.st_size) != sb.st_size) {
1d803566 206 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
207 path, xstrerror());
208 xfree(text);
209 text = NULL;
1e74c110 210 }
1d803566 211 file_close(fd);
c68e9c6b 212 if (strstr(text, "%s") == NULL)
213 strcat(text, "%S"); /* add signature */
1d803566 214 return text;
6eb42cae 215}
216
02922e76 217static ErrorDynamicPageInfo *
218errorDynamicPageInfoCreate(int id, const char *page_name)
219{
4ff59fc7 220 ErrorDynamicPageInfo *info = new ErrorDynamicPageInfo;
02922e76 221 info->id = id;
222 info->page_name = xstrdup(page_name);
223 return info;
224}
225
226static void
1afe05c5 227errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
02922e76 228{
229 assert(info);
230 xfree(info->page_name);
4ff59fc7 231 delete info;
02922e76 232}
233
76cdc28d 234static int
235errorPageId(const char *page_name)
236{
528b2c61 237 for (int i = 0; i < ERR_MAX; i++) {
76cdc28d 238 if (strcmp(err_type_str[i], page_name) == 0)
239 return i;
240 }
4ff59fc7 241 for (size_t i = 0; i < ErrorDynamicPages.size(); i++) {
242 if (strcmp(ErrorDynamicPages.items[i]->page_name, page_name) == 0)
528b2c61 243 return i + ERR_MAX;
76cdc28d 244 }
245 return ERR_NONE;
246}
247
e6ccf245 248err_type
02922e76 249errorReservePageId(const char *page_name)
250{
76cdc28d 251 ErrorDynamicPageInfo *info;
252 int id = errorPageId(page_name);
253 if (id == ERR_NONE) {
4ff59fc7 254 info = errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.size(), page_name);
255 ErrorDynamicPages.push_back(info);
76cdc28d 256 id = info->id;
257 }
e6ccf245 258 return (err_type)id;
02922e76 259}
260
c68e9c6b 261static const char *
262errorPageName(int pageId)
53ad48e6 263{
c68e9c6b 264 if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */
265 return err_type_str[pageId];
4ff59fc7 266 if (pageId >= ERR_MAX && pageId - ERR_MAX < (ssize_t)ErrorDynamicPages.size())
267 return ErrorDynamicPages.items[pageId - ERR_MAX]->page_name;
c68e9c6b 268 return "ERR_UNKNOWN"; /* should not happen */
53ad48e6 269}
270
fe40a877 271/*
272 * Function: errorCon
273 *
274 * Abstract: This function creates a ErrorState object.
275 */
276ErrorState *
728da2ee 277errorCon(err_type type, http_status status)
fe40a877 278{
28c60158 279 ErrorState *err;
72711e31 280 err = cbdataAlloc(ErrorState);
1afe05c5 281 err->page_id = type; /* has to be reset manually if needed */
fe40a877 282 err->type = type;
29b8d8d6 283 err->httpStatus = status;
fe40a877 284 return err;
285}
286
287/*
288 * Function: errorAppendEntry
289 *
290 * Arguments: err - This object is destroyed after use in this function.
291 *
292 * Abstract: This function generates a error page from the info contained
4e3f29eb 293 * by 'err' and then stores the text in the specified store
294 * entry. This function should only be called by ``server
295 * side routines'' which need to communicate errors to the
79e0dc20 296 * client side. It should also be called from client_side.c
297 * because we now support persistent connections, and
298 * cannot assume that we can immediately write to the socket
299 * for an error.
fe40a877 300 */
301void
302errorAppendEntry(StoreEntry * entry, ErrorState * err)
303{
cb69b4c7 304 HttpReply *rep;
cb69b4c7 305 MemObject *mem = entry->mem_obj;
b50179a6 306 assert(mem != NULL);
528b2c61 307 assert (entry->isEmpty());
1ea80e98 308 if (entry->store_status != STORE_PENDING) {
309 /*
310 * If the entry is not STORE_PENDING, then no clients
311 * care about it, and we don't need to generate an
312 * error message
313 */
314 assert(EBIT_TEST(entry->flags, ENTRY_ABORTED));
315 assert(mem->nclients == 0);
316 errorStateFree(err);
317 return;
318 }
76cdc28d 319 if (err->page_id == TCP_RESET) {
98264874 320 if (err->request) {
76cdc28d 321 debug(4, 2) ("RSTing this reply\n");
e429f975 322 err->request->flags.setResetTCP();
98264874 323 }
324 }
5507c7d5 325 storeLockObject(entry);
429cf70b 326 storeBuffer(entry);
cb69b4c7 327 rep = errorBuildReply(err);
1cfdbcf0 328 /* Add authentication header */
94439e4e 329 /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
330 * depends on authenticate behaviour: all schemes to date send no extra data
331 * on 407/401 responses, and do not check the accel state on 401/407 responses
332 */
721b0310 333 authenticateFixHeader(rep, err->auth_user_request, err->request, 0, 1);
cb69b4c7 334 httpReplySwapOut(rep, entry);
ee08bdf5 335 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
429cf70b 336 storeBufferFlush(entry);
528b2c61 337 entry->complete();
b50179a6 338 storeNegativeCache(entry);
339 storeReleaseRequest(entry);
5507c7d5 340 storeUnlockObject(entry);
fe40a877 341 errorStateFree(err);
fe40a877 342}
343
344/*
345 * Function: errorSend
346 *
347 * Arguments: err - This object is destroyed after use in this function.
348 *
349 * Abstract: This function generates a error page from the info contained
350 * by 'err' and then sends it to the client.
4e3f29eb 351 * The callback function errorSendComplete() is called after
352 * the page has been written to the client socket (fd).
353 * errorSendComplete() deallocates 'err'. We need to add
354 * 'err' to the cbdata because comm_write() requires it
355 * for all callback data pointers.
79e0dc20 356 *
357 * Note, normally errorSend() should only be called from
358 * routines in ssl.c and pass.c, where we don't have any
359 * StoreEntry's. In client_side.c we must allocate a StoreEntry
360 * for errors and use errorAppendEntry() to account for
361 * persistent/pipeline connections.
fe40a877 362 */
363void
364errorSend(int fd, ErrorState * err)
365{
cb69b4c7 366 HttpReply *rep;
fe40a877 367 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
368 assert(fd >= 0);
88aad2e5 369 /*
370 * ugh, this is how we make sure error codes get back to
371 * the client side for logging and error tracking.
372 */
373 if (err->request)
29b8d8d6 374 err->request->errType = err->type;
cb69b4c7 375 /* moved in front of errorBuildBuf @?@ */
b515fc11 376 err->flags.flag_cbdata = 1;
cb69b4c7 377 rep = errorBuildReply(err);
d4cb310b 378 comm_old_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
cb69b4c7 379 httpReplyDestroy(rep);
fe40a877 380}
381
382/*
383 * Function: errorSendComplete
384 *
4e3f29eb 385 * Abstract: Called by commHandleWrite() after data has been written
386 * to the client socket.
fe40a877 387 *
388 * Note: If there is a callback, the callback is responsible for
389 * closeing the FD, otherwise we do it ourseves.
390 */
391static void
3d7e9d7c 392errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
fe40a877 393{
e6ccf245 394 ErrorState *err = static_cast<ErrorState *>(data);
32754419 395 debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd, (long int) size);
fe40a877 396 if (errflag != COMM_ERR_CLOSING) {
94439e4e 397 if (err->callback) {
398 debug(4, 3) ("errorSendComplete: callback\n");
fe40a877 399 err->callback(fd, err->callback_data, size);
94439e4e 400 } else {
fe40a877 401 comm_close(fd);
94439e4e 402 debug(4, 3) ("errorSendComplete: comm_close\n");
403 }
fe40a877 404 }
405 errorStateFree(err);
fe40a877 406}
407
cb69b4c7 408void
9b312a19 409errorStateFree(ErrorState * err)
6eb42cae 410{
9b312a19 411 requestUnlink(err->request);
412 safe_free(err->redirect_url);
413 safe_free(err->url);
76a5501f 414 safe_free(err->host);
5f3c4e9a 415 safe_free(err->dnsserver_msg);
b5af8569 416 safe_free(err->request_hdrs);
9bc73deb 417 wordlistDestroy(&err->ftp.server_msg);
418 safe_free(err->ftp.request);
419 safe_free(err->ftp.reply);
94439e4e 420 if (err->auth_user_request)
421 authenticateAuthUserRequestUnlock(err->auth_user_request);
5d146f7d 422 err->auth_user_request = NULL;
28c60158 423 cbdataFree(err);
1e74c110 424}
8213067d 425
b5fb34f1 426static int
427errorDump(ErrorState * err, MemBuf * mb)
428{
429 request_t *r = err->request;
430 MemBuf str = MemBufNULL;
431 const char *p = NULL; /* takes priority over mb if set */
432 memBufReset(&str);
433 /* email subject line */
434 memBufPrintf(&str, "CacheErrorInfo - %s", errorPageName(err->type));
435 memBufPrintf(mb, "?subject=%s", rfc1738_escape_part(str.buf));
436 memBufReset(&str);
437 /* email body */
438 memBufPrintf(&str, "CacheHost: %s\r\n", getMyHostname());
439 /* - Err Msgs */
440 memBufPrintf(&str, "ErrPage: %s\r\n", errorPageName(err->type));
441 if (err->xerrno) {
442 memBufPrintf(&str, "Err: (%d) %s\r\n", err->xerrno, strerror(err->xerrno));
443 } else {
444 memBufPrintf(&str, "Err: [none]\r\n");
445 }
446 if (authenticateAuthUserRequestMessage(err->auth_user_request)) {
447 memBufPrintf(&str, "extAuth ErrMsg: %s\r\n", authenticateAuthUserRequestMessage(err->auth_user_request));
448 }
449 if (err->dnsserver_msg) {
450 memBufPrintf(&str, "DNS Server ErrMsg: %s\r\n", err->dnsserver_msg);
451 }
452 /* - TimeStamp */
453 memBufPrintf(&str, "TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
454 /* - IP stuff */
455 memBufPrintf(&str, "ClientIP: %s\r\n", inet_ntoa(err->src_addr));
456 if (err->host) {
457 memBufPrintf(&str, "ServerIP: %s\r\n", err->host);
458 }
459 memBufPrintf(&str, "\r\n");
460 /* - HTTP stuff */
461 memBufPrintf(&str, "HTTP Request:\r\n");
462 if (NULL != r) {
463 Packer p;
464 memBufPrintf(&str, "%s %s HTTP/%d.%d\n",
465 RequestMethodStr[r->method],
528b2c61 466 r->urlpath.size() ? r->urlpath.buf() : "/",
b5fb34f1 467 r->http_ver.major, r->http_ver.minor);
468 packerToMemInit(&p, &str);
469 httpHeaderPackInto(&r->header, &p);
470 packerClean(&p);
471 } else if (err->request_hdrs) {
472 p = err->request_hdrs;
473 } else {
474 p = "[none]";
475 }
476 memBufPrintf(&str, "\r\n");
477 /* - FTP stuff */
478 if (err->ftp.request) {
479 memBufPrintf(&str, "FTP Request: %s\r\n", err->ftp.request);
480 memBufPrintf(&str, "FTP Reply: %s\r\n", err->ftp.reply);
481 memBufPrintf(&str, "FTP Msg: ");
482 wordlistCat(err->ftp.server_msg, &str);
483 memBufPrintf(&str, "\r\n");
484 }
485 memBufPrintf(&str, "\r\n");
486 memBufPrintf(mb, "&body=%s", rfc1738_escape_part(str.buf));
487 memBufClean(&str);
488 return 0;
489}
490
2658f489 491#define CVT_BUF_SZ 512
fe40a877 492
493/*
1d803566 494 * B - URL with FTP %2f hack x
495 * c - Squid error code x
496 * d - seconds elapsed since request received x
fe40a877 497 * e - errno x
498 * E - strerror() x
fe40a877 499 * f - FTP request line x
969c39b9 500 * F - FTP reply line x
7131112f 501 * g - FTP server message x
fe40a877 502 * h - cache hostname x
969c39b9 503 * H - server host name x
fe40a877 504 * i - client IP address x
505 * I - server IP address x
506 * L - HREF link for more info/contact x
507 * M - Request Method x
066ed5c1 508 * m - Error message returned by external Auth. x
fe40a877 509 * p - URL port # x
510 * P - Protocol x
b5af8569 511 * R - Full HTTP Request x
1d803566 512 * S - squid signature from ERR_SIGNATURE x
513 * s - caching proxy software with version x
fe40a877 514 * t - local time x
515 * T - UTC x
969c39b9 516 * U - URL without password x
76cdc28d 517 * u - URL with password x
fe40a877 518 * w - cachemgr email address x
b5fb34f1 519 * W - error data (to be included in the mailto links)
fe40a877 520 * z - dns server error message x
521 */
522
523static const char *
9b312a19 524errorConvert(char token, ErrorState * err)
8213067d 525{
2658f489 526 request_t *r = err->request;
137ee196 527 static MemBuf mb = MemBufNULL;
eeb423fb 528 const char *p = NULL; /* takes priority over mb if set */
10270faa 529 int do_quote = 1;
eeb423fb 530
137ee196 531 memBufReset(&mb);
9b312a19 532 switch (token) {
8f872bb6 533 case 'B':
534 p = r ? ftpUrlWith2f(r) : "[no URL]";
535 break;
42b51993 536 case 'c':
bd704830 537 p = errorPageName(err->type);
42b51993 538 break;
042461c3 539 case 'e':
137ee196 540 memBufPrintf(&mb, "%d", err->xerrno);
042461c3 541 break;
542 case 'E':
23d92c64 543 if (err->xerrno)
137ee196 544 memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
23d92c64 545 else
137ee196 546 memBufPrintf(&mb, "[No Error]");
03d7b07f 547 break;
fe40a877 548 case 'f':
549 /* FTP REQUEST LINE */
550 if (err->ftp.request)
551 p = err->ftp.request;
552 else
a8af3a35 553 p = "nothing";
fe40a877 554 break;
555 case 'F':
556 /* FTP REPLY LINE */
557 if (err->ftp.request)
558 p = err->ftp.reply;
559 else
a8af3a35 560 p = "nothing";
03d7b07f 561 break;
7131112f 562 case 'g':
563 /* FTP SERVER MESSAGE */
9bc73deb 564 wordlistCat(err->ftp.server_msg, &mb);
7131112f 565 break;
03d7b07f 566 case 'h':
137ee196 567 memBufPrintf(&mb, "%s", getMyHostname());
f787fb1e 568 break;
fe40a877 569 case 'H':
570 p = r ? r->host : "[unknown host]";
f787fb1e 571 break;
572 case 'i':
137ee196 573 memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 574 break;
f787fb1e 575 case 'I':
576 if (err->host) {
137ee196 577 memBufPrintf(&mb, "%s", err->host);
f787fb1e 578 } else
fe40a877 579 p = "[unknown]";
580 break;
581 case 'L':
582 if (Config.errHtmlText) {
137ee196 583 memBufPrintf(&mb, "%s", Config.errHtmlText);
406ecea9 584 do_quote = 0;
fe40a877 585 } else
586 p = "[not available]";
587 break;
066ed5c1 588 case 'm':
94439e4e 589 p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
066ed5c1 590 break;
fe40a877 591 case 'M':
592 p = r ? RequestMethodStr[r->method] : "[unkown method]";
593 break;
594 case 'p':
595 if (r) {
137ee196 596 memBufPrintf(&mb, "%d", (int) r->port);
fe40a877 597 } else {
598 p = "[unknown port]";
599 }
600 break;
601 case 'P':
602 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
603 break;
b5af8569 604 case 'R':
865094d7 605 if (NULL != r) {
865094d7 606 Packer p;
ccf44862 607 memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
865094d7 608 RequestMethodStr[r->method],
528b2c61 609 r->urlpath.size() ? r->urlpath.buf() : "/",
ccf44862 610 r->http_ver.major, r->http_ver.minor);
865094d7 611 packerToMemInit(&p, &mb);
612 httpHeaderPackInto(&r->header, &p);
613 packerClean(&p);
865094d7 614 } else if (err->request_hdrs) {
615 p = err->request_hdrs;
616 } else {
617 p = "[no request]";
618 }
b5af8569 619 break;
1d803566 620 case 's':
621 p = full_appname_string;
622 break;
623 case 'S':
624 /* signature may contain %-escapes, recursion */
02922e76 625 if (err->page_id != ERR_SQUID_SIGNATURE) {
626 const int saved_id = err->page_id;
137ee196 627 MemBuf sign_mb;
02922e76 628 err->page_id = ERR_SQUID_SIGNATURE;
137ee196 629 sign_mb = errorBuildContent(err);
630 memBufPrintf(&mb, "%s", sign_mb.buf);
631 memBufClean(&sign_mb);
02922e76 632 err->page_id = saved_id;
10270faa 633 do_quote = 0;
1d803566 634 } else {
635 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
636 p = "[%S]";
637 }
638 break;
fe40a877 639 case 't':
137ee196 640 memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
f787fb1e 641 break;
f8291f8f 642 case 'T':
137ee196 643 memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
f8291f8f 644 break;
fe40a877 645 case 'U':
b5af8569 646 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
b9916917 647 break;
76cdc28d 648 case 'u':
649 p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]";
650 break;
fe40a877 651 case 'w':
137ee196 652 if (Config.adminEmail)
653 memBufPrintf(&mb, "%s", Config.adminEmail);
654 else
fe40a877 655 p = "[unknown]";
656 break;
b5fb34f1 657 case 'W':
658 if (Config.adminEmail && Config.onoff.emailErrData)
659 errorDump(err, &mb);
660 break;
fe40a877 661 case 'z':
662 if (err->dnsserver_msg)
663 p = err->dnsserver_msg;
b9916917 664 else
fe40a877 665 p = "[unknown]";
b9916917 666 break;
e347f8e5 667 case '%':
668 p = "%";
669 break;
9b312a19 670 default:
6b8e7481 671 memBufPrintf(&mb, "%%%c", token);
9b312a19 672 break;
673 }
137ee196 674 if (!p)
eeb423fb 675 p = mb.buf; /* do not use mb after this assignment! */
137ee196 676 assert(p);
e102ebda 677 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
10270faa 678 if (do_quote)
679 p = html_quote(p);
9b312a19 680 return p;
8213067d 681}
e381a13d 682
cb69b4c7 683/* allocates and initializes an error response */
684HttpReply *
2ac76861 685errorBuildReply(ErrorState * err)
cb69b4c7 686{
cb69b4c7 687 HttpReply *rep = httpReplyCreate();
76cdc28d 688 const char *name = errorPageName(err->page_id);
ccf44862 689 http_version_t version;
cb69b4c7 690 /* no LMT for error pages; error pages expire immediately */
bffee5af 691 httpBuildVersion(&version, 1, 0);
76cdc28d 692 if (strchr(name, ':')) {
693 /* Redirection */
694 char *quoted_url = rfc1738_escape_part(errorConvert('u', err));
695 httpReplySetHeaders(rep, version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, squid_curtime);
696 httpHeaderPutStrf(&rep->header, HDR_LOCATION, name, quoted_url);
29b8d8d6 697 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s\n", err->httpStatus, "Access Denied");
76cdc28d 698 } else {
699 MemBuf content = errorBuildContent(err);
29b8d8d6 700 httpReplySetHeaders(rep, version, err->httpStatus, NULL, "text/html", content.size, 0, squid_curtime);
76cdc28d 701 /*
702 * include some information for downstream caches. Implicit
703 * replaceable content. This isn't quite sufficient. xerrno is not
704 * necessarily meaningful to another system, so we really should
705 * expand it. Additionally, we should identify ourselves. Someone
706 * might want to know. Someone _will_ want to know OTOH, the first
707 * X-CACHE-MISS entry should tell us who.
708 */
709 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d",
710 name, err->xerrno);
711 httpBodySet(&rep->body, &content);
712 /* do not memBufClean() the content, it was absorbed by httpBody */
713 }
cb69b4c7 714 return rep;
715}
716
1d803566 717static MemBuf
718errorBuildContent(ErrorState * err)
cb69b4c7 719{
1d803566 720 MemBuf content;
1d803566 721 const char *m;
722 const char *p;
cb69b4c7 723 const char *t;
724 assert(err != NULL);
02922e76 725 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
1d803566 726 memBufDefInit(&content);
02922e76 727 m = error_text[err->page_id];
1d803566 728 assert(m);
cb69b4c7 729 while ((p = strchr(m, '%'))) {
2ac76861 730 memBufAppend(&content, m, p - m); /* copy */
731 t = errorConvert(*++p, err); /* convert */
732 memBufPrintf(&content, "%s", t); /* copy */
733 m = p + 1; /* advance */
cb69b4c7 734 }
1d803566 735 if (*m)
2ac76861 736 memBufPrintf(&content, "%s", m); /* copy tail */
e6ccf245 737 assert((size_t)content.size == strlen(content.buf));
cb69b4c7 738 return content;
739}