]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/errorpage.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / errorpage.cc
index 1d696609d5c0542cee03d7bb7a8718b2b89a4496..4d257bc8c6ce2971489b23164c5ae9d50468ed1f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -14,7 +14,8 @@
 #include "clients/forward.h"
 #include "comm/Connection.h"
 #include "comm/Write.h"
-#include "err_detail_type.h"
+#include "error/Detail.h"
+#include "error/SysErrorDetail.h"
 #include "errorpage.h"
 #include "fde.h"
 #include "format/Format.h"
@@ -28,7 +29,6 @@
 #include "rfc1738.h"
 #include "sbuf/Stream.h"
 #include "SquidConfig.h"
-#include "SquidTime.h"
 #include "Store.h"
 #include "tools.h"
 #include "wordlist.h"
@@ -39,6 +39,8 @@
 #include "ssl/ErrorDetailManager.h"
 #endif
 
+#include <array>
+
 /**
  \defgroup ErrorPageInternal Error Page Internals
  \ingroup ErrorPageAPI
@@ -142,43 +144,49 @@ static void ValidateStaticError(const int page_id, const SBuf &inputLocation);
 
 /* local constant and vars */
 
-/**
- \ingroup ErrorPageInternal
- *
- \note  hard coded error messages are not appended with %S
- *      automagically to give you more control on the format
- */
-static const struct {
-    int type;           /* and page_id */
-    const char *text;
-}
-
-error_hard_text[] = {
+/// an error page (or a part of an error page) with hard-coded template text
+class HardCodedError {
+public:
+    err_type type; ///< identifies the error (or a special error template part)
+    const char *text; ///< a string literal containing the error template
+};
 
+/// error messages that cannot be configured/customized externally
+static const std::array<HardCodedError, 7> HardCodedErrors = {
     {
-        ERR_SQUID_SIGNATURE,
-        "\n<br>\n"
-        "<hr>\n"
-        "<div id=\"footer\">\n"
-        "Generated %T by %h (%s)\n"
-        "</div>\n"
-        "</body></html>\n"
-    },
-    {
-        TCP_RESET,
-        "reset"
-    },
-    {
-        ERR_SECURE_ACCEPT_FAIL,
-        "secure accept fail"
-    },
-    {
-        ERR_REQUEST_START_TIMEOUT,
-        "request start timedout"
-    },
-    {
-        MGR_INDEX,
-        "mgr_index"
+        {
+            ERR_SQUID_SIGNATURE,
+            "\n<br>\n"
+            "<hr>\n"
+            "<div id=\"footer\">\n"
+            "Generated %T by %h (%s)\n"
+            "</div>\n"
+            "</body></html>\n"
+        },
+        {
+            TCP_RESET,
+            "reset"
+        },
+        {
+            ERR_CLIENT_GONE,
+            "unexpected client disconnect"
+        },
+        {
+            ERR_SECURE_ACCEPT_FAIL,
+            "secure accept fail"
+        },
+        {
+            ERR_REQUEST_START_TIMEOUT,
+            "request start timedout"
+        },
+        {
+            ERR_REQUEST_PARSE_TIMEOUT,
+            "request parse timedout"
+        },
+        {
+            ERR_RELAY_REMOTE,
+            "relay server response"
+        }
     }
 };
 
@@ -188,10 +196,7 @@ static std::vector<ErrorDynamicPageInfo *> ErrorDynamicPages;
 /* local prototypes */
 
 /// \ingroup ErrorPageInternal
-static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
-
-/// \ingroup ErrorPageInternal
-static char **error_text = NULL;
+static char **error_text = nullptr;
 
 /// \ingroup ErrorPageInternal
 static int error_page_count = 0;
@@ -213,14 +218,15 @@ public:
     const char *text() { return template_.c_str(); }
 
 protected:
-    virtual void setDefault() override {
+    void setDefault() override {
         template_ = "Internal Error: Missing Template ";
         template_.append(templateName.termedBuf());
     }
 };
 
 /// \ingroup ErrorPageInternal
-err_type &operator++ (err_type &anErr)
+static err_type &
+operator++ (err_type &anErr)
 {
     int tmp = (int)anErr;
     anErr = (err_type)(++tmp);
@@ -228,7 +234,8 @@ err_type &operator++ (err_type &anErr)
 }
 
 /// \ingroup ErrorPageInternal
-int operator - (err_type const &anErr, err_type const &anErr2)
+static int
+operator -(err_type const &anErr, err_type const &anErr2)
 {
     return (int)anErr - (int)anErr2;
 }
@@ -333,13 +340,11 @@ errorClean(void)
 static const char *
 errorFindHardText(err_type type)
 {
-    int i;
-
-    for (i = 0; i < error_hard_text_count; ++i)
-        if (error_hard_text[i].type == type)
-            return error_hard_text[i].text;
-
-    return NULL;
+    for (const auto &m: HardCodedErrors) {
+        if (m.type == type)
+            return m.text;
+    }
+    return nullptr;
 }
 
 TemplateFile::TemplateFile(const char *name, const err_type code): silent(false), wasLoaded(false), templateName(name), templateCode(code)
@@ -364,7 +369,7 @@ TemplateFile::loadDefault()
     /** test error_default_language location */
     if (!loaded() && Config.errorDefaultLanguage) {
         if (!tryLoadTemplate(Config.errorDefaultLanguage)) {
-            debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "Unable to load default error language files. Reset to backups.");
+            debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "ERROR: Unable to load default error language files. Reset to backups.");
         }
     }
 #endif
