errorpage.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 04 Error Generation */
10 
11 #include "squid.h"
12 #include "AccessLogEntry.h"
13 #include "base/CharacterSet.h"
14 #include "base/IoManip.h"
15 #include "cache_cf.h"
16 #include "clients/forward.h"
17 #include "comm/Connection.h"
18 #include "comm/Write.h"
19 #include "error/Detail.h"
20 #include "error/SysErrorDetail.h"
21 #include "errorpage.h"
22 #include "fde.h"
23 #include "format/Format.h"
24 #include "fs_io.h"
25 #include "html/Quoting.h"
26 #include "HttpHeaderTools.h"
27 #include "HttpReply.h"
28 #include "HttpRequest.h"
29 #include "MemBuf.h"
30 #include "MemObject.h"
31 #include "rfc1738.h"
32 #include "sbuf/Stream.h"
33 #include "SquidConfig.h"
34 #include "Store.h"
35 #include "tools.h"
36 #include "wordlist.h"
37 #if USE_AUTH
38 #include "auth/UserRequest.h"
39 #endif
40 #if USE_OPENSSL
41 #include "ssl/ErrorDetailManager.h"
42 #endif
43 
44 #include <array>
45 
57 #if !defined(DEFAULT_SQUID_ERROR_DIR)
58 
61 #define DEFAULT_SQUID_ERROR_DIR DEFAULT_SQUID_DATA_DIR"/errors"
62 #endif
63 
66 
67 const SBuf ErrorState::LogformatMagic("@Squid{");
68 
69 /* local types */
70 
73 public:
74  ErrorDynamicPageInfo(const int anId, const char *aName, const SBuf &aCfgLocation);
76 
78  int id;
79 
83  char *page_name;
84 
86  const char *uri;
87 
89  const char *filename;
90 
93 
94  // XXX: Misnamed. Not just for redirects.
97 
98 private:
99  // no copying of any kind
101 };
102 
103 namespace ErrorPage {
104 
106 class Build
107 {
108 public:
110  const char *input = nullptr;
111  bool building_deny_info_url = false;
112  bool allowRecursion = false;
113 };
114 
117 {
118 public:
119  BuildErrorPrinter(const SBuf &anInputLocation, int aPage, const char *aMsg, const char *anErrorLocation):
120  inputLocation(anInputLocation),
121  page_id(aPage),
122  msg(aMsg),
123  errorLocation(anErrorLocation)
124  {}
125 
127  std::ostream &print(std::ostream &) const;
128 
130  std::ostream &printLocation(std::ostream &os) const;
131 
132  /* saved constructor parameters */
134  const int page_id;
135  const char *msg;
136  const char *errorLocation;
137 };
138 
139 static inline std::ostream &
140 operator <<(std::ostream &os, const BuildErrorPrinter &context)
141 {
142  return context.print(os);
143 }
144 
145 static const char *IsDenyInfoUri(const int page_id);
146 
147 static void ImportStaticErrorText(const int page_id, const char *text, const SBuf &inputLocation);
148 static void ValidateStaticError(const int page_id, const SBuf &inputLocation);
149 
150 } // namespace ErrorPage
151 
152 /* local constant and vars */
153 
156 public:
158  const char *text;
159 };
160 
162 static const std::array<HardCodedError, 7> HardCodedErrors = {
163  {
164  {
166  "\n<br>\n"
167  "<hr>\n"
168  "<div id=\"footer\">\n"
169  "Generated %T by %h (%s)\n"
170  "</div>\n"
171  "</body></html>\n"
172  },
173  {
174  TCP_RESET,
175  "reset"
176  },
177  {
179  "unexpected client disconnect"
180  },
181  {
183  "secure accept fail"
184  },
185  {
187  "request start timedout"
188  },
189  {
191  "request parse timedout"
192  },
193  {
195  "relay server response"
196  }
197  }
198 };
199 
201 static std::vector<ErrorDynamicPageInfo *> ErrorDynamicPages;
202 
203 /* local prototypes */
204 
206 static char **error_text = nullptr;
207 
209 static int error_page_count = 0;
210 
213 
214 static const char *errorFindHardText(err_type type);
216 
220 {
221 public:
222  ErrorPageFile(const char *name, const err_type code) : TemplateFile(name, code) {}
223 
225  const char *text() { return template_.c_str(); }
226 
227 protected:
228  void setDefault() override {
229  template_ = "Internal Error: Missing Template ";
230  template_.append(templateName.termedBuf());
231  }
232 };
233 
235 static err_type &
237 {
238  int tmp = (int)anErr;
239  anErr = (err_type)(++tmp);
240  return anErr;
241 }
242 
244 static int
245 operator -(err_type const &anErr, err_type const &anErr2)
246 {
247  return (int)anErr - (int)anErr2;
248 }
249 
252 static const char *
253 ErrorPage::IsDenyInfoUri(const int page_id)
254 {
255  if (ERR_MAX <= page_id && page_id < error_page_count)
256  return ErrorDynamicPages.at(page_id - ERR_MAX)->uri; // may be nil
257  return nullptr;
258 }
259 
260 void
262 {
264 
265  err_type i;
266  const char *text;
268  error_text = static_cast<char **>(xcalloc(error_page_count, sizeof(char *)));
269 
270  for (i = ERR_NONE, ++i; i < error_page_count; ++i) {
271  safe_free(error_text[i]);
272 
273  if ((text = errorFindHardText(i))) {
277  static const SBuf builtIn("built-in");
278  ImportStaticErrorText(i, text, builtIn);
279 
280  } else if (i < ERR_MAX) {
286  ErrorPageFile errTmpl(err_type_str[i], i);
287  errTmpl.loadDefault();
288  ImportStaticErrorText(i, errTmpl.text(), errTmpl.filename);
289  } else {
294  assert(info && info->id == i && info->page_name);
295 
296  if (info->filename) {
298  ErrorPageFile errTmpl(info->filename, ERR_MAX);
299  errTmpl.loadDefault();
300  ImportStaticErrorText(i, errTmpl.text(), errTmpl.filename);
301  } else {
302  assert(info->uri);
304  }
305  }
306  }
307 
309 
310  // look for and load stylesheet into global MemBuf for it.
311  if (Config.errorStylesheet) {
312  ErrorPageFile tmpl("StylesSheet", ERR_MAX);
314  error_stylesheet.appendf("%s",tmpl.text());
315  }
316 
317 #if USE_OPENSSL
319 #endif
320 }
321 
322 void
324 {
325  if (error_text) {
326  int i;
327 
328  for (i = ERR_NONE + 1; i < error_page_count; ++i)
329  safe_free(error_text[i]);
330 
332  }
333 
334  while (!ErrorDynamicPages.empty()) {
335  delete ErrorDynamicPages.back();
336  ErrorDynamicPages.pop_back();
337  }
338 
339  error_page_count = 0;
340 
341 #if USE_OPENSSL
343 #endif
344 }
345 
347 static const char *
349 {
350  for (const auto &m: HardCodedErrors) {
351  if (m.type == type)
352  return m.text;
353  }
354  return nullptr;
355 }
356 
357 TemplateFile::TemplateFile(const char *name, const err_type code): silent(false), wasLoaded(false), templateName(name), templateCode(code)
358 {
359  assert(name);
360 }
361 
362 void
364 {
365  if (loaded()) // already loaded?
366  return;
367 
369  if (Config.errorDirectory) {
370  char path[MAXPATHLEN];
371  snprintf(path, sizeof(path), "%s/%s", Config.errorDirectory, templateName.termedBuf());
372  loadFromFile(path);
373  }
374 
375 #if USE_ERR_LOCALES
376 
377  if (!loaded() && Config.errorDefaultLanguage) {
379  debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "ERROR: Unable to load default error language files. Reset to backups.");
380  }
381  }
382 #endif
383 
384  /* test default location if failed (templates == English translation base templates) */
385  if (!loaded()) {
386  tryLoadTemplate("templates");
387  }
388 
389  /* giving up if failed */
390  if (!loaded()) {
391  debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "WARNING: failed to find or read error text file " << templateName);
392  template_.clear();
393  setDefault();
394  wasLoaded = true;
395  }
396 }
397 
398 bool
400 {
401  assert(lang);
402 
403  char path[MAXPATHLEN];
404  /* TODO: prep the directory path string to prevent snprintf ... */
405  snprintf(path, sizeof(path), "%s/%s/%s",
407  path[MAXPATHLEN-1] = '\0';
408 
409  if (loadFromFile(path))
410  return true;
411 
412 #if HAVE_GLOB
413  if ( strlen(lang) == 2) {
414  /* TODO glob the error directory for sub-dirs matching: <tag> '-*' */
415  /* use first result. */
416  debugs(4,2, "wildcard fallback errors not coded yet.");
417  }
418 #endif
419 
420  return false;
421 }
422 
423 bool
424 TemplateFile::loadFromFile(const char *path)
425 {
426  int fd;
427  char buf[4096];
428  ssize_t len;
429 
430  if (loaded()) // already loaded?
431  return true;
432 
433  fd = file_open(path, O_RDONLY | O_TEXT);
434 
435  if (fd < 0) {
436  /* with dynamic locale negotiation we may see some failures before a success. */
437  if (!silent && templateCode < TCP_RESET) {
438  int xerrno = errno;
439  debugs(4, DBG_CRITICAL, "ERROR: loading file '" << path << "': " << xstrerr(xerrno));
440  }
441  wasLoaded = false;
442  return wasLoaded;
443  }
444 
445  template_.clear();
446  while ((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
447  template_.append(buf, len);
448  }
449 
450  if (len < 0) {
451  int xerrno = errno;
452  file_close(fd);
453  debugs(4, DBG_CRITICAL, MYNAME << "ERROR: failed to fully read: '" << path << "': " << xstrerr(xerrno));
454  wasLoaded = false;
455  return false;
456  }
457 
458  file_close(fd);
459 
460  filename = SBuf(path);
461 
462  if (!parse()) {
463  debugs(4, DBG_CRITICAL, "ERROR: parsing error in template file: " << path);
464  wasLoaded = false;
465  return false;
466  }
467 
468  wasLoaded = true;
469  return wasLoaded;
470 }
471 
472 bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
473 {
474  while (pos < hdr.size()) {
475 
476  /* skip any initial whitespace. */
477  while (pos < hdr.size() && xisspace(hdr[pos]))
478  ++pos;
479 
480  /*
481  * Header value format:
482  * - sequence of whitespace delimited tags
483  * - each tag may suffix with ';'.* which we can ignore.
484  * - IFF a tag contains only two characters we can wildcard ANY translations matching: <it> '-'? .*
485  * with preference given to an exact match.
486  */
487  bool invalid_byte = false;
488  char *dt = lang;
489  while (pos < hdr.size() && hdr[pos] != ';' && hdr[pos] != ',' && !xisspace(hdr[pos]) && dt < (lang + (langLen -1)) ) {
490  if (!invalid_byte) {
491 #if USE_HTTP_VIOLATIONS
492  // if accepting violations we may as well accept some broken browsers
493  // which may send us the right code, wrong ISO formatting.
494  if (hdr[pos] == '_')
495  *dt = '-';
496  else
497 #endif
498  *dt = xtolower(hdr[pos]);
499  // valid codes only contain A-Z, hyphen (-) and *
500  if (*dt != '-' && *dt != '*' && (*dt < 'a' || *dt > 'z') )
501  invalid_byte = true;
502  else
503  ++dt; // move to next destination byte.
504  }
505  ++pos;
506  }
507  *dt = '\0'; // nul-terminated the filename content string before system use.
508 
509  // if we terminated the tag on garbage or ';' we need to skip to the next ',' or end of header.
510  while (pos < hdr.size() && hdr[pos] != ',')
511  ++pos;
512 
513  if (pos < hdr.size() && hdr[pos] == ',')
514  ++pos;
515 
516  debugs(4, 9, "STATE: lang=" << lang << ", pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
517 
518  /* if we found anything we might use, try it. */
519  if (*lang != '\0' && !invalid_byte)
520  return true;
521  }
522  return false;
523 }
524 
525 bool
527 {
528  String hdr;
529 
530 #if USE_ERR_LOCALES
531  if (loaded()) // already loaded?
532  return true;
533 
534  if (!request || !request->header.getList(Http::HdrType::ACCEPT_LANGUAGE, &hdr))
535  return false;
536 
537  char lang[256];
538  size_t pos = 0; // current parsing position in header string
539 
540  debugs(4, 6, "Testing Header: '" << hdr << "'");
541 
542  while ( strHdrAcptLangGetItem(hdr, lang, 256, pos) ) {
543 
544  /* wildcard uses the configured default language */
545  if (lang[0] == '*' && lang[1] == '\0') {
546  debugs(4, 6, "Found language '" << lang << "'. Using configured default.");
547  return false;
548  }
549 
550  debugs(4, 6, "Found language '" << lang << "', testing for available template");
551 
552  if (tryLoadTemplate(lang)) {
553  /* store the language we found for the Content-Language reply header */
554  errLanguage = lang;
555  break;
556  } else if (Config.errorLogMissingLanguages) {
557  debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << lang);
558  }
559  }
560 #else
561  (void)request;
562 #endif
563 
564  return loaded();
565 }
566 
567 ErrorDynamicPageInfo::ErrorDynamicPageInfo(const int anId, const char *aName, const SBuf &aCfgLocation):
568  id(anId),
569  page_name(xstrdup(aName)),
570  uri(nullptr),
571  filename(nullptr),
572  cfgLocation(aCfgLocation),
573  page_redirect(static_cast<Http::StatusCode>(atoi(page_name)))
574 {
575  const char *filenameOrUri = nullptr;
576  if (xisdigit(*page_name)) {
577  if (const char *statusCodeEnd = strchr(page_name, ':'))
578  filenameOrUri = statusCodeEnd + 1;
579  } else {
581  filenameOrUri = page_name;
582  }
583 
584  // Guessed uri, filename, or both values may be nil or malformed.
585  // They are validated later.
586  if (!page_redirect) {
587  if (filenameOrUri && strchr(filenameOrUri, ':')) // looks like a URL
588  uri = filenameOrUri;
589  else
590  filename = filenameOrUri;
591  }
592  else if (page_redirect/100 == 3) {
593  // redirects imply a URL
594  uri = filenameOrUri;
595  } else {
596  // non-redirects imply an error page name
597  filename = filenameOrUri;
598  }
599 
600  const auto info = this; // source code change reduction hack
601  // TODO: Move and refactor to avoid self_destruct()s in reconfigure.
602 
603  /* WARNING on redirection status:
604  * 2xx are permitted, but not documented officially.
605  * - might be useful for serving static files (PAC etc) in special cases
606  * 3xx require a URL suitable for Location: header.
607  * - the current design does not allow for a Location: URI as well as a local file template
608  * although this possibility is explicitly permitted in the specs.
609  * 4xx-5xx require a local file template.
610  * - sending Location: on these codes with no body is invalid by the specs.
611  * - current result is Squid crashing or XSS problems as dynamic deny_info load random disk files.
612  * - a future redesign of the file loading may result in loading remote objects sent inline as local body.
613  */
614  if (info->page_redirect == Http::scNone)
615  ; // special case okay.
616  else if (info->page_redirect < 200 || info->page_redirect > 599) {
617  // out of range
618  debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " is not valid on '" << page_name << "'");
619  self_destruct();
620  } else if ( /* >= 200 && */ info->page_redirect < 300 && strchr(&(page_name[4]), ':')) {
621  // 2xx require a local template file
622  debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a template on '" << page_name << "'");
623  self_destruct();
624  } else if (info->page_redirect >= 300 && info->page_redirect <= 399 && !strchr(&(page_name[4]), ':')) {
625  // 3xx require an absolute URL
626  debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a URL on '" << page_name << "'");
627  self_destruct();
628  } else if (info->page_redirect >= 400 /* && <= 599 */ && strchr(&(page_name[4]), ':')) {
629  // 4xx/5xx require a local template file
630  debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a template on '" << page_name << "'");
631  self_destruct();
632  }
633  // else okay.
634 }
635 
637 static int
638 errorPageId(const char *page_name)
639 {
640  for (int i = 0; i < ERR_MAX; ++i) {
641  if (strcmp(err_type_str[i], page_name) == 0)
642  return i;
643  }
644 
645  for (size_t j = 0; j < ErrorDynamicPages.size(); ++j) {
646  if (strcmp(ErrorDynamicPages[j]->page_name, page_name) == 0)
647  return j + ERR_MAX;
648  }
649 
650  return ERR_NONE;
651 }
652 
653 err_type
654 errorReservePageId(const char *page_name, const SBuf &cfgLocation)
655 {
656  int id = errorPageId(page_name);
657 
658  if (id == ERR_NONE) {
659  id = ERR_MAX + ErrorDynamicPages.size();
660  const auto info = new ErrorDynamicPageInfo(id, page_name, cfgLocation);
661  ErrorDynamicPages.push_back(info);
662  }
663 
664  return (err_type)id;
665 }
666 
668 const char *
669 errorPageName(int pageId)
670 {
671  if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */
672  return err_type_str[pageId];
673 
674  if (pageId >= ERR_MAX && pageId - ERR_MAX < (ssize_t)ErrorDynamicPages.size())
675  return ErrorDynamicPages[pageId - ERR_MAX]->page_name;
676 
677  return "ERR_UNKNOWN"; /* should not happen */
678 }
679 
681 static std::ostream &
682 operator <<(std::ostream &os, const ErrorState &err)
683 {
684  os << errorPageName(err.type);
685  if (err.httpStatus != Http::scNone)
686  os << "/http_status=" << err.httpStatus;
687  return os;
688 }
689 
690 ErrorState *
692 {
693  const Http::StatusCode status = (request && request->flags.needValidation) ?
695  return new ErrorState(type, status, request.getRaw(), ale);
696 }
697 
699  type(t),
700  page_id(t),
701  callback(nullptr),
702  ale(anAle)
703 {
704 }
705 
707  ErrorState(t, anAle)
708 {
709  if (page_id >= ERR_MAX && ErrorDynamicPages[page_id - ERR_MAX]->page_redirect != Http::scNone)
710  httpStatus = ErrorDynamicPages[page_id - ERR_MAX]->page_redirect;
711  else
712  httpStatus = status;
713 
714  if (req) {
715  request = req;
716  src_addr = req->client_addr;
717  }
718 
719  debugs(4, 3, "constructed, this=" << static_cast<void*>(this) << ' ' << *this);
720 }
721 
724 {
725  Must(errorReply);
726  response_ = errorReply;
727  httpStatus = errorReply->sline.status();
728 
729  if (req) {
730  request = req;
731  src_addr = req->client_addr;
732  }
733 
734  debugs(4, 3, "constructed, this=" << static_cast<void*>(this) << " relaying " << *this);
735 }
736 
737 void
739 {
740  assert(entry->mem_obj != nullptr);
741  assert (entry->isEmpty());
742  debugs(4, 4, "storing " << err << " in " << *entry);
743 
744  if (const auto &request = err->request) {
745  if (const auto &bodyPipe = request->body_pipe) {
746  // We cannot expectNoConsumption() here (yet): This request may be a
747  // virgin request being consumed by adaptation that should continue
748  // even in error-handling cases. startAutoConsumptionIfNeeded() call
749  // triggered by enableAutoConsumption() below skips such requests.
750  //
751  // Today, we also cannot enableAutoConsumption() earlier because it
752  // could result in premature consumption in BodyPipe::postAppend()
753  // followed by an unwanted setConsumerIfNotLate() failure.
754  //
755  // TODO: Simplify BodyPipe auto-consumption by automatically
756  // enabling it when no new consumers are expected, removing the need
757  // for explicit enableAutoConsumption() calls like the one below.
758  //
759  // Code like clientReplyContext::sendClientOldEntry() might use
760  // another StoreEntry for this master transaction, but we want to
761  // consume this request body even in those hypothetical error cases
762  // to prevent stuck (client-Squid or REQMOD) transactions.
763  bodyPipe->enableAutoConsumption();
764  }
765  }
766 
767  if (entry->store_status != STORE_PENDING) {
768  debugs(4, 2, "Skipping error page due to store_status: " << entry->store_status);
769  /*
770  * If the entry is not STORE_PENDING, then no clients
771  * care about it, and we don't need to generate an
772  * error message
773  */
775  assert(entry->mem_obj->nclients == 0);
776  delete err;
777  return;
778  }
779 
780  if (err->page_id == TCP_RESET) {
781  if (err->request) {
782  debugs(4, 2, "RSTing this reply");
783  err->request->flags.resetTcp = true;
784  }
785  }
786 
787  entry->storeErrorResponse(err->BuildHttpReply());
788  delete err;
789 }
790 
791 void
793 {
794  debugs(4, 3, conn << ", err=" << err);
795  assert(Comm::IsConnOpen(conn));
796 
797  HttpReplyPointer rep(err->BuildHttpReply());
798 
799  MemBuf *mb = rep->pack();
800  AsyncCall::Pointer call = commCbCall(78, 5, "errorSendComplete",
802  Comm::Write(conn, mb, call);
803  delete mb;
804 }
805 
815 static void
816 errorSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int, void *data)
817 {
818  ErrorState *err = static_cast<ErrorState *>(data);
819  debugs(4, 3, conn << ", size=" << size);
820 
821  if (errflag != Comm::ERR_CLOSING) {
822  if (err->callback) {
823  debugs(4, 3, "errorSendComplete: callback");
824  err->callback(conn->fd, err->callback_data, size);
825  } else {
826  debugs(4, 3, "errorSendComplete: comm_close");
827  conn->close();
828  }
829  }
830 
831  delete err;
832 }
833 
835 {
836  debugs(4, 7, "destructing, this=" << static_cast<void*>(this));
837 
839  safe_free(url);
841  wordlistDestroy(&ftp.server_msg);
842  safe_free(ftp.request);
843  safe_free(ftp.reply);
845 #if USE_ERR_LOCALES
847 #endif
849 }
850 
851 int
853 {
854  PackableStream out(*mb);
855  const auto &encoding = CharacterSet::RFC3986_UNRESERVED();
856 
857  out << "?subject=" <<
858  AnyP::Uri::Encode(SBuf("CacheErrorInfo - "),encoding) <<
860 
861  SBufStream body;
862  body << "CacheHost: " << getMyHostname() << "\r\n" <<
863  "ErrPage: " << errorPageName(type) << "\r\n" <<
864  "TimeStamp: " << Time::FormatRfc1123(squid_curtime) << "\r\n" <<
865  "\r\n";
866 
867  body << "ClientIP: " << src_addr << "\r\n";
868 
869  if (request && request->hier.host[0] != '\0')
870  body << "ServerIP: " << request->hier.host << "\r\n";
871 
872  if (xerrno)
873  body << "Err: (" << xerrno << ") " << strerror(xerrno) << "\r\n";
874 
875 #if USE_AUTH
877  body << "Auth ErrMsg: " << auth_user_request->denyMessage() << "\r\n";
878 #endif
879 
880  if (dnsError)
881  body << "DNS ErrMsg: " << *dnsError << "\r\n";
882 
883  body << "\r\n";
884 
885  if (request) {
886  body << "HTTP Request:\r\n";
887  MemBuf r;
888  r.init();
889  request->pack(&r);
890  body << r.content();
891  }
892 
893  /* - FTP stuff */
894 
895  if (ftp.request) {
896  body << "FTP Request: " << ftp.request << "\r\n";
897  if (ftp.reply)
898  body << "FTP Reply: " << ftp.reply << "\r\n";
899  if (ftp.server_msg)
900  body << "FTP Msg: " << AsList(*ftp.server_msg).delimitedBy("\n") << "\r\n";
901  body << "\r\n";
902  }
903 
904  out << "&body=" << AnyP::Uri::Encode(body.buf(), encoding);
905 
906  return 0;
907 }
908 
910 #define CVT_BUF_SZ 512
911 
912 void
914 {
916 
917  try {
918  const auto logformat = build.input + LogformatMagic.length();
919 
920  // Logformat supports undocumented "external" encoding specifications
921  // like [%>h] or "%<a". To preserve the possibility of extending
922  // @Squid{} syntax to non-logformat sequences, we require logformat
923  // sequences to start with '%'. This restriction does not limit
924  // logformat quoting abilities. TODO: Deprecate "external" encoding?
925  if (*logformat != '%')
926  throw TexcHere("logformat expressions that do not start with % are not supported");
927 
928  static MemBuf result;
929  result.reset();
930  const auto logformatLen = Format::AssembleOne(logformat, result, ale);
931  assert(logformatLen > 0);
932  const auto closure = logformat + logformatLen;
933  if (*closure != '}')
934  throw TexcHere("Missing closing brace (})");
935  build.output.append(result.content(), result.contentSize());
936  build.input = closure + 1;
937  return;
938  } catch (...) {
939  noteBuildError("Bad @Squid{logformat} sequence", build.input);
940  }
941 
942  // we cannot recover reliably so stop interpreting the rest of input
943  const auto remainingSize = strlen(build.input);
944  build.output.append(build.input, remainingSize);
945  build.input += remainingSize;
946 }
947 
948 void
950 {
951  static MemBuf mb;
952  const char *p = nullptr; /* takes priority over mb if set */
953  int do_quote = 1;
954  int no_urlescape = 0; /* if true then item is NOT to be further URL-encoded */
955  char ntoabuf[MAX_IPSTRLEN];
956 
957  mb.reset();
958 
959  const auto &building_deny_info_url = build.building_deny_info_url; // a change reduction hack
960 
961  const auto letter = build.input[1];
962 
963  switch (letter) {
964 
965  case 'a':
966 #if USE_AUTH
969  if (!p)
970 #endif
971  p = "-";
972  break;
973 
974  case 'A':
975  // TODO: When/if we get ALE here, pass it as well
976  if (const auto addr = FindListeningPortAddress(request.getRaw(), nullptr))
977  mb.appendf("%s", addr->toStr(ntoabuf, MAX_IPSTRLEN));
978  else
979  p = "-";
980  break;
981 
982  case 'b':
983  mb.appendf("%u", getMyPort());
984  break;
985 
986  case 'B':
987  if (building_deny_info_url) break;
988  if (request) {
989  const SBuf &tmp = Ftp::UrlWith2f(request.getRaw());
990  mb.append(tmp.rawContent(), tmp.length());
991  } else
992  p = "[no URL]";
993  break;
994 
995  case 'c':
996  if (building_deny_info_url) break;
997  p = errorPageName(type);
998  break;
999 
1000  case 'D':
1001  if (!build.allowRecursion)
1002  p = "%D"; // if recursion is not allowed, do not convert
1003  else if (detail) {
1004  auto rawDetail = detail->verbose(request);
1005  // XXX: Performance regression. c_str() reallocates
1006  const auto compiledDetail = compileBody(rawDetail.c_str(), false);
1007  mb.append(compiledDetail.rawContent(), compiledDetail.length());
1008  do_quote = 0;
1009  }
1010  if (!mb.contentSize())
1011  mb.append("[No Error Detail]", 17);
1012  break;
1013 
1014  case 'e':
1015  mb.appendf("%d", xerrno);
1016  break;
1017 
1018  case 'E':
1019  if (xerrno)
1020  mb.appendf("(%d) %s", xerrno, strerror(xerrno));
1021  else
1022  mb.append("[No Error]", 10);
1023  break;
1024 
1025  case 'f':
1026  if (building_deny_info_url) break;
1027  /* FTP REQUEST LINE */
1028  if (ftp.request)
1029  p = ftp.request;
1030  else
1031  p = "nothing";
1032  break;
1033 
1034  case 'F':
1035  if (building_deny_info_url) break;
1036  /* FTP REPLY LINE */
1037  if (ftp.reply)
1038  p = ftp.reply;
1039  else
1040  p = "nothing";
1041  break;
1042 
1043  case 'g':
1044  if (building_deny_info_url) break;
1045  /* FTP SERVER RESPONSE */
1046  if (ftp.listing) {
1047  mb.append(ftp.listing->content(), ftp.listing->contentSize());
1048  do_quote = 0;
1049  } else if (ftp.server_msg) {
1050  wordlistCat(ftp.server_msg, &mb);
1051  }
1052  break;
1053 
1054  case 'h':
1055  mb.appendf("%s", getMyHostname());
1056  break;
1057 
1058  case 'H':
1059  if (request) {
1060  if (request->hier.host[0] != '\0') // if non-empty string.
1061  p = request->hier.host;
1062  else
1063  p = request->url.host();
1064  } else if (!building_deny_info_url)
1065  p = "[unknown host]";
1066  break;
1067 
1068  case 'i':
1069  mb.appendf("%s", src_addr.toStr(ntoabuf,MAX_IPSTRLEN));
1070  break;
1071 
1072  case 'I':
1073  if (request && request->hier.tcpServer)
1074  p = request->hier.tcpServer->remote.toStr(ntoabuf,MAX_IPSTRLEN);
1075  else if (!building_deny_info_url)
1076  p = "[unknown]";
1077  break;
1078 
1079  case 'l':
1080  if (building_deny_info_url) break;
1082  do_quote = 0;
1083  break;
1084 
1085  case 'L':
1086  if (building_deny_info_url) break;
1087  if (Config.errHtmlText) {
1088  mb.appendf("%s", Config.errHtmlText);
1089  do_quote = 0;
1090  } else
1091  p = "[not available]";
1092  break;
1093 
1094  case 'm':
1095  if (building_deny_info_url) break;
1096 #if USE_AUTH
1097  if (auth_user_request.getRaw())
1098  p = auth_user_request->denyMessage("[not available]");
1099  else
1100  p = "[not available]";
1101 #else
1102  p = "-";
1103 #endif
1104  break;
1105 
1106  case 'M':
1107  if (request) {
1108  const SBuf &m = request->method.image();
1109  mb.append(m.rawContent(), m.length());
1110  } else if (!building_deny_info_url)
1111  p = "[unknown method]";
1112  break;
1113 
1114  case 'O':
1115  if (!building_deny_info_url)
1116  do_quote = 0;
1117  [[fallthrough]];
1118  case 'o':
1120  if (!p && !building_deny_info_url)
1121  p = "[not available]";
1122  break;
1123 
1124  case 'p':
1125  if (request && request->url.port()) {
1126  mb.appendf("%hu", *request->url.port());
1127  } else if (!building_deny_info_url) {
1128  p = "[unknown port]";
1129  }
1130  break;
1131 
1132  case 'P':
1133  if (request) {
1134  const SBuf &m = request->url.getScheme().image();
1135  mb.append(m.rawContent(), m.length());
1136  } else if (!building_deny_info_url) {
1137  p = "[unknown protocol]";
1138  }
1139  break;
1140 
1141  case 'R':
1142  if (building_deny_info_url) {
1143  if (request != nullptr) {
1144  const SBuf &tmp = request->url.path();
1145  mb.append(tmp.rawContent(), tmp.length());
1146  no_urlescape = 1;
1147  } else
1148  p = "[no request]";
1149  break;
1150  }
1151  if (request) {
1152  mb.appendf(SQUIDSBUFPH " " SQUIDSBUFPH " %s/%d.%d\n",
1157  request->header.packInto(&mb, true); //hide authorization data
1158  } else if (request_hdrs) {
1159  p = request_hdrs;
1160  } else {
1161  p = "[no request]";
1162  }
1163  break;
1164 
1165  case 's':
1166  /* for backward compat we make %s show the full URL. Drop this in some future release. */
1167  if (building_deny_info_url) {
1168  if (request) {
1169  const SBuf &tmp = request->effectiveRequestUri();
1170  mb.append(tmp.rawContent(), tmp.length());
1171  } else
1172  p = url;
1173  debugs(0, DBG_CRITICAL, "WARNING: deny_info now accepts coded tags. Use %u to get the full URL instead of %s");
1174  } else
1176  break;
1177 
1178  case 'S':
1179  if (building_deny_info_url) {
1181  break;
1182  }
1183  /* signature may contain %-escapes, recursion */
1184  if (page_id != ERR_SQUID_SIGNATURE) {
1185  const int saved_id = page_id;
1187  const auto signature = buildBody();
1188  mb.append(signature.rawContent(), signature.length());
1189  page_id = saved_id;
1190  do_quote = 0;
1191  } else {
1192  /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
1193  p = "[%S]";
1194  }
1195  break;
1196 
1197  case 't':
1199  break;
1200 
1201  case 'T':
1203  break;
1204 
1205  case 'U':
1206  /* Using the fake-https version of absolute-URI so error pages see https:// */
1207  /* even when the url-path cannot be shown as more than '*' */
1208  if (request)
1210  else if (url)
1211  p = url;
1212  else if (!building_deny_info_url)
1213  p = "[no URL]";
1214  break;
1215 
1216  case 'u':
1217  if (request) {
1218  const SBuf &tmp = request->effectiveRequestUri();
1219  mb.append(tmp.rawContent(), tmp.length());
1220  } else if (url)
1221  p = url;
1222  else if (!building_deny_info_url)
1223  p = "[no URL]";
1224  break;
1225 
1226  case 'w':
1227  if (Config.adminEmail)
1228  mb.appendf("%s", Config.adminEmail);
1229  else if (!building_deny_info_url)
1230  p = "[unknown]";
1231  break;
1232 
1233  case 'W':
1234  if (building_deny_info_url) break;
1236  Dump(&mb);
1237  no_urlescape = 1;
1238  do_quote = 0;
1239  break;
1240 
1241  case 'x':
1242  if (detail) {
1243  const auto brief = detail->brief();
1244  mb.append(brief.rawContent(), brief.length());
1245  } else if (!building_deny_info_url) {
1246  p = "[Unknown Error Code]";
1247  }
1248  break;
1249 
1250  case 'z':
1251  if (building_deny_info_url) break;
1252  if (dnsError)
1253  p = dnsError->c_str();
1254  else if (ftp.cwd_msg)
1255  p = ftp.cwd_msg;
1256  else
1257  p = "[unknown]";
1258  break;
1259 
1260  case 'Z':
1261  if (building_deny_info_url) break;
1262  if (err_msg)
1263  p = err_msg;
1264  else
1265  p = "[unknown]";
1266  break;
1267 
1268  case '%':
1269  p = "%";
1270  break;
1271 
1272  default:
1273  if (building_deny_info_url)
1274  bypassBuildErrorXXX("Unsupported deny_info %code", build.input);
1275  else if (letter != ';')
1276  bypassBuildErrorXXX("Unsupported error page %code", build.input);
1277  // else too many "font-size: 100%;" template errors to report
1278 
1279  mb.append(build.input, 2);
1280  do_quote = 0;
1281  break;
1282  }
1283 
1284  if (!p)
1285  p = mb.buf; /* do not use mb after this assignment! */
1286 
1287  assert(p);
1288 
1289  debugs(4, 3, "%" << letter << " --> '" << p << "'" );
1290 
1291  if (do_quote)
1292  p = html_quote(p);
1293 
1294  if (building_deny_info_url && !no_urlescape)
1295  p = rfc1738_escape_part(p);
1296 
1297  // TODO: Optimize by replacing mb with direct build.output usage.
1298  build.output.append(p, strlen(p));
1299  build.input += 2;
1300 }
1301 
1302 void
1304 {
1305  if (const auto urlTemplate = ErrorPage::IsDenyInfoUri(page_id)) {
1306  (void)compile(urlTemplate, true, true);
1307  } else {
1308  assert(page_id > ERR_NONE);
1310  (void)compileBody(error_text[page_id], true);
1311  }
1312 }
1313 
1314 HttpReply *
1316 {
1317  // Make sure error codes get back to the client side for logging and
1318  // error tracking.
1319  if (request) {
1322  } else if (ale) {
1323  Error err(type, detail);
1325  ale->updateError(err);
1326  }
1327 
1328  if (response_)
1329  return response_.getRaw();
1330 
1331  HttpReply *rep = new HttpReply;
1332  const char *name = errorPageName(page_id);
1333  /* no LMT for error pages; error pages expire immediately */
1334 
1335  if (const auto urlTemplate = ErrorPage::IsDenyInfoUri(page_id)) {
1336  /* Redirection */
1338  // Use configured 3xx reply status if set.
1339  if (name[0] == '3')
1340  status = httpStatus;
1341  else {
1342  // Use 307 for HTTP/1.1 non-GET/HEAD requests.
1344  status = Http::scTemporaryRedirect;
1345  }
1346 
1347  rep->setHeaders(status, nullptr, "text/html;charset=utf-8", 0, 0, -1);
1348 
1349  if (request) {
1350  auto location = compile(urlTemplate, true, true);
1351  rep->header.putStr(Http::HdrType::LOCATION, location.c_str());
1352  }
1353 
1354  httpHeaderPutStrf(&rep->header, Http::HdrType::X_SQUID_ERROR, "%d %s", httpStatus, "Access Denied");
1355  } else {
1356  const auto body = buildBody();
1357  rep->setHeaders(httpStatus, nullptr, "text/html;charset=utf-8", body.length(), 0, -1);
1358  /*
1359  * include some information for downstream caches. Implicit
1360  * replaceable content. This isn't quite sufficient. xerrno is not
1361  * necessarily meaningful to another system, so we really should
1362  * expand it. Additionally, we should identify ourselves. Someone
1363  * might want to know. Someone _will_ want to know OTOH, the first
1364  * X-CACHE-MISS entry should tell us who.
1365  */
1367 
1368 #if USE_ERR_LOCALES
1369  /*
1370  * If error page auto-negotiate is enabled in any way, send the Vary.
1371  * RFC 2616 section 13.6 and 14.44 says MAY and SHOULD do this.
1372  * We have even better reasons though:
1373  * see https://wiki.squid-cache.org/KnowledgeBase/VaryNotCaching
1374  */
1375  if (!Config.errorDirectory) {
1376  /* We 'negotiated' this ONLY from the Accept-Language. */
1377  static const SBuf acceptLanguage("Accept-Language");
1378  rep->header.updateOrAddStr(Http::HdrType::VARY, acceptLanguage);
1379  }
1380 
1381  /* add the Content-Language header according to RFC section 14.12 */
1382  if (err_language) {
1384  } else
1385 #endif /* USE_ERROR_LOCALES */
1386  {
1387  /* default templates are in English */
1388  /* language is known unless error_directory override used */
1389  if (!Config.errorDirectory)
1391  }
1392 
1393  rep->body.set(body);
1394  }
1395 
1396  return rep;
1397 }
1398 
1399 SBuf
1401 {
1403 
1404 #if USE_ERR_LOCALES
1405 
1409  if (!Config.errorDirectory && page_id < ERR_MAX) {
1412 
1413  ErrorPageFile localeTmpl(err_type_str[page_id], static_cast<err_type>(page_id));
1414  if (localeTmpl.loadFor(request.getRaw())) {
1415  inputLocation = localeTmpl.filename;
1416  assert(localeTmpl.language());
1417  err_language = xstrdup(localeTmpl.language());
1418  return compileBody(localeTmpl.text(), true);
1419  }
1420  }
1421 #endif /* USE_ERR_LOCALES */
1422 
1427 #if USE_ERR_LOCALES
1428  if (!Config.errorDirectory)
1430 #endif
1431  debugs(4, 2, "No existing error page language negotiated for " << this << ". Using default error file.");
1432  return compileBody(error_text[page_id], true);
1433 }
1434 
1435 SBuf
1436 ErrorState::compileBody(const char *input, bool allowRecursion)
1437 {
1438  return compile(input, false, allowRecursion);
1439 }
1440 
1441 SBuf
1442 ErrorState::compile(const char *input, bool building_deny_info_url, bool allowRecursion)
1443 {
1444  assert(input);
1445 
1446  Build build;
1447  build.building_deny_info_url = building_deny_info_url;
1448  build.allowRecursion = allowRecursion;
1449  build.input = input;
1450 
1451  auto blockStart = build.input;
1452  while (const auto letter = *build.input) {
1453  if (letter == '%') {
1454  build.output.append(blockStart, build.input - blockStart);
1455  compileLegacyCode(build);
1456  blockStart = build.input;
1457  }
1458  else if (letter == '@' && LogformatMagic.cmp(build.input, LogformatMagic.length()) == 0) {
1459  build.output.append(blockStart, build.input - blockStart);
1460  compileLogformatCode(build);
1461  blockStart = build.input;
1462  } else {
1463  ++build.input;
1464  }
1465  }
1466  build.output.append(blockStart, build.input - blockStart);
1467  return build.output;
1468 }
1469 
1477 void
1478 ErrorState::noteBuildError_(const char *const msg, const char * const errorLocation, const bool forceBypass)
1479 {
1481  const auto runtime = !starting_up;
1482  if (runtime || forceBypass) {
1483  // swallow this problem because the admin may not be (and/or the page
1484  // building code is not) ready to handle throwing consequences
1485 
1486  static unsigned int seenErrors = 0;
1487  ++seenErrors;
1488 
1489  const auto debugLevel =
1490  (seenErrors > 100) ? DBG_DATA:
1492  3; // most other errors have been reported as configuration errors
1493 
1494  // Error fatality depends on the error context: Reconfiguration errors
1495  // are, like startup ones, DBG_CRITICAL but will never become FATAL.
1496  if (starting_up && seenErrors <= 10)
1497  debugs(4, debugLevel, "WARNING: The following configuration error will be fatal in future Squid versions");
1498 
1499  debugs(4, debugLevel, "ERROR: " << BuildErrorPrinter(inputLocation, page_id, msg, errorLocation));
1500  } else {
1501  throw TexcHere(ToSBuf(BuildErrorPrinter(inputLocation, page_id, msg, errorLocation)));
1502  }
1503 }
1504 
1505 /* ErrorPage::BuildErrorPrinter */
1506 
1507 std::ostream &
1509  if (!inputLocation.isEmpty())
1510  return os << inputLocation;
1511 
1512  if (page_id < ERR_NONE || page_id >= error_page_count)
1513  return os << "[error page " << page_id << "]"; // should not happen
1514 
1515  if (page_id < ERR_MAX)
1516  return os << err_type_str[page_id];
1517 
1518  return os << "deny_info " << ErrorDynamicPages.at(page_id - ERR_MAX)->page_name;
1519 }
1520 
1521 std::ostream &
1522 ErrorPage::BuildErrorPrinter::print(std::ostream &os) const {
1523  printLocation(os) << ": " << msg << " near ";
1524 
1525  // TODO: Add support for prefix printing to Raw
1526  const size_t maxContextLength = 15; // plus "..."
1527  if (strlen(errorLocation) > maxContextLength) {
1528  os.write(errorLocation, maxContextLength);
1529  os << "...";
1530  } else {
1531  os << errorLocation;
1532  }
1533 
1534  // XXX: We should not be converting (inner) exception to text if we are
1535  // going to throw again. See "add arbitrary (re)thrower-supplied details"
1536  // TODO in TextException.h for a long-term in-catcher solution.
1537  if (std::current_exception())
1538  os << "\n additional info: " << CurrentException;
1539 
1540  return os;
1541 }
1542 
1544 static void
1545 ErrorPage::ImportStaticErrorText(const int page_id, const char *text, const SBuf &inputLocation)
1546 {
1547  assert(!error_text[page_id]);
1548  error_text[page_id] = xstrdup(text);
1549  ValidateStaticError(page_id, inputLocation);
1550 }
1551 
1553 static void
1554 ErrorPage::ValidateStaticError(const int page_id, const SBuf &inputLocation)
1555 {
1556  // Supplying nil ALE pointer limits validation to logformat %code
1557  // recognition by Format::Token::parse(). This is probably desirable
1558  // because actual %code assembly is slow and should not affect validation
1559  // when our ALE cannot have any real data (this code is not associated
1560  // with any real transaction).
1561  ErrorState anErr(err_type(page_id), Http::scNone, nullptr, nullptr);
1562  anErr.inputLocation = inputLocation;
1563  anErr.validate();
1564 }
1565 
1566 std::ostream &
1567 operator <<(std::ostream &os, const ErrorState *err)
1568 {
1569  os << RawPointer(err).orNil();
1570  return os;
1571 }
1572 
char * errorDirectory
Definition: SquidConfig.h:429
const char * xstrerr(int error)
Definition: xstrerror.cc:83
an error page created from admin-configurable metadata (e.g. deny_info)
Definition: errorpage.cc:72
static IOCB errorSendComplete
Definition: errorpage.cc:215
std::ostream & printLocation(std::ostream &os) const
print() helper to report where the error was found
Definition: errorpage.cc:1508
char * buf
Definition: MemBuf.h:134
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
unsigned int major
major version number
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
int getMyPort(void)
Definition: tools.cc:1041
AnyP::ProtocolVersion http_ver
Definition: Message.h:72
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
@ METHOD_HEAD
Definition: MethodType.h:28
#define DBG_CRITICAL
Definition: Stream.h:37
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
static char ** error_text
Definition: errorpage.cc:206
static std::ostream & operator<<(std::ostream &os, const BuildErrorPrinter &context)
Definition: errorpage.cc:140
err_type errorReservePageId(const char *page_name, const SBuf &cfgLocation)
allocates a new slot for the error page
Definition: errorpage.cc:654
static const std::array< HardCodedError, 7 > HardCodedErrors
error messages that cannot be configured/customized externally
Definition: errorpage.cc:162
void setDefault() override
recover from loadDefault() failure to load or parse() a template
Definition: errorpage.cc:228
ERCB * callback
Definition: errorpage.h:185
@ scNone
Definition: StatusCode.h:21
bool needValidation
Definition: RequestFlags.h:50
String templateName
The name of the template.
Definition: errorpage.h:336
HttpHeader header
Definition: Message.h:74
static std::vector< ErrorDynamicPageInfo * > ErrorDynamicPages
Definition: errorpage.cc:201
bool loadFromFile(const char *path)
Definition: errorpage.cc:424
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:738
bool isEmpty() const
Definition: SBuf.h:435
MemObject * mem_obj
Definition: Store.h:220
RequestFlags flags
Definition: HttpRequest.h:141
@ TCP_RESET
Definition: forward.h:77
bool loaded() const
return true if the data loaded from disk without any problem
Definition: errorpage.h:287
int Dump(MemBuf *mb)
Definition: errorpage.cc:852
err_type type
identifies the error (or a special error template part)
Definition: errorpage.cc:157
bool wasLoaded
True if the template data read from disk without any problem.
Definition: errorpage.h:334
const char * external_acl_message
Http::StatusCode page_redirect
admin-configured HTTP status code
Definition: errorpage.cc:96
void updateError(const Error &)
sets (or updates the already stored) transaction error as needed
HttpReplyPointer response_
Definition: errorpage.h:207
#define O_TEXT
Definition: defines.h:131
static err_type & operator++(err_type &anErr)
Definition: errorpage.cc:236
HttpReply * BuildHttpReply(void)
Definition: errorpage.cc:1315
@ ENTRY_ABORTED
Definition: enums.h:110
unsigned int minor
minor version number
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
const char * ProtocolType_str[]
Definition: SBuf.h:93
@ ERR_REQUEST_PARSE_TIMEOUT
Definition: forward.h:82
AccessLogEntryPointer ale
transaction details (or nil)
Definition: errorpage.h:199
#define xstrdup
String errLanguage
The error language of the template.
Definition: errorpage.h:335
int page_id
Definition: errorpage.h:171
const char * errorPageName(int pageId)
error ID to string
Definition: errorpage.cc:669
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:788
const char * filename
admin-configured name for the error page template (custom or standard)
Definition: errorpage.cc:89
struct SquidConfig::@97 onoff
@ CONTENT_LANGUAGE
const char * err_type_str[]
Auth::UserRequest::Pointer auth_user_request
Definition: HttpRequest.h:127
C * getRaw() const
Definition: RefCount.h:89
uint16_t flags
Definition: Store.h:231
const SBuf & image() const
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
char * errorDefaultLanguage
Definition: SquidConfig.h:431
void updateOrAddStr(Http::HdrType, const SBuf &)
Definition: HttpHeader.cc:1083
Http::StatusLine sline
Definition: HttpReply.h:56
char * errHtmlText
Definition: SquidConfig.h:231
@ ERR_NONE
Definition: forward.h:15
StatusCode
Definition: StatusCode.h:20
err_type
Definition: forward.h:14
Definition: forward.h:17
err_type templateCode
The internal code for this template.
Definition: errorpage.h:337
void file_close(int fd)
Definition: fs_io.cc:93
#define xtolower(x)
Definition: xis.h:17
@ ERR_CLOSING
Definition: Flag.h:24
int errorLogMissingLanguages
Definition: SquidConfig.h:432
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
ProtocolType protocol
which protocol this version is for
Error error
the first transaction problem encountered (or falsy)
Definition: HttpRequest.h:161
void self_destruct(void)
Definition: cache_cf.cc:276
const char * text()
The template text data read from disk.
Definition: errorpage.cc:225
const char * input
template bytes that need to be compiled
Definition: errorpage.cc:110
static void ValidateStaticError(const int page_id, const SBuf &inputLocation)
validate static error page
Definition: errorpage.cc:1554
size_t AssembleOne(const char *start, MemBuf &buf, const AccessLogEntryPointer &ale)
Definition: Format.cc:99
an error page (or a part of an error page) with hard-coded template text
Definition: errorpage.cc:155
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:812
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:63
const char * FormatHttpd(time_t)
Definition: gadgets.cc:116
SBuf output
compilation result
Definition: errorpage.cc:109
char * strerror(int ern)
Definition: strerror.c:22
bool building_deny_info_url
whether we compile deny_info URI
Definition: errorpage.cc:111
#define DBG_DATA
Definition: Stream.h:40
void errorDetailInitialize()
a transaction problem
Definition: Error.h:27
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
ErrorPageFile(const char *name, const err_type code)
Definition: errorpage.cc:222
void compileLogformatCode(Build &build)
compile @Squid{code} sequence containing a single logformat code
Definition: errorpage.cc:913
@ scGatewayTimeout
Definition: StatusCode.h:77
TemplateFile(const char *name, const err_type code)
Definition: errorpage.cc:357
HttpBody body
Definition: HttpReply.h:58
bool silent
Whether to print error messages on cache.log file or not. It is user defined.
Definition: errorpage.h:317
const char * FormatRfc1123(time_t)
Definition: rfc1123.cc:202
@ ERR_REQUEST_START_TIMEOUT
Definition: forward.h:81
void set(const SBuf &newContent)
Definition: HttpBody.h:26
virtual void setDefault()
recover from loadDefault() failure to load or parse() a template
Definition: errorpage.h:324
void loadDefault()
Definition: errorpage.cc:363
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
@ scTemporaryRedirect
Definition: StatusCode.h:43
bool allowRecursion
whether top-level compile() calls are OK
Definition: errorpage.cc:112
int size
Definition: ModDevPoll.cc:69
void errorSend(const Comm::ConnectionPointer &conn, ErrorState *err)
Definition: errorpage.cc:792
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
const char * rawContent() const
Definition: SBuf.cc:509
ErrorState()=delete
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
void errorDetailClean()
static ErrorDetail::Pointer NewIfAny(const int errorNo)
@ ERR_SECURE_ACCEPT_FAIL
Definition: forward.h:80
@ ACCEPT_LANGUAGE
err_type type
Definition: errorpage.h:170
const char * username() const
Definition: UserRequest.cc:32
HttpRequestPointer request
Definition: errorpage.h:177
bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
Definition: errorpage.cc:472
MemBuf * pack() const
Definition: HttpReply.cc:112
char * request_hdrs
Definition: errorpage.h:196
void noteBuildError_(const char *msg, const char *errorLocation, bool forceBypass)
Definition: errorpage.cc:1478
char * html_quote(const char *string)
Definition: Quoting.cc:42
Definition: MemBuf.h:23
void bypassBuildErrorXXX(const char *const msg, const char *const errorLocation)
Definition: errorpage.h:158
SBuf text("GET http://resource.com/path HTTP/1.1\r\n" "Host: resource.com\r\n" "Cookie: laijkpk3422r j1noin \r\n" "\r\n")
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
BuildErrorPrinter(const SBuf &anInputLocation, int aPage, const char *aMsg, const char *anErrorLocation)
Definition: errorpage.cc:119
bool tryLoadTemplate(const char *lang)
Definition: errorpage.cc:399
SBuf cfgLocation
deny_info directive position in squid.conf (for reporting)
Definition: errorpage.cc:92
int starting_up
pretty-prints error page/deny_info building error
Definition: errorpage.cc:116
virtual bool parse()
post-process the loaded template
Definition: errorpage.h:321
void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data)
Definition: CommCalls.h:34
char * adminEmail
Definition: SquidConfig.h:193
void validate()
ensures that a future BuildHttpReply() is likely to succeed
Definition: errorpage.cc:1303
int reconfiguring
int emailErrData
Definition: SquidConfig.h:320
void wordlistCat(const wordlist *w, MemBuf *mb)
Definition: wordlist.cc:35
Ip::Address src_addr
Definition: errorpage.h:183
#define safe_free(x)
Definition: xalloc.h:73
Ip::Address remote
Definition: Connection.h:149
store_status_t store_status
Definition: Store.h:243
const char * visible_appname_string
state and parameters shared by several ErrorState::compile*() methods
Definition: errorpage.cc:106
char * err_msg
Definition: errorpage.h:197
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:312
virtual SBuf brief() const =0
#define assert(EX)
Definition: assert.h:17
SBuf compileBody(const char *text, bool allowRecursion)
Definition: errorpage.cc:1436
SBuf image() const
Definition: UriScheme.h:57
auto & delimitedBy(const char *const d)
a c-string to print between consecutive items (if any). Caller must ensure lifetime.
Definition: IoManip.h:188
HierarchyLogEntry hier
Definition: HttpRequest.h:157
std::ostream manipulator to print containers as flat lists
Definition: IoManip.h:176
SBuf buf()
bytes written so far
Definition: Stream.h:41
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define xisdigit(x)
Definition: xis.h:18
@ scServiceUnavailable
Definition: StatusCode.h:76
std::optional< SBuf > dnsError
DNS lookup error message.
Definition: errorpage.h:180
const AnyP::UriScheme & getScheme() const
Definition: Uri.h:58
void packInto(Packable *p, bool mask_sensitive_info=false) const
Definition: HttpHeader.cc:539
void port(const Port p)
reset authority port subcomponent
Definition: Uri.h:90
static std::ostream & operator<<(std::ostream &os, const ErrorState &err)
compactly prints top-level ErrorState information (for debugging)
Definition: errorpage.cc:682
char * err_language
Definition: errorpage.h:172
ErrorDetail::Pointer detail
Definition: errorpage.h:205
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
time_t squid_curtime
Definition: stub_libtime.cc:20
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
@ ERR_MAX
Definition: forward.h:88
#define xfree
int code
Definition: smb-errors.c:145
ErrorDynamicPageInfo(const int anId, const char *aName, const SBuf &aCfgLocation)
Definition: errorpage.cc:567
int xerrno
Definition: errorpage.h:179
const char * uri
admin-configured HTTP Location header value for redirection responses
Definition: errorpage.cc:86
Flag
Definition: Flag.h:15
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
const Ip::Address * FindListeningPortAddress(const HttpRequest *callerRequest, const AccessLogEntry *ale)
Definition: HttpRequest.cc:863
const char * urlCanonicalFakeHttps(const HttpRequest *request)
Definition: Uri.cc:776
void compileLegacyCode(Build &build)
compile a single-letter code like D
Definition: errorpage.cc:949
const char * language()
The language used for the template.
Definition: errorpage.h:313
Comm::ConnectionPointer tcpServer
TCP/IP level details of the last peer/server connection.
char * redirect_url
Definition: errorpage.h:184
@ scFound
Definition: StatusCode.h:39
SBuf inputLocation
the source of the error template (for reporting purposes)
Definition: errorpage.h:117
HttpRequestMethod method
Definition: HttpRequest.h:114
void path(const char *p)
Definition: Uri.h:96
const char * denyMessage(char const *const default_message=nullptr) const
Definition: UserRequest.cc:127
const char * termedBuf() const
Definition: SquidString.h:92
SBuf filename
where the template was loaded from
Definition: errorpage.h:315
char * url
Definition: errorpage.h:178
const SBuf & UrlWith2f(HttpRequest *)
Definition: FtpGateway.cc:2600
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:279
@ ERR_SQUID_SIGNATURE
Definition: forward.h:71
static int operator-(err_type const &anErr, err_type const &anErr2)
Definition: errorpage.cc:245
static ErrorState * NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &)
Creates a general request forwarding error with the right http_status.
Definition: errorpage.cc:691
char * content()
start of the added data
Definition: MemBuf.h:41
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:995
#define DEFAULT_SQUID_ERROR_DIR
Definition: errorpage.cc:61
@ ERR_CLIENT_GONE
Definition: forward.h:79
size_type size() const
Definition: SquidString.h:73
const char * getMyHostname(void)
Definition: tools.cc:467
bool loadFor(const HttpRequest *request)
Definition: errorpage.cc:526
const char * text
a string literal containing the error template
Definition: errorpage.cc:158
bool isEmpty() const
Definition: Store.h:65
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
static MemBuf error_stylesheet
Definition: errorpage.cc:212
static const CharacterSet & RFC3986_UNRESERVED()
allowed URI characters that do not have a reserved purpose, RFC 3986
char * errorStylesheet
Definition: SquidConfig.h:434
#define Must(condition)
Definition: TextException.h:75
#define DBG_IMPORTANT
Definition: Stream.h:38
SBuf buildBody()
locates the right error page template for this error and compiles it
Definition: errorpage.cc:1400
#define MYNAME
Definition: Stream.h:219
void reset()
Definition: MemBuf.cc:129
String extacl_message
Definition: HttpRequest.h:184
void update(const Error &)
if necessary, stores the given error information (if any)
Definition: Error.cc:51
char host[SQUIDHOSTNAMELEN]
String substr(size_type from, size_type to) const
Definition: String.cc:190
int id
error_text[] index for response body (unused in redirection responses)
Definition: errorpage.cc:78
static void ImportStaticErrorText(const int page_id, const char *text, const SBuf &inputLocation)
add error page template to the global index
Definition: errorpage.cc:1545
std::ostream & print(std::ostream &) const
reports error details (for admin-visible exceptions and debugging)
Definition: errorpage.cc:1522
@ ERR_RELAY_REMOTE
Definition: forward.h:83
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
#define xisspace(x)
Definition: xis.h:15
#define MAXPATHLEN
Definition: stdio.h:62
static const char * IsDenyInfoUri(const int page_id)
Definition: errorpage.cc:253
SBuf template_
raw template contents
Definition: errorpage.h:333
int file_open(const char *path, int mode)
Definition: fs_io.cc:65
static const char * errorFindHardText(err_type type)
Definition: errorpage.cc:348
virtual SBuf verbose(const HttpRequestPointer &) const =0
#define rfc1738_escape_part(x)
Definition: rfc1738.h:55
@ METHOD_GET
Definition: MethodType.h:25
RawPointerT< Pointer > RawPointer(const char *label, const Pointer &ptr)
convenience wrapper for creating RawPointerT<> objects
Definition: IoManip.h:73
void * callback_data
Definition: errorpage.h:186
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:744
void errorClean(void)
Definition: errorpage.cc:323
SBuf compile(const char *input, bool building_deny_info_url, bool allowRecursion)
Definition: errorpage.cc:1442
struct ErrorState::@54 ftp
Ip::Address client_addr
Definition: HttpRequest.h:149
void host(const char *src)
Definition: Uri.cc:123
void noteBuildError(const char *const msg, const char *const errorLocation)
Definition: errorpage.h:149
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static const SBuf LogformatMagic
marks each embedded logformat entry
Definition: errorpage.h:212
static SBuf Encode(const SBuf &, const CharacterSet &expected)
Definition: Uri.cc:57
int nclients
Definition: MemObject.h:156
static int errorPageId(const char *page_name)
Definition: errorpage.cc:638
Http::StatusCode httpStatus
Definition: errorpage.h:173
void storeErrorResponse(HttpReply *reply)
Store a prepared error response. MemObject locks the reply object.
Definition: store.cc:1688
Auth::UserRequest::Pointer auth_user_request
Definition: errorpage.h:175
void errorInitialize(void)
Definition: errorpage.cc:261
#define SQUIDSBUFPH
Definition: SBuf.h:31
void pack(Packable *p) const
Definition: HttpRequest.cc:344
class SquidConfig Config
Definition: SquidConfig.cc:12
int unsigned int
Definition: stub_fd.cc:19
static int error_page_count
Definition: errorpage.cc:209
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
@ STORE_PENDING
Definition: enums.h:46

 

Introduction

Documentation

Support

Miscellaneous