/* * 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. * Please see the COPYING and CONTRIBUTORS files for details. */ /* DEBUG: section 04 Error Generation */ #ifndef SQUID_SRC_ERRORPAGE_H #define SQUID_SRC_ERRORPAGE_H #include "cbdata.h" #include "comm/forward.h" #include "error/Detail.h" #include "error/forward.h" #include "http/forward.h" #include "http/StatusCode.h" #include "ip/Address.h" #include "log/forward.h" #include "sbuf/SBuf.h" #include "SquidString.h" /* auth/UserRequest.h is empty unless USE_AUTH is defined */ #include "auth/UserRequest.h" #include /// error page callback typedef void ERCB(int fd, void *, size_t); /** \defgroup ErrorPageAPI Error Pages API \ingroup Components \section ErrorPageStringCodes Error Page % codes for text insertion. * \verbatim a - User identity x A - Local listening IP address x B - URL with FTP %2f hack x c - Squid error code x d - seconds elapsed since request received x D - Error details x e - errno x E - strerror() x f - FTP request line x F - FTP reply line x g - FTP server message x h - cache hostname x H - server host name x i - client IP address x I - server IP address x l - HREF link for CSS stylesheet inclusion x L - HREF link for more info/contact x M - Request Method x m - Error message returned by auth helper x o - Message returned external acl helper x p - URL port # x P - Protocol x R - Full HTTP Request x S - squid signature from ERR_SIGNATURE x s - caching proxy software with version x t - local time x T - UTC x U - URL without password x u - URL with password x w - cachemgr email address x W - error data (to be included in the mailto links) x - error name x z - dns server error message x Z - Preformatted error message x \endverbatim * * Plus logformat %codes embedded using @Squid{%logformat_code} syntax. */ class MemBuf; class StoreEntry; class wordlist; namespace ErrorPage { class Build; } // namespace ErrorPage /// \ingroup ErrorPageAPI class ErrorState { CBDATA_CLASS(ErrorState); public: /// creates an error of type other than ERR_RELAY_REMOTE ErrorState(err_type type, Http::StatusCode, HttpRequest * request, const AccessLogEntryPointer &al); ErrorState() = delete; // not implemented. /// creates an ERR_RELAY_REMOTE error ErrorState(HttpRequest * request, HttpReply *, const AccessLogEntryPointer &); ~ErrorState(); /// Creates a general request forwarding error with the right http_status. static ErrorState *NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &); /** * Allocates and initializes an error response */ HttpReply *BuildHttpReply(void); /// set error type-specific detail code void detailError(const ErrorDetail::Pointer &dCode) { detail = dCode; } /// ensures that a future BuildHttpReply() is likely to succeed void validate(); /// the source of the error template (for reporting purposes) SBuf inputLocation; private: typedef ErrorPage::Build Build; /// initializations shared by public constructors ErrorState(err_type, const AccessLogEntryPointer &); /// locates the right error page template for this error and compiles it SBuf buildBody(); /// compiles error page or error detail template (i.e. anything but deny_url) /// \param input the template text to be compiled /// \param allowRecursion whether to compile %codes which produce %codes SBuf compileBody(const char *text, bool allowRecursion); /// compile a single-letter %code like %D void compileLegacyCode(Build &build); /// compile @Squid{%code} sequence containing a single logformat %code void compileLogformatCode(Build &build); /// replaces all legacy and logformat %codes in the given input /// \param input the template text to be converted /// \param building_deny_info_url whether input is a deny_info URL parameter /// \param allowRecursion whether to compile %codes which produce %codes /// \returns the given input with all %codes substituted SBuf compile(const char *input, bool building_deny_info_url, bool allowRecursion); /// React to a compile() error, throwing if buildContext allows. /// \param msg description of what went wrong /// \param errorLocation approximate start of the problematic input void noteBuildError(const char *const msg, const char * const errorLocation) { noteBuildError_(msg, errorLocation, false); } /// Note a compile() error but do not throw for backwards /// compatibility with older configurations that may have such errors. /// Should eventually be replaced with noteBuildError(). /// \param msg description of what went wrong /// \param errorLocation approximate start of the problematic input void bypassBuildErrorXXX(const char *const msg, const char * const errorLocation) { noteBuildError_(msg, errorLocation, true); } /** * CacheManager / Debug dump of the ErrorState object. * Writes output into the given MemBuf. \retval 0 successful completion. */ int Dump(MemBuf * mb); public: err_type type = ERR_NONE; int page_id = ERR_NONE; char *err_language = nullptr; Http::StatusCode httpStatus = Http::scNone; #if USE_AUTH Auth::UserRequest::Pointer auth_user_request; #endif HttpRequestPointer request; char *url = nullptr; int xerrno = 0; std::optional dnsError; ///< DNS lookup error message time_t ttl = 0; Ip::Address src_addr; char *redirect_url = nullptr; ERCB *callback; void *callback_data = nullptr; struct { wordlist *server_msg = nullptr; char *request = nullptr; char *reply = nullptr; char *cwd_msg = nullptr; MemBuf *listing = nullptr; } ftp; char *request_hdrs = nullptr; char *err_msg = nullptr; /* Preformatted error message from the cache */ AccessLogEntryPointer ale; ///< transaction details (or nil) // TODO: Replace type, xerrno and detail with Error while adding a virtual // Error::Detail::sysError() method to extract errno in detailError(). /// type-specific detail about the transaction error; /// overwrites xerrno; ErrorDetail::Pointer detail; HttpReplyPointer response_; private: void noteBuildError_(const char *msg, const char *errorLocation, bool forceBypass); static const SBuf LogformatMagic; ///< marks each embedded logformat entry }; /** \ingroup ErrorPageAPI * * This function finds the error messages formats, and stores * them in error_text[] * \par Global effects: * error_text[] - is modified */ void errorInitialize(void); /// \ingroup ErrorPageAPI void errorClean(void); /** * \ingroup ErrorPageAPI * * This function generates a error page from the info contained * by err and then sends it to the client. * The callback function errorSendComplete() is called after * the page has been written to the client (clientConn). * errorSendComplete() deallocates err. We need to add * err to the cbdata because comm_write() requires it * for all callback data pointers. * \note normally errorSend() should only be called from * routines in ssl.c and pass.c, where we don't have any * StoreEntry's. In client_side.c we must allocate a StoreEntry * for errors and use errorAppendEntry() to account for * persistent/pipeline connections. * \param clientConn socket where page object is to be written \param err This object is destroyed after use in this function. */ void errorSend(const Comm::ConnectionPointer &conn, ErrorState *err); /** \ingroup ErrorPageAPI * * This function generates a error page from the info contained * by err and then stores the text in the specified store * entry. * This function should only be called by "server * side routines" which need to communicate errors to the * client side. It should also be called from client_side.c * because we now support persistent connections, and * cannot assume that we can immediately write to the socket * for an error. * \param entry ?? \param err This object is destroyed after use in this function. */ void errorAppendEntry(StoreEntry *entry, ErrorState *err); /// allocates a new slot for the error page err_type errorReservePageId(const char *page_name, const SBuf &cfgLocation); const char *errorPageName(int pageId); ///< error ID to string /** \ingroup ErrorPageAPI * * loads text templates used for error pages and details; * supports translation of templates */ class TemplateFile { public: TemplateFile(const char *name, const err_type code); virtual ~TemplateFile() {} /// return true if the data loaded from disk without any problem bool loaded() const {return wasLoaded;} /** * Load the page_name template from a file which probably exist at: * (a) admin specified custom directory (error_directory) * (b) default language translation directory (error_default_language) * (c) English sub-directory where errors should ALWAYS exist * If all of the above fail, setDefault() is called. */ void loadDefault(); /** * Load an error template for a given HTTP request. This function examines the * Accept-Language header and select the first available template. If the default * template selected (eg because of a "Accept-Language: *"), or not available * template found this function return false. */ bool loadFor(const HttpRequest *request); /** * Load the file given by "path". It uses the "parse()" method. * On success return true and sets the "defined" member */ bool loadFromFile(const char *path); /// The language used for the template const char *language() {return errLanguage.termedBuf();} SBuf filename; ///< where the template was loaded from bool silent; ///< Whether to print error messages on cache.log file or not. It is user defined. protected: /// post-process the loaded template virtual bool parse() { return true; } /// recover from loadDefault() failure to load or parse() a template virtual void setDefault() {} /** * Try to load the "page_name" template for a given language "lang" * from squid errors directory \return true on success false otherwise */ bool tryLoadTemplate(const char *lang); SBuf template_; ///< raw template contents bool wasLoaded; ///< True if the template data read from disk without any problem String errLanguage; ///< The error language of the template. String templateName; ///< The name of the template err_type templateCode; ///< The internal code for this template. }; /** * Parses the Accept-Language header value and return one language item on * each call. * Will ignore any whitespace, q-values, and detectably invalid language * codes in the header. * * \param hdr is the Accept-Language header value * \param lang a buffer to store parsed language code in * \param langlen the length of the lang buffer * \param pos is used to store the offset state of parsing. Must be "0" on first call. * Will be altered to point at the start of next field-value. * \return true if something looking like a language token has been placed in lang, false otherwise */ bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos); std::ostream &operator <<(std::ostream &, const ErrorState *); #endif /* SQUID_SRC_ERRORPAGE_H */