]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
For erroneous requests, we might not have a http->request structure,
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
79e0dc20 3 * $Id: errorpage.cc,v 1.108 1997/12/02 23:55:06 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
9b312a19 41static char *error_text[ERR_MAX];
42
f5b8bbc4 43static void errorStateFree(ErrorState * err);
fe40a877 44static const char *errorConvert(char token, ErrorState * err);
45static const char *errorBuildBuf(ErrorState * err, int *len);
9b312a19 46static CWCB errorSendComplete;
1e74c110 47
fe40a877 48/*
49 * Function: errorInitialize
50 *
51 * Abstract: This function reads in the error messages formats, and stores
52 * them in error_text[];
53 *
54 * Global effects:
55 * error_text[] - is modified
56 */
b8d8561b 57void
0673c0ba 58errorInitialize(void)
fa966b74 59{
9b312a19 60 err_type i;
61 int fd;
62 char path[MAXPATHLEN];
63 struct stat sb;
9b312a19 64 for (i = ERR_NONE + 1; i < ERR_MAX; i++) {
65 snprintf(path, MAXPATHLEN, "%s/%s",
27cd7235 66 Config.errorDirectory, err_type_str[i]);
9b312a19 67 fd = file_open(path, O_RDONLY, NULL, NULL);
68 if (fd < 0) {
69 debug(4, 0) ("errorInitialize: %s: %s\n", path, xstrerror());
70 fatal("Failed to open error text file");
71 }
72 if (fstat(fd, &sb) < 0)
fe40a877 73 fatal("fstat() failed on error text file");
9b312a19 74 safe_free(error_text[i]);
f787fb1e 75 error_text[i] = xcalloc(sb.st_size + 1, 1);
9b312a19 76 if (read(fd, error_text[i], sb.st_size) != sb.st_size)
fe40a877 77 fatal("failed to fully read error text file");
9b312a19 78 file_close(fd);
1e74c110 79 }
6eb42cae 80}
81
fe40a877 82/*
83 * Function: errorCon
84 *
85 * Abstract: This function creates a ErrorState object.
86 */
87ErrorState *
88errorCon(err_type type, http_status status)
89{
90 ErrorState *err = xcalloc(1, sizeof(ErrorState));
91 err->type = type;
92 err->http_status = status;
93 return err;
94}
95
96/*
97 * Function: errorAppendEntry
98 *
99 * Arguments: err - This object is destroyed after use in this function.
100 *
101 * Abstract: This function generates a error page from the info contained
4e3f29eb 102 * by 'err' and then stores the text in the specified store
103 * entry. This function should only be called by ``server
104 * side routines'' which need to communicate errors to the
79e0dc20 105 * client side. It should also be called from client_side.c
106 * because we now support persistent connections, and
107 * cannot assume that we can immediately write to the socket
108 * for an error.
fe40a877 109 */
110void
111errorAppendEntry(StoreEntry * entry, ErrorState * err)
112{
113 const char *buf;
114 MemObject *mem = entry->mem_obj;
115 int len;
116 assert(entry->store_status == STORE_PENDING);
4e3f29eb 117#if WE_SHOULD_PROBABLY_REQUIRE_THIS
118 assert(mem->inmem_hi == 0);
119#endif
fe40a877 120 buf = errorBuildBuf(err, &len);
121 storeAppend(entry, buf, len);
122 if (mem)
123 mem->reply->code = err->http_status;
124 errorStateFree(err);
fe40a877 125}
126
127/*
128 * Function: errorSend
129 *
130 * Arguments: err - This object is destroyed after use in this function.
131 *
132 * Abstract: This function generates a error page from the info contained
133 * by 'err' and then sends it to the client.
4e3f29eb 134 * The callback function errorSendComplete() is called after
135 * the page has been written to the client socket (fd).
136 * errorSendComplete() deallocates 'err'. We need to add
137 * 'err' to the cbdata because comm_write() requires it
138 * for all callback data pointers.
79e0dc20 139 *
140 * Note, normally errorSend() should only be called from
141 * routines in ssl.c and pass.c, where we don't have any
142 * StoreEntry's. In client_side.c we must allocate a StoreEntry
143 * for errors and use errorAppendEntry() to account for
144 * persistent/pipeline connections.
fe40a877 145 */
146void
147errorSend(int fd, ErrorState * err)
148{
149 const char *buf;
150 int len;
151 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
152 assert(fd >= 0);
153 buf = errorBuildBuf(err, &len);
79a15e0a 154 EBIT_SET(err->flags, ERR_FLAG_CBDATA);
fe40a877 155 cbdataAdd(err);
156 comm_write(fd, xstrdup(buf), len, errorSendComplete, err, xfree);
157}
158
159/*
160 * Function: errorSendComplete
161 *
4e3f29eb 162 * Abstract: Called by commHandleWrite() after data has been written
163 * to the client socket.
fe40a877 164 *
165 * Note: If there is a callback, the callback is responsible for
166 * closeing the FD, otherwise we do it ourseves.
167 */
168static void
79a15e0a 169errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
fe40a877 170{
171 ErrorState *err = data;
172 debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
173 if (errflag != COMM_ERR_CLOSING) {
174 if (err->callback)
175 err->callback(fd, err->callback_data, size);
176 else
177 comm_close(fd);
178 }
179 errorStateFree(err);
fe40a877 180}
181
9b312a19 182static void
183errorStateFree(ErrorState * err)
6eb42cae 184{
9b312a19 185 requestUnlink(err->request);
186 safe_free(err->redirect_url);
187 safe_free(err->url);
76a5501f 188 safe_free(err->host);
5f3c4e9a 189 safe_free(err->dnsserver_msg);
b5af8569 190 safe_free(err->request_hdrs);
79a15e0a 191 if (EBIT_TEST(err->flags, ERR_FLAG_CBDATA))
38d04788 192 cbdataFree(err);
bb0929d8 193 else
194 safe_free(err);
1e74c110 195}
8213067d 196
2658f489 197#define CVT_BUF_SZ 512
fe40a877 198
199/*
be335c22 200 * B - URL with FTP %2f hack x
fe40a877 201 * c - Squid error code
202 * d - seconds elapsed since request received
203 * e - errno x
204 * E - strerror() x
fe40a877 205 * f - FTP request line x
969c39b9 206 * F - FTP reply line x
fe40a877 207 * h - cache hostname x
969c39b9 208 * H - server host name x
fe40a877 209 * i - client IP address x
210 * I - server IP address x
211 * L - HREF link for more info/contact x
212 * M - Request Method x
213 * p - URL port # x
214 * P - Protocol x
b5af8569 215 * R - Full HTTP Request x
fe40a877 216 * t - local time x
217 * T - UTC x
969c39b9 218 * U - URL without password x
219 * u - URL without password, %2f added to path x
fe40a877 220 * w - cachemgr email address x
221 * z - dns server error message x
222 */
223
224static const char *
9b312a19 225errorConvert(char token, ErrorState * err)
8213067d 226{
2658f489 227 request_t *r = err->request;
228 static char buf[CVT_BUF_SZ];
fe40a877 229 const char *p = buf;
9b312a19 230 switch (token) {
8f872bb6 231 case 'B':
232 p = r ? ftpUrlWith2f(r) : "[no URL]";
233 break;
042461c3 234 case 'e':
c45ed9ad 235 snprintf(buf, CVT_BUF_SZ, "%d", err->xerrno);
042461c3 236 break;
237 case 'E':
c45ed9ad 238 snprintf(buf, CVT_BUF_SZ, "(%d) %s", err->xerrno, strerror(err->xerrno));
03d7b07f 239 break;
fe40a877 240 case 'f':
241 /* FTP REQUEST LINE */
242 if (err->ftp.request)
243 p = err->ftp.request;
244 else
245 p = "<none>";
246 break;
247 case 'F':
248 /* FTP REPLY LINE */
249 if (err->ftp.request)
250 p = err->ftp.reply;
251 else
252 p = "<none>";
03d7b07f 253 break;
254 case 'h':
255 snprintf(buf, CVT_BUF_SZ, "%s", getMyHostname());
f787fb1e 256 break;
fe40a877 257 case 'H':
258 p = r ? r->host : "[unknown host]";
f787fb1e 259 break;
260 case 'i':
261 snprintf(buf, CVT_BUF_SZ, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 262 break;
f787fb1e 263 case 'I':
264 if (err->host) {
265 snprintf(buf, CVT_BUF_SZ, "%s", err->host);
f787fb1e 266 } else
fe40a877 267 p = "[unknown]";
268 break;
269 case 'L':
270 if (Config.errHtmlText) {
271 snprintf(buf, CVT_BUF_SZ, "%s", Config.errHtmlText);
272 } else
273 p = "[not available]";
274 break;
275 case 'M':
276 p = r ? RequestMethodStr[r->method] : "[unkown method]";
277 break;
278 case 'p':
279 if (r) {
280 snprintf(buf, CVT_BUF_SZ, "%d", (int) r->port);
281 } else {
282 p = "[unknown port]";
283 }
284 break;
285 case 'P':
286 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
287 break;
b5af8569 288 case 'R':
289 p = err->request_hdrs ? err->request_hdrs : "[no request]";
290 break;
fe40a877 291 case 't':
292 xstrncpy(buf, mkhttpdlogtime(&squid_curtime), 128);
f787fb1e 293 break;
f8291f8f 294 case 'T':
295 snprintf(buf, CVT_BUF_SZ, "%s", mkrfc1123(squid_curtime));
f8291f8f 296 break;
fe40a877 297 case 'U':
b5af8569 298 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
b9916917 299 break;
fe40a877 300 case 'w':
301 if (Config.adminEmail) {
302 snprintf(buf, CVT_BUF_SZ, "%s", Config.adminEmail);
303 } else
304 p = "[unknown]";
305 break;
306 case 'z':
307 if (err->dnsserver_msg)
308 p = err->dnsserver_msg;
b9916917 309 else
fe40a877 310 p = "[unknown]";
b9916917 311 break;
e347f8e5 312 case '%':
313 p = "%";
314 break;
9b312a19 315 default:
316 p = "%UNKNOWN%";
317 break;
318 }
9e6aef51 319 assert(p != NULL);
e102ebda 320 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
9b312a19 321 return p;
8213067d 322}
e381a13d 323
fe40a877 324static const char *
9b312a19 325errorBuildBuf(ErrorState * err, int *len)
e381a13d 326{
9b312a19 327 LOCAL_ARRAY(char, buf, ERROR_BUF_SZ);
328 LOCAL_ARRAY(char, content, ERROR_BUF_SZ);
329 char *hdr;
330 int clen;
331 int tlen;
332 char *m;
b8b780a4 333 char *mx;
9b312a19 334 char *p;
fe40a877 335 const char *t;
9b312a19 336 assert(err != NULL);
337 assert(err->type > ERR_NONE && err->type < ERR_MAX);
b8b780a4 338 mx = m = xstrdup(error_text[err->type]);
9b312a19 339 clen = 0;
340 while ((p = strchr(m, '%'))) {
341 *p = '\0'; /* terminate */
342 xstrncpy(content + clen, m, ERROR_BUF_SZ - clen); /* copy */
343 clen += (p - m); /* advance */
344 if (clen >= ERROR_BUF_SZ)
345 break;
346 p++;
347 m = p + 1;
348 t = errorConvert(*p, err); /* convert */
349 xstrncpy(content + clen, t, ERROR_BUF_SZ - clen); /* copy */
350 clen += strlen(t); /* advance */
351 if (clen >= ERROR_BUF_SZ)
352 break;
353 }
354 if (clen < ERROR_BUF_SZ && m != NULL) {
355 xstrncpy(content + clen, m, ERROR_BUF_SZ - clen);
356 clen += strlen(m);
357 }
358 if (clen >= ERROR_BUF_SZ) {
359 clen = ERROR_BUF_SZ - 1;
360 *(content + clen) = '\0';
361 }
362 assert(clen == strlen(content));
363 hdr = httpReplyHeader((double) 1.0,
364 err->http_status,
b8d8561b 365 "text/html",
9b312a19 366 clen,
437e2060 367 0, /* no LMT for error pages */
9b312a19 368 squid_curtime);
369 tlen = snprintf(buf, ERROR_BUF_SZ, "%s\r\n%s", hdr, content);
370 if (len)
371 *len = tlen;
b8b780a4 372 xfree(mx);
9b312a19 373 return buf;
e381a13d 374}