]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/errorpage.cc
3 * $Id: errorpage.cc,v 1.190 2003/08/10 11:00:42 robertc Exp $
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
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)
44 #include "authenticate.h"
46 #include "HttpReply.h"
47 #include "HttpRequest.h"
48 #include "MemObject.h"
61 /* local constant and vars */
64 * note: hard coded error messages are not appended with %S automagically
65 * to give you more control on the format
70 int type
; /* and page_id */
78 "\n<BR clear=\"all\">\n"
79 "<HR noshade size=\"1px\">\n"
81 "Generated %T by %h (%s)\n"
91 static Vector
<ErrorDynamicPageInfo
*> ErrorDynamicPages
;
93 /* local prototypes */
95 static const int error_hard_text_count
= sizeof(error_hard_text
) / sizeof(*error_hard_text
);
96 static char **error_text
= NULL
;
97 static int error_page_count
= 0;
99 static char *errorTryLoadText(const char *page_name
, const char *dir
);
100 static char *errorLoadText(const char *page_name
);
101 static const char *errorFindHardText(err_type type
);
102 static ErrorDynamicPageInfo
*errorDynamicPageInfoCreate(int id
, const char *page_name
);
103 static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo
* info
);
104 static MemBuf
errorBuildContent(ErrorState
* err
);
105 static int errorDump(ErrorState
* err
, MemBuf
* mb
);
106 static const char *errorConvert(char token
, ErrorState
* err
);
107 static CWCB errorSendComplete
;
110 err_type
&operator++ (err_type
&anErr
)
112 int tmp
= (int)anErr
;
113 anErr
= (err_type
)(++tmp
);
117 int operator - (err_type
const &anErr
, err_type
const &anErr2
)
119 return (int)anErr
- (int)anErr2
;
123 * Function: errorInitialize
125 * Abstract: This function finds the error messages formats, and stores
126 * them in error_text[];
129 * error_text[] - is modified
132 errorInitialize(void)
136 error_page_count
= ERR_MAX
+ ErrorDynamicPages
.size();
137 error_text
= static_cast<char **>(xcalloc(error_page_count
, sizeof(char *)));
139 for (i
= ERR_NONE
, ++i
; i
< error_page_count
; ++i
) {
140 safe_free(error_text
[i
]);
143 if ((text
= errorFindHardText(i
)))
144 error_text
[i
] = xstrdup(text
);
145 else if (i
< ERR_MAX
) {
147 error_text
[i
] = errorLoadText(err_type_str
[i
]);
150 ErrorDynamicPageInfo
*info
= ErrorDynamicPages
.items
[i
- ERR_MAX
];
151 assert(info
&& info
->id
== i
&& info
->page_name
);
153 if (strchr(info
->page_name
, ':') == NULL
) {
154 /* Not on redirected errors... */
155 error_text
[i
] = errorLoadText(info
->page_name
);
167 for (i
= ERR_NONE
+ 1; i
< error_page_count
; i
++)
168 safe_free(error_text
[i
]);
170 safe_free(error_text
);
173 while (ErrorDynamicPages
.size())
174 errorDynamicPageInfoDestroy(ErrorDynamicPages
.pop_back());
176 error_page_count
= 0;
180 errorFindHardText(err_type type
)
184 for (i
= 0; i
< error_hard_text_count
; i
++)
185 if (error_hard_text
[i
].type
== type
)
186 return error_hard_text
[i
].text
;
193 errorLoadText(const char *page_name
)
195 /* test configured location */
196 char *text
= errorTryLoadText(page_name
, Config
.errorDirectory
);
197 /* test default location if failed */
199 if (!text
&& strcmp(Config
.errorDirectory
, DEFAULT_SQUID_ERROR_DIR
))
200 text
= errorTryLoadText(page_name
, DEFAULT_SQUID_ERROR_DIR
);
202 /* giving up if failed */
204 fatal("failed to find or read error text file.");
210 errorTryLoadText(const char *page_name
, const char *dir
)
213 char path
[MAXPATHLEN
];
219 snprintf(path
, sizeof(path
), "%s/%s", dir
, page_name
);
220 fd
= file_open(path
, O_RDONLY
| O_TEXT
);
223 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path
, xstrerror());
227 memBufDefInit(&textbuf
);
229 while((len
= FD_READ_METHOD(fd
, buf
, sizeof(buf
))) > 0) {
230 memBufAppend(&textbuf
, buf
, len
);
234 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
240 if (strstr(textbuf
.buf
, "%s") == NULL
)
241 memBufAppend(&textbuf
, "%S", 2); /* add signature */
243 /* Shrink memory size down to exact size. MemBuf has a tencendy
244 * to be rather large..
246 text
= xstrdup(textbuf
.buf
);
248 memBufClean(&textbuf
);
253 static ErrorDynamicPageInfo
*
254 errorDynamicPageInfoCreate(int id
, const char *page_name
)
256 ErrorDynamicPageInfo
*info
= new ErrorDynamicPageInfo
;
258 info
->page_name
= xstrdup(page_name
);
263 errorDynamicPageInfoDestroy(ErrorDynamicPageInfo
* info
)
266 xfree(info
->page_name
);
271 errorPageId(const char *page_name
)
273 for (int i
= 0; i
< ERR_MAX
; i
++) {
274 if (strcmp(err_type_str
[i
], page_name
) == 0)
278 for (size_t j
= 0; j
< ErrorDynamicPages
.size(); j
++) {
279 if (strcmp(ErrorDynamicPages
.items
[j
]->page_name
, page_name
) == 0)
287 errorReservePageId(const char *page_name
)
289 ErrorDynamicPageInfo
*info
;
290 int id
= errorPageId(page_name
);
292 if (id
== ERR_NONE
) {
293 info
= errorDynamicPageInfoCreate(ERR_MAX
+ ErrorDynamicPages
.size(), page_name
);
294 ErrorDynamicPages
.push_back(info
);
302 errorPageName(int pageId
)
304 if (pageId
>= ERR_NONE
&& pageId
< ERR_MAX
) /* common case */
305 return err_type_str
[pageId
];
307 if (pageId
>= ERR_MAX
&& pageId
- ERR_MAX
< (ssize_t
)ErrorDynamicPages
.size())
308 return ErrorDynamicPages
.items
[pageId
- ERR_MAX
]->page_name
;
310 return "ERR_UNKNOWN"; /* should not happen */
316 * Abstract: This function creates a ErrorState object.
319 errorCon(err_type type
, http_status status
)
322 err
= cbdataAlloc(ErrorState
);
323 err
->page_id
= type
; /* has to be reset manually if needed */
325 err
->httpStatus
= status
;
330 * Function: errorAppendEntry
332 * Arguments: err - This object is destroyed after use in this function.
334 * Abstract: This function generates a error page from the info contained
335 * by 'err' and then stores the text in the specified store
336 * entry. This function should only be called by ``server
337 * side routines'' which need to communicate errors to the
338 * client side. It should also be called from client_side.c
339 * because we now support persistent connections, and
340 * cannot assume that we can immediately write to the socket
344 errorAppendEntry(StoreEntry
* entry
, ErrorState
* err
)
347 MemObject
*mem
= entry
->mem_obj
;
349 assert (entry
->isEmpty());
351 if (entry
->store_status
!= STORE_PENDING
) {
353 * If the entry is not STORE_PENDING, then no clients
354 * care about it, and we don't need to generate an
357 assert(EBIT_TEST(entry
->flags
, ENTRY_ABORTED
));
358 assert(mem
->nclients
== 0);
363 if (err
->page_id
== TCP_RESET
) {
365 debug(4, 2) ("RSTing this reply\n");
366 err
->request
->flags
.setResetTCP();
370 storeLockObject(entry
);
372 rep
= errorBuildReply(err
);
373 /* Add authentication header */
374 /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
375 * depends on authenticate behaviour: all schemes to date send no extra data
376 * on 407/401 responses, and do not check the accel state on 401/407 responses
378 authenticateFixHeader(rep
, err
->auth_user_request
, err
->request
, 0, 1);
379 httpReplySwapOut(rep
, entry
);
380 EBIT_CLR(entry
->flags
, ENTRY_FWD_HDR_WAIT
);
381 storeBufferFlush(entry
);
383 storeNegativeCache(entry
);
384 storeReleaseRequest(entry
);
385 storeUnlockObject(entry
);
390 * Function: errorSend
392 * Arguments: err - This object is destroyed after use in this function.
394 * Abstract: This function generates a error page from the info contained
395 * by 'err' and then sends it to the client.
396 * The callback function errorSendComplete() is called after
397 * the page has been written to the client socket (fd).
398 * errorSendComplete() deallocates 'err'. We need to add
399 * 'err' to the cbdata because comm_write() requires it
400 * for all callback data pointers.
402 * Note, normally errorSend() should only be called from
403 * routines in ssl.c and pass.c, where we don't have any
404 * StoreEntry's. In client_side.c we must allocate a StoreEntry
405 * for errors and use errorAppendEntry() to account for
406 * persistent/pipeline connections.
409 errorSend(int fd
, ErrorState
* err
)
412 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd
, err
);
415 * ugh, this is how we make sure error codes get back to
416 * the client side for logging and error tracking.
420 err
->request
->errType
= err
->type
;
422 /* moved in front of errorBuildBuf @?@ */
423 err
->flags
.flag_cbdata
= 1;
425 rep
= errorBuildReply(err
);
427 comm_old_write_mbuf(fd
, httpReplyPack(rep
), errorSendComplete
, err
);
429 httpReplyDestroy(rep
);
433 * Function: errorSendComplete
435 * Abstract: Called by commHandleWrite() after data has been written
436 * to the client socket.
438 * Note: If there is a callback, the callback is responsible for
439 * closeing the FD, otherwise we do it ourseves.
442 errorSendComplete(int fd
, char *bufnotused
, size_t size
, comm_err_t errflag
, void *data
)
444 ErrorState
*err
= static_cast<ErrorState
*>(data
);
445 debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd
, (long int) size
);
447 if (errflag
!= COMM_ERR_CLOSING
) {
449 debug(4, 3) ("errorSendComplete: callback\n");
450 err
->callback(fd
, err
->callback_data
, size
);
453 debug(4, 3) ("errorSendComplete: comm_close\n");
461 errorStateFree(ErrorState
* err
)
463 requestUnlink(err
->request
);
464 safe_free(err
->redirect_url
);
466 safe_free(err
->host
);
467 safe_free(err
->dnsserver_msg
);
468 safe_free(err
->request_hdrs
);
469 wordlistDestroy(&err
->ftp
.server_msg
);
470 safe_free(err
->ftp
.request
);
471 safe_free(err
->ftp
.reply
);
473 if (err
->auth_user_request
)
474 authenticateAuthUserRequestUnlock(err
->auth_user_request
);
476 err
->auth_user_request
= NULL
;
478 safe_free(err
->err_msg
);
484 errorDump(ErrorState
* err
, MemBuf
* mb
)
486 HttpRequest
*r
= err
->request
;
487 MemBuf str
= MemBufNULL
;
488 const char *p
= NULL
; /* takes priority over mb if set */
490 /* email subject line */
491 memBufPrintf(&str
, "CacheErrorInfo - %s", errorPageName(err
->type
));
492 memBufPrintf(mb
, "?subject=%s", rfc1738_escape_part(str
.buf
));
495 memBufPrintf(&str
, "CacheHost: %s\r\n", getMyHostname());
497 memBufPrintf(&str
, "ErrPage: %s\r\n", errorPageName(err
->type
));
500 memBufPrintf(&str
, "Err: (%d) %s\r\n", err
->xerrno
, strerror(err
->xerrno
));
502 memBufPrintf(&str
, "Err: [none]\r\n");
505 if (authenticateAuthUserRequestMessage(err
->auth_user_request
)) {
506 memBufPrintf(&str
, "extAuth ErrMsg: %s\r\n", authenticateAuthUserRequestMessage(err
->auth_user_request
));
509 if (err
->dnsserver_msg
) {
510 memBufPrintf(&str
, "DNS Server ErrMsg: %s\r\n", err
->dnsserver_msg
);
514 memBufPrintf(&str
, "TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime
));
517 memBufPrintf(&str
, "ClientIP: %s\r\n", inet_ntoa(err
->src_addr
));
520 memBufPrintf(&str
, "ServerIP: %s\r\n", err
->host
);
523 memBufPrintf(&str
, "\r\n");
525 memBufPrintf(&str
, "HTTP Request:\r\n");
529 memBufPrintf(&str
, "%s %s HTTP/%d.%d\n",
530 RequestMethodStr
[r
->method
],
531 r
->urlpath
.size() ? r
->urlpath
.buf() : "/",
532 r
->http_ver
.major
, r
->http_ver
.minor
);
533 packerToMemInit(&p
, &str
);
534 httpHeaderPackInto(&r
->header
, &p
);
536 } else if (err
->request_hdrs
) {
537 p
= err
->request_hdrs
;
542 memBufPrintf(&str
, "\r\n");
545 if (err
->ftp
.request
) {
546 memBufPrintf(&str
, "FTP Request: %s\r\n", err
->ftp
.request
);
547 memBufPrintf(&str
, "FTP Reply: %s\r\n", err
->ftp
.reply
);
548 memBufPrintf(&str
, "FTP Msg: ");
549 wordlistCat(err
->ftp
.server_msg
, &str
);
550 memBufPrintf(&str
, "\r\n");
553 memBufPrintf(&str
, "\r\n");
554 memBufPrintf(mb
, "&body=%s", rfc1738_escape_part(str
.buf
));
559 #define CVT_BUF_SZ 512
562 * B - URL with FTP %2f hack x
563 * c - Squid error code x
564 * d - seconds elapsed since request received x
567 * f - FTP request line x
568 * F - FTP reply line x
569 * g - FTP server message x
570 * h - cache hostname x
571 * H - server host name x
572 * i - client IP address x
573 * I - server IP address x
574 * L - HREF link for more info/contact x
575 * M - Request Method x
576 * m - Error message returned by external Auth. x
579 * R - Full HTTP Request x
580 * S - squid signature from ERR_SIGNATURE x
581 * s - caching proxy software with version x
584 * U - URL without password x
585 * u - URL with password x
586 * w - cachemgr email address x
587 * W - error data (to be included in the mailto links)
588 * z - dns server error message x
589 * Z - Preformatted error message x
593 errorConvert(char token
, ErrorState
* err
)
595 HttpRequest
*r
= err
->request
;
596 static MemBuf mb
= MemBufNULL
;
597 const char *p
= NULL
; /* takes priority over mb if set */
605 p
= r
? ftpUrlWith2f(r
) : "[no URL]";
609 p
= errorPageName(err
->type
);
613 memBufPrintf(&mb
, "%d", err
->xerrno
);
619 memBufPrintf(&mb
, "(%d) %s", err
->xerrno
, strerror(err
->xerrno
));
621 memBufPrintf(&mb
, "[No Error]");
626 /* FTP REQUEST LINE */
627 if (err
->ftp
.request
)
628 p
= err
->ftp
.request
;
636 if (err
->ftp
.request
)
644 /* FTP SERVER MESSAGE */
645 wordlistCat(err
->ftp
.server_msg
, &mb
);
650 memBufPrintf(&mb
, "%s", getMyHostname());
655 p
= r
? r
->host
: "[unknown host]";
660 memBufPrintf(&mb
, "%s", inet_ntoa(err
->src_addr
));
666 memBufPrintf(&mb
, "%s", err
->host
);
673 if (Config
.errHtmlText
) {
674 memBufPrintf(&mb
, "%s", Config
.errHtmlText
);
677 p
= "[not available]";
682 p
= authenticateAuthUserRequestMessage(err
->auth_user_request
) ? authenticateAuthUserRequestMessage(err
->auth_user_request
) : "[not available]";
687 p
= r
? RequestMethodStr
[r
->method
] : "[unkown method]";
692 p
= external_acl_message
? external_acl_message
: "[not available]";
698 memBufPrintf(&mb
, "%d", (int) r
->port
);
700 p
= "[unknown port]";
706 p
= r
? ProtocolStr
[r
->protocol
] : "[unkown protocol]";
713 memBufPrintf(&mb
, "%s %s HTTP/%d.%d\n",
714 RequestMethodStr
[r
->method
],
715 r
->urlpath
.size() ? r
->urlpath
.buf() : "/",
716 r
->http_ver
.major
, r
->http_ver
.minor
);
717 packerToMemInit(&p
, &mb
);
718 httpHeaderPackInto(&r
->header
, &p
);
720 } else if (err
->request_hdrs
) {
721 p
= err
->request_hdrs
;
729 p
= full_appname_string
;
733 /* signature may contain %-escapes, recursion */
735 if (err
->page_id
!= ERR_SQUID_SIGNATURE
) {
736 const int saved_id
= err
->page_id
;
738 err
->page_id
= ERR_SQUID_SIGNATURE
;
739 sign_mb
= errorBuildContent(err
);
740 memBufPrintf(&mb
, "%s", sign_mb
.buf
);
741 memBufClean(&sign_mb
);
742 err
->page_id
= saved_id
;
745 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
752 memBufPrintf(&mb
, "%s", mkhttpdlogtime(&squid_curtime
));
756 memBufPrintf(&mb
, "%s", mkrfc1123(squid_curtime
));
760 p
= r
? urlCanonicalClean(r
) : err
->url
? err
->url
: "[no URL]";
764 p
= r
? urlCanonical(r
) : err
->url
? err
->url
: "[no URL]";
769 if (Config
.adminEmail
)
770 memBufPrintf(&mb
, "%s", Config
.adminEmail
);
777 if (Config
.adminEmail
&& Config
.onoff
.emailErrData
)
783 if (err
->dnsserver_msg
)
784 p
= err
->dnsserver_msg
;
804 memBufPrintf(&mb
, "%%%c", token
);
810 p
= mb
.buf
; /* do not use mb after this assignment! */
814 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token
, p
);
822 /* allocates and initializes an error response */
824 errorBuildReply(ErrorState
* err
)
826 HttpReply
*rep
= httpReplyCreate();
827 const char *name
= errorPageName(err
->page_id
);
828 http_version_t version
;
829 /* no LMT for error pages; error pages expire immediately */
830 httpBuildVersion(&version
, 1, 0);
832 if (strchr(name
, ':')) {
834 char *quoted_url
= rfc1738_escape_part(errorConvert('u', err
));
835 httpReplySetHeaders(rep
, version
, HTTP_MOVED_TEMPORARILY
, NULL
, "text/html", 0, 0, squid_curtime
);
836 httpHeaderPutStrf(&rep
->header
, HDR_LOCATION
, name
, quoted_url
);
837 httpHeaderPutStrf(&rep
->header
, HDR_X_SQUID_ERROR
, "%d %s\n", err
->httpStatus
, "Access Denied");
839 MemBuf content
= errorBuildContent(err
);
840 httpReplySetHeaders(rep
, version
, err
->httpStatus
, NULL
, "text/html", content
.size
, 0, squid_curtime
);
842 * include some information for downstream caches. Implicit
843 * replaceable content. This isn't quite sufficient. xerrno is not
844 * necessarily meaningful to another system, so we really should
845 * expand it. Additionally, we should identify ourselves. Someone
846 * might want to know. Someone _will_ want to know OTOH, the first
847 * X-CACHE-MISS entry should tell us who.
849 httpHeaderPutStrf(&rep
->header
, HDR_X_SQUID_ERROR
, "%s %d",
851 httpBodySet(&rep
->body
, &content
);
852 /* do not memBufClean() the content, it was absorbed by httpBody */
859 errorBuildContent(ErrorState
* err
)
866 assert(err
->page_id
> ERR_NONE
&& err
->page_id
< error_page_count
);
867 memBufDefInit(&content
);
868 m
= error_text
[err
->page_id
];
871 while ((p
= strchr(m
, '%'))) {
872 memBufAppend(&content
, m
, p
- m
); /* copy */
873 t
= errorConvert(*++p
, err
); /* convert */
874 memBufPrintf(&content
, "%s", t
); /* copy */
875 m
= p
+ 1; /* advance */
879 memBufPrintf(&content
, "%s", m
); /* copy tail */
881 assert((size_t)content
.size
== strlen(content
.buf
));