]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Refactored delay pools registration
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
4d5a6db8 3 * $Id: forward.cc,v 1.175 2008/02/11 22:26:39 rousskov Exp $
41462d93 4 *
5 * DEBUG: section 17 Request Forwarding
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
41462d93 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
41462d93 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
41462d93 34 */
35
36
37#include "squid.h"
b6b6f466 38#include "forward.h"
4fb35c3c 39#include "ACLChecklist.h"
8000a965 40#include "ACL.h"
aa839030 41#include "CacheManager.h"
42#include "event.h"
43#include "errorpage.h"
44#include "fde.h"
924f73bc 45#include "HttpReply.h"
aa839030 46#include "HttpRequest.h"
47#include "MemObject.h"
781ce8ff 48#include "pconn.h"
aa839030 49#include "SquidTime.h"
50#include "Store.h"
41462d93 51
34ec5c62
AJ
52/* for IPInterceptor API */
53#include "IPInterception.h"
fc68f6b1 54
b6b6f466 55static PSC fwdStartCompleteWrapper;
56static PF fwdServerClosedWrapper;
57#if USE_SSL
58static PF fwdNegotiateSSLWrapper;
59#endif
60static PF fwdConnectTimeoutWrapper;
61static EVH fwdConnectStartWrapper;
62static CNCB fwdConnectDoneWrapper;
63
8ddcc35d 64static OBJH fwdStats;
b6b6f466 65static void fwdServerFree(FwdServer * fs);
8ddcc35d 66
67#define MAX_FWD_STATS_IDX 9
9977e14b 68static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 69
225644d7 70#if WIP_FWD_LOG
71static void fwdLog(FwdState * fwdState);
72static Logfile *logfile = NULL;
73#endif
74
781ce8ff 75static PconnPool *fwdPconnPool = new PconnPool("server-side");
b6b6f466 76CBDATA_CLASS_INIT(FwdState);
781ce8ff 77
429871db 78void
79FwdState::abort(void* d)
80{
81 FwdState* fwd = (FwdState*)d;
6ecaf21a 82 Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope.
429871db 83
84 if (fwd->server_fd >= 0) {
85 comm_close(fwd->server_fd);
86 fwd->server_fd = -1;
87 }
88
89 fwd->self = NULL;
90}
91
b6b6f466 92/**** PUBLIC INTERFACE ********************************************************/
c7f9eb6d 93
b6b6f466 94FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r)
db1cd23c 95{
b6b6f466 96 entry = e;
97 client_fd = fd;
98 server_fd = -1;
6dd9f4bd 99 request = HTTPMSGLOCK(r);
b6b6f466 100 start_t = squid_curtime;
34266cde 101
3d0ac046 102 e->lock();
b6b6f466 103 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7a0fb323 104}
105
106// Called once, right after object creation, when it is safe to set self
fc68f6b1 107void FwdState::start(Pointer aSelf)
108{
7a0fb323 109 // Protect ourselves from being destroyed when the only Server pointing
110 // to us is gone (while we expect to talk to more Servers later).
111 // Once we set self, we are responsible for clearing it when we do not
112 // expect to talk to any servers.
113 self = aSelf; // refcounted
114
115 // We hope that either the store entry aborts or peer is selected.
116 // Otherwise we are going to leak our object.
34266cde 117
3900307b 118 entry->registerAbort(FwdState::abort, this);
7a0fb323 119 peerSelect(request, entry, fwdStartCompleteWrapper, this);
429871db 120
fc68f6b1 121 // TODO: set self _after_ the peer is selected because we do not need
7a0fb323 122 // self until we start talking to some Server.
db1cd23c 123}
124
802a8c1d 125void
126FwdState::completed()
41462d93 127{
fc68f6b1 128 if (flags.forward_completed == 1) {
129 debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
130 return;
131 }
132
133 flags.forward_completed = 1;
802a8c1d 134
bc87dc25 135#if URL_CHECKSUM_DEBUG
62e76326 136
b6b6f466 137 entry->mem_obj->checkUrlChecksum();
225644d7 138#endif
139#if WIP_FWD_LOG
62e76326 140
b6b6f466 141 log();
bc87dc25 142#endif
62e76326 143
b6b6f466 144 if (entry->store_status == STORE_PENDING) {
145 if (entry->isEmpty()) {
146 assert(err);
147 errorAppendEntry(entry, err);
148 err = NULL;
62e76326 149 } else {
b6b6f466 150 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
151 entry->complete();
d88e3c49 152 entry->releaseRequest();
62e76326 153 }
f563eea9 154 }
62e76326 155
b6b6f466 156 if (storePendingNClients(entry) > 0)
157 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 158
802a8c1d 159}
160
161FwdState::~FwdState()
162{
163 debugs(17, 3, HERE << "FwdState destructor starting");
fc68f6b1 164
802a8c1d 165 if (! flags.forward_completed)
fc68f6b1 166 completed();
802a8c1d 167
b6b6f466 168 serversFree(&servers);
62e76326 169
6dd9f4bd 170 HTTPMSGUNLOCK(request);
62e76326 171
b6b6f466 172 if (err)
173 errorStateFree(err);
62e76326 174
3900307b 175 entry->unregisterAbort();
429871db 176
97b5e68f 177 entry->unlock();
62e76326 178
b6b6f466 179 entry = NULL;
62e76326 180
c2f45110 181 int fd = server_fd;
182
183 if (fd > -1) {
b6b6f466 184 server_fd = -1;
c2f45110 185 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
bf8fe701 186 debugs(17, 3, "fwdStateFree: closing FD " << fd);
c2f45110 187 comm_close(fd);
b6b6f466 188 }
fc68f6b1 189
b6b6f466 190 debugs(17, 3, HERE << "FwdState destructor done");
191}
62e76326 192
38413773 193/**
b6b6f466 194 * This is the entry point for client-side to start forwarding
195 * a transaction. It is a static method that may or may not
196 * allocate a FwdState.
197 */
be0c6690 198void
b6b6f466 199FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
200{
201 /*
202 * client_addr == no_addr indicates this is an "internal" request
203 * from peer_digest.c, asn.c, netdb.c, etc and should always
204 * be allowed. yuck, I know.
205 */
62e76326 206
cc192b50 207 if ( !request->client_addr.IsNoAddr() && request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) {
b6b6f466 208 /*
209 * Check if this host is allowed to fetch MISSES from us (miss_access)
210 */
211 ACLChecklist ch;
212 ch.src_addr = request->client_addr;
213 ch.my_addr = request->my_addr;
6dd9f4bd 214 ch.request = HTTPMSGLOCK(request);
b6b6f466 215 ch.accessList = cbdataReference(Config.accessList.miss);
216 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
217 int answer = ch.fastCheck();
218
219 if (answer == 0) {
220 err_type page_id;
9ce7856a 221 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 222
223 if (page_id == ERR_NONE)
224 page_id = ERR_FORWARDING_DENIED;
225
2cc81f1f 226 ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN, request);
b6b6f466 227
228 errorAppendEntry(entry, anErr); // frees anErr
229
be0c6690 230 return;
b6b6f466 231 }
232 }
233
bf8fe701 234 debugs(17, 3, "FwdState::start() '" << entry->url() << "'");
f4ef658f 235 /*
236 * This seems like an odd place to bind mem_obj and request.
237 * Might want to assert that request is NULL at this point
238 */
6dd9f4bd 239 entry->mem_obj->request = HTTPMSGLOCK(request);
b6b6f466 240#if URL_CHECKSUM_DEBUG
241
242 entry->mem_obj->checkUrlChecksum();
243#endif
244
245 if (shutting_down) {
246 /* more yuck */
2cc81f1f 247 ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 248 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 249 return;
6801f8a8 250 }
62e76326 251
b6b6f466 252 switch (request->protocol) {
253
254 case PROTO_INTERNAL:
255 internalStart(request, entry);
be0c6690 256 return;
b6b6f466 257
258 case PROTO_CACHEOBJ:
c83f0bd5 259 CacheManager::GetInstance()->Start(client_fd, request, entry);
be0c6690 260 return;
b6b6f466 261
262 case PROTO_URN:
263 urnStart(request, entry);
be0c6690 264 return;
b6b6f466 265
266 default:
7a0fb323 267 FwdState::Pointer fwd = new FwdState(client_fd, entry, request);
2ad20b4f 268
fc68f6b1 269 /* If we need to transparently proxy the request
cc192b50 270 * then we need the client source protocol, address and port */
f165d2fb 271 if(request->flags.spoof_client_ip) {
2ad20b4f
AJ
272 fwd->src = request->client_addr;
273 // AYJ: do we need to pass on the transparent flag also?
274 }
fc68f6b1 275
7a0fb323 276 fwd->start(fwd);
be0c6690 277 return;
b6b6f466 278 }
279
280 /* NOTREACHED */
41462d93 281}
282
b6b6f466 283void
284FwdState::fail(ErrorState * errorState)
285{
38413773 286 debugs(17, 3, HERE << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() );
b6b6f466 287
288 if (err)
289 errorStateFree(err);
290
291 err = errorState;
292
293 if (!errorState->request)
6dd9f4bd 294 errorState->request = HTTPMSGLOCK(request);
b6b6f466 295}
296
38413773 297/**
b6b6f466 298 * Frees fwdState without closing FD or generating an abort
299 */
300void
301FwdState::unregister(int fd)
302{
38413773 303 debugs(17, 3, HERE << entry->url() );
b6b6f466 304 assert(fd == server_fd);
305 assert(fd > -1);
306 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
307 server_fd = -1;
308}
309
38413773 310/**
b6b6f466 311 * server-side modules call fwdComplete() when they are done
312 * downloading an object. Then, we either 1) re-forward the
313 * request somewhere else if needed, or 2) call storeComplete()
314 * to finish it off
315 */
316void
317FwdState::complete()
318{
319 StoreEntry *e = entry;
320 assert(entry->store_status == STORE_PENDING);
38413773 321 debugs(17, 3, HERE << e->url() << "\n\tstatus " << entry->getReply()->sline.status );
b6b6f466 322#if URL_CHECKSUM_DEBUG
323
324 entry->mem_obj->checkUrlChecksum();
325#endif
326
327 logReplyStatus(n_tries, entry->getReply()->sline.status);
328
329 if (reforward()) {
bf8fe701 330 debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url());
b6b6f466 331
332 if (server_fd > -1)
333 unregister(server_fd);
334
3900307b 335 e->reset();
b6b6f466 336
337 startComplete(servers);
338 } else {
bf8fe701 339 debugs(17, 3, "fwdComplete: not re-forwarding status " << entry->getReply()->sline.status);
b6b6f466 340 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
fc68f6b1 341 entry->complete();
342
343 if (server_fd < 0)
344 completed();
345
7a0fb323 346 self = NULL; // refcounted
b6b6f466 347 }
348}
349
350
351/**** CALLBACK WRAPPERS ************************************************************/
352
353static void
354fwdStartCompleteWrapper(FwdServer * servers, void *data)
355{
356 FwdState *fwd = (FwdState *) data;
357 fwd->startComplete(servers);
358}
359
360static void
361fwdServerClosedWrapper(int fd, void *data)
362{
363 FwdState *fwd = (FwdState *) data;
364 fwd->serverClosed(fd);
365}
366
367static void
368fwdConnectStartWrapper(void *data)
369{
370 FwdState *fwd = (FwdState *) data;
371 fwd->connectStart();
372}
373
374#if USE_SSL
375static void
376fwdNegotiateSSLWrapper(int fd, void *data)
377{
378 FwdState *fwd = (FwdState *) data;
379 fwd->negotiateSSL(fd);
380}
381
382#endif
383
384static void
385fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
386{
387 FwdState *fwd = (FwdState *) data;
388 fwd->connectDone(server_fd, status, xerrno);
389}
390
391static void
392fwdConnectTimeoutWrapper(int fd, void *data)
393{
394 FwdState *fwd = (FwdState *) data;
395 fwd->connectTimeout(fd);
396}
397
398/*
399 * Accounts for closed persistent connections
400 */
401static void
402fwdPeerClosed(int fd, void *data)
403{
404 peer *p = (peer *)data;
405 p->stats.conn_open--;
406}
407
408/**** PRIVATE *****************************************************************/
409
545782b8 410/*
411 * FwdState::checkRetry
412 *
413 * Return TRUE if the request SHOULD be retried. This method is
414 * called when the HTTP connection fails, or when the connection
415 * is closed before server-side read the end of HTTP headers.
416 */
b6b6f466 417bool
418FwdState::checkRetry()
68bd6892 419{
d8fd0f18 420 if (shutting_down)
b6b6f466 421 return false;
62e76326 422
b6b6f466 423 if (entry->store_status != STORE_PENDING)
424 return false;
62e76326 425
b6b6f466 426 if (!entry->isEmpty())
427 return false;
62e76326 428
b6b6f466 429 if (n_tries > 10)
430 return false;
62e76326 431
b6b6f466 432 if (origin_tries > 2)
433 return false;
4ed0e075 434
b6b6f466 435 if (squid_curtime - start_t > Config.Timeout.forward)
436 return false;
62e76326 437
b6b6f466 438 if (flags.dont_retry)
439 return false;
62e76326 440
5d4989a8 441 if (!checkRetriable())
442 return false;
443
58217e94 444 if (request->bodyNibbled())
b6b6f466 445 return false;
62e76326 446
b6b6f466 447 return true;
68bd6892 448}
449
545782b8 450/*
451 * FwdState::checkRetriable
452 *
453 * Return TRUE if this is the kind of request that can be retried
454 * after a failure. If the request is not retriable then we don't
455 * want to risk sending it on a persistent connection. Instead we'll
456 * force it to go on a new HTTP connection.
457 */
b6b6f466 458bool
459FwdState::checkRetriable()
cb928909 460{
461 /* If there is a request body then Squid can only try once
462 * even if the method is indempotent
463 */
464
5f8252d2 465 if (request->body_pipe != NULL)
b6b6f466 466 return false;
cb928909 467
468 /* RFC2616 9.1 Safe and Idempotent Methods */
914b89a2 469 switch (request->method.id()) {
cb928909 470 /* 9.1.1 Safe Methods */
471
472 case METHOD_GET:
473
474 case METHOD_HEAD:
914b89a2 475 /* 9.1.2 Idempotent Methods */
cb928909 476
477 case METHOD_PUT:
478
479 case METHOD_DELETE:
480
481 case METHOD_OPTIONS:
482
483 case METHOD_TRACE:
484 break;
485
486 default:
b6b6f466 487 return false;
cb928909 488 }
489
b6b6f466 490 return true;
cb928909 491}
492
b6b6f466 493void
494FwdState::serverClosed(int fd)
910169e5 495{
bf8fe701 496 debugs(17, 2, "fwdServerClosed: FD " << fd << " " << entry->url());
b6b6f466 497 assert(server_fd == fd);
498 server_fd = -1;
62e76326 499
3e8c047e 500 retryOrBail();
501}
502
503void
504FwdState::retryOrBail() {
25557294 505 if (!self) { // we have aborted before the server called us back
506 debugs(17, 5, HERE << "not retrying because of earlier abort");
507 // we will be destroyed when the server clears its Pointer to us
508 return;
509 }
3e8c047e 510
b6b6f466 511 if (checkRetry()) {
512 int originserver = (servers->_peer == NULL);
4a7a3d56 513 debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
62e76326 514
b6b6f466 515 if (servers->next) {
62e76326 516 /* use next, or cycle if origin server isn't last */
b6b6f466 517 FwdServer *fs = servers;
62e76326 518 FwdServer **T, *T2 = NULL;
b6b6f466 519 servers = fs->next;
62e76326 520
3d0ac046 521 for (T = &servers; *T; T2 = *T, T = &(*T)->next);
62e76326 522 if (T2 && T2->_peer) {
523 /* cycle */
524 *T = fs;
525 fs->next = NULL;
526 } else {
527 /* Use next. The last "direct" entry is retried multiple times */
b6b6f466 528 servers = fs->next;
62e76326 529 fwdServerFree(fs);
4ed0e075 530 originserver = 0;
62e76326 531 }
532 }
533
4ed0e075 534 /* use eventAdd to break potential call sequence loops and to slow things down a little */
b6b6f466 535 eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
62e76326 536
537 return;
d8fd0f18 538 }
62e76326 539
b6b6f466 540 if (!err && shutting_down) {
2cc81f1f 541 errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
f563eea9 542 }
62e76326 543
b6b6f466 544 self = NULL; // refcounted
910169e5 545}
546
3e8c047e 547// called by the server that failed after calling unregister()
548void
549FwdState::handleUnregisteredServerEnd()
550{
551 debugs(17, 2, "handleUnregisteredServerEnd: self=" << self <<
552 " err=" << err << ' ' << entry->url());
553 assert(server_fd < 0);
554 retryOrBail();
555}
556
a7ad6e4e 557#if USE_SSL
b6b6f466 558void
559FwdState::negotiateSSL(int fd)
a7ad6e4e 560{
b6b6f466 561 FwdServer *fs = servers;
a7ad6e4e 562 SSL *ssl = fd_table[fd].ssl;
563 int ret;
62e76326 564
a7ad6e4e 565 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 566 int ssl_error = SSL_get_error(ssl, ret);
567
568 switch (ssl_error) {
569
570 case SSL_ERROR_WANT_READ:
b6b6f466 571 commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
62e76326 572 return;
573
574 case SSL_ERROR_WANT_WRITE:
b6b6f466 575 commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
62e76326 576 return;
577
578 default:
bf8fe701 579 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
580 ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error <<
581 "/" << ret << "/" << errno << ")");
4d5a6db8 582 ErrorState *anErr = errorCon(ERR_SECURE_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
a7ad6e4e 583#ifdef EPROTO
62e76326 584
b6b6f466 585 anErr->xerrno = EPROTO;
a7ad6e4e 586#else
62e76326 587
b6b6f466 588 anErr->xerrno = EACCES;
a7ad6e4e 589#endif
62e76326 590
b6b6f466 591 fail(anErr);
62e76326 592
593 if (fs->_peer) {
594 peerConnectFailed(fs->_peer);
595 fs->_peer->stats.conn_open--;
596 }
597
598 comm_close(fd);
599 return;
600 }
a7ad6e4e 601 }
62e76326 602
f38c5e43 603 if (fs->_peer && !SSL_session_reused(ssl)) {
604 if (fs->_peer->sslSession)
605 SSL_SESSION_free(fs->_peer->sslSession);
606
607 fs->_peer->sslSession = SSL_get1_session(ssl);
608 }
609
b6b6f466 610 dispatch();
a7ad6e4e 611}
612
b6b6f466 613void
614FwdState::initiateSSL()
a7ad6e4e 615{
b6b6f466 616 FwdServer *fs = servers;
617 int fd = server_fd;
a7ad6e4e 618 SSL *ssl;
619 SSL_CTX *sslContext = NULL;
620 peer *peer = fs->_peer;
62e76326 621
a7ad6e4e 622 if (peer) {
62e76326 623 assert(peer->use_ssl);
624 sslContext = peer->sslContext;
a7ad6e4e 625 } else {
62e76326 626 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 627 }
62e76326 628
a7ad6e4e 629 assert(sslContext);
62e76326 630
a7ad6e4e 631 if ((ssl = SSL_new(sslContext)) == NULL) {
bf8fe701 632 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) );
2cc81f1f 633 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 634 anErr->xerrno = errno;
b6b6f466 635 fail(anErr);
636 self = NULL; // refcounted
62e76326 637 return;
a7ad6e4e 638 }
62e76326 639
a7ad6e4e 640 SSL_set_fd(ssl, fd);
62e76326 641
a7ad6e4e 642 if (peer) {
62e76326 643 if (peer->ssldomain)
644 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
645
a7ad6e4e 646#if NOT_YET
62e76326 647
648 else if (peer->name)
649 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
650
a7ad6e4e 651#endif
62e76326 652
653 else
654 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
f38c5e43 655
656 if (peer->sslSession)
657 SSL_set_session(ssl, peer->sslSession);
658
a7ad6e4e 659 } else {
cc192b50 660 SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost());
a7ad6e4e 661 }
62e76326 662
4d5a6db8 663 // Create the ACL check list now, while we have access to more info.
664 // The list is used in ssl_verify_cb() and is freed in ssl_free().
665 if (acl_access *acl = Config.ssl_client.cert_error) {
666 ACLChecklist *check = aclChecklistCreate(acl, request, dash_str);
667 check->fd(fd);
668 SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check);
669 }
670
a7ad6e4e 671 fd_table[fd].ssl = ssl;
672 fd_table[fd].read_method = &ssl_read_method;
673 fd_table[fd].write_method = &ssl_write_method;
b6b6f466 674 negotiateSSL(fd);
a7ad6e4e 675}
62e76326 676
a7ad6e4e 677#endif
678
b6b6f466 679void
680FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
41462d93 681{
b6b6f466 682 FwdServer *fs = servers;
683 assert(server_fd == aServerFD);
62e76326 684
beed27a2 685 if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
74780d33 686 updateHierarchyInfo();
beed27a2 687
41462d93 688 if (status == COMM_ERR_DNS) {
62e76326 689 /*
690 * Only set the dont_retry flag if the DNS lookup fails on
691 * a direct connection. If DNS lookup fails when trying
692 * a neighbor cache, we may want to retry another option.
693 */
694
695 if (NULL == fs->_peer)
b6b6f466 696 flags.dont_retry = 1;
62e76326 697
cc192b50 698 debugs(17, 4, "fwdConnectDone: Unknown host: " << request->GetHost());
62e76326 699
2cc81f1f 700 ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
62e76326 701
b6b6f466 702 anErr->dnsserver_msg = xstrdup(dns_error_message);
62e76326 703
b6b6f466 704 fail(anErr);
62e76326 705
62e76326 706 comm_close(server_fd);
41462d93 707 } else if (status != COMM_OK) {
62e76326 708 assert(fs);
2cc81f1f 709 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 710 anErr->xerrno = xerrno;
62e76326 711
b6b6f466 712 fail(anErr);
62e76326 713
a4bd7d1f 714 if (fs->_peer)
62e76326 715 peerConnectFailed(fs->_peer);
62e76326 716
717 comm_close(server_fd);
41462d93 718 } else {
bf8fe701 719 debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
62e76326 720
721 if (fs->_peer)
722 peerConnectSucceded(fs->_peer);
723
a7ad6e4e 724#if USE_SSL
62e76326 725
726 if ((fs->_peer && fs->_peer->use_ssl) ||
727 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
b6b6f466 728 initiateSSL();
62e76326 729 return;
730 }
731
a7ad6e4e 732#endif
b6b6f466 733 dispatch();
41462d93 734 }
41462d93 735}
736
b6b6f466 737void
738FwdState::connectTimeout(int fd)
41462d93 739{
beed27a2 740 FwdServer *fs = servers;
741
bf8fe701 742 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
b6b6f466 743 assert(fd == server_fd);
62e76326 744
beed27a2 745 if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
74780d33 746 updateHierarchyInfo();
beed27a2 747
528b2c61 748 if (entry->isEmpty()) {
2cc81f1f 749 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
b6b6f466 750 anErr->xerrno = ETIMEDOUT;
751 fail(anErr);
62e76326 752 /*
753 * This marks the peer DOWN ...
754 */
755
b6b6f466 756 if (servers)
757 if (servers->_peer)
758 peerConnectFailed(servers->_peer);
41462d93 759 }
62e76326 760
41462d93 761 comm_close(fd);
762}
763
b6b6f466 764void
765FwdState::connectStart()
41462d93 766{
3900307b 767 const char *url = entry->url();
cb928909 768 int fd = -1;
b6b6f466 769 FwdServer *fs = servers;
db1cd23c 770 const char *host;
771 unsigned short port;
bd0723ad 772 const char *domain = NULL;
777831e0 773 int ctimeout;
b6b6f466 774 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
62e76326 775
cc192b50 776 IPAddress outgoing;
d6827718 777 unsigned short tos;
fc68f6b1 778
cc192b50 779 IPAddress client_addr;
db1cd23c 780 assert(fs);
b6b6f466 781 assert(server_fd == -1);
bf8fe701 782 debugs(17, 3, "fwdConnectStart: " << url);
62e76326 783
29b8d8d6 784 if (fs->_peer) {
37181402 785 host = fs->_peer->host;
62e76326 786 port = fs->_peer->http_port;
787 ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
788 : Config.Timeout.peer_connect;
bd0723ad 789
7171e7ee 790 if (fs->_peer->options.originserver)
cc192b50 791 domain = request->GetHost();
db1cd23c 792 } else {
cc192b50 793 host = request->GetHost();
b6b6f466 794 port = request->port;
62e76326 795 ctimeout = Config.Timeout.connect;
db1cd23c 796 }
62e76326 797
f165d2fb 798 if (request->flags.spoof_client_ip)
cc192b50 799 client_addr = request->client_addr;
fc68f6b1 800
777831e0 801 if (ftimeout < 0)
802 ftimeout = 5;
803
804 if (ftimeout < ctimeout)
805 ctimeout = ftimeout;
806
c8ceec27 807 fd = fwdPconnPool->pop(host, port, domain, client_addr, checkRetriable());
808 if (fd >= 0) {
809 debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
810 server_fd = fd;
811 n_tries++;
4ed0e075 812
74780d33 813 if (!fs->_peer)
c8ceec27 814 origin_tries++;
74780d33
AJ
815
816 updateHierarchyInfo();
4ed0e075 817
c8ceec27 818 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
4ed0e075 819
c8ceec27 820 dispatch();
4ed0e075 821
c8ceec27 822 return;
41462d93 823 }
62e76326 824
bc87dc25 825#if URL_CHECKSUM_DEBUG
b6b6f466 826 entry->mem_obj->checkUrlChecksum();
62e76326 827
bc87dc25 828#endif
62e76326 829
b6b6f466 830 outgoing = getOutgoingAddr(request);
62e76326 831
b6b6f466 832 tos = getOutgoingTOS(request);
d6827718 833
cc192b50 834 debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << tos);
62e76326 835
f165d2fb 836 if (request->flags.spoof_client_ip) {
c303f6e3 837 fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, (COMM_NONBLOCKING|COMM_TRANSPARENT), tos, url);
2ad20b4f 838 } else {
c303f6e3
AJ
839 fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, COMM_NONBLOCKING, tos, url);
840 }
62e76326 841
cc192b50 842 debugs(17, 3, "fwdConnectStart: got TCP FD " << fd);
843
41462d93 844 if (fd < 0) {
bf8fe701 845 debugs(50, 4, "fwdConnectStart: " << xstrerror());
2cc81f1f 846 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 847 anErr->xerrno = errno;
848 fail(anErr);
849 self = NULL; // refcounted
62e76326 850 return;
41462d93 851 }
62e76326 852
b6b6f466 853 server_fd = fd;
854 n_tries++;
4ed0e075 855
856 if (!fs->_peer)
b6b6f466 857 origin_tries++;
4ed0e075 858
c7f9eb6d 859 /*
860 * stats.conn_open is used to account for the number of
861 * connections that we have open to the peer, so we can limit
862 * based on the max-conn option. We need to increment here,
863 * even if the connection may fail.
864 */
62e76326 865
a4bd7d1f 866 if (fs->_peer) {
62e76326 867 fs->_peer->stats.conn_open++;
a4bd7d1f 868 comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
869 }
62e76326 870
b6b6f466 871 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
62e76326 872
b6b6f466 873 commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
62e76326 874
40d6264d 875#if LINUX_TPROXY2
2b17881e
AJ
876 if (!fs->_peer && request->flags.spoof_client_ip) {
877 // try to set the outgoing address using TPROXY v2
878 // if it fails we abort any further TPROXY actions on this connection
879 if(IPInterceptor.SetTproxy2OutgoingAddr(int fd, const IPAddress &src) == -1) {
880 request->flags.spoof_client_ip = 0;
fc68f6b1 881 }
fc68f6b1 882 }
2b17881e 883#endif
beed27a2 884
74780d33 885 updateHierarchyInfo();
b6b6f466 886 commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
41462d93 887}
888
b6b6f466 889void
890FwdState::startComplete(FwdServer * theServers)
41462d93 891{
bf8fe701 892 debugs(17, 3, "fwdStartComplete: " << entry->url() );
62e76326 893
b6b6f466 894 if (theServers != NULL) {
895 servers = theServers;
896 connectStart();
41462d93 897 } else {
b6b6f466 898 startFail();
41462d93 899 }
41462d93 900}
901
b6b6f466 902void
903FwdState::startFail()
41462d93 904{
bf8fe701 905 debugs(17, 3, "fwdStartFail: " << entry->url() );
2cc81f1f 906 ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 907 anErr->xerrno = errno;
908 fail(anErr);
909 self = NULL; // refcounted
41462d93 910}
910169e5 911
b6b6f466 912void
913FwdState::dispatch()
41462d93 914{
c7f9eb6d 915 peer *p = NULL;
60745f24 916 debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'" );
e0ebe27c 917 /*
918 * Assert that server_fd is set. This is to guarantee that fwdState
919 * is attached to something and will be deallocated when server_fd
920 * is closed.
921 */
a7ad6e4e 922 assert(server_fd > -1);
62e76326 923
3900307b 924 fd_note(server_fd, entry->url());
62e76326 925
781ce8ff 926 fd_table[server_fd].noteUse(fwdPconnPool);
62e76326 927
a7ad6e4e 928 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
929 assert(entry->ping_status != PING_WAITING);
62e76326 930
a7ad6e4e 931 assert(entry->lock_count);
62e76326 932
a7ad6e4e 933 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 934
cc192b50 935 netdbPingSite(request->GetHost());
62e76326 936
7172612f
AJ
937#if USE_ZPH_QOS
938 /* Retrieves remote server TOS value, and stores it as part of the
939 * original client request FD object. It is later used to forward
940 * remote server's TOS in the response to the client in case of a MISS.
941 */
942 fde * clientFde = &fd_table[client_fd];
943 if (clientFde)
944 {
945 int tos = 1;
946 int tos_len = sizeof(tos);
947 clientFde->upstreamTOS = 0;
948 if (setsockopt(server_fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0)
949 {
950 unsigned char buf[512];
951 int len = 512;
952 if (getsockopt(server_fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0)
953 {
954 /* Parse the PKTOPTIONS structure to locate the TOS data message
955 * prepared in the kernel by the ZPH incoming TCP TOS preserving
956 * patch.
957 */
958 unsigned char * p = buf;
959 while (p-buf < len)
960 {
961 struct cmsghdr *o = (struct cmsghdr*)p;
962 if (o->cmsg_len<=0)
963 break;
964
965 if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS)
966 {
967 clientFde->upstreamTOS = (unsigned char)(*(int*)CMSG_DATA(o));
968 break;
969 }
970 p += CMSG_LEN(o->cmsg_len);
971 }
972 }
973 else
974 {
975 debugs(33, 1, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD "<<server_fd<<" "<<xstrerror());
976 }
977 }
978 else
979 {
980 debugs(33, 1, "ZPH: error in setsockopt(IP_RECVTOS) on FD "<<server_fd<<" "<<xstrerror());
981 }
982 }
983#endif
984
b6b6f466 985 if (servers && (p = servers->_peer)) {
62e76326 986 p->stats.fetches++;
b6b6f466 987 request->peer_login = p->login;
988 request->peer_domain = p->domain;
989 httpStart(this);
41462d93 990 } else {
b6b6f466 991 request->peer_login = NULL;
992 request->peer_domain = NULL;
62e76326 993
994 switch (request->protocol) {
a7ad6e4e 995#if USE_SSL
62e76326 996
997 case PROTO_HTTPS:
b6b6f466 998 httpStart(this);
62e76326 999 break;
a7ad6e4e 1000#endif
62e76326 1001
1002 case PROTO_HTTP:
b6b6f466 1003 httpStart(this);
62e76326 1004 break;
1005
1006 case PROTO_GOPHER:
b6b6f466 1007 gopherStart(this);
62e76326 1008 break;
1009
1010 case PROTO_FTP:
b6b6f466 1011 ftpStart(this);
62e76326 1012 break;
1013
62e76326 1014 case PROTO_CACHEOBJ:
1015
1016 case PROTO_INTERNAL:
1017
1018 case PROTO_URN:
1019 fatal_dump("Should never get here");
1020 break;
1021
1022 case PROTO_WHOIS:
b6b6f466 1023 whoisStart(this);
62e76326 1024 break;
1025
db80e881 1026 case PROTO_WAIS: /* Not implemented */
fc68f6b1 1027
62e76326 1028 default:
bf8fe701 1029 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry->url() << "'" );
2cc81f1f 1030 ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 1031 fail(anErr);
62e76326 1032 /*
1033 * Force a persistent connection to be closed because
1034 * some Netscape browsers have a bug that sends CONNECT
1035 * requests as GET's over persistent connections.
1036 */
1037 request->flags.proxy_keepalive = 0;
1038 /*
1039 * Set the dont_retry flag becuase this is not a
1040 * transient (network) error; its a bug.
1041 */
b6b6f466 1042 flags.dont_retry = 1;
1043 comm_close(server_fd);
62e76326 1044 break;
1045 }
41462d93 1046 }
1047}
1048
545782b8 1049/*
1050 * FwdState::reforward
1051 *
1052 * returns TRUE if the transaction SHOULD be re-forwarded to the
1053 * next choice in the FwdServers list. This method is called when
1054 * server-side communication completes normally, or experiences
1055 * some error after receiving the end of HTTP headers.
1056 */
b6b6f466 1057int
1058FwdState::reforward()
db1cd23c 1059{
b6b6f466 1060 StoreEntry *e = entry;
1061 FwdServer *fs = servers;
db1cd23c 1062 http_status s;
1063 assert(e->store_status == STORE_PENDING);
1064 assert(e->mem_obj);
bc87dc25 1065#if URL_CHECKSUM_DEBUG
62e76326 1066
528b2c61 1067 e->mem_obj->checkUrlChecksum();
bc87dc25 1068#endif
62e76326 1069
bf8fe701 1070 debugs(17, 3, "fwdReforward: " << e->url() << "?" );
62e76326 1071
d6eb18d6 1072 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
bf8fe701 1073 debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1074 return 0;
d6eb18d6 1075 }
62e76326 1076
b6b6f466 1077 if (n_tries > 9)
62e76326 1078 return 0;
1079
b6b6f466 1080 if (origin_tries > 1)
4ed0e075 1081 return 0;
1082
58217e94 1083 if (request->bodyNibbled())
62e76326 1084 return 0;
1085
db1cd23c 1086 assert(fs);
62e76326 1087
b6b6f466 1088 servers = fs->next;
62e76326 1089
db1cd23c 1090 fwdServerFree(fs);
62e76326 1091
b6b6f466 1092 if (servers == NULL) {
bf8fe701 1093 debugs(17, 3, "fwdReforward: No forward-servers left");
62e76326 1094 return 0;
db1cd23c 1095 }
62e76326 1096
528b2c61 1097 s = e->getReply()->sline.status;
4a7a3d56 1098 debugs(17, 3, "fwdReforward: status " << s);
b6b6f466 1099 return reforwardableStatus(s);
db1cd23c 1100}
1101
b6b6f466 1102static void
1103fwdStats(StoreEntry * s)
64d8034e 1104{
b6b6f466 1105 int i;
1106 int j;
1107 storeAppendPrintf(s, "Status");
62e76326 1108
b6b6f466 1109 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1110 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1111 }
64d8034e 1112
b6b6f466 1113 storeAppendPrintf(s, "\n");
0185bd6f 1114
b6b6f466 1115 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1116 if (FwdReplyCodes[0][i] == 0)
1117 continue;
0185bd6f 1118
b6b6f466 1119 storeAppendPrintf(s, "%3d", i);
0185bd6f 1120
b6b6f466 1121 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1122 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1123 }
62e76326 1124
b6b6f466 1125 storeAppendPrintf(s, "\n");
e0ebe27c 1126 }
7197b20d 1127}
1128
a4bd7d1f 1129
b6b6f466 1130/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1131
b6b6f466 1132bool
1133FwdState::reforwardableStatus(http_status s)
db1cd23c 1134{
b6b6f466 1135 switch (s) {
62e76326 1136
b6b6f466 1137 case HTTP_BAD_GATEWAY:
62e76326 1138
b6b6f466 1139 case HTTP_GATEWAY_TIMEOUT:
1140 return true;
62e76326 1141
b6b6f466 1142 case HTTP_FORBIDDEN:
62e76326 1143
b6b6f466 1144 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1145
b6b6f466 1146 case HTTP_NOT_IMPLEMENTED:
62e76326 1147
b6b6f466 1148 case HTTP_SERVICE_UNAVAILABLE:
1149 return Config.retry.onerror;
62e76326 1150
b6b6f466 1151 default:
1152 return false;
db1cd23c 1153 }
b6b6f466 1154
1155 /* NOTREACHED */
db1cd23c 1156}
8ddcc35d 1157
781ce8ff 1158void
fc68f6b1 1159
cc192b50 1160FwdState::pconnPush(int fd, const char *host, int port, const char *domain, IPAddress &client_addr)
781ce8ff 1161{
fc68f6b1 1162 fwdPconnPool->push(fd, host, port, domain, client_addr);
781ce8ff 1163}
1164
8ddcc35d 1165void
b6b6f466 1166FwdState::initModule()
8ddcc35d 1167{
b6b6f466 1168 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
1169
225644d7 1170#if WIP_FWD_LOG
62e76326 1171
225644d7 1172 if (logfile)
62e76326 1173 (void) 0;
225644d7 1174 else if (NULL == Config.Log.forward)
62e76326 1175 (void) 0;
225644d7 1176 else
62e76326 1177 logfile = logfileOpen(Config.Log.forward, 0, 1);
1178
225644d7 1179#endif
8ddcc35d 1180}
1181
62ee09ca 1182void
1183FwdState::RegisterWithCacheManager(CacheManager & manager)
1184{
1185 manager.registerAction("forward",
1186 "Request Forwarding Statistics",
1187 fwdStats, 0, 1);
1188}
1189
b6b6f466 1190void
1191FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1192{
1193 if (status > HTTP_INVALID_HEADER)
62e76326 1194 return;
1195
8ddcc35d 1196 assert(tries);
62e76326 1197
8ddcc35d 1198 tries--;
62e76326 1199
8ddcc35d 1200 if (tries > MAX_FWD_STATS_IDX)
62e76326 1201 tries = MAX_FWD_STATS_IDX;
1202
8ddcc35d 1203 FwdReplyCodes[tries][status]++;
1204}
1205
b6b6f466 1206void
1207FwdState::serversFree(FwdServer ** FSVR)
8ddcc35d 1208{
b6b6f466 1209 FwdServer *fs;
62e76326 1210
b6b6f466 1211 while ((fs = *FSVR)) {
1212 *FSVR = fs->next;
1213 fwdServerFree(fs);
9977e14b 1214 }
b6b6f466 1215}
62e76326 1216
74780d33
AJ
1217// updates HierarchyLogEntry, guessing nextHop and its format
1218void
1219FwdState::updateHierarchyInfo()
1220{
1221 assert(request);
1222
1223 FwdServer *fs = servers;
1224 assert(fs);
1225
1226 // some callers use one condition, some use the other; are they the same?
1227 assert((fs->code == HIER_DIRECT) == !fs->_peer);
1228
1229 const char *nextHop = NULL;
1230
1231 if (fs->_peer) {
1232 // went to peer, log peer domain name
1233 nextHop = fs->_peer->host;
1234 } else {
1235 // went DIRECT, must honor log_ip_on_direct
1236
1237 // XXX: or should we use request->host_addr here? how?
1238 assert(server_fd >= 0);
1239 nextHop = fd_table[server_fd].ipaddr;
1240 if (!Config.onoff.log_ip_on_direct || !nextHop[0])
1241 nextHop = request->GetHost(); // domain name
1242 }
1243
1244 assert(nextHop);
1245 hierarchyNote(&request->hier, fs->code, nextHop);
1246}
1247
1248
b6b6f466 1249/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1250
b6b6f466 1251static void
1252fwdServerFree(FwdServer * fs)
1253{
1254 cbdataReferenceDone(fs->_peer);
1255 memFree(fs, MEM_FWD_SERVER);
1256}
62e76326 1257
cc192b50 1258static IPAddress
1259aclMapAddr(acl_address * head, ACLChecklist * ch)
b6b6f466 1260{
1261 acl_address *l;
62e76326 1262
cc192b50 1263 IPAddress addr;
62e76326 1264
b6b6f466 1265 for (l = head; l; l = l->next)
1266 {
1267 if (ch->matchAclListFast(l->aclList))
1268 return l->addr;
9977e14b 1269 }
b6b6f466 1270
cc192b50 1271 addr.SetAnyAddr();
b6b6f466 1272 return addr;
8ddcc35d 1273}
b6a2f15e 1274
057f5854 1275/*
1276 * DPW 2007-05-19
1277 * Formerly static, but now used by client_side_request.cc
1278 */
1279int
b6b6f466 1280aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1281{
b6b6f466 1282 acl_tos *l;
62e76326 1283
b6b6f466 1284 for (l = head; l; l = l->next) {
1285 if (ch->matchAclListFast(l->aclList))
1286 return l->tos;
1287 }
5894ad28 1288
b6b6f466 1289 return 0;
1290}
5894ad28 1291
cc192b50 1292IPAddress
1293getOutgoingAddr(HttpRequest * request)
b6b6f466 1294{
1295 ACLChecklist ch;
62e76326 1296
f165d2fb 1297 if (request && request->flags.spoof_client_ip)
c303f6e3 1298 return request->client_addr;
c303f6e3 1299
b6b6f466 1300 if (request)
1301 {
1302 ch.src_addr = request->client_addr;
1303 ch.my_addr = request->my_addr;
6dd9f4bd 1304 ch.request = HTTPMSGLOCK(request);
b6b6f466 1305 }
62e76326 1306
b6b6f466 1307 return aclMapAddr(Config.accessList.outgoing_address, &ch);
1308}
62e76326 1309
b6b6f466 1310unsigned long
1311getOutgoingTOS(HttpRequest * request)
1312{
1313 ACLChecklist ch;
62e76326 1314
b6b6f466 1315 if (request) {
1316 ch.src_addr = request->client_addr;
1317 ch.my_addr = request->my_addr;
6dd9f4bd 1318 ch.request = HTTPMSGLOCK(request);
b6a2f15e 1319 }
62e76326 1320
b6b6f466 1321 return aclMapTOS(Config.accessList.outgoing_tos, &ch);
b6a2f15e 1322}
225644d7 1323
b6b6f466 1324
1325/**** WIP_FWD_LOG *************************************************************/
1326
225644d7 1327#if WIP_FWD_LOG
1328void
1329fwdUninit(void)
1330{
dab0cec3 1331 if (NULL == logfile)
62e76326 1332 return;
1333
225644d7 1334 logfileClose(logfile);
62e76326 1335
225644d7 1336 logfile = NULL;
1337}
1338
1339void
1340fwdLogRotate(void)
1341{
1342 if (logfile)
62e76326 1343 logfileRotate(logfile);
225644d7 1344}
1345
1346static void
b6b6f466 1347FwdState::log()
225644d7 1348{
1349 if (NULL == logfile)
62e76326 1350 return;
1351
225644d7 1352 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
62e76326 1353 (int) current_time.tv_sec,
1354 (int) current_time.tv_usec / 1000,
b6b6f466 1355 last_status,
60745f24 1356 RequestMethodStr(request->method),
b6b6f466 1357 request->canonical);
225644d7 1358}
1359
1360void
b6b6f466 1361FwdState::status(http_status s)
225644d7 1362{
b6b6f466 1363 last_status = s;
225644d7 1364}
1365
1366#endif