]>
Commit | Line | Data |
---|---|---|
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 | ||
53 | typedef 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 | 64 | static 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 | 84 | static Vector<ErrorDynamicPageInfo *> ErrorDynamicPages; |
02922e76 | 85 | |
86 | /* local prototypes */ | |
87 | ||
2ac76861 | 88 | static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text); |
02922e76 | 89 | static char **error_text = NULL; |
90 | static int error_page_count = 0; | |
9b312a19 | 91 | |
02922e76 | 92 | static char *errorTryLoadText(const char *page_name, const char *dir); |
93 | static char *errorLoadText(const char *page_name); | |
1d803566 | 94 | static const char *errorFindHardText(err_type type); |
c68e9c6b | 95 | static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name); |
96 | static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info); | |
1d803566 | 97 | static MemBuf errorBuildContent(ErrorState * err); |
b5fb34f1 | 98 | static int errorDump(ErrorState * err, MemBuf * mb); |
fe40a877 | 99 | static const char *errorConvert(char token, ErrorState * err); |
9b312a19 | 100 | static CWCB errorSendComplete; |
1e74c110 | 101 | |
e6ccf245 | 102 | |
103 | err_type &operator++ (err_type &anErr) | |
104 | { | |
105 | anErr = (err_type)(++(int)anErr); | |
106 | return anErr; | |
107 | } | |
4ff59fc7 | 108 | |
109 | int 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 | 122 | void |
0673c0ba | 123 | errorInitialize(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 | 149 | void |
150 | errorClean(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 | 163 | static const char * |
164 | errorFindHardText(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 | ||
174 | static char * | |
02922e76 | 175 | errorLoadText(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 | ||
188 | static char * | |
02922e76 | 189 | errorTryLoadText(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 | 217 | static ErrorDynamicPageInfo * |
218 | errorDynamicPageInfoCreate(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 | ||
226 | static void | |
1afe05c5 | 227 | errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info) |
02922e76 | 228 | { |
229 | assert(info); | |
230 | xfree(info->page_name); | |
4ff59fc7 | 231 | delete info; |
02922e76 | 232 | } |
233 | ||
76cdc28d | 234 | static int |
235 | errorPageId(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 | 248 | err_type |
02922e76 | 249 | errorReservePageId(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 | 261 | static const char * |
262 | errorPageName(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 | */ | |
276 | ErrorState * | |
728da2ee | 277 | errorCon(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 | */ |
301 | void | |
302 | errorAppendEntry(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 | */ |
363 | void | |
364 | errorSend(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 | */ | |
391 | static void | |
3d7e9d7c | 392 | errorSendComplete(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 | 408 | void |
9b312a19 | 409 | errorStateFree(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 | 426 | static int |
427 | errorDump(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 | ||
523 | static const char * | |
9b312a19 | 524 | errorConvert(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 */ |
684 | HttpReply * | |
2ac76861 | 685 | errorBuildReply(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 | 717 | static MemBuf |
718 | errorBuildContent(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 | } |