]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Bug #1900: Double "squid -k shutdown" makes Squid restart again
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
057f5854 3 * $Id: forward.cc,v 1.165 2007/05/26 06:38:04 wessels 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
fc68f6b1 52#if LINUX_TPROXY
53#include <linux/netfilter_ipv4/ip_tproxy.h>
54#endif
55
b6b6f466 56static PSC fwdStartCompleteWrapper;
57static PF fwdServerClosedWrapper;
58#if USE_SSL
59static PF fwdNegotiateSSLWrapper;
60#endif
61static PF fwdConnectTimeoutWrapper;
62static EVH fwdConnectStartWrapper;
63static CNCB fwdConnectDoneWrapper;
64
8ddcc35d 65static OBJH fwdStats;
b6b6f466 66static void fwdServerFree(FwdServer * fs);
8ddcc35d 67
68#define MAX_FWD_STATS_IDX 9
9977e14b 69static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1];
db1cd23c 70
225644d7 71#if WIP_FWD_LOG
72static void fwdLog(FwdState * fwdState);
73static Logfile *logfile = NULL;
74#endif
75
781ce8ff 76static PconnPool *fwdPconnPool = new PconnPool("server-side");
b6b6f466 77CBDATA_CLASS_INIT(FwdState);
781ce8ff 78
429871db 79void
80FwdState::abort(void* d)
81{
82 FwdState* fwd = (FwdState*)d;
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
102 e->lock()
103
104 ;
b6b6f466 105 EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT);
7a0fb323 106}
107
108// Called once, right after object creation, when it is safe to set self
fc68f6b1 109void FwdState::start(Pointer aSelf)
110{
7a0fb323 111 // Protect ourselves from being destroyed when the only Server pointing
112 // to us is gone (while we expect to talk to more Servers later).
113 // Once we set self, we are responsible for clearing it when we do not
114 // expect to talk to any servers.
115 self = aSelf; // refcounted
116
117 // We hope that either the store entry aborts or peer is selected.
118 // Otherwise we are going to leak our object.
34266cde 119
3900307b 120 entry->registerAbort(FwdState::abort, this);
7a0fb323 121 peerSelect(request, entry, fwdStartCompleteWrapper, this);
429871db 122
fc68f6b1 123 // TODO: set self _after_ the peer is selected because we do not need
7a0fb323 124 // self until we start talking to some Server.
db1cd23c 125}
126
802a8c1d 127void
128FwdState::completed()
41462d93 129{
fc68f6b1 130 if (flags.forward_completed == 1) {
131 debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!");
132 return;
133 }
134
135 flags.forward_completed = 1;
802a8c1d 136
bc87dc25 137#if URL_CHECKSUM_DEBUG
62e76326 138
b6b6f466 139 entry->mem_obj->checkUrlChecksum();
225644d7 140#endif
141#if WIP_FWD_LOG
62e76326 142
b6b6f466 143 log();
bc87dc25 144#endif
62e76326 145
b6b6f466 146 if (entry->store_status == STORE_PENDING) {
147 if (entry->isEmpty()) {
148 assert(err);
149 errorAppendEntry(entry, err);
150 err = NULL;
62e76326 151 } else {
b6b6f466 152 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
153 entry->complete();
d88e3c49 154 entry->releaseRequest();
62e76326 155 }
f563eea9 156 }
62e76326 157
b6b6f466 158 if (storePendingNClients(entry) > 0)
159 assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT));
62e76326 160
802a8c1d 161}
162
163FwdState::~FwdState()
164{
165 debugs(17, 3, HERE << "FwdState destructor starting");
fc68f6b1 166
802a8c1d 167 if (! flags.forward_completed)
fc68f6b1 168 completed();
802a8c1d 169
b6b6f466 170 serversFree(&servers);
62e76326 171
6dd9f4bd 172 HTTPMSGUNLOCK(request);
62e76326 173
b6b6f466 174 if (err)
175 errorStateFree(err);
62e76326 176
3900307b 177 entry->unregisterAbort();
429871db 178
97b5e68f 179 entry->unlock();
62e76326 180
b6b6f466 181 entry = NULL;
62e76326 182
c2f45110 183 int fd = server_fd;
184
185 if (fd > -1) {
b6b6f466 186 server_fd = -1;
c2f45110 187 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
bf8fe701 188 debugs(17, 3, "fwdStateFree: closing FD " << fd);
c2f45110 189 comm_close(fd);
b6b6f466 190 }
fc68f6b1 191
b6b6f466 192 debugs(17, 3, HERE << "FwdState destructor done");
193}
62e76326 194
b6b6f466 195/*
196 * This is the entry point for client-side to start forwarding
197 * a transaction. It is a static method that may or may not
198 * allocate a FwdState.
199 */
be0c6690 200void
b6b6f466 201FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request)
202{
203 /*
204 * client_addr == no_addr indicates this is an "internal" request
205 * from peer_digest.c, asn.c, netdb.c, etc and should always
206 * be allowed. yuck, I know.
207 */
62e76326 208
b6b6f466 209 if (request->client_addr.s_addr != no_addr.s_addr && request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) {
210 /*
211 * Check if this host is allowed to fetch MISSES from us (miss_access)
212 */
213 ACLChecklist ch;
214 ch.src_addr = request->client_addr;
215 ch.my_addr = request->my_addr;
216 ch.my_port = request->my_port;
6dd9f4bd 217 ch.request = HTTPMSGLOCK(request);
b6b6f466 218 ch.accessList = cbdataReference(Config.accessList.miss);
219 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
220 int answer = ch.fastCheck();
221
222 if (answer == 0) {
223 err_type page_id;
9ce7856a 224 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1);
b6b6f466 225
226 if (page_id == ERR_NONE)
227 page_id = ERR_FORWARDING_DENIED;
228
2cc81f1f 229 ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN, request);
b6b6f466 230
231 errorAppendEntry(entry, anErr); // frees anErr
232
be0c6690 233 return;
b6b6f466 234 }
235 }
236
bf8fe701 237 debugs(17, 3, "FwdState::start() '" << entry->url() << "'");
f4ef658f 238 /*
239 * This seems like an odd place to bind mem_obj and request.
240 * Might want to assert that request is NULL at this point
241 */
6dd9f4bd 242 entry->mem_obj->request = HTTPMSGLOCK(request);
b6b6f466 243#if URL_CHECKSUM_DEBUG
244
245 entry->mem_obj->checkUrlChecksum();
246#endif
247
248 if (shutting_down) {
249 /* more yuck */
2cc81f1f 250 ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 251 errorAppendEntry(entry, anErr); // frees anErr
be0c6690 252 return;
6801f8a8 253 }
62e76326 254
b6b6f466 255 switch (request->protocol) {
256
257 case PROTO_INTERNAL:
258 internalStart(request, entry);
be0c6690 259 return;
b6b6f466 260
261 case PROTO_CACHEOBJ:
262 cachemgrStart(client_fd, request, entry);
be0c6690 263 return;
b6b6f466 264
265 case PROTO_URN:
266 urnStart(request, entry);
be0c6690 267 return;
b6b6f466 268
269 default:
7a0fb323 270 FwdState::Pointer fwd = new FwdState(client_fd, entry, request);
fc68f6b1 271#if LINUX_TPROXY
272 /* If we need to transparently proxy the request
273 * then we need the client source address and port */
274 fwd->src.sin_family = AF_INET;
275 fwd->src.sin_addr = request->client_addr;
276 fwd->src.sin_port = request->client_port;
277#endif
278
7a0fb323 279 fwd->start(fwd);
be0c6690 280 return;
b6b6f466 281 }
282
283 /* NOTREACHED */
41462d93 284}
285
b6b6f466 286void
287FwdState::fail(ErrorState * errorState)
288{
bf8fe701 289 debugs(17, 3, "fwdFail: " << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() );
b6b6f466 290
291 if (err)
292 errorStateFree(err);
293
294 err = errorState;
295
296 if (!errorState->request)
6dd9f4bd 297 errorState->request = HTTPMSGLOCK(request);
b6b6f466 298}
299
300/*
301 * Frees fwdState without closing FD or generating an abort
302 */
303void
304FwdState::unregister(int fd)
305{
bf8fe701 306 debugs(17, 3, "fwdUnregister: " << entry->url() );
b6b6f466 307 assert(fd == server_fd);
308 assert(fd > -1);
309 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
310 server_fd = -1;
311}
312
313/*
314 * server-side modules call fwdComplete() when they are done
315 * downloading an object. Then, we either 1) re-forward the
316 * request somewhere else if needed, or 2) call storeComplete()
317 * to finish it off
318 */
319void
320FwdState::complete()
321{
322 StoreEntry *e = entry;
323 assert(entry->store_status == STORE_PENDING);
bf8fe701 324 debugs(17, 3, "fwdComplete: " << e->url() << "\n\tstatus " << entry->getReply()->sline.status );
b6b6f466 325#if URL_CHECKSUM_DEBUG
326
327 entry->mem_obj->checkUrlChecksum();
328#endif
329
330 logReplyStatus(n_tries, entry->getReply()->sline.status);
331
332 if (reforward()) {
bf8fe701 333 debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url());
b6b6f466 334
335 if (server_fd > -1)
336 unregister(server_fd);
337
3900307b 338 e->reset();
b6b6f466 339
340 startComplete(servers);
341 } else {
bf8fe701 342 debugs(17, 3, "fwdComplete: not re-forwarding status " << entry->getReply()->sline.status);
b6b6f466 343 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
fc68f6b1 344 entry->complete();
345
346 if (server_fd < 0)
347 completed();
348
7a0fb323 349 self = NULL; // refcounted
b6b6f466 350 }
351}
352
353
354/**** CALLBACK WRAPPERS ************************************************************/
355
356static void
357fwdStartCompleteWrapper(FwdServer * servers, void *data)
358{
359 FwdState *fwd = (FwdState *) data;
360 fwd->startComplete(servers);
361}
362
363static void
364fwdServerClosedWrapper(int fd, void *data)
365{
366 FwdState *fwd = (FwdState *) data;
367 fwd->serverClosed(fd);
368}
369
370static void
371fwdConnectStartWrapper(void *data)
372{
373 FwdState *fwd = (FwdState *) data;
374 fwd->connectStart();
375}
376
377#if USE_SSL
378static void
379fwdNegotiateSSLWrapper(int fd, void *data)
380{
381 FwdState *fwd = (FwdState *) data;
382 fwd->negotiateSSL(fd);
383}
384
385#endif
386
387static void
388fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
389{
390 FwdState *fwd = (FwdState *) data;
391 fwd->connectDone(server_fd, status, xerrno);
392}
393
394static void
395fwdConnectTimeoutWrapper(int fd, void *data)
396{
397 FwdState *fwd = (FwdState *) data;
398 fwd->connectTimeout(fd);
399}
400
401/*
402 * Accounts for closed persistent connections
403 */
404static void
405fwdPeerClosed(int fd, void *data)
406{
407 peer *p = (peer *)data;
408 p->stats.conn_open--;
409}
410
411/**** PRIVATE *****************************************************************/
412
545782b8 413/*
414 * FwdState::checkRetry
415 *
416 * Return TRUE if the request SHOULD be retried. This method is
417 * called when the HTTP connection fails, or when the connection
418 * is closed before server-side read the end of HTTP headers.
419 */
b6b6f466 420bool
421FwdState::checkRetry()
68bd6892 422{
d8fd0f18 423 if (shutting_down)
b6b6f466 424 return false;
62e76326 425
b6b6f466 426 if (entry->store_status != STORE_PENDING)
427 return false;
62e76326 428
b6b6f466 429 if (!entry->isEmpty())
430 return false;
62e76326 431
b6b6f466 432 if (n_tries > 10)
433 return false;
62e76326 434
b6b6f466 435 if (origin_tries > 2)
436 return false;
4ed0e075 437
b6b6f466 438 if (squid_curtime - start_t > Config.Timeout.forward)
439 return false;
62e76326 440
b6b6f466 441 if (flags.dont_retry)
442 return false;
62e76326 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 */
b6b6f466 469 switch (request->method) {
cb928909 470 /* 9.1.1 Safe Methods */
471
472 case METHOD_GET:
473
474 case METHOD_HEAD:
475 /* 9.1.2 Indepontent Methods */
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
b6b6f466 500 if (checkRetry()) {
501 int originserver = (servers->_peer == NULL);
4a7a3d56 502 debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)");
62e76326 503
b6b6f466 504 if (servers->next) {
62e76326 505 /* use next, or cycle if origin server isn't last */
b6b6f466 506 FwdServer *fs = servers;
62e76326 507 FwdServer **T, *T2 = NULL;
b6b6f466 508 servers = fs->next;
62e76326 509
b6b6f466 510 for (T = &servers; *T; T2 = *T, T = &(*T)->next)
62e76326 511
512 ;
513 if (T2 && T2->_peer) {
514 /* cycle */
515 *T = fs;
516 fs->next = NULL;
517 } else {
518 /* Use next. The last "direct" entry is retried multiple times */
b6b6f466 519 servers = fs->next;
62e76326 520 fwdServerFree(fs);
4ed0e075 521 originserver = 0;
62e76326 522 }
523 }
524
4ed0e075 525 /* use eventAdd to break potential call sequence loops and to slow things down a little */
b6b6f466 526 eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
62e76326 527
528 return;
d8fd0f18 529 }
62e76326 530
b6b6f466 531 if (!err && shutting_down) {
2cc81f1f 532 errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
f563eea9 533 }
62e76326 534
b6b6f466 535 self = NULL; // refcounted
910169e5 536}
537
a7ad6e4e 538#if USE_SSL
b6b6f466 539void
540FwdState::negotiateSSL(int fd)
a7ad6e4e 541{
b6b6f466 542 FwdServer *fs = servers;
a7ad6e4e 543 SSL *ssl = fd_table[fd].ssl;
544 int ret;
62e76326 545
a7ad6e4e 546 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 547 int ssl_error = SSL_get_error(ssl, ret);
548
549 switch (ssl_error) {
550
551 case SSL_ERROR_WANT_READ:
b6b6f466 552 commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
62e76326 553 return;
554
555 case SSL_ERROR_WANT_WRITE:
b6b6f466 556 commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
62e76326 557 return;
558
559 default:
bf8fe701 560 debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
561 ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error <<
562 "/" << ret << "/" << errno << ")");
2cc81f1f 563 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
a7ad6e4e 564#ifdef EPROTO
62e76326 565
b6b6f466 566 anErr->xerrno = EPROTO;
a7ad6e4e 567#else
62e76326 568
b6b6f466 569 anErr->xerrno = EACCES;
a7ad6e4e 570#endif
62e76326 571
b6b6f466 572 fail(anErr);
62e76326 573
574 if (fs->_peer) {
575 peerConnectFailed(fs->_peer);
576 fs->_peer->stats.conn_open--;
577 }
578
579 comm_close(fd);
580 return;
581 }
a7ad6e4e 582 }
62e76326 583
f38c5e43 584 if (fs->_peer && !SSL_session_reused(ssl)) {
585 if (fs->_peer->sslSession)
586 SSL_SESSION_free(fs->_peer->sslSession);
587
588 fs->_peer->sslSession = SSL_get1_session(ssl);
589 }
590
b6b6f466 591 dispatch();
a7ad6e4e 592}
593
b6b6f466 594void
595FwdState::initiateSSL()
a7ad6e4e 596{
b6b6f466 597 FwdServer *fs = servers;
598 int fd = server_fd;
a7ad6e4e 599 SSL *ssl;
600 SSL_CTX *sslContext = NULL;
601 peer *peer = fs->_peer;
62e76326 602
a7ad6e4e 603 if (peer) {
62e76326 604 assert(peer->use_ssl);
605 sslContext = peer->sslContext;
a7ad6e4e 606 } else {
62e76326 607 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 608 }
62e76326 609
a7ad6e4e 610 assert(sslContext);
62e76326 611
a7ad6e4e 612 if ((ssl = SSL_new(sslContext)) == NULL) {
bf8fe701 613 debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) );
2cc81f1f 614 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 615 anErr->xerrno = errno;
b6b6f466 616 fail(anErr);
617 self = NULL; // refcounted
62e76326 618 return;
a7ad6e4e 619 }
62e76326 620
a7ad6e4e 621 SSL_set_fd(ssl, fd);
62e76326 622
a7ad6e4e 623 if (peer) {
62e76326 624 if (peer->ssldomain)
625 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
626
a7ad6e4e 627#if NOT_YET
62e76326 628
629 else if (peer->name)
630 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
631
a7ad6e4e 632#endif
62e76326 633
634 else
635 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
f38c5e43 636
637 if (peer->sslSession)
638 SSL_set_session(ssl, peer->sslSession);
639
a7ad6e4e 640 } else {
b6b6f466 641 SSL_set_ex_data(ssl, ssl_ex_index_server, request->host);
a7ad6e4e 642 }
62e76326 643
a7ad6e4e 644 fd_table[fd].ssl = ssl;
645 fd_table[fd].read_method = &ssl_read_method;
646 fd_table[fd].write_method = &ssl_write_method;
b6b6f466 647 negotiateSSL(fd);
a7ad6e4e 648}
62e76326 649
a7ad6e4e 650#endif
651
b6b6f466 652void
653FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
41462d93 654{
b6b6f466 655 FwdServer *fs = servers;
656 assert(server_fd == aServerFD);
62e76326 657
beed27a2 658 if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
659 hierarchyNote(&request->hier, fs->code, fd_table[server_fd].ipaddr);
660
41462d93 661 if (status == COMM_ERR_DNS) {
62e76326 662 /*
663 * Only set the dont_retry flag if the DNS lookup fails on
664 * a direct connection. If DNS lookup fails when trying
665 * a neighbor cache, we may want to retry another option.
666 */
667
668 if (NULL == fs->_peer)
b6b6f466 669 flags.dont_retry = 1;
62e76326 670
bf8fe701 671 debugs(17, 4, "fwdConnectDone: Unknown host: " << request->host);
62e76326 672
2cc81f1f 673 ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
62e76326 674
b6b6f466 675 anErr->dnsserver_msg = xstrdup(dns_error_message);
62e76326 676
b6b6f466 677 fail(anErr);
62e76326 678
62e76326 679 comm_close(server_fd);
41462d93 680 } else if (status != COMM_OK) {
62e76326 681 assert(fs);
2cc81f1f 682 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 683 anErr->xerrno = xerrno;
62e76326 684
b6b6f466 685 fail(anErr);
62e76326 686
a4bd7d1f 687 if (fs->_peer)
62e76326 688 peerConnectFailed(fs->_peer);
62e76326 689
690 comm_close(server_fd);
41462d93 691 } else {
bf8fe701 692 debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
62e76326 693
694 if (fs->_peer)
695 peerConnectSucceded(fs->_peer);
696
a7ad6e4e 697#if USE_SSL
62e76326 698
699 if ((fs->_peer && fs->_peer->use_ssl) ||
700 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
b6b6f466 701 initiateSSL();
62e76326 702 return;
703 }
704
a7ad6e4e 705#endif
b6b6f466 706 dispatch();
41462d93 707 }
41462d93 708}
709
b6b6f466 710void
711FwdState::connectTimeout(int fd)
41462d93 712{
beed27a2 713 FwdServer *fs = servers;
714
bf8fe701 715 debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" );
b6b6f466 716 assert(fd == server_fd);
62e76326 717
beed27a2 718 if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
719 hierarchyNote(&request->hier, fs->code, fd_table[fd].ipaddr);
720
528b2c61 721 if (entry->isEmpty()) {
2cc81f1f 722 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
b6b6f466 723 anErr->xerrno = ETIMEDOUT;
724 fail(anErr);
62e76326 725 /*
726 * This marks the peer DOWN ...
727 */
728
b6b6f466 729 if (servers)
730 if (servers->_peer)
731 peerConnectFailed(servers->_peer);
41462d93 732 }
62e76326 733
41462d93 734 comm_close(fd);
735}
736
b6b6f466 737void
738FwdState::connectStart()
41462d93 739{
3900307b 740 const char *url = entry->url();
cb928909 741 int fd = -1;
b6b6f466 742 FwdServer *fs = servers;
db1cd23c 743 const char *host;
744 unsigned short port;
bd0723ad 745 const char *domain = NULL;
777831e0 746 int ctimeout;
b6b6f466 747 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
fc68f6b1 748#if LINUX_TPROXY
749
750 struct in_tproxy itp;
751#endif
62e76326 752
ddfcbc22 753 struct IN_ADDR outgoing;
d6827718 754 unsigned short tos;
fc68f6b1 755
756 struct IN_ADDR *client_addr = NULL;
db1cd23c 757 assert(fs);
b6b6f466 758 assert(server_fd == -1);
bf8fe701 759 debugs(17, 3, "fwdConnectStart: " << url);
62e76326 760
29b8d8d6 761 if (fs->_peer) {
37181402 762 host = fs->_peer->host;
62e76326 763 port = fs->_peer->http_port;
764 ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
765 : Config.Timeout.peer_connect;
bd0723ad 766
7171e7ee 767 if (fs->_peer->options.originserver)
b6b6f466 768 domain = request->host;
db1cd23c 769 } else {
b6b6f466 770 host = request->host;
771 port = request->port;
62e76326 772 ctimeout = Config.Timeout.connect;
db1cd23c 773 }
62e76326 774
fc68f6b1 775#if LINUX_TPROXY
776 if (request->flags.tproxy)
777 client_addr = &request->client_addr;
778
779#endif
780
777831e0 781 if (ftimeout < 0)
782 ftimeout = 5;
783
784 if (ftimeout < ctimeout)
785 ctimeout = ftimeout;
786
c8ceec27 787 fd = fwdPconnPool->pop(host, port, domain, client_addr, checkRetriable());
788 if (fd >= 0) {
789 debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd);
790 server_fd = fd;
791 n_tries++;
4ed0e075 792
c8ceec27 793 if (!fs->_peer)
794 origin_tries++;
4ed0e075 795
c8ceec27 796 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
4ed0e075 797
c8ceec27 798 dispatch();
4ed0e075 799
c8ceec27 800 return;
41462d93 801 }
62e76326 802
bc87dc25 803#if URL_CHECKSUM_DEBUG
b6b6f466 804 entry->mem_obj->checkUrlChecksum();
62e76326 805
bc87dc25 806#endif
62e76326 807
b6b6f466 808 outgoing = getOutgoingAddr(request);
62e76326 809
b6b6f466 810 tos = getOutgoingTOS(request);
d6827718 811
bf8fe701 812 debugs(17, 3, "fwdConnectStart: got addr " << inet_ntoa(outgoing) << ", tos " << tos);
62e76326 813
d6827718 814 fd = comm_openex(SOCK_STREAM,
bdb741f4 815 IPPROTO_TCP,
62e76326 816 outgoing,
817 0,
818 COMM_NONBLOCKING,
819 tos,
820 url);
821
41462d93 822 if (fd < 0) {
bf8fe701 823 debugs(50, 4, "fwdConnectStart: " << xstrerror());
2cc81f1f 824 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 825 anErr->xerrno = errno;
826 fail(anErr);
827 self = NULL; // refcounted
62e76326 828 return;
41462d93 829 }
62e76326 830
b6b6f466 831 server_fd = fd;
832 n_tries++;
4ed0e075 833
834 if (!fs->_peer)
b6b6f466 835 origin_tries++;
4ed0e075 836
c7f9eb6d 837 /*
838 * stats.conn_open is used to account for the number of
839 * connections that we have open to the peer, so we can limit
840 * based on the max-conn option. We need to increment here,
841 * even if the connection may fail.
842 */
62e76326 843
a4bd7d1f 844 if (fs->_peer) {
62e76326 845 fs->_peer->stats.conn_open++;
a4bd7d1f 846 comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
847 }
62e76326 848
b6b6f466 849 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
62e76326 850
b6b6f466 851 commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
62e76326 852
fc68f6b1 853 if (fs->_peer) {
beed27a2 854 hierarchyNote(&request->hier, fs->code, fs->_peer->host);
fc68f6b1 855 } else {
856#if LINUX_TPROXY
857
858 if (request->flags.tproxy) {
859 itp.v.addr.faddr.s_addr = src.sin_addr.s_addr;
860 itp.v.addr.fport = 0;
861
862 /* If these syscalls fail then we just fallback to connecting
863 * normally by simply ignoring the errors...
864 */
865 itp.op = TPROXY_ASSIGN;
866
867 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
bf8fe701 868 debugs(20, 1, "tproxy ip=" << inet_ntoa(itp.v.addr.faddr) <<
311baa61 869 ",0x" << std::hex << itp.v.addr.faddr.s_addr << std::dec <<
bf8fe701 870 ",port=" << itp.v.addr.fport << " ERROR ASSIGN");
871
fc68f6b1 872 request->flags.tproxy = 0;
873 } else {
874 itp.op = TPROXY_FLAGS;
875 itp.v.flags = ITP_CONNECT;
876
877 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
311baa61 878 debugs(20, 1, "tproxy ip=" << std::hex <<
879 itp.v.addr.faddr.s_addr << std::dec << ",port=" <<
bf8fe701 880 itp.v.addr.fport << " ERROR CONNECT");
881
fc68f6b1 882 request->flags.tproxy = 0;
883 }
884 }
885 }
886
887#endif
beed27a2 888 hierarchyNote(&request->hier, fs->code, request->host);
fc68f6b1 889 }
beed27a2 890
b6b6f466 891 commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
41462d93 892}
893
b6b6f466 894void
895FwdState::startComplete(FwdServer * theServers)
41462d93 896{
bf8fe701 897 debugs(17, 3, "fwdStartComplete: " << entry->url() );
62e76326 898
b6b6f466 899 if (theServers != NULL) {
900 servers = theServers;
901 connectStart();
41462d93 902 } else {
b6b6f466 903 startFail();
41462d93 904 }
41462d93 905}
906
b6b6f466 907void
908FwdState::startFail()
41462d93 909{
bf8fe701 910 debugs(17, 3, "fwdStartFail: " << entry->url() );
2cc81f1f 911 ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 912 anErr->xerrno = errno;
913 fail(anErr);
914 self = NULL; // refcounted
41462d93 915}
910169e5 916
b6b6f466 917void
918FwdState::dispatch()
41462d93 919{
c7f9eb6d 920 peer *p = NULL;
bf8fe701 921 debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr[request->method] << " " << entry->url() << "'" );
e0ebe27c 922 /*
923 * Assert that server_fd is set. This is to guarantee that fwdState
924 * is attached to something and will be deallocated when server_fd
925 * is closed.
926 */
a7ad6e4e 927 assert(server_fd > -1);
62e76326 928
3900307b 929 fd_note(server_fd, entry->url());
62e76326 930
781ce8ff 931 fd_table[server_fd].noteUse(fwdPconnPool);
62e76326 932
a7ad6e4e 933 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
934 assert(entry->ping_status != PING_WAITING);
62e76326 935
a7ad6e4e 936 assert(entry->lock_count);
62e76326 937
a7ad6e4e 938 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 939
a7ad6e4e 940 netdbPingSite(request->host);
62e76326 941
b6b6f466 942 if (servers && (p = servers->_peer)) {
62e76326 943 p->stats.fetches++;
b6b6f466 944 request->peer_login = p->login;
945 request->peer_domain = p->domain;
946 httpStart(this);
41462d93 947 } else {
b6b6f466 948 request->peer_login = NULL;
949 request->peer_domain = NULL;
62e76326 950
951 switch (request->protocol) {
a7ad6e4e 952#if USE_SSL
62e76326 953
954 case PROTO_HTTPS:
b6b6f466 955 httpStart(this);
62e76326 956 break;
a7ad6e4e 957#endif
62e76326 958
959 case PROTO_HTTP:
b6b6f466 960 httpStart(this);
62e76326 961 break;
962
963 case PROTO_GOPHER:
b6b6f466 964 gopherStart(this);
62e76326 965 break;
966
967 case PROTO_FTP:
b6b6f466 968 ftpStart(this);
62e76326 969 break;
970
62e76326 971 case PROTO_CACHEOBJ:
972
973 case PROTO_INTERNAL:
974
975 case PROTO_URN:
976 fatal_dump("Should never get here");
977 break;
978
979 case PROTO_WHOIS:
b6b6f466 980 whoisStart(this);
62e76326 981 break;
982
db80e881 983 case PROTO_WAIS: /* Not implemented */
fc68f6b1 984
62e76326 985 default:
bf8fe701 986 debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry->url() << "'" );
2cc81f1f 987 ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 988 fail(anErr);
62e76326 989 /*
990 * Force a persistent connection to be closed because
991 * some Netscape browsers have a bug that sends CONNECT
992 * requests as GET's over persistent connections.
993 */
994 request->flags.proxy_keepalive = 0;
995 /*
996 * Set the dont_retry flag becuase this is not a
997 * transient (network) error; its a bug.
998 */
b6b6f466 999 flags.dont_retry = 1;
1000 comm_close(server_fd);
62e76326 1001 break;
1002 }
41462d93 1003 }
1004}
1005
545782b8 1006/*
1007 * FwdState::reforward
1008 *
1009 * returns TRUE if the transaction SHOULD be re-forwarded to the
1010 * next choice in the FwdServers list. This method is called when
1011 * server-side communication completes normally, or experiences
1012 * some error after receiving the end of HTTP headers.
1013 */
b6b6f466 1014int
1015FwdState::reforward()
db1cd23c 1016{
b6b6f466 1017 StoreEntry *e = entry;
1018 FwdServer *fs = servers;
db1cd23c 1019 http_status s;
1020 assert(e->store_status == STORE_PENDING);
1021 assert(e->mem_obj);
bc87dc25 1022#if URL_CHECKSUM_DEBUG
62e76326 1023
528b2c61 1024 e->mem_obj->checkUrlChecksum();
bc87dc25 1025#endif
62e76326 1026
bf8fe701 1027 debugs(17, 3, "fwdReforward: " << e->url() << "?" );
62e76326 1028
d6eb18d6 1029 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
bf8fe701 1030 debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set");
62e76326 1031 return 0;
d6eb18d6 1032 }
62e76326 1033
b6b6f466 1034 if (n_tries > 9)
62e76326 1035 return 0;
1036
b6b6f466 1037 if (origin_tries > 1)
4ed0e075 1038 return 0;
1039
58217e94 1040 if (request->bodyNibbled())
62e76326 1041 return 0;
1042
db1cd23c 1043 assert(fs);
62e76326 1044
b6b6f466 1045 servers = fs->next;
62e76326 1046
db1cd23c 1047 fwdServerFree(fs);
62e76326 1048
b6b6f466 1049 if (servers == NULL) {
bf8fe701 1050 debugs(17, 3, "fwdReforward: No forward-servers left");
62e76326 1051 return 0;
db1cd23c 1052 }
62e76326 1053
528b2c61 1054 s = e->getReply()->sline.status;
4a7a3d56 1055 debugs(17, 3, "fwdReforward: status " << s);
b6b6f466 1056 return reforwardableStatus(s);
db1cd23c 1057}
1058
b6b6f466 1059static void
1060fwdStats(StoreEntry * s)
64d8034e 1061{
b6b6f466 1062 int i;
1063 int j;
1064 storeAppendPrintf(s, "Status");
62e76326 1065
b6b6f466 1066 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1067 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1068 }
64d8034e 1069
b6b6f466 1070 storeAppendPrintf(s, "\n");
0185bd6f 1071
b6b6f466 1072 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1073 if (FwdReplyCodes[0][i] == 0)
1074 continue;
0185bd6f 1075
b6b6f466 1076 storeAppendPrintf(s, "%3d", i);
0185bd6f 1077
b6b6f466 1078 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1079 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1080 }
62e76326 1081
b6b6f466 1082 storeAppendPrintf(s, "\n");
e0ebe27c 1083 }
7197b20d 1084}
1085
a4bd7d1f 1086
b6b6f466 1087/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1088
b6b6f466 1089bool
1090FwdState::reforwardableStatus(http_status s)
db1cd23c 1091{
b6b6f466 1092 switch (s) {
62e76326 1093
b6b6f466 1094 case HTTP_BAD_GATEWAY:
62e76326 1095
b6b6f466 1096 case HTTP_GATEWAY_TIMEOUT:
1097 return true;
62e76326 1098
b6b6f466 1099 case HTTP_FORBIDDEN:
62e76326 1100
b6b6f466 1101 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1102
b6b6f466 1103 case HTTP_NOT_IMPLEMENTED:
62e76326 1104
b6b6f466 1105 case HTTP_SERVICE_UNAVAILABLE:
1106 return Config.retry.onerror;
62e76326 1107
b6b6f466 1108 default:
1109 return false;
db1cd23c 1110 }
b6b6f466 1111
1112 /* NOTREACHED */
db1cd23c 1113}
8ddcc35d 1114
781ce8ff 1115void
fc68f6b1 1116
1117FwdState::pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr)
781ce8ff 1118{
fc68f6b1 1119 fwdPconnPool->push(fd, host, port, domain, client_addr);
781ce8ff 1120}
1121
8ddcc35d 1122void
b6b6f466 1123FwdState::initModule()
8ddcc35d 1124{
b6b6f466 1125 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
1126
225644d7 1127#if WIP_FWD_LOG
62e76326 1128
225644d7 1129 if (logfile)
62e76326 1130 (void) 0;
225644d7 1131 else if (NULL == Config.Log.forward)
62e76326 1132 (void) 0;
225644d7 1133 else
62e76326 1134 logfile = logfileOpen(Config.Log.forward, 0, 1);
1135
225644d7 1136#endif
8ddcc35d 1137}
1138
62ee09ca 1139void
1140FwdState::RegisterWithCacheManager(CacheManager & manager)
1141{
1142 manager.registerAction("forward",
1143 "Request Forwarding Statistics",
1144 fwdStats, 0, 1);
1145}
1146
b6b6f466 1147void
1148FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1149{
1150 if (status > HTTP_INVALID_HEADER)
62e76326 1151 return;
1152
8ddcc35d 1153 assert(tries);
62e76326 1154
8ddcc35d 1155 tries--;
62e76326 1156
8ddcc35d 1157 if (tries > MAX_FWD_STATS_IDX)
62e76326 1158 tries = MAX_FWD_STATS_IDX;
1159
8ddcc35d 1160 FwdReplyCodes[tries][status]++;
1161}
1162
b6b6f466 1163void
1164FwdState::serversFree(FwdServer ** FSVR)
8ddcc35d 1165{
b6b6f466 1166 FwdServer *fs;
62e76326 1167
b6b6f466 1168 while ((fs = *FSVR)) {
1169 *FSVR = fs->next;
1170 fwdServerFree(fs);
9977e14b 1171 }
b6b6f466 1172}
62e76326 1173
b6b6f466 1174/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1175
b6b6f466 1176static void
1177fwdServerFree(FwdServer * fs)
1178{
1179 cbdataReferenceDone(fs->_peer);
1180 memFree(fs, MEM_FWD_SERVER);
1181}
62e76326 1182
b6b6f466 1183static struct IN_ADDR
1184 aclMapAddr(acl_address * head, ACLChecklist * ch)
1185{
1186 acl_address *l;
62e76326 1187
b6b6f466 1188 struct IN_ADDR addr;
62e76326 1189
b6b6f466 1190 for (l = head; l; l = l->next)
1191 {
1192 if (ch->matchAclListFast(l->aclList))
1193 return l->addr;
9977e14b 1194 }
b6b6f466 1195
1196 addr.s_addr = INADDR_ANY;
1197 return addr;
8ddcc35d 1198}
b6a2f15e 1199
057f5854 1200/*
1201 * DPW 2007-05-19
1202 * Formerly static, but now used by client_side_request.cc
1203 */
1204int
b6b6f466 1205aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1206{
b6b6f466 1207 acl_tos *l;
62e76326 1208
b6b6f466 1209 for (l = head; l; l = l->next) {
1210 if (ch->matchAclListFast(l->aclList))
1211 return l->tos;
1212 }
5894ad28 1213
b6b6f466 1214 return 0;
1215}
5894ad28 1216
b6b6f466 1217struct IN_ADDR
1218 getOutgoingAddr(HttpRequest * request)
1219{
1220 ACLChecklist ch;
62e76326 1221
b6b6f466 1222 if (request)
1223 {
1224 ch.src_addr = request->client_addr;
1225 ch.my_addr = request->my_addr;
1226 ch.my_port = request->my_port;
6dd9f4bd 1227 ch.request = HTTPMSGLOCK(request);
b6b6f466 1228 }
62e76326 1229
b6b6f466 1230 return aclMapAddr(Config.accessList.outgoing_address, &ch);
1231}
62e76326 1232
b6b6f466 1233unsigned long
1234getOutgoingTOS(HttpRequest * request)
1235{
1236 ACLChecklist ch;
62e76326 1237
b6b6f466 1238 if (request) {
1239 ch.src_addr = request->client_addr;
1240 ch.my_addr = request->my_addr;
1241 ch.my_port = request->my_port;
6dd9f4bd 1242 ch.request = HTTPMSGLOCK(request);
b6a2f15e 1243 }
62e76326 1244
b6b6f466 1245 return aclMapTOS(Config.accessList.outgoing_tos, &ch);
b6a2f15e 1246}
225644d7 1247
b6b6f466 1248
1249/**** WIP_FWD_LOG *************************************************************/
1250
225644d7 1251#if WIP_FWD_LOG
1252void
1253fwdUninit(void)
1254{
dab0cec3 1255 if (NULL == logfile)
62e76326 1256 return;
1257
225644d7 1258 logfileClose(logfile);
62e76326 1259
225644d7 1260 logfile = NULL;
1261}
1262
1263void
1264fwdLogRotate(void)
1265{
1266 if (logfile)
62e76326 1267 logfileRotate(logfile);
225644d7 1268}
1269
1270static void
b6b6f466 1271FwdState::log()
225644d7 1272{
1273 if (NULL == logfile)
62e76326 1274 return;
1275
225644d7 1276 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
62e76326 1277 (int) current_time.tv_sec,
1278 (int) current_time.tv_usec / 1000,
b6b6f466 1279 last_status,
1280 RequestMethodStr[request->method],
1281 request->canonical);
225644d7 1282}
1283
1284void
b6b6f466 1285FwdState::status(http_status s)
225644d7 1286{
b6b6f466 1287 last_status = s;
225644d7 1288}
1289
1290#endif