@@ -401,7 +406,7 @@ TemplateFile::tryLoadTemplate(const char *lang)
     if ( strlen(lang) == 2) {
         /* TODO glob the error directory for sub-dirs matching: <tag> '-*'   */
         /* use first result. */
-        debugs(4,2, HERE << "wildcard fallback errors not coded yet.");
+        debugs(4,2, "wildcard fallback errors not coded yet.");
     }
 #endif
 
@@ -525,17 +530,17 @@ TemplateFile::loadFor(const HttpRequest *request)
     char lang[256];
     size_t pos = 0; // current parsing position in header string
 
-    debugs(4, 6, HERE << "Testing Header: '" << hdr << "'");
+    debugs(4, 6, "Testing Header: '" << hdr << "'");
 
     while ( strHdrAcptLangGetItem(hdr, lang, 256, pos) ) {
 
         /* wildcard uses the configured default language */
         if (lang[0] == '*' && lang[1] == '\0') {
-            debugs(4, 6, HERE << "Found language '" << lang << "'. Using configured default.");
+            debugs(4, 6, "Found language '" << lang << "'. Using configured default.");
             return false;
         }
 
-        debugs(4, 6, HERE << "Found language '" << lang << "', testing for available template");
+        debugs(4, 6, "Found language '" << lang << "', testing for available template");
 
         if (tryLoadTemplate(lang)) {
             /* store the language we found for the Content-Language reply header */
@@ -545,6 +550,8 @@ TemplateFile::loadFor(const HttpRequest *request)
             debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << lang);
         }
     }
+#else
+    (void)request;
 #endif
 
     return loaded();
