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