]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Updates for running on squid-cache.org
[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
de48c85e 702 anErr->dnsserver_msg = xstrdup(dns_error_message_safe());
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
d67acb4e
AJ
807
808 request->flags.pinned = 0;
809 if (fs->code == PINNED) {
810 ConnStateData *pinned_connection = request->pinnedConnection();
811 assert(pinned_connection);
812 fd = pinned_connection->validatePinnedConnection(request, fs->_peer);
813 if (fd >= 0) {
814 pinned_connection->unpinConnection();
815#if 0
816 if (!fs->_peer)
817 fs->code = HIER_DIRECT;
818#endif
819 server_fd = fd;
820 n_tries++;
821 request->flags.pinned = 1;
822 if (pinned_connection->pinnedAuth())
823 request->flags.auth = 1;
824 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
825 connectDone(fd, COMM_OK, 0);
826 return;
827 }
828 /* Failure. Fall back on next path */
829 request->releasePinnedConnection();
830 servers = fs->next;
831 fwdServerFree(fs);
832 connectStart();
833 return;
834 }
835
c8ceec27 836 fd = fwdPconnPool->pop(host, port, domain, client_addr, checkRetriable());
837 if (fd >= 0) {
838 debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
839 server_fd = fd;
840 n_tries++;
4ed0e075 841
74780d33 842 if (!fs->_peer)
c8ceec27 843 origin_tries++;
74780d33
AJ
844
845 updateHierarchyInfo();
4ed0e075 846
c8ceec27 847 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
4ed0e075 848
c8ceec27 849 dispatch();
4ed0e075 850
c8ceec27 851 return;
41462d93 852 }
62e76326 853
bc87dc25 854#if URL_CHECKSUM_DEBUG
b6b6f466 855 entry->mem_obj->checkUrlChecksum();
62e76326 856
bc87dc25 857#endif
62e76326 858
b6b6f466 859 outgoing = getOutgoingAddr(request);
62e76326 860
b6b6f466 861 tos = getOutgoingTOS(request);
d6827718 862
cc192b50 863 debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << tos);
62e76326 864
f165d2fb 865 if (request->flags.spoof_client_ip) {
c303f6e3 866 fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, (COMM_NONBLOCKING|COMM_TRANSPARENT), tos, url);
2ad20b4f 867 } else {
c303f6e3
AJ
868 fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, COMM_NONBLOCKING, tos, url);
869 }
62e76326 870
cc192b50 871 debugs(17, 3, "fwdConnectStart: got TCP FD " << fd);
872
41462d93 873 if (fd < 0) {
bf8fe701 874 debugs(50, 4, "fwdConnectStart: " << xstrerror());
2cc81f1f 875 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 876 anErr->xerrno = errno;
877 fail(anErr);
878 self = NULL; // refcounted
62e76326 879 return;
41462d93 880 }
62e76326 881
b6b6f466 882 server_fd = fd;
883 n_tries++;
4ed0e075 884
885 if (!fs->_peer)
b6b6f466 886 origin_tries++;
4ed0e075 887
c7f9eb6d 888 /*
889 * stats.conn_open is used to account for the number of
890 * connections that we have open to the peer, so we can limit
891 * based on the max-conn option. We need to increment here,
892 * even if the connection may fail.
893 */
62e76326 894
a4bd7d1f 895 if (fs->_peer) {
62e76326 896 fs->_peer->stats.conn_open++;
a4bd7d1f 897 comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
898 }
62e76326 899
b6b6f466 900 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
62e76326 901
b6b6f466 902 commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
62e76326 903
40d6264d 904#if LINUX_TPROXY2
2b17881e
AJ
905 if (!fs->_peer && request->flags.spoof_client_ip) {
906 // try to set the outgoing address using TPROXY v2
907 // if it fails we abort any further TPROXY actions on this connection
908 if(IPInterceptor.SetTproxy2OutgoingAddr(int fd, const IPAddress &src) == -1) {
909 request->flags.spoof_client_ip = 0;
fc68f6b1 910 }
fc68f6b1 911 }
2b17881e 912#endif
beed27a2 913
74780d33 914 updateHierarchyInfo();
b6b6f466 915 commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
41462d93 916}
917
b6b6f466 918void
919FwdState::startComplete(FwdServer * theServers)
41462d93 920{
bf8fe701 921 debugs(17, 3, "fwdStartComplete: " << entry->url() );
62e76326 922
b6b6f466 923 if (theServers != NULL) {
924 servers = theServers;
925 connectStart();
41462d93 926 } else {
b6b6f466 927 startFail();
41462d93 928 }
41462d93 929}
930
b6b6f466 931void
932FwdState::startFail()
41462d93 933{
bf8fe701 934 debugs(17, 3, "fwdStartFail: " << entry->url() );
2cc81f1f 935 ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 936 anErr->xerrno = errno;
937 fail(anErr);
938 self = NULL; // refcounted
41462d93 939}
910169e5 940
b6b6f466 941void
942FwdState::dispatch()
41462d93 943{
c7f9eb6d 944 peer *p = NULL;
60745f24 945 debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'" );
e0ebe27c 946 /*
947 * Assert that server_fd is set. This is to guarantee that fwdState
948 * is attached to something and will be deallocated when server_fd
949 * is closed.
950 */
a7ad6e4e 951 assert(server_fd > -1);
62e76326 952
3900307b 953 fd_note(server_fd, entry->url());
62e76326 954
781ce8ff 955 fd_table[server_fd].noteUse(fwdPconnPool);
62e76326 956
a7ad6e4e 957 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
958 assert(entry->ping_status != PING_WAITING);
62e76326 959
a7ad6e4e 960 assert(entry->lock_count);
62e76326 961
a7ad6e4e 962 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 963
cc192b50 964 netdbPingSite(request->GetHost());
62e76326 965
7172612f
AJ
966#if USE_ZPH_QOS
967 /* Retrieves remote server TOS value, and stores it as part of the
968 * original client request FD object. It is later used to forward
969 * remote server's TOS in the response to the client in case of a MISS.
970 */
971 fde * clientFde = &fd_table[client_fd];
972 if (clientFde)
973 {
974 int tos = 1;
975 int tos_len = sizeof(tos);
976 clientFde->upstreamTOS = 0;
977 if (setsockopt(server_fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0)
978 {
979 unsigned char buf[512];
980 int len = 512;
981 if (getsockopt(server_fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0)
982 {
983 /* Parse the PKTOPTIONS structure to locate the TOS data message
984 * prepared in the kernel by the ZPH incoming TCP TOS preserving
985 * patch.
986 */
987 unsigned char * p = buf;
988 while (p-buf < len)
989 {
990 struct cmsghdr *o = (struct cmsghdr*)p;
991 if (o->cmsg_len<=0)
992 break;
993
994 if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS)
995 {
996 clientFde->upstreamTOS = (unsigned char)(*(int*)CMSG_DATA(o));
997 break;
998 }
999 p += CMSG_LEN(o->cmsg_len);
1000 }
1001 }
1002 else
1003 {
1004 debugs(33, 1, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD "<<server_fd<<" "<<xstrerror());
1005 }
1006 }
1007 else
1008 {
1009 debugs(33, 1, "ZPH: error in setsockopt(IP_RECVTOS) on FD "<<server_fd<<" "<<xstrerror());
1010 }
1011 }
1012#endif
1013
b6b6f466 1014 if (servers && (p = servers->_peer)) {
62e76326 1015 p->stats.fetches++;
b6b6f466 1016 request->peer_login = p->login;
1017 request->peer_domain = p->domain;
1018 httpStart(this);
41462d93 1019 } else {
b6b6f466 1020 request->peer_login = NULL;
1021 request->peer_domain = NULL;
62e76326 1022
1023 switch (request->protocol) {
a7ad6e4e 1024#if USE_SSL
62e76326 1025
1026 case PROTO_HTTPS:
b6b6f466 1027 httpStart(this);
62e76326 1028 break;
a7ad6e4e 1029#endif
62e76326 1030
1031 case PROTO_HTTP:
b6b6f466 1032 httpStart(this);
62e76326 1033 break;
1034
1035 case PROTO_GOPHER:
b6b6f466 1036 gopherStart(this);
62e76326 1037 break;
1038
1039 case PROTO_FTP:
b6b6f466 1040 ftpStart(this);
62e76326 1041 break;
1042
62e76326 1043 case PROTO_CACHEOBJ:
1044
1045 case PROTO_INTERNAL:
1046
1047 case PROTO_URN:
1048 fatal_dump("Should never get here");
1049 break;
1050
1051 case PROTO_WHOIS:
b6b6f466 1052 whoisStart(this);
62e76326 1053 break;
1054
db80e881 1055 case PROTO_WAIS: /* Not implemented */
fc68f6b1 1056
62e76326 1057 default:
bf8fe701 1058 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry->url() << "'" );
2cc81f1f 1059 ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 1060 fail(anErr);
62e76326 1061 /*
1062 * Force a persistent connection to be closed because
1063 * some Netscape browsers have a bug that sends CONNECT
1064 * requests as GET's over persistent connections.
1065 */
1066 request->flags.proxy_keepalive = 0;
1067 /*
1068 * Set the dont_retry flag becuase this is not a
1069 * transient (network) error; its a bug.
1070 */
b6b6f466 1071 flags.dont_retry = 1;
1072 comm_close(server_fd);
62e76326 1073 break;
1074 }
41462d93 1075 }
1076}
1077
545782b8 1078/*
1079 * FwdState::reforward
1080 *
1081 * returns TRUE if the transaction SHOULD be re-forwarded to the
1082 * next choice in the FwdServers list. This method is called when
1083 * server-side communication completes normally, or experiences
1084 * some error after receiving the end of HTTP headers.
1085 */
b6b6f466 1086int
1087FwdState::reforward()
db1cd23c 1088{
b6b6f466 1089 StoreEntry *e = entry;
1090 FwdServer *fs = servers;
db1cd23c 1091 http_status s;
1092 assert(e->store_status == STORE_PENDING);
1093 assert(e->mem_obj);
bc87dc25 1094#if URL_CHECKSUM_DEBUG
62e76326 1095
528b2c61 1096 e->mem_obj->checkUrlChecksum();
bc87dc25 1097#endif
62e76326 1098
bf8fe701 1099 debugs(17, 3, "fwdReforward: " << e->url() << "?" );
62e76326 1100
d6eb18d6 1101 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
bf8fe701 1102 debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1103 return 0;
d6eb18d6 1104 }
62e76326 1105
b6b6f466 1106 if (n_tries > 9)
62e76326 1107 return 0;
1108
b6b6f466 1109 if (origin_tries > 1)
4ed0e075 1110 return 0;
1111
58217e94 1112 if (request->bodyNibbled())
62e76326 1113 return 0;
1114
db1cd23c 1115 assert(fs);
62e76326 1116
b6b6f466 1117 servers = fs->next;
62e76326 1118
db1cd23c 1119 fwdServerFree(fs);
62e76326 1120
b6b6f466 1121 if (servers == NULL) {
bf8fe701 1122 debugs(17, 3, "fwdReforward: No forward-servers left");
62e76326 1123 return 0;
db1cd23c 1124 }
62e76326 1125
528b2c61 1126 s = e->getReply()->sline.status;
4a7a3d56 1127 debugs(17, 3, "fwdReforward: status " << s);
b6b6f466 1128 return reforwardableStatus(s);
db1cd23c 1129}
1130
b6b6f466 1131static void
1132fwdStats(StoreEntry * s)
64d8034e 1133{
b6b6f466 1134 int i;
1135 int j;
1136 storeAppendPrintf(s, "Status");
62e76326 1137
b6b6f466 1138 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1139 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1140 }
64d8034e 1141
b6b6f466 1142 storeAppendPrintf(s, "\n");
0185bd6f 1143
b6b6f466 1144 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1145 if (FwdReplyCodes[0][i] == 0)
1146 continue;
0185bd6f 1147
b6b6f466 1148 storeAppendPrintf(s, "%3d", i);
0185bd6f 1149
b6b6f466 1150 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1151 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1152 }
62e76326 1153
b6b6f466 1154 storeAppendPrintf(s, "\n");
e0ebe27c 1155 }
7197b20d 1156}
1157
a4bd7d1f 1158
b6b6f466 1159/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1160
b6b6f466 1161bool
1162FwdState::reforwardableStatus(http_status s)
db1cd23c 1163{
b6b6f466 1164 switch (s) {
62e76326 1165
b6b6f466 1166 case HTTP_BAD_GATEWAY:
62e76326 1167
b6b6f466 1168 case HTTP_GATEWAY_TIMEOUT:
1169 return true;
62e76326 1170
b6b6f466 1171 case HTTP_FORBIDDEN:
62e76326 1172
b6b6f466 1173 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1174
b6b6f466 1175 case HTTP_NOT_IMPLEMENTED:
62e76326 1176
b6b6f466 1177 case HTTP_SERVICE_UNAVAILABLE:
1178 return Config.retry.onerror;
62e76326 1179
b6b6f466 1180 default:
1181 return false;
db1cd23c 1182 }
b6b6f466 1183
1184 /* NOTREACHED */
db1cd23c 1185}
8ddcc35d 1186
781ce8ff 1187void
fc68f6b1 1188
cc192b50 1189FwdState::pconnPush(int fd, const char *host, int port, const char *domain, IPAddress &client_addr)
781ce8ff 1190{
fc68f6b1 1191 fwdPconnPool->push(fd, host, port, domain, client_addr);
781ce8ff 1192}
1193
8ddcc35d 1194void
b6b6f466 1195FwdState::initModule()
8ddcc35d 1196{
b6b6f466 1197 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
1198
225644d7 1199#if WIP_FWD_LOG
62e76326 1200
225644d7 1201 if (logfile)
62e76326 1202 (void) 0;
225644d7 1203 else if (NULL == Config.Log.forward)
62e76326 1204 (void) 0;
225644d7 1205 else
62e76326 1206 logfile = logfileOpen(Config.Log.forward, 0, 1);
1207
225644d7 1208#endif
6852be71
FC
1209
1210 RegisterWithCacheManager();
8ddcc35d 1211}
1212
62ee09ca 1213void
84f50787 1214FwdState::RegisterWithCacheManager(void)
62ee09ca 1215{
84f50787
FC
1216 CacheManager::GetInstance()->
1217 registerAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1);
62ee09ca 1218}
1219
b6b6f466 1220void
1221FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1222{
1223 if (status > HTTP_INVALID_HEADER)
62e76326 1224 return;
1225
8ddcc35d 1226 assert(tries);
62e76326 1227
8ddcc35d 1228 tries--;
62e76326 1229
8ddcc35d 1230 if (tries > MAX_FWD_STATS_IDX)
62e76326 1231 tries = MAX_FWD_STATS_IDX;
1232
8ddcc35d 1233 FwdReplyCodes[tries][status]++;
1234}
1235
b6b6f466 1236void
1237FwdState::serversFree(FwdServer ** FSVR)
8ddcc35d 1238{
b6b6f466 1239 FwdServer *fs;
62e76326 1240
b6b6f466 1241 while ((fs = *FSVR)) {
1242 *FSVR = fs->next;
1243 fwdServerFree(fs);
9977e14b 1244 }
b6b6f466 1245}
62e76326 1246
93280d3c
AR
1247/** From Comment #5 by Henrik Nordstrom made at
1248http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19
1249
1250updateHierarchyInfo should be called each time a new path has been
1251selected or when more information about the path is available (i.e. the
1252server IP), and when it's called it needs to be given reasonable
1253arguments describing the now selected path..
1254
1255It does not matter from a functional perspective if it gets called a few
1256times more than what is really needed, but calling it too often may
1257obviously hurt performance.
1258
1259\todo Current code looks fine, even if using !fs->_peer as condition
1260instead of HIER_DIRECT would be clearer.
1261*/
74780d33
AJ
1262// updates HierarchyLogEntry, guessing nextHop and its format
1263void
1264FwdState::updateHierarchyInfo()
1265{
1266 assert(request);
1267
1268 FwdServer *fs = servers;
1269 assert(fs);
1270
74780d33
AJ
1271 const char *nextHop = NULL;
1272
dc029c2f 1273 if (fs->_peer) {
ed9f056a
HN
1274 // went to peer, log peer host name
1275 nextHop = fs->_peer->name;
74780d33
AJ
1276 } else {
1277 // went DIRECT, must honor log_ip_on_direct
1278
1279 // XXX: or should we use request->host_addr here? how?
1280 assert(server_fd >= 0);
1281 nextHop = fd_table[server_fd].ipaddr;
1282 if (!Config.onoff.log_ip_on_direct || !nextHop[0])
1283 nextHop = request->GetHost(); // domain name
dc029c2f 1284 }
74780d33
AJ
1285
1286 assert(nextHop);
1287 hierarchyNote(&request->hier, fs->code, nextHop);
1288}
1289
1290
b6b6f466 1291/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1292
b6b6f466 1293static void
1294fwdServerFree(FwdServer * fs)
1295{
1296 cbdataReferenceDone(fs->_peer);
1297 memFree(fs, MEM_FWD_SERVER);
1298}
62e76326 1299
cc192b50 1300static IPAddress
1301aclMapAddr(acl_address * head, ACLChecklist * ch)
b6b6f466 1302{
1303 acl_address *l;
62e76326 1304
cc192b50 1305 IPAddress addr;
62e76326 1306
b6b6f466 1307 for (l = head; l; l = l->next)
1308 {
1309 if (ch->matchAclListFast(l->aclList))
1310 return l->addr;
9977e14b 1311 }
b6b6f466 1312
cc192b50 1313 addr.SetAnyAddr();
b6b6f466 1314 return addr;
8ddcc35d 1315}
b6a2f15e 1316
057f5854 1317/*
1318 * DPW 2007-05-19
1319 * Formerly static, but now used by client_side_request.cc
1320 */
1321int
b6b6f466 1322aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1323{
b6b6f466 1324 acl_tos *l;
62e76326 1325
b6b6f466 1326 for (l = head; l; l = l->next) {
1327 if (ch->matchAclListFast(l->aclList))
1328 return l->tos;
1329 }
5894ad28 1330
b6b6f466 1331 return 0;
1332}
5894ad28 1333
cc192b50 1334IPAddress
1335getOutgoingAddr(HttpRequest * request)
b6b6f466 1336{
1337 ACLChecklist ch;
62e76326 1338
f165d2fb 1339 if (request && request->flags.spoof_client_ip)
c303f6e3 1340 return request->client_addr;
c303f6e3 1341
b6b6f466 1342 if (request)
1343 {
1344 ch.src_addr = request->client_addr;
1345 ch.my_addr = request->my_addr;
6dd9f4bd 1346 ch.request = HTTPMSGLOCK(request);
b6b6f466 1347 }
62e76326 1348
b6b6f466 1349 return aclMapAddr(Config.accessList.outgoing_address, &ch);
1350}
62e76326 1351
b6b6f466 1352unsigned long
1353getOutgoingTOS(HttpRequest * request)
1354{
1355 ACLChecklist ch;
62e76326 1356
b6b6f466 1357 if (request) {
1358 ch.src_addr = request->client_addr;
1359 ch.my_addr = request->my_addr;
6dd9f4bd 1360 ch.request = HTTPMSGLOCK(request);
b6a2f15e 1361 }
62e76326 1362
b6b6f466 1363 return aclMapTOS(Config.accessList.outgoing_tos, &ch);
b6a2f15e 1364}
225644d7 1365
b6b6f466 1366
1367/**** WIP_FWD_LOG *************************************************************/
1368
225644d7 1369#if WIP_FWD_LOG
1370void
1371fwdUninit(void)
1372{
dab0cec3 1373 if (NULL == logfile)
62e76326 1374 return;
1375
225644d7 1376 logfileClose(logfile);
62e76326 1377
225644d7 1378 logfile = NULL;
1379}
1380
1381void
1382fwdLogRotate(void)
1383{
1384 if (logfile)
62e76326 1385 logfileRotate(logfile);
225644d7 1386}
1387
1388static void
b6b6f466 1389FwdState::log()
225644d7 1390{
1391 if (NULL == logfile)
62e76326 1392 return;
1393
225644d7 1394 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
62e76326 1395 (int) current_time.tv_sec,
1396 (int) current_time.tv_usec / 1000,
b6b6f466 1397 last_status,
60745f24 1398 RequestMethodStr(request->method),
b6b6f466 1399 request->canonical);
225644d7 1400}
1401
1402void
b6b6f466 1403FwdState::status(http_status s)
225644d7 1404{
b6b6f466 1405 last_status = s;
225644d7 1406}
1407
1408#endif