redirect.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 61 Redirector */
10 
11 #include "squid.h"
12 #include "acl/Checklist.h"
13 #include "cache_cf.h"
14 #include "client_side.h"
15 #include "client_side_reply.h"
16 #include "client_side_request.h"
17 #include "clientStream.h"
18 #include "comm/Connection.h"
19 #include "fde.h"
20 #include "format/Format.h"
21 #include "globals.h"
22 #include "helper.h"
23 #include "helper/Reply.h"
24 #include "http/Stream.h"
25 #include "HttpRequest.h"
26 #include "mgr/Registration.h"
27 #include "redirect.h"
28 #include "rfc1738.h"
29 #include "sbuf/SBuf.h"
30 #include "SquidConfig.h"
31 #include "Store.h"
32 #if USE_AUTH
33 #include "auth/UserRequest.h"
34 #endif
35 #if USE_OPENSSL
36 #include "ssl/support.h"
37 #endif
38 
40 #define MAX_REDIRECTOR_REQUEST_STRLEN (MAX_URL + 1024)
41 
43 {
45 
46 public:
47  explicit RedirectStateData(const char *url);
49 
50  void *data;
52 
54 };
55 
62 static int redirectorBypassed = 0;
63 static int storeIdBypassed = 0;
65 static Format::Format *storeIdExtrasFmt = nullptr;
66 
68 
70  data(nullptr),
71  orig_url(url),
72  handler(nullptr)
73 {
74 }
75 
77 {
78 }
79 
80 static void
81 redirectHandleReply(void *data, const Helper::Reply &reply)
82 {
83  RedirectStateData *r = static_cast<RedirectStateData *>(data);
84  debugs(61, 5, "reply=" << reply);
85 
86  // XXX: This function is now kept only to check for and display the garbage use-case
87  // and to map the old helper response format(s) into new format result code and key=value pairs
88  // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
89 
90  if (reply.result == Helper::Unknown) {
91  // BACKWARD COMPATIBILITY 2012-06-15:
92  // Some nasty old helpers send back the entire input line including extra format keys.
93  // This is especially bad for simple perl search-replace filter scripts.
94  //
95  // * trim all but the first word off the response.
96  // * warn once every 50 responses that this will stop being fixed-up soon.
97  //
98  if (reply.other().hasContent()) {
99  const char * res = reply.other().content();
100  size_t replySize = 0;
101  if (const char *t = strchr(res, ' ')) {
102  static int warn = 0;
103  debugs(61, (!(warn++%50)? DBG_CRITICAL:2), "WARNING: UPGRADE: URL rewriter reponded with garbage '" << t <<
104  "'. Future Squid will treat this as part of the URL.");
105  replySize = t - res;
106  } else
107  replySize = reply.other().contentSize();
108 
109  // if we still have anything in other() after all that
110  // parse it into status=, url= and rewrite-url= keys
111  if (replySize) {
112  MemBuf replyBuffer;
113  replyBuffer.init(replySize, replySize);
114  replyBuffer.append(reply.other().content(), reply.other().contentSize());
115  char * result = replyBuffer.content();
116 
117  Helper::Reply newReply;
118  // BACKWARD COMPATIBILITY 2012-06-15:
119  // We got Helper::Unknown reply result but new
120  // RedirectStateData handlers require Helper::Okay,
121  // else will drop the helper reply
122  newReply.result = Helper::Okay;
123  newReply.notes.append(&reply.notes);
124 
125  // check and parse for obsoleted Squid-2 urlgroup feature
126  if (*result == '!') {
127  static int urlgroupWarning = 0;
128  if (!urlgroupWarning++)
129  debugs(85, DBG_IMPORTANT, "WARNING: UPGRADE: URL rewriter using obsolete Squid-2 urlgroup feature needs updating.");
130  if (char *t = strchr(result+1, '!')) {
131  *t = '\0';
132  newReply.notes.add("urlgroup", result+1);
133  result = t + 1;
134  }
135  }
136 
137  const Http::StatusCode status = static_cast<Http::StatusCode>(atoi(result));
138 
139  if (status == Http::scMovedPermanently
140  || status == Http::scFound
141  || status == Http::scSeeOther
142  || status == Http::scPermanentRedirect
143  || status == Http::scTemporaryRedirect) {
144 
145  if (const char *t = strchr(result, ':')) {
146  char statusBuf[4];
147  snprintf(statusBuf, sizeof(statusBuf),"%3u",status);
148  newReply.notes.add("status", statusBuf);
149  ++t;
150  // TODO: validate the URL produced here is RFC 2616 compliant URI
151  newReply.notes.add("url", t);
152  } else {
153  debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << result);
154  }
155  } else {
156  // status code is not a redirect code (or does not exist)
157  // treat as a re-write URL request
158  // TODO: validate the URL produced here is RFC 2616 compliant URI
159  if (*result)
160  newReply.notes.add("rewrite-url", result);
161  }
162 
163  void *cbdata;
165  r->handler(cbdata, newReply);
166 
167  delete r;
168  return;
169  }
170  }
171  }
172 
173  void *cbdata;
175  r->handler(cbdata, reply);
176 
177  delete r;
178 }
179 
180 static void
181 storeIdHandleReply(void *data, const Helper::Reply &reply)
182 {
183  RedirectStateData *r = static_cast<RedirectStateData *>(data);
184  debugs(61, 5,"StoreId helper: reply=" << reply);
185 
186  // XXX: This function is now kept only to check for and display the garbage use-case
187  // and to map the old helper response format(s) into new format result code and key=value pairs
188  // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
189  void *cbdata;
191  r->handler(cbdata, reply);
192 
193  delete r;
194 }
195 
196 static void
198 {
199  if (redirectors == nullptr) {
200  storeAppendPrintf(sentry, "No redirectors defined\n");
201  return;
202  }
203 
204  redirectors->packStatsInto(sentry, "Redirector Statistics");
205 
207  storeAppendPrintf(sentry, "\nNumber of requests bypassed "
208  "because all redirectors were busy: %d\n", redirectorBypassed);
209 }
210 
211 static void
213 {
214  if (storeIds == nullptr) {
215  storeAppendPrintf(sentry, "No StoreId helpers defined\n");
216  return;
217  }
218 
219  storeIds->packStatsInto(sentry, "StoreId helper Statistics");
220 
222  storeAppendPrintf(sentry, "\nNumber of requests bypassed "
223  "because all StoreId helpers were busy: %d\n", storeIdBypassed);
224 }
225 
226 static void
227 constructHelperQuery(const char * const name, const Helper::Client::Pointer &hlp, HLPCB * const replyHandler, ClientHttpRequest * const http, HLPCB * const handler, void * const data, Format::Format * const requestExtrasFmt)
228 {
230  int sz;
231  Http::StatusCode status;
232 
236  RedirectStateData *r = new RedirectStateData(http->uri);
237  r->handler = handler;
238  r->data = cbdataReference(data);
239 
240  static MemBuf requestExtras;
241  requestExtras.reset();
242  if (requestExtrasFmt)
243  requestExtrasFmt->assemble(requestExtras, http->al, 0);
244 
245  sz = snprintf(buf, MAX_REDIRECTOR_REQUEST_STRLEN, "%s%s%s\n",
246  r->orig_url.c_str(),
247  requestExtras.hasContent() ? " " : "",
248  requestExtras.hasContent() ? requestExtras.content() : "");
249 
250  if ((sz<=0) || (sz>=MAX_REDIRECTOR_REQUEST_STRLEN)) {
251  if (sz<=0) {
253  debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Can not build request to be passed to " << name << ". Request ABORTED.");
254  } else {
255  status = Http::scUriTooLong;
256  debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Request passed to " << name << " exceeds MAX_REDIRECTOR_REQUEST_STRLEN (" << MAX_REDIRECTOR_REQUEST_STRLEN << "). Request ABORTED.");
257  }
258 
260  clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
261  assert (repContext);
262  repContext->setReplyToError(ERR_GATEWAY_FAILURE, status,
263  nullptr,
264  http->getConn(),
265  http->request,
266  nullptr,
267 #if USE_AUTH
268  http->getConn() != nullptr && http->getConn()->getAuth() != nullptr ?
269  http->getConn()->getAuth() : http->request->auth_user_request);
270 #else
271  nullptr);
272 #endif
273 
275  clientStreamRead(node, http, node->readBuffer);
276  return;
277  }
278 
279  debugs(61,6, "sending '" << buf << "' to the " << name << " helper");
280  helperSubmit(hlp, buf, replyHandler, r);
281 }
282 
283 /**** PUBLIC FUNCTIONS ****/
284 
285 void
286 redirectStart(ClientHttpRequest * http, HLPCB * handler, void *data)
287 {
288  assert(http);
289  assert(handler);
290  debugs(61, 5, "redirectStart: '" << http->uri << "'");
291 
292  // TODO: Deprecate Config.onoff.redirector_bypass in favor of either
293  // onPersistentOverload or a new onOverload option that applies to all helpers.
294  if (Config.onoff.redirector_bypass && redirectors->willOverload()) {
295  /* Skip redirector if the queue is full */
297  Helper::Reply bypassReply;
298  bypassReply.result = Helper::Okay;
299  bypassReply.notes.add("message","URL rewrite/redirect queue too long. Bypassed.");
300  handler(data, bypassReply);
301  return;
302  }
303 
304  constructHelperQuery("redirector", redirectors, redirectHandleReply, http, handler, data, redirectorExtrasFmt);
305 }
306 
311 void
312 storeIdStart(ClientHttpRequest * http, HLPCB * handler, void *data)
313 {
314  assert(http);
315  assert(handler);
316  debugs(61, 5, "storeIdStart: '" << http->uri << "'");
317 
318  if (Config.onoff.store_id_bypass && storeIds->willOverload()) {
319  /* Skip StoreID Helper if the queue is full */
320  ++storeIdBypassed;
321  Helper::Reply bypassReply;
322 
323  bypassReply.result = Helper::Okay;
324 
325  bypassReply.notes.add("message","StoreId helper queue too long. Bypassed.");
326  handler(data, bypassReply);
327  return;
328  }
329 
330  constructHelperQuery("storeId helper", storeIds, storeIdHandleReply, http, handler, data, storeIdExtrasFmt);
331 }
332 
333 void
335 {
336  static bool init = false;
337 
338  if (!init) {
339  Mgr::RegisterAction("redirector", "URL Redirector Stats", redirectStats, 0, 1);
340  Mgr::RegisterAction("store_id", "StoreId helper Stats", storeIdStats, 0, 1);
341  }
342 
343  if (Config.Program.redirect) {
344 
345  if (redirectors == nullptr)
346  redirectors = Helper::Client::Make("redirector");
347 
348  redirectors->cmdline = Config.Program.redirect;
349 
350  // BACKWARD COMPATIBILITY:
351  // if redirectot_bypass is set then use queue_size=0 as default size
354 
355  redirectors->childs.updateLimits(Config.redirectChildren);
356 
357  redirectors->ipc_type = IPC_STREAM;
358 
360 
362  redirectors->retryBrokenHelper = true; // XXX: make this configurable ?
363  redirectors->onTimedOutResponse.clear();
365  redirectors->onTimedOutResponse.assign(Config.onUrlRewriteTimeout.response);
366 
367  redirectors->openSessions();
368  }
369 
370  if (Config.Program.store_id) {
371 
372  if (storeIds == nullptr)
373  storeIds = Helper::Client::Make("store_id");
374 
375  storeIds->cmdline = Config.Program.store_id;
376 
377  // BACKWARD COMPATIBILITY:
378  // if store_id_bypass is set then use queue_size=0 as default size
381 
382  storeIds->childs.updateLimits(Config.storeIdChildren);
383 
384  storeIds->ipc_type = IPC_STREAM;
385 
386  storeIds->retryBrokenHelper = true; // XXX: make this configurable ?
387 
388  storeIds->openSessions();
389  }
390 
392  delete redirectorExtrasFmt;
393  redirectorExtrasFmt = new ::Format::Format("url_rewrite_extras");
395  }
396 
397  if (Config.storeId_extras) {
398  delete storeIdExtrasFmt;
399  storeIdExtrasFmt = new ::Format::Format("store_id_extras");
401  }
402 
403  init = true;
404 }
405 
406 void
408 {
413  if (redirectors)
415 
416  if (storeIds)
418 
419  if (!shutting_down)
420  return;
421 
422  redirectors = nullptr;
423 
424  storeIds = nullptr;
425 
426  delete redirectorExtrasFmt;
427  redirectorExtrasFmt = nullptr;
428 
429  delete storeIdExtrasFmt;
430  storeIdExtrasFmt = nullptr;
431 }
432 
433 void
435 {
437  redirectInit();
438 }
439 
Definition: parse.c:104
static int redirectorBypassed
Definition: redirect.cc:62
#define DBG_CRITICAL
Definition: Stream.h:37
unsigned int queue_size
Definition: ChildConfig.h:91
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
@ Unknown
Definition: ResultCode.h:17
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
ConnStateData * getConn() const
static Format::Format * redirectorExtrasFmt
Definition: redirect.cc:64
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
bool hasContent() const
Definition: MemBuf.h:54
struct SquidConfig::@90 Program
Helper::ChildConfig storeIdChildren
Definition: SquidConfig.h:217
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:93
@ scMovedPermanently
Definition: StatusCode.h:38
char * redirector_extras
Definition: SquidConfig.h:530
Definition: cbdata.cc:37
struct SquidConfig::@97 onoff
Auth::UserRequest::Pointer auth_user_request
Definition: HttpRequest.h:127
char * storeId_extras
Definition: SquidConfig.h:537
StatusCode
Definition: StatusCode.h:20
#define cbdataReference(var)
Definition: cbdata.h:348
bool parse(const char *def)
Definition: Format.cc:66
static Pointer Make(const char *name)
Definition: helper.cc:757
@ scSeeOther
Definition: StatusCode.h:40
static OBJH redirectStats
Definition: redirect.cc:60
void helperShutdown(const Helper::Client::Pointer &hlp)
Definition: helper.cc:769
static int storeIdBypassed
Definition: redirect.cc:63
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
@ scTemporaryRedirect
Definition: StatusCode.h:43
const MemBuf & other() const
Definition: Reply.h:42
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
void OBJH(StoreEntry *)
Definition: forward.h:44
wordlist * store_id
Definition: SquidConfig.h:202
static HLPCB storeIdHandleReply
Definition: redirect.cc:57
Helper::ResultCode result
The helper response 'result' field.
Definition: Reply.h:59
struct SquidConfig::UrlHelperTimeout onUrlRewriteTimeout
Definition: MemBuf.h:23
RedirectStateData(const char *url)
Definition: redirect.cc:69
NotePairs notes
Definition: Reply.h:62
static Format::Format * storeIdExtrasFmt
Definition: redirect.cc:65
int redirector_bypass
Definition: SquidConfig.h:301
@ scPermanentRedirect
Definition: StatusCode.h:44
#define assert(EX)
Definition: assert.h:17
@ Okay
Definition: ResultCode.h:18
void warn(char *format,...)
@ scUriTooLong
Definition: StatusCode.h:59
int store_id_bypass
Definition: SquidConfig.h:302
@ toutActRetry
Definition: redirect.h:16
static void constructHelperQuery(const char *const name, const Helper::Client::Pointer &hlp, HLPCB *const replyHandler, ClientHttpRequest *const http, HLPCB *const handler, void *const data, Format::Format *const requestExtrasFmt)
Definition: redirect.cc:227
@ toutActUseConfiguredResponse
Definition: redirect.h:16
void helperSubmit(const Helper::Client::Pointer &hlp, const char *const buf, HLPCB *const callback, void *const data)
Definition: helper.cc:480
Format
whether Action report uses valid YAML or unspecified/legacy formatting
@ scInternalServerError
Definition: StatusCode.h:73
const char * c_str()
Definition: SBuf.cc:516
#define MAX_REDIRECTOR_REQUEST_STRLEN
url maximum length + extra information passed to redirector
Definition: redirect.cc:40
static Helper::Client::Pointer storeIds
Definition: redirect.cc:59
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
static HLPCB redirectHandleReply
Definition: redirect.cc:56
void redirectReconfigure()
Definition: redirect.cc:434
const Auth::UserRequest::Pointer & getAuth() const
Definition: client_side.h:123
@ scFound
Definition: StatusCode.h:39
static Helper::Client::Pointer redirectors
Definition: redirect.cc:58
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition: Notes.cc:379
time_t urlRewrite
Definition: SquidConfig.h:132
wordlist * redirect
Definition: SquidConfig.h:201
struct SquidConfig::@84 Timeout
char * content()
start of the added data
Definition: MemBuf.h:41
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
void redirectStart(ClientHttpRequest *http, HLPCB *handler, void *data)
Definition: redirect.cc:286
#define DBG_IMPORTANT
Definition: Stream.h:38
void redirectInit(void)
Definition: redirect.cc:334
Helper::ChildConfig redirectChildren
Definition: SquidConfig.h:216
void reset()
Definition: MemBuf.cc:129
HLPCB * handler
Definition: redirect.cc:53
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:317
void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const
assemble the state information into a formatted line.
Definition: Format.cc:377
const AccessLogEntry::Pointer al
access.log entry
void storeIdStart(ClientHttpRequest *http, HLPCB *handler, void *data)
Definition: redirect.cc:312
int shutting_down
void redirectShutdown(void)
Definition: redirect.cc:407
void HLPCB(void *, const Helper::Reply &)
Definition: forward.h:33
static OBJH storeIdStats
Definition: redirect.cc:61
#define IPC_STREAM
Definition: defines.h:104
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
class SquidConfig Config
Definition: SquidConfig.cc:12
HttpRequest *const request

 

Introduction

Documentation

Support

Miscellaneous