]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
peer->ip_lookup_pending should not be needed any more
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
901e234d 3 * $Id: errorpage.cc,v 1.122 1998/03/17 04:00:13 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
1e74c110 31
fe40a877 32/*
4e3f29eb 33 * Abstract: These routines are used to generate error messages to be
34 * sent to clients. The error type is used to select between
35 * the various message formats. (formats are stored in the
36 * Config.errorDirectory)
fe40a877 37 */
38
44a47c6e 39#include "squid.h"
1e74c110 40
02922e76 41
42/* local types */
43
44typedef struct {
45 int id;
46 char *page_name;
47} ErrorDynamicPageInfo;
48
49/* local constant and vars */
50
1d803566 51/*
52 * note: hard coded error messages are not appended with %S automagically
53 * to give you more control on the format
54 */
2ac76861 55static const struct {
02922e76 56 int type; /* and page_id */
2ac76861 57 const char *text;
58} error_hard_text[] = {
2ac76861 59 {
60 ERR_SQUID_SIGNATURE,
61 "\n<br clear=\"all\">\n"
62 "<hr noshade size=1>\n"
02922e76 63 "Generated on %T by <a href=\"http://squid.nlanr.net/\">%s</a>@%h\n"
1d803566 64 }
65};
02922e76 66
67static Stack ErrorDynamicPages;
68
69/* local prototypes */
70
2ac76861 71static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
02922e76 72static char **error_text = NULL;
73static int error_page_count = 0;
9b312a19 74
02922e76 75static char *errorTryLoadText(const char *page_name, const char *dir);
76static char *errorLoadText(const char *page_name);
1d803566 77static const char *errorFindHardText(err_type type);
78static MemBuf errorBuildContent(ErrorState * err);
fe40a877 79static const char *errorConvert(char token, ErrorState * err);
9b312a19 80static CWCB errorSendComplete;
1e74c110 81
fe40a877 82/*
83 * Function: errorInitialize
84 *
1d803566 85 * Abstract: This function finds the error messages formats, and stores
fe40a877 86 * them in error_text[];
87 *
88 * Global effects:
89 * error_text[] - is modified
90 */
b8d8561b 91void
0673c0ba 92errorInitialize(void)
fa966b74 93{
02922e76 94 int i;
1d803566 95 const char *text;
02922e76 96 error_page_count = ERR_MAX + ErrorDynamicPages.count;
97 error_text = xcalloc(error_page_count, sizeof(char*));
98 for (i = ERR_NONE + 1; i < error_page_count; i++) {
1d803566 99 safe_free(error_text[i]);
100 /* hard-coded ? */
101 if ((text = errorFindHardText(i)))
102 error_text[i] = xstrdup(text);
103 else
02922e76 104 /* precompiled ? */
105 if (i < ERR_MAX)
106 error_text[i] = errorLoadText(err_type_str[i]);
107 /* dynamic */
108 else {
109 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i-ERR_MAX];
110 assert(info && info->id == i && info->page_name);
111 error_text[i] = errorLoadText(info->page_name);
112 }
1d803566 113 assert(error_text[i]);
114 }
115}
116
117static const char *
118errorFindHardText(err_type type)
119{
120 int i;
121 for (i = 0; i < error_hard_text_count; i++)
122 if (error_hard_text[i].type == type)
123 return error_hard_text[i].text;
124 return NULL;
125}
126
127
128static char *
02922e76 129errorLoadText(const char *page_name)
1d803566 130{
131 /* test configured location */
02922e76 132 char *text = errorTryLoadText(page_name, Config.errorDirectory);
1d803566 133 /* test default location if failed */
134 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
02922e76 135 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
1d803566 136 /* giving up if failed */
137 if (!text)
138 fatal("failed to find or read error text file.");
139 return text;
140}
141
142static char *
02922e76 143errorTryLoadText(const char *page_name, const char *dir)
1d803566 144{
9b312a19 145 int fd;
146 char path[MAXPATHLEN];
147 struct stat sb;
1d803566 148 char *text;
149
150 snprintf(path, MAXPATHLEN, "%s/%s",
02922e76 151 dir, page_name);
1d803566 152 fd = file_open(path, O_RDONLY, NULL, NULL, NULL);
153 if (fd < 0 || fstat(fd, &sb) < 0) {
154 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
155 if (fd >= 0)
156 file_close(fd);
157 return NULL;
158 }
159 text = xcalloc(sb.st_size + 2 + 1, 1);
160 if (read(fd, text, sb.st_size) != sb.st_size) {
161 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
162 path, xstrerror());
163 xfree(text);
164 text = NULL;
1e74c110 165 }
1d803566 166 file_close(fd);
2ac76861 167 strcat(text, "%S"); /* add signature */
1d803566 168 return text;
6eb42cae 169}
170
02922e76 171static ErrorDynamicPageInfo *
172errorDynamicPageInfoCreate(int id, const char *page_name)
173{
174 ErrorDynamicPageInfo *info = xcalloc(1, sizeof(ErrorDynamicPageInfo));
175 info->id = id;
176 info->page_name = xstrdup(page_name);
177 return info;
178}
179
180static void
181errorDynamicPageInfoDestroy(ErrorDynamicPageInfo *info)
182{
183 assert(info);
184 xfree(info->page_name);
185 xfree(info);
186}
187
188int
189errorReservePageId(const char *page_name)
190{
191 ErrorDynamicPageInfo *info =
192 errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.count, page_name);
193 stackPush(&ErrorDynamicPages, info);
194 return info->id;
195}
196
53ad48e6 197void
198errorFree(void)
199{
200 int i;
02922e76 201 for (i = ERR_NONE + 1; i < error_page_count; i++)
53ad48e6 202 safe_free(error_text[i]);
02922e76 203 while (ErrorDynamicPages.count)
204 errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages));
205 safe_free(error_text);
206 error_page_count = 0;
53ad48e6 207}
208
fe40a877 209/*
210 * Function: errorCon
211 *
212 * Abstract: This function creates a ErrorState object.
213 */
214ErrorState *
02922e76 215errorCon(int type, http_status status)
fe40a877 216{
217 ErrorState *err = xcalloc(1, sizeof(ErrorState));
02922e76 218 err->page_id = type; /* has to be reset manually if needed */
fe40a877 219 err->type = type;
220 err->http_status = status;
221 return err;
222}
223
224/*
225 * Function: errorAppendEntry
226 *
227 * Arguments: err - This object is destroyed after use in this function.
228 *
229 * Abstract: This function generates a error page from the info contained
4e3f29eb 230 * by 'err' and then stores the text in the specified store
231 * entry. This function should only be called by ``server
232 * side routines'' which need to communicate errors to the
79e0dc20 233 * client side. It should also be called from client_side.c
234 * because we now support persistent connections, and
235 * cannot assume that we can immediately write to the socket
236 * for an error.
fe40a877 237 */
238void
239errorAppendEntry(StoreEntry * entry, ErrorState * err)
240{
cb69b4c7 241 HttpReply *rep;
cb69b4c7 242 MemObject *mem = entry->mem_obj;
901e234d 243#if 0
244 /*
245 * Kostas sez PUT "success" replies might not be STORE_PENDING?
246 */
fe40a877 247 assert(entry->store_status == STORE_PENDING);
54220df8 248#endif
b50179a6 249 assert(mem != NULL);
4e3f29eb 250 assert(mem->inmem_hi == 0);
cb69b4c7 251 rep = errorBuildReply(err);
252 httpReplySwapOut(rep, entry);
253 httpReplyDestroy(rep);
cb69b4c7 254 mem->reply->sline.status = err->http_status;
00691420 255 storeComplete(entry);
b50179a6 256 storeNegativeCache(entry);
257 storeReleaseRequest(entry);
fe40a877 258 errorStateFree(err);
fe40a877 259}
260
261/*
262 * Function: errorSend
263 *
264 * Arguments: err - This object is destroyed after use in this function.
265 *
266 * Abstract: This function generates a error page from the info contained
267 * by 'err' and then sends it to the client.
4e3f29eb 268 * The callback function errorSendComplete() is called after
269 * the page has been written to the client socket (fd).
270 * errorSendComplete() deallocates 'err'. We need to add
271 * 'err' to the cbdata because comm_write() requires it
272 * for all callback data pointers.
79e0dc20 273 *
274 * Note, normally errorSend() should only be called from
275 * routines in ssl.c and pass.c, where we don't have any
276 * StoreEntry's. In client_side.c we must allocate a StoreEntry
277 * for errors and use errorAppendEntry() to account for
278 * persistent/pipeline connections.
fe40a877 279 */
280void
281errorSend(int fd, ErrorState * err)
282{
cb69b4c7 283 HttpReply *rep;
fe40a877 284 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
285 assert(fd >= 0);
88aad2e5 286 /*
287 * ugh, this is how we make sure error codes get back to
288 * the client side for logging and error tracking.
289 */
290 if (err->request)
291 err->request->err_type = err->type;
cb69b4c7 292 /* moved in front of errorBuildBuf @?@ */
79a15e0a 293 EBIT_SET(err->flags, ERR_FLAG_CBDATA);
3f6c0fb2 294 cbdataAdd(err, MEM_NONE);
cb69b4c7 295 rep = errorBuildReply(err);
296 comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
297 httpReplyDestroy(rep);
fe40a877 298}
299
300/*
301 * Function: errorSendComplete
302 *
4e3f29eb 303 * Abstract: Called by commHandleWrite() after data has been written
304 * to the client socket.
fe40a877 305 *
306 * Note: If there is a callback, the callback is responsible for
307 * closeing the FD, otherwise we do it ourseves.
308 */
309static void
79a15e0a 310errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
fe40a877 311{
312 ErrorState *err = data;
313 debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
314 if (errflag != COMM_ERR_CLOSING) {
315 if (err->callback)
316 err->callback(fd, err->callback_data, size);
317 else
318 comm_close(fd);
319 }
320 errorStateFree(err);
fe40a877 321}
322
cb69b4c7 323void
9b312a19 324errorStateFree(ErrorState * err)
6eb42cae 325{
9b312a19 326 requestUnlink(err->request);
327 safe_free(err->redirect_url);
328 safe_free(err->url);
76a5501f 329 safe_free(err->host);
5f3c4e9a 330 safe_free(err->dnsserver_msg);
b5af8569 331 safe_free(err->request_hdrs);
79a15e0a 332 if (EBIT_TEST(err->flags, ERR_FLAG_CBDATA))
38d04788 333 cbdataFree(err);
bb0929d8 334 else
335 safe_free(err);
1e74c110 336}
8213067d 337
2658f489 338#define CVT_BUF_SZ 512
fe40a877 339
340/*
1d803566 341 * B - URL with FTP %2f hack x
342 * c - Squid error code x
343 * d - seconds elapsed since request received x
fe40a877 344 * e - errno x
345 * E - strerror() x
fe40a877 346 * f - FTP request line x
969c39b9 347 * F - FTP reply line x
fe40a877 348 * h - cache hostname x
969c39b9 349 * H - server host name x
fe40a877 350 * i - client IP address x
351 * I - server IP address x
352 * L - HREF link for more info/contact x
353 * M - Request Method x
354 * p - URL port # x
355 * P - Protocol x
b5af8569 356 * R - Full HTTP Request x
1d803566 357 * S - squid signature from ERR_SIGNATURE x
358 * s - caching proxy software with version x
fe40a877 359 * t - local time x
360 * T - UTC x
969c39b9 361 * U - URL without password x
362 * u - URL without password, %2f added to path x
fe40a877 363 * w - cachemgr email address x
364 * z - dns server error message x
365 */
366
367static const char *
9b312a19 368errorConvert(char token, ErrorState * err)
8213067d 369{
2658f489 370 request_t *r = err->request;
371 static char buf[CVT_BUF_SZ];
fe40a877 372 const char *p = buf;
9b312a19 373 switch (token) {
8f872bb6 374 case 'B':
375 p = r ? ftpUrlWith2f(r) : "[no URL]";
376 break;
042461c3 377 case 'e':
c45ed9ad 378 snprintf(buf, CVT_BUF_SZ, "%d", err->xerrno);
042461c3 379 break;
380 case 'E':
23d92c64 381 if (err->xerrno)
53ad48e6 382 snprintf(buf, CVT_BUF_SZ, "(%d) %s", err->xerrno, strerror(err->xerrno));
23d92c64 383 else
0d87e358 384 snprintf(buf, CVT_BUF_SZ, "[No Error]");
03d7b07f 385 break;
fe40a877 386 case 'f':
387 /* FTP REQUEST LINE */
388 if (err->ftp.request)
389 p = err->ftp.request;
390 else
391 p = "<none>";
392 break;
393 case 'F':
394 /* FTP REPLY LINE */
395 if (err->ftp.request)
396 p = err->ftp.reply;
397 else
398 p = "<none>";
03d7b07f 399 break;
400 case 'h':
401 snprintf(buf, CVT_BUF_SZ, "%s", getMyHostname());
f787fb1e 402 break;
fe40a877 403 case 'H':
404 p = r ? r->host : "[unknown host]";
f787fb1e 405 break;
406 case 'i':
407 snprintf(buf, CVT_BUF_SZ, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 408 break;
f787fb1e 409 case 'I':
410 if (err->host) {
411 snprintf(buf, CVT_BUF_SZ, "%s", err->host);
f787fb1e 412 } else
fe40a877 413 p = "[unknown]";
414 break;
415 case 'L':
416 if (Config.errHtmlText) {
417 snprintf(buf, CVT_BUF_SZ, "%s", Config.errHtmlText);
418 } else
419 p = "[not available]";
420 break;
421 case 'M':
422 p = r ? RequestMethodStr[r->method] : "[unkown method]";
423 break;
424 case 'p':
425 if (r) {
426 snprintf(buf, CVT_BUF_SZ, "%d", (int) r->port);
427 } else {
428 p = "[unknown port]";
429 }
430 break;
431 case 'P':
432 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
433 break;
b5af8569 434 case 'R':
435 p = err->request_hdrs ? err->request_hdrs : "[no request]";
436 break;
1d803566 437 case 's':
438 p = full_appname_string;
439 break;
440 case 'S':
441 /* signature may contain %-escapes, recursion */
02922e76 442 if (err->page_id != ERR_SQUID_SIGNATURE) {
443 const int saved_id = err->page_id;
1d803566 444 MemBuf mb;
02922e76 445 err->page_id = ERR_SQUID_SIGNATURE;
1d803566 446 mb = errorBuildContent(err);
447 snprintf(buf, CVT_BUF_SZ, "%s", mb.buf);
448 memBufClean(&mb);
02922e76 449 err->page_id = saved_id;
1d803566 450 } else {
451 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
452 p = "[%S]";
453 }
454 break;
fe40a877 455 case 't':
456 xstrncpy(buf, mkhttpdlogtime(&squid_curtime), 128);
f787fb1e 457 break;
f8291f8f 458 case 'T':
459 snprintf(buf, CVT_BUF_SZ, "%s", mkrfc1123(squid_curtime));
f8291f8f 460 break;
fe40a877 461 case 'U':
b5af8569 462 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
b9916917 463 break;
fe40a877 464 case 'w':
465 if (Config.adminEmail) {
466 snprintf(buf, CVT_BUF_SZ, "%s", Config.adminEmail);
467 } else
468 p = "[unknown]";
469 break;
470 case 'z':
471 if (err->dnsserver_msg)
472 p = err->dnsserver_msg;
b9916917 473 else
fe40a877 474 p = "[unknown]";
b9916917 475 break;
e347f8e5 476 case '%':
477 p = "%";
478 break;
9b312a19 479 default:
480 p = "%UNKNOWN%";
481 break;
482 }
9e6aef51 483 assert(p != NULL);
e102ebda 484 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
9b312a19 485 return p;
8213067d 486}
e381a13d 487
cb69b4c7 488/* allocates and initializes an error response */
489HttpReply *
2ac76861 490errorBuildReply(ErrorState * err)
cb69b4c7 491{
cb69b4c7 492 HttpReply *rep = httpReplyCreate();
1d803566 493 MemBuf content = errorBuildContent(err);
cb69b4c7 494 /* no LMT for error pages; error pages expire immediately */
1d803566 495 httpReplySetHeaders(rep, 1.0, err->http_status, NULL, "text/html", content.size, 0, squid_curtime);
2ac76861 496 httpBodySet(&rep->body, content.buf, content.size + 1, NULL);
1d803566 497 memBufClean(&content);
cb69b4c7 498 return rep;
499}
500
1d803566 501static MemBuf
502errorBuildContent(ErrorState * err)
cb69b4c7 503{
1d803566 504 MemBuf content;
1d803566 505 const char *m;
506 const char *p;
cb69b4c7 507 const char *t;
508 assert(err != NULL);
02922e76 509 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
1d803566 510 memBufDefInit(&content);
02922e76 511 m = error_text[err->page_id];
1d803566 512 assert(m);
cb69b4c7 513 while ((p = strchr(m, '%'))) {
2ac76861 514 memBufAppend(&content, m, p - m); /* copy */
515 t = errorConvert(*++p, err); /* convert */
516 memBufPrintf(&content, "%s", t); /* copy */
517 m = p + 1; /* advance */
cb69b4c7 518 }
1d803566 519 if (*m)
2ac76861 520 memBufPrintf(&content, "%s", m); /* copy tail */
1d803566 521 assert(content.size == strlen(content.buf));
cb69b4c7 522 return content;
523}