]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side_request.cc
SourceLayout: shuffle DNS primitives to dns/libdns.la
[thirdparty/squid.git] / src / client_side_request.cc
CommitLineData
edce4d98 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
0a9297f6 3 *
bbc27441
AJ
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.
edce4d98 7 */
8
bbc27441
AJ
9/* DEBUG: section 85 Client-side Request Routines */
10
69660be0 11/*
12 * General logic of request processing:
26ac0430 13 *
69660be0 14 * We run a series of tests to determine if access will be permitted, and to do
15 * any redirection. Then we call into the result clientStream to retrieve data.
16 * From that point on it's up to reply management.
edce4d98 17 */
18
582c2af2 19#include "squid.h"
c0941a6a
AR
20#include "acl/FilledChecklist.h"
21#include "acl/Gadgets.h"
65d448bc 22#include "anyp/PortCfg.h"
9e104535 23#include "base/AsyncJobCalls.h"
27bc2077
AJ
24#include "client_side.h"
25#include "client_side_reply.h"
26#include "client_side_request.h"
602d9612 27#include "ClientRequestContext.h"
582c2af2 28#include "clientStream.h"
5c336a3b 29#include "comm/Connection.h"
ec41b64c 30#include "comm/Write.h"
582c2af2 31#include "err_detail_type.h"
2bd84e5f 32#include "errorpage.h"
c4ad1349 33#include "fd.h"
27bc2077 34#include "fde.h"
31971e6a 35#include "format/Token.h"
d05c079c 36#include "gopher.h"
e166785a 37#include "helper.h"
24438ec5 38#include "helper/Reply.h"
5c0c642e 39#include "http.h"
ce394734 40#include "HttpHdrCc.h"
27bc2077
AJ
41#include "HttpReply.h"
42#include "HttpRequest.h"
425de4c8 43#include "ip/QosConfig.h"
602d9612 44#include "ipcache.h"
1c7ae5ff 45#include "log/access_log.h"
27bc2077 46#include "MemObject.h"
8a01b99e 47#include "Parsing.h"
582c2af2 48#include "profiler/Profiler.h"
e166785a 49#include "redirect.h"
4d5904f7 50#include "SquidConfig.h"
27bc2077 51#include "SquidTime.h"
582c2af2 52#include "Store.h"
28204b3b 53#include "StrList.h"
685c6ff5 54#include "tools.h"
b1bd952a 55#include "URL.h"
27bc2077 56#include "wordlist.h"
582c2af2
FC
57#if USE_AUTH
58#include "auth/UserRequest.h"
59#endif
60#if USE_ADAPTATION
61#include "adaptation/AccessCheck.h"
62#include "adaptation/Answer.h"
63#include "adaptation/Iterator.h"
64#include "adaptation/Service.h"
65#if ICAP_CLIENT
66#include "adaptation/icap/History.h"
67#endif
68#endif
cb4f4424 69#if USE_OPENSSL
2bd84e5f 70#include "ssl/ServerBump.h"
602d9612 71#include "ssl/support.h"
4db984be 72#endif
3ff65596 73
edce4d98 74#if LINGERING_CLOSE
75#define comm_close comm_lingering_close
76#endif
77
78static const char *const crlf = "\r\n";
79
609c620a 80#if FOLLOW_X_FORWARDED_FOR
2efeb0b7 81static void clientFollowXForwardedForCheck(allow_t answer, void *data);
609c620a 82#endif /* FOLLOW_X_FORWARDED_FOR */
3d674977 83
955394ce 84ErrorState *clientBuildError(err_type, Http::StatusCode, char const *url, Ip::Address &, HttpRequest *);
2bd84e5f 85
8e2745f4 86CBDATA_CLASS_INIT(ClientRequestContext);
87
edce4d98 88/* Local functions */
edce4d98 89/* other */
2efeb0b7 90static void clientAccessCheckDoneWrapper(allow_t, void *);
cb4f4424 91#if USE_OPENSSL
2efeb0b7 92static void sslBumpAccessCheckDoneWrapper(allow_t, void *);
e0c0d54c 93#endif
59a1efb2 94static int clientHierarchical(ClientHttpRequest * http);
95static void clientInterpretRequestHeaders(ClientHttpRequest * http);
e166785a 96static HLPCB clientRedirectDoneWrapper;
a8a0b1c2 97static HLPCB clientStoreIdDoneWrapper;
2efeb0b7 98static void checkNoCacheDoneWrapper(allow_t, void *);
82afb125
FC
99SQUIDCEXTERN CSR clientGetMoreData;
100SQUIDCEXTERN CSS clientReplyStatus;
101SQUIDCEXTERN CSD clientReplyDetach;
528b2c61 102static void checkFailureRatio(err_type, hier_code);
edce4d98 103
8e2745f4 104ClientRequestContext::~ClientRequestContext()
105{
de31d06f 106 /*
a546b04b 107 * Release our "lock" on our parent, ClientHttpRequest, if we
108 * still have one
de31d06f 109 */
a546b04b 110
111 if (http)
112 cbdataReferenceDone(http);
62e76326 113
2bd84e5f 114 delete error;
5f8252d2 115 debugs(85,3, HERE << this << " ClientRequestContext destructed");
8e2745f4 116}
117
a8a0b1c2 118ClientRequestContext::ClientRequestContext(ClientHttpRequest *anHttp) : http(cbdataReference(anHttp)), acl_checklist (NULL), redirect_state (REDIRECT_NONE), store_id_state(REDIRECT_NONE),error(NULL), readNextRequest(false)
edce4d98 119{
57abaac0 120 http_access_done = false;
121 redirect_done = false;
a8a0b1c2 122 store_id_done = false;
57abaac0 123 no_cache_done = false;
124 interpreted_req_hdrs = false;
cb4f4424 125#if USE_OPENSSL
e0c0d54c
CT
126 sslBumpCheckDone = false;
127#endif
0b86805b 128 debugs(85,3, HERE << this << " ClientRequestContext constructed");
edce4d98 129}
130
528b2c61 131CBDATA_CLASS_INIT(ClientHttpRequest);
8e2745f4 132
26ac0430 133ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
a83c6ed6 134#if USE_ADAPTATION
f53969cc 135 AsyncJob("ClientHttpRequest"),
1cf238db 136#endif
f53969cc 137 loggingEntry_(NULL)
528b2c61 138{
a0355e95 139 setConn(aConn);
41ebd397 140 al = new AccessLogEntry;
af0ded40 141 al->cache.start_time = current_time;
41ebd397 142 al->tcpClient = clientConnection = aConn->clientConnection;
fa720bfb 143 al->cache.port = aConn->port;
d4806c91
CT
144 al->cache.caddr = aConn->log_addr;
145
cb4f4424 146#if USE_OPENSSL
f4698e0b
CT
147 if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) {
148 if (SSL *ssl = fd_table[aConn->clientConnection->fd].ssl)
149 al->cache.sslClientCert.reset(SSL_get_peer_certificate(ssl));
150 }
151#endif
a0355e95 152 dlinkAdd(this, &active, &ClientActiveRequests);
a83c6ed6 153#if USE_ADAPTATION
b044675d 154 request_satisfaction_mode = false;
155#endif
cb4f4424 156#if USE_OPENSSL
08097970 157 sslBumpNeed_ = Ssl::bumpEnd;
e0c0d54c 158#endif
528b2c61 159}
160
0655fa4d 161/*
162 * returns true if client specified that the object must come from the cache
163 * without contacting origin server
164 */
165bool
166ClientHttpRequest::onlyIfCached()const
167{
168 assert(request);
169 return request->cache_control &&
4ce6e3b5 170 request->cache_control->onlyIfCached();
0655fa4d 171}
172
77aacca5 173/**
528b2c61 174 * This function is designed to serve a fairly specific purpose.
175 * Occasionally our vBNS-connected caches can talk to each other, but not
176 * the rest of the world. Here we try to detect frequent failures which
177 * make the cache unusable (e.g. DNS lookup and connect() failures). If
178 * the failure:success ratio goes above 1.0 then we go into "hit only"
179 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
180 * will only fetch HITs from us if they are using the ICP protocol. We
181 * stay in this mode for 5 minutes.
26ac0430 182 *
528b2c61 183 * Duane W., Sept 16, 1996
184 */
528b2c61 185static void
186checkFailureRatio(err_type etype, hier_code hcode)
187{
77aacca5
AJ
188 // Can be set at compile time with -D compiler flag
189#ifndef FAILURE_MODE_TIME
190#define FAILURE_MODE_TIME 300
191#endif
192
8d74a311
AJ
193 if (hcode == HIER_NONE)
194 return;
195
196 // don't bother when ICP is disabled.
197 if (Config.Port.icp <= 0)
198 return;
199
528b2c61 200 static double magic_factor = 100.0;
201 double n_good;
202 double n_bad;
62e76326 203
528b2c61 204 n_good = magic_factor / (1.0 + request_failure_ratio);
62e76326 205
528b2c61 206 n_bad = magic_factor - n_good;
62e76326 207
528b2c61 208 switch (etype) {
62e76326 209
528b2c61 210 case ERR_DNS_FAIL:
62e76326 211
528b2c61 212 case ERR_CONNECT_FAIL:
3712be3f 213 case ERR_SECURE_CONNECT_FAIL:
62e76326 214
528b2c61 215 case ERR_READ_ERROR:
5086523e 216 ++n_bad;
62e76326 217 break;
218
528b2c61 219 default:
5086523e 220 ++n_good;
528b2c61 221 }
62e76326 222
528b2c61 223 request_failure_ratio = n_bad / n_good;
62e76326 224
528b2c61 225 if (hit_only_mode_until > squid_curtime)
62e76326 226 return;
227
528b2c61 228 if (request_failure_ratio < 1.0)
62e76326 229 return;
230
77aacca5 231 debugs(33, DBG_CRITICAL, "WARNING: Failure Ratio at "<< std::setw(4)<<
bf8fe701 232 std::setprecision(3) << request_failure_ratio);
62e76326 233
8d74a311 234 debugs(33, DBG_CRITICAL, "WARNING: ICP going into HIT-only mode for " <<
bf8fe701 235 FAILURE_MODE_TIME / 60 << " minutes...");
62e76326 236
528b2c61 237 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
62e76326 238
f53969cc 239 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
528b2c61 240}
241
242ClientHttpRequest::~ClientHttpRequest()
243{
bf8fe701 244 debugs(33, 3, "httpRequestFree: " << uri);
72bdee4c 245 PROF_start(httpRequestFree);
62e76326 246
5f8252d2 247 // Even though freeResources() below may destroy the request,
248 // we no longer set request->body_pipe to NULL here
249 // because we did not initiate that pipe (ConnStateData did)
62e76326 250
528b2c61 251 /* the ICP check here was erroneous
26ac0430 252 * - StoreEntry::releaseRequest was always called if entry was valid
528b2c61 253 */
254 assert(logType < LOG_TYPE_MAX);
9ce7856a 255
528b2c61 256 logRequest();
9ce7856a 257
0976f8db 258 loggingEntry(NULL);
259
528b2c61 260 if (request)
41ebd397 261 checkFailureRatio(request->errType, al->hier.code);
62e76326 262
528b2c61 263 freeResources();
62e76326 264
a83c6ed6
AR
265#if USE_ADAPTATION
266 announceInitiatorAbort(virginHeadSource);
9d4d7c5e 267
a83c6ed6
AR
268 if (adaptedBodySource != NULL)
269 stopConsumingFrom(adaptedBodySource);
de31d06f 270#endif
9ce7856a 271
de31d06f 272 if (calloutContext)
273 delete calloutContext;
274
be364179
AJ
275 clientConnection = NULL;
276
26ac0430
AJ
277 if (conn_)
278 cbdataReferenceDone(conn_);
1cf238db 279
528b2c61 280 /* moving to the next connection is handled by the context free */
281 dlinkDelete(&active, &ClientActiveRequests);
9ce7856a 282
72bdee4c 283 PROF_stop(httpRequestFree);
528b2c61 284}
62e76326 285
11992b6f
AJ
286/**
287 * Create a request and kick it off
288 *
289 * \retval 0 success
290 * \retval -1 failure
291 *
69660be0 292 * TODO: Pass in the buffers to be used in the inital Read request, as they are
293 * determined by the user
edce4d98 294 */
11992b6f 295int
60745f24 296clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * streamcallback,
0655fa4d 297 CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
62e76326 298 char *tailbuf, size_t taillen)
edce4d98 299{
300 size_t url_sz;
a0355e95 301 ClientHttpRequest *http = new ClientHttpRequest(NULL);
190154cf 302 HttpRequest *request;
528b2c61 303 StoreIOBuffer tempBuffer;
af0ded40
CT
304 if (http->al != NULL)
305 http->al->cache.start_time = current_time;
edce4d98 306 /* this is only used to adjust the connection offset in client_side.c */
307 http->req_sz = 0;
c8be6d7b 308 tempBuffer.length = taillen;
309 tempBuffer.data = tailbuf;
edce4d98 310 /* client stream setup */
311 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
0655fa4d 312 clientReplyStatus, new clientReplyContext(http), streamcallback,
62e76326 313 streamdetach, streamdata, tempBuffer);
edce4d98 314 /* make it visible in the 'current acctive requests list' */
edce4d98 315 /* Set flags */
a46d2c0e 316 /* internal requests only makes sense in an
317 * accelerator today. TODO: accept flags ? */
be4d35dc 318 http->flags.accel = true;
edce4d98 319 /* allow size for url rewriting */
320 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 321 http->uri = (char *)xcalloc(url_sz, 1);
edce4d98 322 strcpy(http->uri, url);
323
c21ad0f5 324 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
bf8fe701 325 debugs(85, 5, "Invalid URL: " << http->uri);
62e76326 326 return -1;
edce4d98 327 }
62e76326 328
69660be0 329 /*
11992b6f 330 * now update the headers in request with our supplied headers. urlParse
69660be0 331 * should return a blank header set, but we use Update to be sure of
332 * correctness.
edce4d98 333 */
334 if (header)
a9925b40 335 request->header.update(header, NULL);
62e76326 336
edce4d98 337 http->log_uri = xstrdup(urlCanonicalClean(request));
62e76326 338
edce4d98 339 /* http struct now ready */
340
69660be0 341 /*
342 * build new header list *? TODO
edce4d98 343 */
45e5102d 344 request->flags.accelerated = http->flags.accel;
62e76326 345
e857372a 346 request->flags.internalClient = true;
a46d2c0e 347
348 /* this is an internally created
349 * request, not subject to acceleration
350 * target overrides */
69660be0 351 /*
352 * FIXME? Do we want to detect and handle internal requests of internal
353 * objects ?
354 */
edce4d98 355
356 /* Internally created requests cannot have bodies today */
357 request->content_length = 0;
62e76326 358
4dd643d5 359 request->client_addr.setNoAddr();
62e76326 360
3d674977 361#if FOLLOW_X_FORWARDED_FOR
4dd643d5 362 request->indirect_client_addr.setNoAddr();
3d674977 363#endif /* FOLLOW_X_FORWARDED_FOR */
26ac0430 364
f53969cc 365 request->my_addr.setNoAddr(); /* undefined for internal requests */
62e76326 366
4dd643d5 367 request->my_addr.port(0);
62e76326 368
2592bc70 369 request->http_ver = Http::ProtocolVersion();
62e76326 370
b248c2a3
AJ
371 http->request = request;
372 HTTPMSGLOCK(http->request);
edce4d98 373
374 /* optional - skip the access check ? */
de31d06f 375 http->calloutContext = new ClientRequestContext(http);
376
57abaac0 377 http->calloutContext->http_access_done = false;
de31d06f 378
57abaac0 379 http->calloutContext->redirect_done = true;
de31d06f 380
57abaac0 381 http->calloutContext->no_cache_done = true;
de31d06f 382
383 http->doCallouts();
62e76326 384
edce4d98 385 return 0;
386}
387
de31d06f 388bool
389ClientRequestContext::httpStateIsValid()
390{
391 ClientHttpRequest *http_ = http;
392
393 if (cbdataReferenceValid(http_))
394 return true;
395
396 http = NULL;
397
398 cbdataReferenceDone(http_);
399
400 return false;
401}
402
3d674977
AJ
403#if FOLLOW_X_FORWARDED_FOR
404/**
a9044668 405 * clientFollowXForwardedForCheck() checks the content of X-Forwarded-For:
3d674977
AJ
406 * against the followXFF ACL, or cleans up and passes control to
407 * clientAccessCheck().
d096ace1
AJ
408 *
409 * The trust model here is a little ambiguous. So to clarify the logic:
410 * - we may always use the direct client address as the client IP.
a9044668 411 * - these trust tests merey tell whether we trust given IP enough to believe the
d096ace1
AJ
412 * IP string which it appended to the X-Forwarded-For: header.
413 * - if at any point we don't trust what an IP adds we stop looking.
414 * - at that point the current contents of indirect_client_addr are the value set
415 * by the last previously trusted IP.
416 * ++ indirect_client_addr contains the remote direct client from the trusted peers viewpoint.
3d674977 417 */
3d674977 418static void
2efeb0b7 419clientFollowXForwardedForCheck(allow_t answer, void *data)
3d674977
AJ
420{
421 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
3d674977
AJ
422
423 if (!calloutContext->httpStateIsValid())
424 return;
425
d096ace1
AJ
426 ClientHttpRequest *http = calloutContext->http;
427 HttpRequest *request = http->request;
428
3d674977
AJ
429 /*
430 * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
431 * called as a result of ACL checks, or -1 if we are called when
432 * there's nothing left to do.
433 */
434 if (answer == ACCESS_ALLOWED &&
26ac0430 435 request->x_forwarded_for_iterator.size () != 0) {
d096ace1 436
3d674977 437 /*
d096ace1
AJ
438 * Remove the last comma-delimited element from the
439 * x_forwarded_for_iterator and use it to repeat the cycle.
440 */
3d674977
AJ
441 const char *p;
442 const char *asciiaddr;
443 int l;
b7ac5457 444 Ip::Address addr;
bb790702 445 p = request->x_forwarded_for_iterator.termedBuf();
3d674977
AJ
446 l = request->x_forwarded_for_iterator.size();
447
448 /*
449 * XXX x_forwarded_for_iterator should really be a list of
450 * IP addresses, but it's a String instead. We have to
451 * walk backwards through the String, biting off the last
452 * comma-delimited part each time. As long as the data is in
453 * a String, we should probably implement and use a variant of
454 * strListGetItem() that walks backwards instead of forwards
455 * through a comma-separated list. But we don't even do that;
456 * we just do the work in-line here.
457 */
458 /* skip trailing space and commas */
459 while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
5e263176 460 --l;
3d674977
AJ
461 request->x_forwarded_for_iterator.cut(l);
462 /* look for start of last item in list */
463 while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
5e263176 464 --l;
3d674977 465 asciiaddr = p+l;
fafd0efa 466 if ((addr = asciiaddr)) {
3d674977
AJ
467 request->indirect_client_addr = addr;
468 request->x_forwarded_for_iterator.cut(l);
d096ace1
AJ
469 calloutContext->acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
470 if (!Config.onoff.acl_uses_indirect_client) {
471 /* override the default src_addr tested if we have to go deeper than one level into XFF */
472 Filled(calloutContext->acl_checklist)->src_addr = request->indirect_client_addr;
3d674977 473 }
d096ace1 474 calloutContext->acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, data);
3d674977
AJ
475 return;
476 }
477 } /*if (answer == ACCESS_ALLOWED &&
478 request->x_forwarded_for_iterator.size () != 0)*/
479
480 /* clean up, and pass control to clientAccessCheck */
26ac0430 481 if (Config.onoff.log_uses_indirect_client) {
3d674977
AJ
482 /*
483 * Ensure that the access log shows the indirect client
484 * instead of the direct client.
485 */
486 ConnStateData *conn = http->getConn();
487 conn->log_addr = request->indirect_client_addr;
d4806c91 488 http->al->cache.caddr = conn->log_addr;
3d674977
AJ
489 }
490 request->x_forwarded_for_iterator.clean();
e857372a 491 request->flags.done_follow_x_forwarded_for = true;
3d674977 492
d096ace1
AJ
493 if (answer != ACCESS_ALLOWED && answer != ACCESS_DENIED) {
494 debugs(28, DBG_CRITICAL, "ERROR: Processing X-Forwarded-For. Stopping at IP address: " << request->indirect_client_addr );
493d3865
AJ
495 }
496
497 /* process actual access ACL as normal. */
498 calloutContext->clientAccessCheck();
3d674977
AJ
499}
500#endif /* FOLLOW_X_FORWARDED_FOR */
501
fe97983f 502static void
4a3b98d7 503hostHeaderIpVerifyWrapper(const ipcache_addrs* ia, const Dns::LookupDetails &dns, void *data)
fe97983f
AJ
504{
505 ClientRequestContext *c = static_cast<ClientRequestContext*>(data);
506 c->hostHeaderIpVerify(ia, dns);
507}
508
509void
4a3b98d7 510ClientRequestContext::hostHeaderIpVerify(const ipcache_addrs* ia, const Dns::LookupDetails &dns)
fe97983f
AJ
511{
512 Comm::ConnectionPointer clientConn = http->getConn()->clientConnection;
513
514 // note the DNS details for the transaction stats.
515 http->request->recordLookup(dns);
516
517 if (ia != NULL && ia->count > 0) {
518 // Is the NAT destination IP in DNS?
5086523e 519 for (int i = 0; i < ia->count; ++i) {
fe97983f
AJ
520 if (clientConn->local.matchIPAddr(ia->in_addrs[i]) == 0) {
521 debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
e857372a 522 http->request->flags.hostVerified = true;
fe97983f
AJ
523 http->doCallouts();
524 return;
525 }
526 debugs(85, 3, HERE << "validate IP " << clientConn->local << " non-match from Host: IP " << ia->in_addrs[i]);
527 }
528 }
529 debugs(85, 3, HERE << "FAIL: validate IP " << clientConn->local << " possible from Host:");
05b28f84 530 hostHeaderVerifyFailed("local IP", "any domain IP");
fe97983f
AJ
531}
532
533void
05b28f84 534ClientRequestContext::hostHeaderVerifyFailed(const char *A, const char *B)
fe97983f 535{
2962f8b8
AJ
536 // IP address validation for Host: failed. Admin wants to ignore them.
537 // NP: we do not yet handle CONNECT tunnels well, so ignore for them
c2a7cefd 538 if (!Config.onoff.hostStrictVerify && http->request->method != Http::METHOD_CONNECT) {
2962f8b8
AJ
539 debugs(85, 3, "SECURITY ALERT: Host header forgery detected on " << http->getConn()->clientConnection <<
540 " (" << A << " does not match " << B << ") on URL: " << urlCanonical(http->request));
541
450fe1cb 542 // NP: it is tempting to use 'flags.noCache' but that is all about READing cache data.
2962f8b8 543 // The problems here are about WRITE for new cache content, which means flags.cachable
e857372a 544 http->request->flags.cachable = false; // MUST NOT cache (for now)
2962f8b8 545 // XXX: when we have updated the cache key to base on raw-IP + URI this cacheable limit can go.
e857372a 546 http->request->flags.hierarchical = false; // MUST NOT pass to peers (for now)
2962f8b8 547 // XXX: when we have sorted out the best way to relay requests properly to peers this hierarchical limit can go.
567fe088 548 http->doCallouts();
2962f8b8
AJ
549 return;
550 }
551
8f489ad7
AJ
552 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: Host header forgery detected on " <<
553 http->getConn()->clientConnection << " (" << A << " does not match " << B << ")");
554 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: By user agent: " << http->request->header.getStr(HDR_USER_AGENT));
555 debugs(85, DBG_IMPORTANT, "SECURITY ALERT: on URL: " << urlCanonical(http->request));
fe97983f
AJ
556
557 // IP address validation for Host: failed. reject the connection.
558 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
559 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
560 assert (repContext);
955394ce 561 repContext->setReplyToError(ERR_CONFLICT_HOST, Http::scConflict,
fe97983f
AJ
562 http->request->method, NULL,
563 http->getConn()->clientConnection->remote,
564 http->request,
565 NULL,
566#if USE_AUTH
cc1e110a
AJ
567 http->getConn() != NULL && http->getConn()->getAuth() != NULL ?
568 http->getConn()->getAuth() : http->request->auth_user_request);
fe97983f
AJ
569#else
570 NULL);
571#endif
572 node = (clientStreamNode *)http->client_stream.tail->data;
573 clientStreamRead(node, http, node->readBuffer);
574}
575
576void
577ClientRequestContext::hostHeaderVerify()
578{
579 // Require a Host: header.
580 const char *host = http->request->header.getStr(HDR_HOST);
fe97983f
AJ
581
582 if (!host) {
583 // TODO: dump out the HTTP/1.1 error about missing host header.
584 // otherwise this is fine, can't forge a header value when its not even set.
585 debugs(85, 3, HERE << "validate skipped with no Host: header present.");
586 http->doCallouts();
587 return;
588 }
589
45e5102d 590 if (http->request->flags.internal) {
8f489ad7
AJ
591 // TODO: kill this when URL handling allows partial URLs out of accel mode
592 // and we no longer screw with the URL just to add our internal host there
593 debugs(85, 6, HERE << "validate skipped due to internal composite URL.");
594 http->doCallouts();
595 return;
596 }
597
fe97983f
AJ
598 // Locate if there is a port attached, strip ready for IP lookup
599 char *portStr = NULL;
91663dce
AJ
600 char *hostB = xstrdup(host);
601 host = hostB;
fe97983f
AJ
602 if (host[0] == '[') {
603 // IPv6 literal.
fe97983f 604 portStr = strchr(hostB, ']');
91663dce
AJ
605 if (portStr && *(++portStr) != ':') {
606 portStr = NULL;
fe97983f 607 }
91663dce 608 } else {
fe97983f 609 // Domain or IPv4 literal with port
fe97983f 610 portStr = strrchr(hostB, ':');
91663dce
AJ
611 }
612
613 uint16_t port = 0;
614 if (portStr) {
615 *portStr = '\0'; // strip the ':'
126e1dc0
AJ
616 if (*(++portStr) != '\0') {
617 char *end = NULL;
618 int64_t ret = strtoll(portStr, &end, 10);
619 if (end == portStr || *end != '\0' || ret < 1 || ret > 0xFFFF) {
620 // invalid port details. Replace the ':'
621 *(--portStr) = ':';
622 portStr = NULL;
623 } else
624 port = (ret & 0xFFFF);
625 }
fe97983f
AJ
626 }
627
628 debugs(85, 3, HERE << "validate host=" << host << ", port=" << port << ", portStr=" << (portStr?portStr:"NULL"));
0d901ef4 629 if (http->request->flags.intercepted || http->request->flags.interceptTproxy) {
ba4d9da0 630 // verify the Host: port (if any) matches the apparent destination
4dd643d5
AJ
631 if (portStr && port != http->getConn()->clientConnection->local.port()) {
632 debugs(85, 3, HERE << "FAIL on validate port " << http->getConn()->clientConnection->local.port() <<
05b28f84
AJ
633 " matches Host: port " << port << " (" << portStr << ")");
634 hostHeaderVerifyFailed("intercepted port", portStr);
ba4d9da0
AJ
635 } else {
636 // XXX: match the scheme default port against the apparent destination
fe97983f 637
ba4d9da0
AJ
638 // verify the destination DNS is one of the Host: headers IPs
639 ipcache_nbgethostbyname(host, hostHeaderIpVerifyWrapper, this);
640 }
06059513 641 } else if (!Config.onoff.hostStrictVerify) {
90529125
AJ
642 debugs(85, 3, HERE << "validate skipped.");
643 http->doCallouts();
8f489ad7
AJ
644 } else if (strlen(host) != strlen(http->request->GetHost())) {
645 // Verify forward-proxy requested URL domain matches the Host: header
646 debugs(85, 3, HERE << "FAIL on validate URL domain length " << http->request->GetHost() << " matches Host: " << host);
647 hostHeaderVerifyFailed(host, http->request->GetHost());
648 } else if (matchDomainName(host, http->request->GetHost()) != 0) {
ba4d9da0 649 // Verify forward-proxy requested URL domain matches the Host: header
fe97983f 650 debugs(85, 3, HERE << "FAIL on validate URL domain " << http->request->GetHost() << " matches Host: " << host);
05b28f84 651 hostHeaderVerifyFailed(host, http->request->GetHost());
ba4d9da0
AJ
652 } else if (portStr && port != http->request->port) {
653 // Verify forward-proxy requested URL domain matches the Host: header
654 debugs(85, 3, HERE << "FAIL on validate URL port " << http->request->port << " matches Host: port " << portStr);
655 hostHeaderVerifyFailed("URL port", portStr);
4e3f4dc7 656 } else if (!portStr && http->request->method != Http::METHOD_CONNECT && http->request->port != urlDefaultPort(http->request->url.getScheme())) {
ba4d9da0 657 // Verify forward-proxy requested URL domain matches the Host: header
65d2fdbf 658 // Special case: we don't have a default-port to check for CONNECT. Assume URL is correct.
4e3f4dc7 659 debugs(85, 3, "FAIL on validate URL port " << http->request->port << " matches Host: default port " << urlDefaultPort(http->request->url.getScheme()));
ba4d9da0
AJ
660 hostHeaderVerifyFailed("URL port", "default port");
661 } else {
662 // Okay no problem.
663 debugs(85, 3, HERE << "validate passed.");
e857372a 664 http->request->flags.hostVerified = true;
ba4d9da0 665 http->doCallouts();
fe97983f 666 }
ba4d9da0 667 safe_free(hostB);
fe97983f
AJ
668}
669
edce4d98 670/* This is the entry point for external users of the client_side routines */
671void
de31d06f 672ClientRequestContext::clientAccessCheck()
edce4d98 673{
fbe9e379 674#if FOLLOW_X_FORWARDED_FOR
f1a1f20a 675 if (!http->request->flags.doneFollowXff() &&
26ac0430
AJ
676 Config.accessList.followXFF &&
677 http->request->header.has(HDR_X_FORWARDED_FOR)) {
d096ace1
AJ
678
679 /* we always trust the direct client address for actual use */
680 http->request->indirect_client_addr = http->request->client_addr;
4dd643d5 681 http->request->indirect_client_addr.port(0);
d096ace1
AJ
682
683 /* setup the XFF iterator for processing */
684 http->request->x_forwarded_for_iterator = http->request->header.getList(HDR_X_FORWARDED_FOR);
685
686 /* begin by checking to see if we trust direct client enough to walk XFF */
687 acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
688 acl_checklist->nonBlockingCheck(clientFollowXForwardedForCheck, this);
3d674977
AJ
689 return;
690 }
fbe9e379 691#endif
493d3865 692
b50e327b
AJ
693 if (Config.accessList.http) {
694 acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
695 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
696 } else {
697 debugs(0, DBG_CRITICAL, "No http_access configuration found. This will block ALL traffic");
698 clientAccessCheckDone(ACCESS_DENIED);
699 }
edce4d98 700}
701
533493da
AJ
702/**
703 * Identical in operation to clientAccessCheck() but performed later using different configured ACL list.
704 * The default here is to allow all. Since the earlier http_access should do a default deny all.
705 * This check is just for a last-minute denial based on adapted request headers.
706 */
707void
708ClientRequestContext::clientAccessCheck2()
709{
710 if (Config.accessList.adapted_http) {
711 acl_checklist = clientAclChecklistCreate(Config.accessList.adapted_http, http);
712 acl_checklist->nonBlockingCheck(clientAccessCheckDoneWrapper, this);
713 } else {
327e2131 714 debugs(85, 2, HERE << "No adapted_http_access configuration. default: ALLOW");
533493da
AJ
715 clientAccessCheckDone(ACCESS_ALLOWED);
716 }
717}
718
edce4d98 719void
2efeb0b7 720clientAccessCheckDoneWrapper(allow_t answer, void *data)
edce4d98 721{
de31d06f 722 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
fbade053 723
de31d06f 724 if (!calloutContext->httpStateIsValid())
62e76326 725 return;
62e76326 726
de31d06f 727 calloutContext->clientAccessCheckDone(answer);
728}
729
9d5e7196
AJ
730void
731ClientRequestContext::clientAccessCheckDone(const allow_t &answer)
de31d06f 732{
733 acl_checklist = NULL;
edce4d98 734 err_type page_id;
955394ce 735 Http::StatusCode status;
7f06a3d8 736 debugs(85, 2, "The request " << http->request->method << ' ' <<
9d5e7196 737 http->uri << " is " << answer <<
6f58d7d7 738 "; last ACL checked: " << (AclMatchedName ? AclMatchedName : "[none]"));
f5691f9c 739
2f1431ea
AJ
740#if USE_AUTH
741 char const *proxy_auth_msg = "<null>";
cc1e110a
AJ
742 if (http->getConn() != NULL && http->getConn()->getAuth() != NULL)
743 proxy_auth_msg = http->getConn()->getAuth()->denyMessage("<null>");
f5691f9c 744 else if (http->request->auth_user_request != NULL)
745 proxy_auth_msg = http->request->auth_user_request->denyMessage("<null>");
2f1431ea 746#endif
62e76326 747
dd332b92 748 if (answer != ACCESS_ALLOWED) {
9d5e7196 749 // auth has a grace period where credentials can be expired but okay not to challenge.
309347ef 750
9d5e7196
AJ
751 /* Send an auth challenge or error */
752 // XXX: do we still need aclIsProxyAuth() ?
dd332b92 753 bool auth_challenge = (answer == ACCESS_AUTH_REQUIRED || aclIsProxyAuth(AclMatchedName));
9d5e7196
AJ
754 debugs(85, 5, "Access Denied: " << http->uri);
755 debugs(85, 5, "AclMatchedName = " << (AclMatchedName ? AclMatchedName : "<null>"));
2f1431ea 756#if USE_AUTH
9d5e7196
AJ
757 if (auth_challenge)
758 debugs(33, 5, "Proxy Auth Message = " << (proxy_auth_msg ? proxy_auth_msg : "<null>"));
2f1431ea 759#endif
9ce7856a 760
9d5e7196
AJ
761 /*
762 * NOTE: get page_id here, based on AclMatchedName because if
763 * USE_DELAY_POOLS is enabled, then AclMatchedName gets clobbered in
764 * the clientCreateStoreEntry() call just below. Pedro Ribeiro
765 * <pribeiro@isel.pt>
766 */
767 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, answer != ACCESS_AUTH_REQUIRED);
9ce7856a 768
9d5e7196 769 http->logType = LOG_TCP_DENIED;
62e76326 770
9d5e7196 771 if (auth_challenge) {
2f1431ea 772#if USE_AUTH
450fe1cb 773 if (http->request->flags.sslBumped) {
9d5e7196 774 /*SSL Bumped request, authentication is not possible*/
955394ce 775 status = Http::scForbidden;
9d5e7196
AJ
776 } else if (!http->flags.accel) {
777 /* Proxy authorisation needed */
955394ce 778 status = Http::scProxyAuthenticationRequired;
9d5e7196
AJ
779 } else {
780 /* WWW authorisation needed */
955394ce 781 status = Http::scUnauthorized;
9d5e7196 782 }
ed6163ef 783#else
9d5e7196 784 // need auth, but not possible to do.
955394ce 785 status = Http::scForbidden;
ed6163ef 786#endif
9d5e7196
AJ
787 if (page_id == ERR_NONE)
788 page_id = ERR_CACHE_ACCESS_DENIED;
789 } else {
955394ce 790 status = Http::scForbidden;
ed6163ef 791
9d5e7196
AJ
792 if (page_id == ERR_NONE)
793 page_id = ERR_ACCESS_DENIED;
794 }
62e76326 795
9d5e7196 796 Ip::Address tmpnoaddr;
4dd643d5 797 tmpnoaddr.setNoAddr();
87f237a9 798 error = clientBuildError(page_id, status,
68715527
CT
799 NULL,
800 http->getConn() != NULL ? http->getConn()->clientConnection->remote : tmpnoaddr,
801 http->request
87f237a9 802 );
68715527 803
2f1431ea 804#if USE_AUTH
87f237a9 805 error->auth_user_request =
cc1e110a
AJ
806 http->getConn() != NULL && http->getConn()->getAuth() != NULL ?
807 http->getConn()->getAuth() : http->request->auth_user_request;
2f1431ea 808#endif
68715527
CT
809
810 readNextRequest = true;
9d5e7196 811 }
de31d06f 812
dd332b92 813 /* ACCESS_ALLOWED continues here ... */
de31d06f 814 safe_free(http->uri);
815
816 http->uri = xstrdup(urlCanonical(http->request));
817
818 http->doCallouts();
819}
820
a83c6ed6 821#if USE_ADAPTATION
de31d06f 822void
79628299 823ClientHttpRequest::noteAdaptationAclCheckDone(Adaptation::ServiceGroupPointer g)
de31d06f 824{
a83c6ed6 825 debugs(93,3,HERE << this << " adaptationAclCheckDone called");
6ec67de9 826
e1381638 827#if ICAP_CLIENT
79628299 828 Adaptation::Icap::History::Pointer ih = request->icapHistory();
e1381638 829 if (ih != NULL) {
6bc2a98d 830 if (getConn() != NULL && getConn()->clientConnection != NULL) {
79628299 831 ih->rfc931 = getConn()->clientConnection->rfc931;
cb4f4424 832#if USE_OPENSSL
6bc2a98d
NH
833 if (getConn()->clientConnection->isOpen()) {
834 ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl);
835 }
e1381638 836#endif
3ff65596 837 }
79628299
CT
838 ih->log_uri = log_uri;
839 ih->req_sz = req_sz;
3ff65596
AR
840 }
841#endif
842
a22e6cd3
AR
843 if (!g) {
844 debugs(85,3, HERE << "no adaptation needed");
79628299 845 doCallouts();
5f8252d2 846 return;
847 }
de31d06f 848
79628299 849 startAdaptation(g);
edce4d98 850}
851
de31d06f 852#endif
853
14cc8559 854static void
2efeb0b7 855clientRedirectAccessCheckDone(allow_t answer, void *data)
14cc8559 856{
857 ClientRequestContext *context = (ClientRequestContext *)data;
9d5e7196
AJ
858 ClientHttpRequest *http = context->http;
859 context->acl_checklist = NULL;
14cc8559 860
861 if (answer == ACCESS_ALLOWED)
9d5e7196 862 redirectStart(http, clientRedirectDoneWrapper, context);
bc98bc4b 863 else {
24438ec5 864 Helper::Reply nilReply;
2428ce02 865 nilReply.result = Helper::Error;
bc98bc4b
AJ
866 context->clientRedirectDone(nilReply);
867 }
14cc8559 868}
869
de31d06f 870void
871ClientRequestContext::clientRedirectStart()
14cc8559 872{
48e7baac 873 debugs(33, 5, HERE << "'" << http->uri << "'");
b11724bb 874 (void)SyncNotes(*http->al, *http->request);
14cc8559 875 if (Config.accessList.redirector) {
de31d06f 876 acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
877 acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
14cc8559 878 } else
de31d06f 879 redirectStart(http, clientRedirectDoneWrapper, this);
14cc8559 880}
881
a8a0b1c2
EC
882/**
883 * This methods handles Access checks result of StoreId access list.
884 * Will handle as "ERR" (no change) in a case Access is not allowed.
885 */
886static void
887clientStoreIdAccessCheckDone(allow_t answer, void *data)
888{
889 ClientRequestContext *context = static_cast<ClientRequestContext *>(data);
890 ClientHttpRequest *http = context->http;
891 context->acl_checklist = NULL;
892
893 if (answer == ACCESS_ALLOWED)
894 storeIdStart(http, clientStoreIdDoneWrapper, context);
895 else {
896 debugs(85, 3, "access denied expected ERR reply handling: " << answer);
24438ec5 897 Helper::Reply nilReply;
2428ce02 898 nilReply.result = Helper::Error;
a8a0b1c2
EC
899 context->clientStoreIdDone(nilReply);
900 }
901}
902
903/**
904 * Start locating an alternative storeage ID string (if any) from admin
905 * configured helper program. This is an asynchronous operation terminating in
906 * ClientRequestContext::clientStoreIdDone() when completed.
907 */
908void
909ClientRequestContext::clientStoreIdStart()
910{
911 debugs(33, 5,"'" << http->uri << "'");
912
913 if (Config.accessList.store_id) {
914 acl_checklist = clientAclChecklistCreate(Config.accessList.store_id, http);
915 acl_checklist->nonBlockingCheck(clientStoreIdAccessCheckDone, this);
916 } else
917 storeIdStart(http, clientStoreIdDoneWrapper, this);
918}
919
edce4d98 920static int
59a1efb2 921clientHierarchical(ClientHttpRequest * http)
edce4d98 922{
190154cf 923 HttpRequest *request = http->request;
60745f24 924 HttpRequestMethod method = request->method;
edce4d98 925
2962f8b8 926 // intercepted requests MUST NOT (yet) be sent to peers unless verified
0d901ef4 927 if (!request->flags.hostVerified && (request->flags.intercepted || request->flags.interceptTproxy))
2962f8b8
AJ
928 return 0;
929
69660be0 930 /*
931 * IMS needs a private key, so we can use the hierarchy for IMS only if our
932 * neighbors support private keys
933 */
62e76326 934
45e5102d 935 if (request->flags.ims && !neighbors_do_private_keys)
62e76326 936 return 0;
937
69660be0 938 /*
939 * This is incorrect: authenticating requests can be sent via a hierarchy
06b97e72 940 * (they can even be cached if the correct headers are set on the reply)
edce4d98 941 */
45e5102d 942 if (request->flags.auth)
62e76326 943 return 0;
944
c2a7cefd 945 if (method == Http::METHOD_TRACE)
62e76326 946 return 1;
947
c2a7cefd 948 if (method != Http::METHOD_GET)
62e76326 949 return 0;
950
450fe1cb 951 if (request->flags.loopDetected)
62e76326 952 return 0;
953
4e3f4dc7 954 if (request->url.getScheme() == AnyP::PROTO_HTTP)
c2a7cefd 955 return method.respMaybeCacheable();
62e76326 956
4e3f4dc7 957 if (request->url.getScheme() == AnyP::PROTO_GOPHER)
62e76326 958 return gopherCachable(request);
959
4e3f4dc7 960 if (request->url.getScheme() == AnyP::PROTO_CACHE_OBJECT)
62e76326 961 return 0;
962
edce4d98 963 return 1;
964}
965
46a1f562
HN
966static void
967clientCheckPinning(ClientHttpRequest * http)
968{
969 HttpRequest *request = http->request;
970 HttpHeader *req_hdr = &request->header;
971 ConnStateData *http_conn = http->getConn();
972
973 /* Internal requests such as those from ESI includes may be without
974 * a client connection
975 */
976 if (!http_conn)
f54f527e 977 return;
46a1f562 978
450fe1cb
FC
979 request->flags.connectionAuthDisabled = http_conn->port->connection_auth_disabled;
980 if (!request->flags.connectionAuthDisabled) {
73c36fd9 981 if (Comm::IsConnOpen(http_conn->pinning.serverConnection)) {
46a1f562 982 if (http_conn->pinning.auth) {
e857372a
FC
983 request->flags.connectionAuth = true;
984 request->flags.auth = true;
46a1f562 985 } else {
e857372a 986 request->flags.connectionProxyAuth = true;
46a1f562 987 }
b1cf2350
AJ
988 // These should already be linked correctly.
989 assert(request->clientConnectionManager == http_conn);
46a1f562
HN
990 }
991 }
992
993 /* check if connection auth is used, and flag as candidate for pinning
45e5102d 994 * in such case.
450fe1cb 995 * Note: we may need to set flags.connectionAuth even if the connection
46a1f562
HN
996 * is already pinned if it was pinned earlier due to proxy auth
997 */
450fe1cb 998 if (!request->flags.connectionAuth) {
46a1f562
HN
999 if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
1000 HttpHeaderPos pos = HttpHeaderInitPos;
1001 HttpHeaderEntry *e;
1002 int may_pin = 0;
1003 while ((e = req_hdr->getEntry(&pos))) {
1004 if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
1005 const char *value = e->value.rawBuf();
1006 if (strncasecmp(value, "NTLM ", 5) == 0
1007 ||
1008 strncasecmp(value, "Negotiate ", 10) == 0
1009 ||
1010 strncasecmp(value, "Kerberos ", 9) == 0) {
1011 if (e->id == HDR_AUTHORIZATION) {
e857372a 1012 request->flags.connectionAuth = true;
46a1f562
HN
1013 may_pin = 1;
1014 } else {
e857372a 1015 request->flags.connectionProxyAuth = true;
46a1f562
HN
1016 may_pin = 1;
1017 }
1018 }
1019 }
1020 }
1021 if (may_pin && !request->pinnedConnection()) {
b1cf2350
AJ
1022 // These should already be linked correctly. Just need the ServerConnection to pinn.
1023 assert(request->clientConnectionManager == http_conn);
46a1f562
HN
1024 }
1025 }
1026 }
1027}
1028
edce4d98 1029static void
59a1efb2 1030clientInterpretRequestHeaders(ClientHttpRequest * http)
edce4d98 1031{
190154cf 1032 HttpRequest *request = http->request;
0ef77270 1033 HttpHeader *req_hdr = &request->header;
5086523e 1034 bool no_cache = false;
edce4d98 1035 const char *str;
62e76326 1036
edce4d98 1037 request->imslen = -1;
a9925b40 1038 request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
62e76326 1039
edce4d98 1040 if (request->ims > 0)
e857372a 1041 request->flags.ims = true;
62e76326 1042
450fe1cb 1043 if (!request->flags.ignoreCc) {
47fbd2a7 1044 if (request->cache_control) {
1259f9cf 1045 if (request->cache_control->hasNoCache())
5086523e 1046 no_cache=true;
62e76326 1047
adc2a453 1048 // RFC 2616: treat Pragma:no-cache as if it was Cache-Control:no-cache when Cache-Control is missing
47fbd2a7 1049 } else if (req_hdr->has(HDR_PRAGMA))
cdf55306
AJ
1050 no_cache = req_hdr->hasListMember(HDR_PRAGMA,"no-cache",',');
1051
432bc83c
HN
1052 /*
1053 * Work around for supporting the Reload button in IE browsers when Squid
1054 * is used as an accelerator or transparent proxy, by turning accelerated
1055 * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
1056 * actually only fixed in SP1, but we can't tell whether we are talking to
1057 * SP1 or not so all 5.5 versions are treated 'normally').
1058 */
1059 if (Config.onoff.ie_refresh) {
45e5102d 1060 if (http->flags.accel && request->flags.ims) {
432bc83c
HN
1061 if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
1062 if (strstr(str, "MSIE 5.01") != NULL)
5086523e 1063 no_cache=true;
432bc83c 1064 else if (strstr(str, "MSIE 5.0") != NULL)
5086523e 1065 no_cache=true;
432bc83c 1066 else if (strstr(str, "MSIE 4.") != NULL)
5086523e 1067 no_cache=true;
432bc83c 1068 else if (strstr(str, "MSIE 3.") != NULL)
5086523e 1069 no_cache=true;
432bc83c 1070 }
62e76326 1071 }
1072 }
edce4d98 1073 }
914b89a2 1074
c2a7cefd 1075 if (request->method == Http::METHOD_OTHER) {
5086523e 1076 no_cache=true;
60745f24 1077 }
62e76326 1078
edce4d98 1079 if (no_cache) {
626096be 1080#if USE_HTTP_VIOLATIONS
62e76326 1081
1082 if (Config.onoff.reload_into_ims)
e857372a 1083 request->flags.nocacheHack = true;
62e76326 1084 else if (refresh_nocache_hack)
e857372a 1085 request->flags.nocacheHack = true;
62e76326 1086 else
edce4d98 1087#endif
62e76326 1088
e857372a 1089 request->flags.noCache = true;
edce4d98 1090 }
62e76326 1091
0ef77270 1092 /* ignore range header in non-GETs or non-HEADs */
c2a7cefd 1093 if (request->method == Http::METHOD_GET || request->method == Http::METHOD_HEAD) {
56713d9a
AR
1094 // XXX: initialize if we got here without HttpRequest::parseHeader()
1095 if (!request->range)
1096 request->range = req_hdr->getRange();
62e76326 1097
1098 if (request->range) {
e857372a 1099 request->flags.isRanged = true;
62e76326 1100 clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
1101 /* XXX: This is suboptimal. We should give the stream the range set,
1102 * and thereby let the top of the stream set the offset when the
26ac0430 1103 * size becomes known. As it is, we will end up requesting from 0
62e76326 1104 * for evey -X range specification.
1105 * RBC - this may be somewhat wrong. We should probably set the range
1106 * iter up at this point.
1107 */
1108 node->readBuffer.offset = request->range->lowestOffset(0);
1109 http->range_iter.pos = request->range->begin();
24a8eaae 1110 http->range_iter.end = request->range->end();
62e76326 1111 http->range_iter.valid = true;
1112 }
edce4d98 1113 }
62e76326 1114
0ef77270 1115 /* Only HEAD and GET requests permit a Range or Request-Range header.
1116 * If these headers appear on any other type of request, delete them now.
1117 */
1118 else {
1119 req_hdr->delById(HDR_RANGE);
1120 req_hdr->delById(HDR_REQUEST_RANGE);
f0baf149 1121 request->ignoreRange("neither HEAD nor GET");
0ef77270 1122 }
1123
a9925b40 1124 if (req_hdr->has(HDR_AUTHORIZATION))
e857372a 1125 request->flags.auth = true;
62e76326 1126
46a1f562 1127 clientCheckPinning(http);
d67acb4e 1128
92d6986d 1129 if (!request->url.userInfo().isEmpty())
e857372a 1130 request->flags.auth = true;
62e76326 1131
a9925b40 1132 if (req_hdr->has(HDR_VIA)) {
30abd221 1133 String s = req_hdr->getList(HDR_VIA);
62e76326 1134 /*
3c4fcf0f 1135 * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
62e76326 1136 * Note ThisCache2 has a space prepended to the hostname so we don't
1137 * accidentally match super-domains.
1138 */
1139
1140 if (strListIsSubstr(&s, ThisCache2, ',')) {
1141 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
1142 request, (ObjPackMethod) & httpRequestPack);
e857372a 1143 request->flags.loopDetected = true;
62e76326 1144 }
1145
21f6708d 1146#if USE_FORW_VIA_DB
bb790702 1147 fvdbCountVia(s.termedBuf());
62e76326 1148
edce4d98 1149#endif
62e76326 1150
30abd221 1151 s.clean();
edce4d98 1152 }
62e76326 1153
21f6708d 1154#if USE_FORW_VIA_DB
62e76326 1155
a9925b40 1156 if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
30abd221 1157 String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
bb790702 1158 fvdbCountForw(s.termedBuf());
30abd221 1159 s.clean();
edce4d98 1160 }
62e76326 1161
edce4d98 1162#endif
62e76326 1163
c2a7cefd 1164 request->flags.cachable = http->request->maybeCacheable();
62e76326 1165
edce4d98 1166 if (clientHierarchical(http))
e857372a 1167 request->flags.hierarchical = true;
62e76326 1168
bf8fe701 1169 debugs(85, 5, "clientInterpretRequestHeaders: REQ_NOCACHE = " <<
450fe1cb 1170 (request->flags.noCache ? "SET" : "NOT SET"));
bf8fe701 1171 debugs(85, 5, "clientInterpretRequestHeaders: REQ_CACHABLE = " <<
45e5102d 1172 (request->flags.cachable ? "SET" : "NOT SET"));
bf8fe701 1173 debugs(85, 5, "clientInterpretRequestHeaders: REQ_HIERARCHICAL = " <<
45e5102d 1174 (request->flags.hierarchical ? "SET" : "NOT SET"));
62e76326 1175
edce4d98 1176}
1177
1178void
24438ec5 1179clientRedirectDoneWrapper(void *data, const Helper::Reply &result)
edce4d98 1180{
de31d06f 1181 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
db02222f 1182
de31d06f 1183 if (!calloutContext->httpStateIsValid())
62e76326 1184 return;
62e76326 1185
de31d06f 1186 calloutContext->clientRedirectDone(result);
1187}
1188
a8a0b1c2 1189void
24438ec5 1190clientStoreIdDoneWrapper(void *data, const Helper::Reply &result)
a8a0b1c2
EC
1191{
1192 ClientRequestContext *calloutContext = (ClientRequestContext *)data;
1193
1194 if (!calloutContext->httpStateIsValid())
1195 return;
1196
1197 calloutContext->clientStoreIdDone(result);
1198}
1199
de31d06f 1200void
24438ec5 1201ClientRequestContext::clientRedirectDone(const Helper::Reply &reply)
de31d06f 1202{
190154cf 1203 HttpRequest *old_request = http->request;
e166785a 1204 debugs(85, 5, HERE << "'" << http->uri << "' result=" << reply);
de31d06f 1205 assert(redirect_state == REDIRECT_PENDING);
1206 redirect_state = REDIRECT_DONE;
62e76326 1207
cf9f0261 1208 // Put helper response Notes into the transaction state record (ALE) eventually
d06e17ea 1209 // do it early to ensure that no matter what the outcome the notes are present.
457857fe
CT
1210 if (http->al != NULL)
1211 (void)SyncNotes(*http->al, *old_request);
1212
1213 UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
d06e17ea 1214
63fc9fb5 1215 switch (reply.result) {
32fd6d8a
CT
1216 case Helper::TimedOut:
1217 if (Config.onUrlRewriteTimeout.action != toutActBypass) {
1218 http->calloutsError(ERR_GATEWAY_FAILURE, ERR_DETAIL_REDIRECTOR_TIMEDOUT);
1219 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper: Timedout");
1220 }
1221 break;
1222
2428ce02
AJ
1223 case Helper::Unknown:
1224 case Helper::TT:
d06e17ea
AJ
1225 // Handler in redirect.cc should have already mapped Unknown
1226 // IF it contained valid entry for the old URL-rewrite helper protocol
1227 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper returned invalid result code. Wrong helper? " << reply);
1228 break;
1229
2428ce02 1230 case Helper::BrokenHelper:
32fd6d8a 1231 debugs(85, DBG_IMPORTANT, "ERROR: URL rewrite helper: " << reply);
d06e17ea
AJ
1232 break;
1233
2428ce02 1234 case Helper::Error:
d06e17ea
AJ
1235 // no change to be done.
1236 break;
62e76326 1237
2428ce02 1238 case Helper::Okay: {
d06e17ea
AJ
1239 // #1: redirect with a specific status code OK status=NNN url="..."
1240 // #2: redirect with a default status code OK url="..."
1241 // #3: re-write the URL OK rewrite-url="..."
1242
cf9f0261
CT
1243 const char *statusNote = reply.notes.findFirst("status");
1244 const char *urlNote = reply.notes.findFirst("url");
d06e17ea
AJ
1245
1246 if (urlNote != NULL) {
1247 // HTTP protocol redirect to be done.
1248
1249 // TODO: change default redirect status for appropriate requests
1250 // Squid defaults to 302 status for now for better compatibility with old clients.
f11c8e2f 1251 // HTTP/1.0 client should get 302 (Http::scFound)
955394ce
AJ
1252 // HTTP/1.1 client contacting reverse-proxy should get 307 (Http::scTemporaryRedirect)
1253 // HTTP/1.1 client being diverted by forward-proxy should get 303 (Http::scSeeOther)
f11c8e2f 1254 Http::StatusCode status = Http::scFound;
d06e17ea 1255 if (statusNote != NULL) {
cf9f0261 1256 const char * result = statusNote;
955394ce 1257 status = static_cast<Http::StatusCode>(atoi(result));
d06e17ea 1258 }
62e76326 1259
955394ce 1260 if (status == Http::scMovedPermanently
f11c8e2f 1261 || status == Http::scFound
955394ce
AJ
1262 || status == Http::scSeeOther
1263 || status == Http::scPermanentRedirect
1264 || status == Http::scTemporaryRedirect) {
62e76326 1265 http->redirect.status = status;
cf9f0261 1266 http->redirect.location = xstrdup(urlNote);
e5b677f0 1267 // TODO: validate the URL produced here is RFC 2616 compliant absolute URI
62e76326 1268 } else {
cf9f0261 1269 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << urlNote);
62e76326 1270 }
d06e17ea
AJ
1271 } else {
1272 // URL-rewrite wanted. Ew.
cf9f0261 1273 urlNote = reply.notes.findFirst("rewrite-url");
d06e17ea
AJ
1274
1275 // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
cf9f0261 1276 if (urlNote != NULL && strcmp(urlNote, http->uri)) {
d06e17ea
AJ
1277 // XXX: validate the URL properly *without* generating a whole new request object right here.
1278 // XXX: the clone() should be done only AFTER we know the new URL is valid.
1279 HttpRequest *new_request = old_request->clone();
cf9f0261 1280 if (urlParse(old_request->method, const_cast<char*>(urlNote), new_request)) {
d06e17ea
AJ
1281 debugs(61,2, HERE << "URL-rewriter diverts URL from " << urlCanonical(old_request) << " to " << urlCanonical(new_request));
1282
1283 // update the new request to flag the re-writing was done on it
e857372a 1284 new_request->flags.redirected = true;
d06e17ea
AJ
1285
1286 // unlink bodypipe from the old request. Not needed there any longer.
1287 if (old_request->body_pipe != NULL) {
1288 old_request->body_pipe = NULL;
1289 debugs(61,2, HERE << "URL-rewriter diverts body_pipe " << new_request->body_pipe <<
1290 " from request " << old_request << " to " << new_request);
1291 }
9be14530 1292
d06e17ea
AJ
1293 // update the current working ClientHttpRequest fields
1294 safe_free(http->uri);
1295 http->uri = xstrdup(urlCanonical(new_request));
1296 HTTPMSGUNLOCK(old_request);
b248c2a3
AJ
1297 http->request = new_request;
1298 HTTPMSGLOCK(http->request);
d06e17ea
AJ
1299 } else {
1300 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid request: " <<
cf9f0261 1301 old_request->method << " " << urlNote << " " << old_request->http_ver);
d06e17ea
AJ
1302 delete new_request;
1303 }
9be14530 1304 }
74b48915 1305 }
edce4d98 1306 }
d06e17ea
AJ
1307 break;
1308 }
62e76326 1309
edce4d98 1310 /* FIXME PIPELINE: This is innacurate during pipelining */
62e76326 1311
73c36fd9
AJ
1312 if (http->getConn() != NULL && Comm::IsConnOpen(http->getConn()->clientConnection))
1313 fd_note(http->getConn()->clientConnection->fd, http->uri);
62e76326 1314
c8be6d7b 1315 assert(http->uri);
62e76326 1316
de31d06f 1317 http->doCallouts();
edce4d98 1318}
1319
a8a0b1c2
EC
1320/**
1321 * This method handles the different replies from StoreID helper.
1322 */
1323void
24438ec5 1324ClientRequestContext::clientStoreIdDone(const Helper::Reply &reply)
a8a0b1c2
EC
1325{
1326 HttpRequest *old_request = http->request;
1327 debugs(85, 5, "'" << http->uri << "' result=" << reply);
1328 assert(store_id_state == REDIRECT_PENDING);
1329 store_id_state = REDIRECT_DONE;
1330
cf9f0261 1331 // Put helper response Notes into the transaction state record (ALE) eventually
a8a0b1c2 1332 // do it early to ensure that no matter what the outcome the notes are present.
457857fe
CT
1333 if (http->al != NULL)
1334 (void)SyncNotes(*http->al, *old_request);
1335
1336 UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
a8a0b1c2
EC
1337
1338 switch (reply.result) {
2428ce02
AJ
1339 case Helper::Unknown:
1340 case Helper::TT:
a8a0b1c2
EC
1341 // Handler in redirect.cc should have already mapped Unknown
1342 // IF it contained valid entry for the old helper protocol
1343 debugs(85, DBG_IMPORTANT, "ERROR: storeID helper returned invalid result code. Wrong helper? " << reply);
1344 break;
1345
32fd6d8a 1346 case Helper::TimedOut:
f53969cc 1347 // Timeouts for storeID are not implemented
2428ce02 1348 case Helper::BrokenHelper:
32fd6d8a 1349 debugs(85, DBG_IMPORTANT, "ERROR: storeID helper: " << reply);
a8a0b1c2
EC
1350 break;
1351
2428ce02 1352 case Helper::Error:
a8a0b1c2
EC
1353 // no change to be done.
1354 break;
1355
2428ce02 1356 case Helper::Okay: {
cf9f0261 1357 const char *urlNote = reply.notes.findFirst("store-id");
a8a0b1c2
EC
1358
1359 // prevent broken helpers causing too much damage. If old URL == new URL skip the re-write.
cf9f0261 1360 if (urlNote != NULL && strcmp(urlNote, http->uri) ) {
a8a0b1c2 1361 // Debug section required for some very specific cases.
cf9f0261
CT
1362 debugs(85, 9, "Setting storeID with: " << urlNote );
1363 http->request->store_id = urlNote;
1364 http->store_id = urlNote;
a8a0b1c2
EC
1365 }
1366 }
1367 break;
1368 }
1369
1370 http->doCallouts();
1371}
1372
b50e327b
AJ
1373/** Test cache allow/deny configuration
1374 * Sets flags.cachable=1 if caching is not denied.
1375 */
edce4d98 1376void
8e2745f4 1377ClientRequestContext::checkNoCache()
edce4d98 1378{
b50e327b
AJ
1379 if (Config.accessList.noCache) {
1380 acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
1381 acl_checklist->nonBlockingCheck(checkNoCacheDoneWrapper, this);
1382 } else {
1383 /* unless otherwise specified, we try to cache. */
2efeb0b7 1384 checkNoCacheDone(ACCESS_ALLOWED);
b50e327b 1385 }
edce4d98 1386}
1387
de31d06f 1388static void
2efeb0b7 1389checkNoCacheDoneWrapper(allow_t answer, void *data)
edce4d98 1390{
de31d06f 1391 ClientRequestContext *calloutContext = (ClientRequestContext *) data;
e4a67a80 1392
de31d06f 1393 if (!calloutContext->httpStateIsValid())
1394 return;
1395
1396 calloutContext->checkNoCacheDone(answer);
8e2745f4 1397}
4fb35c3c 1398
8e2745f4 1399void
2efeb0b7 1400ClientRequestContext::checkNoCacheDone(const allow_t &answer)
62e76326 1401{
8e2745f4 1402 acl_checklist = NULL;
45e5102d 1403 http->request->flags.cachable = (answer == ACCESS_ALLOWED);
de31d06f 1404 http->doCallouts();
edce4d98 1405}
1406
cb4f4424 1407#if USE_OPENSSL
e0c0d54c
CT
1408bool
1409ClientRequestContext::sslBumpAccessCheck()
1410{
08097970
AR
1411 // If SSL connection tunneling or bumping decision has been made, obey it.
1412 const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
1413 if (bumpMode != Ssl::bumpEnd) {
1414 debugs(85, 5, HERE << "SslBump already decided (" << bumpMode <<
1415 "), " << "ignoring ssl_bump for " << http->getConn());
9e104535 1416 http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
71cae389 1417 http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
08097970
AR
1418 return false;
1419 }
e0c0d54c 1420
08097970
AR
1421 // If we have not decided yet, decide whether to bump now.
1422
1423 // Bumping here can only start with a CONNECT request on a bumping port
1424 // (bumping of intercepted SSL conns is decided before we get 1st request).
1425 // We also do not bump redirected CONNECT requests.
c2a7cefd 1426 if (http->request->method != Http::METHOD_CONNECT || http->redirect.status ||
6a25a046
FC
1427 !Config.accessList.ssl_bump ||
1428 !http->getConn()->port->flags.tunnelSslBumping) {
71cae389 1429 http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
08097970 1430 debugs(85, 5, HERE << "cannot SslBump this request");
e0c0d54c
CT
1431 return false;
1432 }
08097970
AR
1433
1434 // Do not bump during authentication: clients would not proxy-authenticate
1435 // if we delay a 407 response and respond with 200 OK to CONNECT.
955394ce 1436 if (error && error->httpStatus == Http::scProxyAuthenticationRequired) {
71cae389 1437 http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
08097970
AR
1438 debugs(85, 5, HERE << "no SslBump during proxy authentication");
1439 return false;
1440 }
1441
1442 debugs(85, 5, HERE << "SslBump possible, checking ACL");
1443
eb898410
AJ
1444 ACLFilledChecklist *aclChecklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
1445 aclChecklist->nonBlockingCheck(sslBumpAccessCheckDoneWrapper, this);
08097970 1446 return true;
e0c0d54c
CT
1447}
1448
f8901ea9 1449/**
e0c0d54c
CT
1450 * A wrapper function to use the ClientRequestContext::sslBumpAccessCheckDone method
1451 * as ACLFilledChecklist callback
1452 */
1453static void
2efeb0b7 1454sslBumpAccessCheckDoneWrapper(allow_t answer, void *data)
e0c0d54c
CT
1455{
1456 ClientRequestContext *calloutContext = static_cast<ClientRequestContext *>(data);
9d5e7196
AJ
1457
1458 if (!calloutContext->httpStateIsValid())
1459 return;
ed6163ef 1460 calloutContext->sslBumpAccessCheckDone(answer);
e0c0d54c
CT
1461}
1462
1463void
ed6163ef 1464ClientRequestContext::sslBumpAccessCheckDone(const allow_t &answer)
e0c0d54c 1465{
ed6163ef
AJ
1466 if (!httpStateIsValid())
1467 return;
1468
08097970 1469 const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
87f237a9 1470 static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpNone;
08097970 1471 http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
71cae389 1472 http->al->ssl.bumpMode = bumpMode; // for logging
caf3666d 1473
e0c0d54c
CT
1474 http->doCallouts();
1475}
1476#endif
1477
69660be0 1478/*
1479 * Identify requests that do not go through the store and client side stream
1480 * and forward them to the appropriate location. All other requests, request
1481 * them.
edce4d98 1482 */
1483void
8e2745f4 1484ClientHttpRequest::processRequest()
edce4d98 1485{
7f06a3d8 1486 debugs(85, 4, request->method << ' ' << uri);
62e76326 1487
c2a7cefd 1488 if (request->method == Http::METHOD_CONNECT && !redirect.status) {
cb4f4424 1489#if USE_OPENSSL
31281814
AJ
1490 if (sslBumpNeeded()) {
1491 sslBumpStart();
1492 return;
1493 }
3712be3f 1494#endif
f84dd7eb 1495 getConn()->stopReading(); // tunnels read for themselves
06521a10 1496 tunnelStart(this, &out.size, &al->http.code, al);
62e76326 1497 return;
edce4d98 1498 }
62e76326 1499
8e2745f4 1500 httpStart();
1501}
1502
1503void
1504ClientHttpRequest::httpStart()
1505{
559da936 1506 PROF_start(httpStart);
8e2745f4 1507 logType = LOG_TAG_NONE;
02c8dde5 1508 debugs(85, 4, LogTags_str[logType] << " for '" << uri << "'");
bf8fe701 1509
edce4d98 1510 /* no one should have touched this */
8e2745f4 1511 assert(out.offset == 0);
edce4d98 1512 /* Use the Stream Luke */
8e2745f4 1513 clientStreamNode *node = (clientStreamNode *)client_stream.tail->data;
1514 clientStreamRead(node, this, node->readBuffer);
559da936 1515 PROF_stop(httpStart);
edce4d98 1516}
0655fa4d 1517
cb4f4424 1518#if USE_OPENSSL
3712be3f 1519
e0c0d54c 1520void
08097970 1521ClientHttpRequest::sslBumpNeed(Ssl::BumpMode mode)
e0c0d54c 1522{
caf3666d 1523 debugs(83, 3, HERE << "sslBump required: "<< Ssl::bumpMode(mode));
08097970 1524 sslBumpNeed_ = mode;
3712be3f 1525}
1526
1527// called when comm_write has completed
1528static void
c8407295 1529SslBumpEstablish(const Comm::ConnectionPointer &, char *, size_t, Comm::Flag errflag, int, void *data)
3712be3f 1530{
1531 ClientHttpRequest *r = static_cast<ClientHttpRequest*>(data);
1532 debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag);
1533
1534 assert(r && cbdataReferenceValid(r));
1535 r->sslBumpEstablish(errflag);
1536}
1537
1538void
c8407295 1539ClientHttpRequest::sslBumpEstablish(Comm::Flag errflag)
3712be3f 1540{
c8407295
AJ
1541 // Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up
1542 if (errflag == Comm::ERR_CLOSING)
3712be3f 1543 return;
1544
1545 if (errflag) {
1fa35be8 1546 debugs(85, 3, HERE << "CONNECT response failure in SslBump: " << errflag);
bbc83914 1547 getConn()->clientConnection->close();
3712be3f 1548 return;
1549 }
1550
40d8be97
AR
1551 // We lack HttpReply which logRequest() uses to log the status code.
1552 // TODO: Use HttpReply instead of the "200 Connection established" string.
71cae389 1553 al->http.code = 200;
40d8be97 1554
21512911
CT
1555#if USE_AUTH
1556 // Preserve authentication info for the ssl-bumped request
1557 if (request->auth_user_request != NULL)
cc1e110a 1558 getConn()->setAuth(request->auth_user_request, "SSL-bumped CONNECT");
21512911 1559#endif
03f00a11 1560
08097970
AR
1561 assert(sslBumpNeeded());
1562 getConn()->switchToHttps(request, sslBumpNeed_);
3712be3f 1563}
1564
1565void
1566ClientHttpRequest::sslBumpStart()
1567{
08097970
AR
1568 debugs(85, 5, HERE << "Confirming " << Ssl::bumpMode(sslBumpNeed_) <<
1569 "-bumped CONNECT tunnel on FD " << getConn()->clientConnection);
1570 getConn()->sslBumpMode = sslBumpNeed_;
3712be3f 1571
9e104535 1572 AsyncCall::Pointer bumpCall = commCbCall(85, 5, "ClientSocketContext::sslBumpEstablish",
f53969cc 1573 CommIoCbPtrFun(&SslBumpEstablish, this));
9e104535
CT
1574
1575 if (request->flags.interceptTproxy || request->flags.intercepted) {
1576 CommIoCbParams &params = GetCommParams<CommIoCbParams>(bumpCall);
1577 params.flag = Comm::OK;
1578 params.conn = getConn()->clientConnection;
1579 ScheduleCallHere(bumpCall);
1580 return;
1581 }
1582
08097970 1583 // send an HTTP 200 response to kick client SSL negotiation
3712be3f 1584 // TODO: Unify with tunnel.cc and add a Server(?) header
b0388924 1585 static const char *const conn_established = "HTTP/1.1 200 Connection established\r\n\r\n";
9e104535 1586 Comm::Write(getConn()->clientConnection, conn_established, strlen(conn_established), bumpCall, NULL);
3712be3f 1587}
1588
1589#endif
1590
0655fa4d 1591bool
1592ClientHttpRequest::gotEnough() const
1593{
86a2f789 1594 /** TODO: should be querying the stream. */
7173d5b0 1595 int64_t contentLength =
06a5ae20 1596 memObject()->getReply()->bodySize(request->method);
0655fa4d 1597 assert(contentLength >= 0);
1598
1599 if (out.offset < contentLength)
1600 return false;
1601
1602 return true;
1603}
1604
86a2f789 1605void
1606ClientHttpRequest::storeEntry(StoreEntry *newEntry)
1607{
1608 entry_ = newEntry;
1609}
1610
0976f8db 1611void
1612ClientHttpRequest::loggingEntry(StoreEntry *newEntry)
1613{
1614 if (loggingEntry_)
1bfe9ade 1615 loggingEntry_->unlock("ClientHttpRequest::loggingEntry");
0976f8db 1616
1617 loggingEntry_ = newEntry;
1618
1619 if (loggingEntry_)
1bfe9ade 1620 loggingEntry_->lock("ClientHttpRequest::loggingEntry");
0976f8db 1621}
86a2f789 1622
de31d06f 1623/*
1624 * doCallouts() - This function controls the order of "callout"
1625 * executions, including non-blocking access control checks, the
1626 * redirector, and ICAP. Previously, these callouts were chained
1627 * together such that "clientAccessCheckDone()" would call
1628 * "clientRedirectStart()" and so on.
1629 *
1630 * The ClientRequestContext (aka calloutContext) class holds certain
1631 * state data for the callout/callback operations. Previously
1632 * ClientHttpRequest would sort of hand off control to ClientRequestContext
1633 * for a short time. ClientRequestContext would then delete itself
1634 * and pass control back to ClientHttpRequest when all callouts
1635 * were finished.
1636 *
1637 * This caused some problems for ICAP because we want to make the
1638 * ICAP callout after checking ACLs, but before checking the no_cache
1639 * list. We can't stuff the ICAP state into the ClientRequestContext
1640 * class because we still need the ICAP state after ClientRequestContext
1641 * goes away.
1642 *
1643 * Note that ClientRequestContext is created before the first call
1644 * to doCallouts().
1645 *
1646 * If one of the callouts notices that ClientHttpRequest is no
1647 * longer valid, it should call cbdataReferenceDone() so that
1648 * ClientHttpRequest's reference count goes to zero and it will get
1649 * deleted. ClientHttpRequest will then delete ClientRequestContext.
1650 *
1651 * Note that we set the _done flags here before actually starting
1652 * the callout. This is strictly for convenience.
1653 */
1654
82afb125
FC
1655tos_t aclMapTOS (acl_tos * head, ACLChecklist * ch);
1656nfmark_t aclMapNfmark (acl_nfmark * head, ACLChecklist * ch);
057f5854 1657
de31d06f 1658void
1659ClientHttpRequest::doCallouts()
1660{
1661 assert(calloutContext);
1662
6fca33e0 1663 /*Save the original request for logging purposes*/
b248c2a3
AJ
1664 if (!calloutContext->http->al->request) {
1665 calloutContext->http->al->request = request;
1666 HTTPMSGLOCK(calloutContext->http->al->request);
457857fe
CT
1667
1668 NotePairs &notes = SyncNotes(*calloutContext->http->al, *calloutContext->http->request);
1669 // Make the previously set client connection ID available as annotation.
1670 if (ConnStateData *csd = calloutContext->http->getConn()) {
1671 if (!csd->connectionTag().isEmpty())
1672 notes.add("clt_conn_tag", SBuf(csd->connectionTag()).c_str());
1673 }
b248c2a3 1674 }
6fca33e0 1675
38450a50 1676 if (!calloutContext->error) {
87f237a9 1677 // CVE-2009-0801: verify the Host: header is consistent with other known details.
38450a50
CT
1678 if (!calloutContext->host_header_verify_done) {
1679 debugs(83, 3, HERE << "Doing calloutContext->hostHeaderVerify()");
1680 calloutContext->host_header_verify_done = true;
1681 calloutContext->hostHeaderVerify();
1682 return;
1683 }
fe97983f 1684
38450a50
CT
1685 if (!calloutContext->http_access_done) {
1686 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
1687 calloutContext->http_access_done = true;
1688 calloutContext->clientAccessCheck();
1689 return;
1690 }
de31d06f 1691
a83c6ed6 1692#if USE_ADAPTATION
38450a50
CT
1693 if (!calloutContext->adaptation_acl_check_done) {
1694 calloutContext->adaptation_acl_check_done = true;
1695 if (Adaptation::AccessCheck::Start(
87f237a9 1696 Adaptation::methodReqmod, Adaptation::pointPreCache,
af0ded40 1697 request, NULL, calloutContext->http->al, this))
38450a50
CT
1698 return; // will call callback
1699 }
de31d06f 1700#endif
1701
38450a50
CT
1702 if (!calloutContext->redirect_done) {
1703 calloutContext->redirect_done = true;
1704 assert(calloutContext->redirect_state == REDIRECT_NONE);
de31d06f 1705
38450a50
CT
1706 if (Config.Program.redirect) {
1707 debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
1708 calloutContext->redirect_state = REDIRECT_PENDING;
1709 calloutContext->clientRedirectStart();
1710 return;
1711 }
de31d06f 1712 }
de31d06f 1713
38450a50
CT
1714 if (!calloutContext->adapted_http_access_done) {
1715 debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck2()");
1716 calloutContext->adapted_http_access_done = true;
1717 calloutContext->clientAccessCheck2();
1718 return;
1719 }
533493da 1720
a8a0b1c2
EC
1721 if (!calloutContext->store_id_done) {
1722 calloutContext->store_id_done = true;
1723 assert(calloutContext->store_id_state == REDIRECT_NONE);
1724
1725 if (Config.Program.store_id) {
1726 debugs(83, 3,"Doing calloutContext->clientStoreIdStart()");
1727 calloutContext->store_id_state = REDIRECT_PENDING;
1728 calloutContext->clientStoreIdStart();
1729 return;
1730 }
1731 }
1732
38450a50
CT
1733 if (!calloutContext->interpreted_req_hdrs) {
1734 debugs(83, 3, HERE << "Doing clientInterpretRequestHeaders()");
1735 calloutContext->interpreted_req_hdrs = 1;
1736 clientInterpretRequestHeaders(this);
1737 }
57abaac0 1738
38450a50
CT
1739 if (!calloutContext->no_cache_done) {
1740 calloutContext->no_cache_done = true;
de31d06f 1741
45e5102d 1742 if (Config.accessList.noCache && request->flags.cachable) {
38450a50
CT
1743 debugs(83, 3, HERE << "Doing calloutContext->checkNoCache()");
1744 calloutContext->checkNoCache();
1745 return;
1746 }
de31d06f 1747 }
38450a50 1748 } // if !calloutContext->error
de31d06f 1749
425de4c8
AJ
1750 if (!calloutContext->tosToClientDone) {
1751 calloutContext->tosToClientDone = true;
73c36fd9 1752 if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
c0941a6a 1753 ACLFilledChecklist ch(NULL, request, NULL);
057f5854 1754 ch.src_addr = request->client_addr;
1755 ch.my_addr = request->my_addr;
425de4c8 1756 tos_t tos = aclMapTOS(Ip::Qos::TheConfig.tosToClient, &ch);
3712be3f 1757 if (tos)
73c36fd9 1758 Ip::Qos::setSockTos(getConn()->clientConnection, tos);
425de4c8
AJ
1759 }
1760 }
1761
1762 if (!calloutContext->nfmarkToClientDone) {
1763 calloutContext->nfmarkToClientDone = true;
73c36fd9 1764 if (getConn() != NULL && Comm::IsConnOpen(getConn()->clientConnection)) {
425de4c8
AJ
1765 ACLFilledChecklist ch(NULL, request, NULL);
1766 ch.src_addr = request->client_addr;
1767 ch.my_addr = request->my_addr;
1768 nfmark_t mark = aclMapNfmark(Ip::Qos::TheConfig.nfmarkToClient, &ch);
1769 if (mark)
73c36fd9 1770 Ip::Qos::setSockNfmark(getConn()->clientConnection, mark);
3712be3f 1771 }
057f5854 1772 }
1773
cb4f4424 1774#if USE_OPENSSL
7a957a93
AR
1775 // We need to check for SslBump even if the calloutContext->error is set
1776 // because bumping may require delaying the error until after CONNECT.
e0c0d54c
CT
1777 if (!calloutContext->sslBumpCheckDone) {
1778 calloutContext->sslBumpCheckDone = true;
1779 if (calloutContext->sslBumpAccessCheck())
1780 return;
1781 /* else no ssl bump required*/
1782 }
d2565320 1783#endif
e0c0d54c 1784
2bd84e5f 1785 if (calloutContext->error) {
a8a0b1c2
EC
1786 const char *storeUri = request->storeId();
1787 StoreEntry *e= storeCreateEntry(storeUri, storeUri, request->flags, request->method);
cb4f4424 1788#if USE_OPENSSL
08097970 1789 if (sslBumpNeeded()) {
9e104535
CT
1790 // We have to serve an error, so bump the client first.
1791 sslBumpNeed(Ssl::bumpClientFirst);
2bd84e5f
CT
1792 // set final error but delay sending until we bump
1793 Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
1794 errorAppendEntry(e, calloutContext->error);
1795 calloutContext->error = NULL;
1796 getConn()->setServerBump(srvBump);
1bfe9ade 1797 e->unlock("ClientHttpRequest::doCallouts+sslBumpNeeded");
87f237a9 1798 } else
2bd84e5f
CT
1799#endif
1800 {
7a957a93 1801 // send the error to the client now
2bd84e5f
CT
1802 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1803 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1804 assert (repContext);
f0baf149 1805 repContext->setReplyToStoreEntry(e, "immediate SslBump error");
2bd84e5f
CT
1806 errorAppendEntry(e, calloutContext->error);
1807 calloutContext->error = NULL;
1808 if (calloutContext->readNextRequest)
1809 getConn()->flags.readMore = true; // resume any pipeline reads.
1810 node = (clientStreamNode *)client_stream.tail->data;
1811 clientStreamRead(node, this, node->readBuffer);
1bfe9ade 1812 e->unlock("ClientHttpRequest::doCallouts-sslBumpNeeded");
2bd84e5f
CT
1813 return;
1814 }
1815 }
1816
de31d06f 1817 cbdataReferenceDone(calloutContext->http);
1818 delete calloutContext;
1819 calloutContext = NULL;
de31d06f 1820#if HEADERS_LOG
1821
1822 headersLog(0, 1, request->method, request);
1823#endif
1824
58d7150d 1825 debugs(83, 3, HERE << "calling processRequest()");
de31d06f 1826 processRequest();
3ff65596
AR
1827
1828#if ICAP_CLIENT
1829 Adaptation::Icap::History::Pointer ih = request->icapHistory();
1830 if (ih != NULL)
1831 ih->logType = logType;
1832#endif
de31d06f 1833}
1834
32d002cb 1835#if !_USE_INLINE_
86a2f789 1836#include "client_side_request.cci"
1837#endif
de31d06f 1838
a83c6ed6 1839#if USE_ADAPTATION
a22e6cd3
AR
1840/// Initiate an asynchronous adaptation transaction which will call us back.
1841void
1842ClientHttpRequest::startAdaptation(const Adaptation::ServiceGroupPointer &g)
3b299123 1843{
a22e6cd3 1844 debugs(85, 3, HERE << "adaptation needed for " << this);
a83c6ed6
AR
1845 assert(!virginHeadSource);
1846 assert(!adaptedBodySource);
a22e6cd3 1847 virginHeadSource = initiateAdaptation(
af0ded40 1848 new Adaptation::Iterator(request, NULL, al, g));
a83c6ed6 1849
e1381638 1850 // we could try to guess whether we can bypass this adaptation
a22e6cd3 1851 // initiation failure, but it should not really happen
4299f876 1852 Must(initiated(virginHeadSource));
de31d06f 1853}
1854
1855void
3af10ac0 1856ClientHttpRequest::noteAdaptationAnswer(const Adaptation::Answer &answer)
de31d06f 1857{
f53969cc 1858 assert(cbdataReferenceValid(this)); // indicates bug
3af10ac0
AR
1859 clearAdaptation(virginHeadSource);
1860 assert(!adaptedBodySource);
1861
1862 switch (answer.kind) {
1863 case Adaptation::Answer::akForward:
b248c2a3 1864 handleAdaptedHeader(const_cast<HttpMsg*>(answer.message.getRaw()));
3af10ac0
AR
1865 break;
1866
1867 case Adaptation::Answer::akBlock:
1868 handleAdaptationBlock(answer);
1869 break;
1870
1871 case Adaptation::Answer::akError:
1872 handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_ABORT, !answer.final);
1873 break;
1874 }
1875}
1876
1877void
ec4d1a1d 1878ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg)
3af10ac0 1879{
5f8252d2 1880 assert(msg);
1881
b044675d 1882 if (HttpRequest *new_req = dynamic_cast<HttpRequest*>(msg)) {
200ac359 1883 /*
5f8252d2 1884 * Replace the old request with the new request.
200ac359 1885 */
6dd9f4bd 1886 HTTPMSGUNLOCK(request);
b248c2a3
AJ
1887 request = new_req;
1888 HTTPMSGLOCK(request);
11f62bfb
AJ
1889
1890 // update the new message to flag whether URL re-writing was done on it
1891 if (strcmp(urlCanonical(request),uri) != 0)
1892 request->flags.redirected = 1;
1893
200ac359 1894 /*
1895 * Store the new URI for logging
1896 */
1897 xfree(uri);
1898 uri = xstrdup(urlCanonical(request));
1899 setLogUri(this, urlCanonicalClean(request));
914b89a2 1900 assert(request->method.id());
b044675d 1901 } else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
1902 debugs(85,3,HERE << "REQMOD reply is HTTP reply");
1903
5f8252d2 1904 // subscribe to receive reply body
1905 if (new_rep->body_pipe != NULL) {
a83c6ed6 1906 adaptedBodySource = new_rep->body_pipe;
d222a56c
HN
1907 int consumer_ok = adaptedBodySource->setConsumerIfNotLate(this);
1908 assert(consumer_ok);
5f8252d2 1909 }
1910
b044675d 1911 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
1912 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
ea25a575 1913 assert(repContext);
b044675d 1914 repContext->createStoreEntry(request->method, request->flags);
1915
1916 EBIT_CLR(storeEntry()->flags, ENTRY_FWD_HDR_WAIT);
1917 request_satisfaction_mode = true;
1918 request_satisfaction_offset = 0;
1919 storeEntry()->replaceHttpReply(new_rep);
97ae5196 1920 storeEntry()->timestampsSet();
cb4c4288 1921
a83c6ed6 1922 if (!adaptedBodySource) // no body
cb4c4288 1923 storeEntry()->complete();
b044675d 1924 clientGetMoreData(node, this);
200ac359 1925 }
de31d06f 1926
5f8252d2 1927 // we are done with getting headers (but may be receiving body)
a83c6ed6 1928 clearAdaptation(virginHeadSource);
5f8252d2 1929
b044675d 1930 if (!request_satisfaction_mode)
1931 doCallouts();
de31d06f 1932}
1933
1934void
3af10ac0 1935ClientHttpRequest::handleAdaptationBlock(const Adaptation::Answer &answer)
de31d06f 1936{
3af10ac0
AR
1937 request->detailError(ERR_ACCESS_DENIED, ERR_DETAIL_REQMOD_BLOCK);
1938 AclMatchedName = answer.ruleId.termedBuf();
1939 assert(calloutContext);
1940 calloutContext->clientAccessCheckDone(ACCESS_DENIED);
1941 AclMatchedName = NULL;
de31d06f 1942}
1943
0ad2b63b
CT
1944void
1945ClientHttpRequest::resumeBodyStorage()
1946{
e83cdc25 1947 if (!adaptedBodySource)
0ad2b63b
CT
1948 return;
1949
1950 noteMoreBodyDataAvailable(adaptedBodySource);
1951}
1952
de31d06f 1953void
1cf238db 1954ClientHttpRequest::noteMoreBodyDataAvailable(BodyPipe::Pointer)
de31d06f 1955{
5f8252d2 1956 assert(request_satisfaction_mode);
a83c6ed6 1957 assert(adaptedBodySource != NULL);
5f8252d2 1958
0ad2b63b 1959 if (size_t contentSize = adaptedBodySource->buf().contentSize()) {
4dc2b072 1960 const size_t spaceAvailable = storeEntry()->bytesWanted(Range<size_t>(0,contentSize));
0ad2b63b
CT
1961
1962 if (spaceAvailable < contentSize ) {
1963 // No or partial body data consuming
1964 typedef NullaryMemFunT<ClientHttpRequest> Dialer;
1965 AsyncCall::Pointer call = asyncCall(93, 5, "ClientHttpRequest::resumeBodyStorage",
1966 Dialer(this, &ClientHttpRequest::resumeBodyStorage));
1967 storeEntry()->deferProducer(call);
1968 }
1969
4dc2b072 1970 if (!spaceAvailable)
0ad2b63b
CT
1971 return;
1972
1973 if (spaceAvailable < contentSize )
1974 contentSize = spaceAvailable;
1975
a83c6ed6 1976 BodyPipeCheckout bpc(*adaptedBodySource);
0ad2b63b 1977 const StoreIOBuffer ioBuf(&bpc.buf, request_satisfaction_offset, contentSize);
5f8252d2 1978 storeEntry()->write(ioBuf);
0ad2b63b
CT
1979 // assume StoreEntry::write() writes the entire ioBuf
1980 request_satisfaction_offset += ioBuf.length;
4ce0e99b 1981 bpc.buf.consume(contentSize);
5f8252d2 1982 bpc.checkIn();
1983 }
1984
a83c6ed6 1985 if (adaptedBodySource->exhausted())
5f8252d2 1986 endRequestSatisfaction();
1987 // else wait for more body data
de31d06f 1988}
1989
1990void
1cf238db 1991ClientHttpRequest::noteBodyProductionEnded(BodyPipe::Pointer)
de31d06f 1992{
a83c6ed6 1993 assert(!virginHeadSource);
0ad2b63b
CT
1994 // should we end request satisfaction now?
1995 if (adaptedBodySource != NULL && adaptedBodySource->exhausted())
5f8252d2 1996 endRequestSatisfaction();
5f8252d2 1997}
3b299123 1998
5f8252d2 1999void
26ac0430
AJ
2000ClientHttpRequest::endRequestSatisfaction()
2001{
5f8252d2 2002 debugs(85,4, HERE << this << " ends request satisfaction");
2003 assert(request_satisfaction_mode);
a83c6ed6 2004 stopConsumingFrom(adaptedBodySource);
3b299123 2005
5f8252d2 2006 // TODO: anything else needed to end store entry formation correctly?
2007 storeEntry()->complete();
2008}
de31d06f 2009
5f8252d2 2010void
1cf238db 2011ClientHttpRequest::noteBodyProducerAborted(BodyPipe::Pointer)
5f8252d2 2012{
a83c6ed6
AR
2013 assert(!virginHeadSource);
2014 stopConsumingFrom(adaptedBodySource);
eae3a9a6
AR
2015
2016 debugs(85,3, HERE << "REQMOD body production failed");
2017 if (request_satisfaction_mode) { // too late to recover or serve an error
2018 request->detailError(ERR_ICAP_FAILURE, ERR_DETAIL_CLT_REQMOD_RESP_BODY);
73c36fd9 2019 const Comm::ConnectionPointer c = getConn()->clientConnection;
e7cea0ed
AJ
2020 Must(Comm::IsConnOpen(c));
2021 c->close(); // drastic, but we may be writing a response already
eae3a9a6
AR
2022 } else {
2023 handleAdaptationFailure(ERR_DETAIL_CLT_REQMOD_REQ_BODY);
2024 }
5f8252d2 2025}
3b299123 2026
5f8252d2 2027void
64b66b76 2028ClientHttpRequest::handleAdaptationFailure(int errDetail, bool bypassable)
5f8252d2 2029{
a83c6ed6 2030 debugs(85,3, HERE << "handleAdaptationFailure(" << bypassable << ")");
3b299123 2031
5f8252d2 2032 const bool usedStore = storeEntry() && !storeEntry()->isEmpty();
2033 const bool usedPipe = request->body_pipe != NULL &&
26ac0430 2034 request->body_pipe->consumedSize() > 0;
3b299123 2035
9d4d7c5e 2036 if (bypassable && !usedStore && !usedPipe) {
2037 debugs(85,3, HERE << "ICAP REQMOD callout failed, bypassing: " << calloutContext);
5f8252d2 2038 if (calloutContext)
2039 doCallouts();
2040 return;
2041 }
3b299123 2042
5f8252d2 2043 debugs(85,3, HERE << "ICAP REQMOD callout failed, responding with error");
3b299123 2044
5f8252d2 2045 clientStreamNode *node = (clientStreamNode *)client_stream.tail->prev->data;
2046 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2047 assert(repContext);
de31d06f 2048
32fd6d8a
CT
2049 calloutsError(ERR_ICAP_FAILURE, errDetail);
2050
2051 if (calloutContext)
2052 doCallouts();
2053}
2054
5f1d7e48
CT
2055#endif
2056
32fd6d8a
CT
2057// XXX: modify and use with ClientRequestContext::clientAccessCheckDone too.
2058void
2059ClientHttpRequest::calloutsError(const err_type error, const int errDetail)
2060{
26ac0430 2061 // The original author of the code also wanted to pass an errno to
5f8252d2 2062 // setReplyToError, but it seems unlikely that the errno reflects the
2063 // true cause of the error at this point, so I did not pass it.
2bd84e5f
CT
2064 if (calloutContext) {
2065 Ip::Address noAddr;
4dd643d5 2066 noAddr.setNoAddr();
2bd84e5f 2067 ConnStateData * c = getConn();
32fd6d8a 2068 calloutContext->error = clientBuildError(error, Http::scInternalServerError,
87f237a9
A
2069 NULL,
2070 c != NULL ? c->clientConnection->remote : noAddr,
2071 request
2072 );
79fc6915 2073#if USE_AUTH
87f237a9 2074 calloutContext->error->auth_user_request =
cc1e110a 2075 c != NULL && c->getAuth() != NULL ? c->getAuth() : request->auth_user_request;
79fc6915 2076#endif
129fe2a1 2077 calloutContext->error->detailError(errDetail);
2bd84e5f 2078 calloutContext->readNextRequest = true;
7830d88a
FC
2079 if (c != NULL)
2080 c->expectNoForwarding();
2bd84e5f
CT
2081 }
2082 //else if(calloutContext == NULL) is it possible?
de31d06f 2083}
2084