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