@@ -710,11 +717,9 @@ ErrorState::ErrorState(HttpRequest * req, HttpReply *errorReply) :
 void
 errorAppendEntry(StoreEntry * entry, ErrorState * err)
 {
-    assert(entry->mem_obj != NULL);
+    assert(entry->mem_obj != nullptr);
     assert (entry->isEmpty());
-    debugs(4, 4, "Creating an error page for entry " << entry <<
-           " with errorstate " << err <<
-           " page id " << err->page_id);
+    debugs(4, 4, "storing " << err << " in " << *entry);
 
     if (entry->store_status != STORE_PENDING) {
         debugs(4, 2, "Skipping error page due to store_status: " << entry->store_status);
@@ -768,7 +773,7 @@ static void
 errorSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int, void *data)
 {
     ErrorState *err = static_cast<ErrorState *>(data);
-    debugs(4, 3, HERE << conn << ", size=" << size);
+    debugs(4, 3, conn << ", size=" << size);
 
     if (errflag != Comm::ERR_CLOSING) {
         if (err->callback) {
@@ -796,9 +801,6 @@ ErrorState::~ErrorState()
     if (err_language != Config.errorDefaultLanguage)
 #endif
         safe_free(err_language);
-#if USE_OPENSSL
-    delete detail;
-#endif
 }
 
 int
@@ -830,7 +832,7 @@ ErrorState::Dump(MemBuf * mb)
         str.appendf("DNS ErrMsg: %s\r\n", dnsError.termedBuf());
 
     /* - TimeStamp */
-    str.appendf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
+    str.appendf("TimeStamp: %s\r\n\r\n", Time::FormatRfc1123(squid_curtime));
 
     /* - IP stuff */
     str.appendf("ClientIP: %s\r\n", src_addr.toStr(ntoabuf,MAX_IPSTRLEN));
@@ -911,7 +913,7 @@ void
 ErrorState::compileLegacyCode(Build &build)
 {
     static MemBuf mb;
-    const char *p = NULL;   /* takes priority over mb if set */
+    const char *p = nullptr;   /* takes priority over mb if set */
     int do_quote = 1;
     int no_urlescape = 0;       /* if true then item is NOT to be further URL-encoded */
     char ntoabuf[MAX_IPSTRLEN];
@@ -962,18 +964,13 @@ ErrorState::compileLegacyCode(Build &build)
     case 'D':
         if (!build.allowRecursion)
             p = "%D";  // if recursion is not allowed, do not convert
-#if USE_OPENSSL
-        // currently only SSL error details implemented
         else if (detail) {
-            detail->useRequest(request.getRaw());
-            const String &errDetail = detail->toString();
-            if (errDetail.size() > 0) {
-                const auto compiledDetail = compileBody(errDetail.termedBuf(), false);
-                mb.append(compiledDetail.rawContent(), compiledDetail.length());
-                do_quote = 0;
-            }
+            auto rawDetail = detail->verbose(request);
+            // XXX: Performance regression. c_str() reallocates
+            const auto compiledDetail = compileBody(rawDetail.c_str(), false);
+            mb.append(compiledDetail.rawContent(), compiledDetail.length());
+            do_quote = 0;
         }
-#endif
         if (!mb.contentSize())
             mb.append("[No Error Detail]", 17);
         break;
@@ -1081,6 +1078,7 @@ ErrorState::compileLegacyCode(Build &build)
     case 'O':
         if (!building_deny_info_url)
             do_quote = 0;
+        [[fallthrough]];
     case 'o':
         p = request ? request->extacl_message.termedBuf() : external_acl_message;
         if (!p && !building_deny_info_url)
@@ -1106,7 +1104,7 @@ ErrorState::compileLegacyCode(Build &build)
 
     case 'R':
         if (building_deny_info_url) {
-            if (request != NULL) {
+            if (request != nullptr) {
                 const SBuf &tmp = request->url.path();
                 mb.append(tmp.rawContent(), tmp.length());
                 no_urlescape = 1;
@@ -1165,7 +1163,7 @@ ErrorState::compileLegacyCode(Build &build)
         break;
 
     case 'T':
-        mb.appendf("%s", mkrfc1123(squid_curtime));
+        mb.appendf("%s", Time::FormatRfc1123(squid_curtime));
         break;
 
     case 'U':
@@ -1204,13 +1202,12 @@ ErrorState::compileLegacyCode(Build &build)
         break;
 
     case 'x':
-#if USE_OPENSSL
-        if (detail)
-            mb.appendf("%s", detail->errorName());
-        else
-#endif
-            if (!building_deny_info_url)
-                p = "[Unknown Error Code]";
+        if (detail) {
+            const auto brief = detail->brief();
+            mb.append(brief.rawContent(), brief.length());
+        } else if (!building_deny_info_url) {
+            p = "[Unknown Error Code]";
+        }
         break;
 
     case 'z':
@@ -1299,7 +1296,7 @@ ErrorState::BuildHttpReply()
                 status = Http::scTemporaryRedirect;
         }
 
-        rep->setHeaders(status, NULL, "text/html;charset=utf-8", 0, 0, -1);
+        rep->setHeaders(status, nullptr, "text/html;charset=utf-8", 0, 0, -1);
 
         if (request) {
             auto location = compile(urlTemplate, true, true);
@@ -1309,7 +1306,7 @@ ErrorState::BuildHttpReply()
         httpHeaderPutStrf(&rep->header, Http::HdrType::X_SQUID_ERROR, "%d %s", httpStatus, "Access Denied");
     } else {
         const auto body = buildBody();
-        rep->setHeaders(httpStatus, NULL, "text/html;charset=utf-8", body.length(), 0, -1);
+        rep->setHeaders(httpStatus, nullptr, "text/html;charset=utf-8", body.length(), 0, -1);
         /*
          * include some information for downstream caches. Implicit
          * replaceable content. This isn't quite sufficient. xerrno is not
@@ -1329,8 +1326,8 @@ ErrorState::BuildHttpReply()
          */
         if (!Config.errorDirectory) {
             /* We 'negotiated' this ONLY from the Accept-Language. */
-            rep->header.delById(Http::HdrType::VARY);
-            rep->header.putStr(Http::HdrType::VARY, "Accept-Language");
+            static const SBuf acceptLanguage("Accept-Language");
+            rep->header.updateOrAddStr(Http::HdrType::VARY, acceptLanguage);
         }
 
         /* add the Content-Language header according to RFC section 14.12 */
@@ -1351,17 +1348,10 @@ ErrorState::BuildHttpReply()
     // Make sure error codes get back to the client side for logging and
     // error tracking.
     if (request) {
-        int edc = ERR_DETAIL_NONE; // error detail code
-#if USE_OPENSSL
         if (detail)
-            edc = detail->errorNo();
+            request->detailError(type, detail);
         else
-#endif
-            if (detailCode)
-                edc = detailCode;
-            else
-                edc = xerrno;
-        request->detailError(type, edc);
+            request->detailError(type, SysErrorDetail::NewIfAny(xerrno));
     }
 
     return rep;
@@ -1399,7 +1389,7 @@ ErrorState::buildBody()
     if (!Config.errorDirectory)
         err_language = Config.errorDefaultLanguage;
 #endif
-    debugs(4, 2, "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file.");
+    debugs(4, 2, "No existing error page language negotiated for " << this << ". Using default error file.");
     return compileBody(error_text[page_id], true);
 }
 
@@ -1534,3 +1524,13 @@ ErrorPage::ValidateStaticError(const int page_id, const SBuf &inputLocation)
     anErr.validate();
 }
 
+std::ostream &
+operator <<(std::ostream &os, const ErrorState *err)
+{
+    if (err)
+        os << errorPageName(err->page_id);
+    else
+        os << "[none]";
+    return os;
+}
+