errorpage.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:17k
源码类别:

代理服务器

开发平台:

Unix_Linux

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