]> git.ipfire.org Git - thirdparty/squid.git/blame - src/forward.cc
Bug #1935 fix: Do not retry a request after sending [some of] its body.
[thirdparty/squid.git] / src / forward.cc
CommitLineData
41462d93 1
2/*
58217e94 3 * $Id: forward.cc,v 1.157 2007/04/16 17:43:27 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
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
7a0fb323 120 storeRegisterAbort(entry, FwdState::abort, this);
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();
154 storeReleaseRequest(entry);
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
429871db 177 storeUnregisterAbort(entry);
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);
188 debug(17, 3) ("fwdStateFree: closing FD %d\n", fd);
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
237 debug(17, 3) ("FwdState::start() '%s'\n", storeUrl(entry));
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{
289 debug(17, 3) ("fwdFail: %s \"%s\"\n\t%s\n",
290 err_type_str[errorState->type],
291 httpStatusString(errorState->httpStatus),
292 storeUrl(entry));
293
294 if (err)
295 errorStateFree(err);
296
297 err = errorState;
298
299 if (!errorState->request)
6dd9f4bd 300 errorState->request = HTTPMSGLOCK(request);
b6b6f466 301}
302
303/*
304 * Frees fwdState without closing FD or generating an abort
305 */
306void
307FwdState::unregister(int fd)
308{
309 debug(17, 3) ("fwdUnregister: %s\n", storeUrl(entry));
310 assert(fd == server_fd);
311 assert(fd > -1);
312 comm_remove_close_handler(fd, fwdServerClosedWrapper, this);
313 server_fd = -1;
314}
315
316/*
317 * server-side modules call fwdComplete() when they are done
318 * downloading an object. Then, we either 1) re-forward the
319 * request somewhere else if needed, or 2) call storeComplete()
320 * to finish it off
321 */
322void
323FwdState::complete()
324{
325 StoreEntry *e = entry;
326 assert(entry->store_status == STORE_PENDING);
327 debug(17, 3) ("fwdComplete: %s\n\tstatus %d\n", storeUrl(e),
328 entry->getReply()->sline.status);
329#if URL_CHECKSUM_DEBUG
330
331 entry->mem_obj->checkUrlChecksum();
332#endif
333
334 logReplyStatus(n_tries, entry->getReply()->sline.status);
335
336 if (reforward()) {
337 debug(17, 3) ("fwdComplete: re-forwarding %d %s\n",
338 entry->getReply()->sline.status,
339 storeUrl(e));
340
341 if (server_fd > -1)
342 unregister(server_fd);
343
344 storeEntryReset(e);
345
346 startComplete(servers);
347 } else {
348 debug(17, 3) ("fwdComplete: not re-forwarding status %d\n",
349 entry->getReply()->sline.status);
350 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
fc68f6b1 351 entry->complete();
352
353 if (server_fd < 0)
354 completed();
355
7a0fb323 356 self = NULL; // refcounted
b6b6f466 357 }
358}
359
360
361/**** CALLBACK WRAPPERS ************************************************************/
362
363static void
364fwdStartCompleteWrapper(FwdServer * servers, void *data)
365{
366 FwdState *fwd = (FwdState *) data;
367 fwd->startComplete(servers);
368}
369
370static void
371fwdServerClosedWrapper(int fd, void *data)
372{
373 FwdState *fwd = (FwdState *) data;
374 fwd->serverClosed(fd);
375}
376
377static void
378fwdConnectStartWrapper(void *data)
379{
380 FwdState *fwd = (FwdState *) data;
381 fwd->connectStart();
382}
383
384#if USE_SSL
385static void
386fwdNegotiateSSLWrapper(int fd, void *data)
387{
388 FwdState *fwd = (FwdState *) data;
389 fwd->negotiateSSL(fd);
390}
391
392#endif
393
394static void
395fwdConnectDoneWrapper(int server_fd, comm_err_t status, int xerrno, void *data)
396{
397 FwdState *fwd = (FwdState *) data;
398 fwd->connectDone(server_fd, status, xerrno);
399}
400
401static void
402fwdConnectTimeoutWrapper(int fd, void *data)
403{
404 FwdState *fwd = (FwdState *) data;
405 fwd->connectTimeout(fd);
406}
407
408/*
409 * Accounts for closed persistent connections
410 */
411static void
412fwdPeerClosed(int fd, void *data)
413{
414 peer *p = (peer *)data;
415 p->stats.conn_open--;
416}
417
418/**** PRIVATE *****************************************************************/
419
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
b6b6f466 450bool
451FwdState::checkRetriable()
cb928909 452{
453 /* If there is a request body then Squid can only try once
454 * even if the method is indempotent
455 */
456
5f8252d2 457 if (request->body_pipe != NULL)
b6b6f466 458 return false;
cb928909 459
460 /* RFC2616 9.1 Safe and Idempotent Methods */
b6b6f466 461 switch (request->method) {
cb928909 462 /* 9.1.1 Safe Methods */
463
464 case METHOD_GET:
465
466 case METHOD_HEAD:
467 /* 9.1.2 Indepontent Methods */
468
469 case METHOD_PUT:
470
471 case METHOD_DELETE:
472
473 case METHOD_OPTIONS:
474
475 case METHOD_TRACE:
476 break;
477
478 default:
b6b6f466 479 return false;
cb928909 480 }
481
b6b6f466 482 return true;
cb928909 483}
484
b6b6f466 485void
486FwdState::serverClosed(int fd)
910169e5 487{
b6b6f466 488 debug(17, 2) ("fwdServerClosed: FD %d %s\n", fd, storeUrl(entry));
489 assert(server_fd == fd);
490 server_fd = -1;
62e76326 491
b6b6f466 492 if (checkRetry()) {
493 int originserver = (servers->_peer == NULL);
62e76326 494 debug(17, 3) ("fwdServerClosed: re-forwarding (%d tries, %d secs)\n",
b6b6f466 495 n_tries,
496 (int) (squid_curtime - start_t));
62e76326 497
b6b6f466 498 if (servers->next) {
62e76326 499 /* use next, or cycle if origin server isn't last */
b6b6f466 500 FwdServer *fs = servers;
62e76326 501 FwdServer **T, *T2 = NULL;
b6b6f466 502 servers = fs->next;
62e76326 503
b6b6f466 504 for (T = &servers; *T; T2 = *T, T = &(*T)->next)
62e76326 505
506 ;
507 if (T2 && T2->_peer) {
508 /* cycle */
509 *T = fs;
510 fs->next = NULL;
511 } else {
512 /* Use next. The last "direct" entry is retried multiple times */
b6b6f466 513 servers = fs->next;
62e76326 514 fwdServerFree(fs);
4ed0e075 515 originserver = 0;
62e76326 516 }
517 }
518
4ed0e075 519 /* use eventAdd to break potential call sequence loops and to slow things down a little */
b6b6f466 520 eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0);
62e76326 521
522 return;
d8fd0f18 523 }
62e76326 524
b6b6f466 525 if (!err && shutting_down) {
2cc81f1f 526 errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request);
f563eea9 527 }
62e76326 528
b6b6f466 529 self = NULL; // refcounted
910169e5 530}
531
a7ad6e4e 532#if USE_SSL
b6b6f466 533void
534FwdState::negotiateSSL(int fd)
a7ad6e4e 535{
b6b6f466 536 FwdServer *fs = servers;
a7ad6e4e 537 SSL *ssl = fd_table[fd].ssl;
538 int ret;
62e76326 539
a7ad6e4e 540 if ((ret = SSL_connect(ssl)) <= 0) {
62e76326 541 int ssl_error = SSL_get_error(ssl, ret);
542
543 switch (ssl_error) {
544
545 case SSL_ERROR_WANT_READ:
b6b6f466 546 commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
62e76326 547 return;
548
549 case SSL_ERROR_WANT_WRITE:
b6b6f466 550 commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
62e76326 551 return;
552
553 default:
81680607 554 debug(81, 1) ("fwdNegotiateSSL: Error negotiating SSL connection on FD %d: %s (%d/%d/%d)\n", fd, ERR_error_string(ERR_get_error(), NULL), ssl_error, ret, errno);
2cc81f1f 555 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
a7ad6e4e 556#ifdef EPROTO
62e76326 557
b6b6f466 558 anErr->xerrno = EPROTO;
a7ad6e4e 559#else
62e76326 560
b6b6f466 561 anErr->xerrno = EACCES;
a7ad6e4e 562#endif
62e76326 563
b6b6f466 564 fail(anErr);
62e76326 565
566 if (fs->_peer) {
567 peerConnectFailed(fs->_peer);
568 fs->_peer->stats.conn_open--;
569 }
570
571 comm_close(fd);
572 return;
573 }
a7ad6e4e 574 }
62e76326 575
f38c5e43 576 if (fs->_peer && !SSL_session_reused(ssl)) {
577 if (fs->_peer->sslSession)
578 SSL_SESSION_free(fs->_peer->sslSession);
579
580 fs->_peer->sslSession = SSL_get1_session(ssl);
581 }
582
b6b6f466 583 dispatch();
a7ad6e4e 584}
585
b6b6f466 586void
587FwdState::initiateSSL()
a7ad6e4e 588{
b6b6f466 589 FwdServer *fs = servers;
590 int fd = server_fd;
a7ad6e4e 591 SSL *ssl;
592 SSL_CTX *sslContext = NULL;
593 peer *peer = fs->_peer;
62e76326 594
a7ad6e4e 595 if (peer) {
62e76326 596 assert(peer->use_ssl);
597 sslContext = peer->sslContext;
a7ad6e4e 598 } else {
62e76326 599 sslContext = Config.ssl_client.sslContext;
a7ad6e4e 600 }
62e76326 601
a7ad6e4e 602 assert(sslContext);
62e76326 603
a7ad6e4e 604 if ((ssl = SSL_new(sslContext)) == NULL) {
62e76326 605 debug(83, 1) ("fwdInitiateSSL: Error allocating handle: %s\n",
606 ERR_error_string(ERR_get_error(), NULL));
2cc81f1f 607 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 608 anErr->xerrno = errno;
b6b6f466 609 fail(anErr);
610 self = NULL; // refcounted
62e76326 611 return;
a7ad6e4e 612 }
62e76326 613
a7ad6e4e 614 SSL_set_fd(ssl, fd);
62e76326 615
a7ad6e4e 616 if (peer) {
62e76326 617 if (peer->ssldomain)
618 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain);
619
a7ad6e4e 620#if NOT_YET
62e76326 621
622 else if (peer->name)
623 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name);
624
a7ad6e4e 625#endif
62e76326 626
627 else
628 SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host);
f38c5e43 629
630 if (peer->sslSession)
631 SSL_set_session(ssl, peer->sslSession);
632
a7ad6e4e 633 } else {
b6b6f466 634 SSL_set_ex_data(ssl, ssl_ex_index_server, request->host);
a7ad6e4e 635 }
62e76326 636
a7ad6e4e 637 fd_table[fd].ssl = ssl;
638 fd_table[fd].read_method = &ssl_read_method;
639 fd_table[fd].write_method = &ssl_write_method;
b6b6f466 640 negotiateSSL(fd);
a7ad6e4e 641}
62e76326 642
a7ad6e4e 643#endif
644
b6b6f466 645void
646FwdState::connectDone(int aServerFD, comm_err_t status, int xerrno)
41462d93 647{
b6b6f466 648 FwdServer *fs = servers;
649 assert(server_fd == aServerFD);
62e76326 650
beed27a2 651 if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
652 hierarchyNote(&request->hier, fs->code, fd_table[server_fd].ipaddr);
653
41462d93 654 if (status == COMM_ERR_DNS) {
62e76326 655 /*
656 * Only set the dont_retry flag if the DNS lookup fails on
657 * a direct connection. If DNS lookup fails when trying
658 * a neighbor cache, we may want to retry another option.
659 */
660
661 if (NULL == fs->_peer)
b6b6f466 662 flags.dont_retry = 1;
62e76326 663
664 debug(17, 4) ("fwdConnectDone: Unknown host: %s\n",
665 request->host);
666
2cc81f1f 667 ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
62e76326 668
b6b6f466 669 anErr->dnsserver_msg = xstrdup(dns_error_message);
62e76326 670
b6b6f466 671 fail(anErr);
62e76326 672
62e76326 673 comm_close(server_fd);
41462d93 674 } else if (status != COMM_OK) {
62e76326 675 assert(fs);
2cc81f1f 676 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 677 anErr->xerrno = xerrno;
62e76326 678
b6b6f466 679 fail(anErr);
62e76326 680
a4bd7d1f 681 if (fs->_peer)
62e76326 682 peerConnectFailed(fs->_peer);
62e76326 683
684 comm_close(server_fd);
41462d93 685 } else {
b6b6f466 686 debug(17, 3) ("fwdConnectDone: FD %d: '%s'\n", server_fd, storeUrl(entry));
62e76326 687
688 if (fs->_peer)
689 peerConnectSucceded(fs->_peer);
690
a7ad6e4e 691#if USE_SSL
62e76326 692
693 if ((fs->_peer && fs->_peer->use_ssl) ||
694 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
b6b6f466 695 initiateSSL();
62e76326 696 return;
697 }
698
a7ad6e4e 699#endif
b6b6f466 700 dispatch();
41462d93 701 }
41462d93 702}
703
b6b6f466 704void
705FwdState::connectTimeout(int fd)
41462d93 706{
beed27a2 707 FwdServer *fs = servers;
708
ec250dfd 709 debug(17, 2) ("fwdConnectTimeout: FD %d: '%s'\n", fd, storeUrl(entry));
b6b6f466 710 assert(fd == server_fd);
62e76326 711
beed27a2 712 if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0])
713 hierarchyNote(&request->hier, fs->code, fd_table[fd].ipaddr);
714
528b2c61 715 if (entry->isEmpty()) {
2cc81f1f 716 ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request);
b6b6f466 717 anErr->xerrno = ETIMEDOUT;
718 fail(anErr);
62e76326 719 /*
720 * This marks the peer DOWN ...
721 */
722
b6b6f466 723 if (servers)
724 if (servers->_peer)
725 peerConnectFailed(servers->_peer);
41462d93 726 }
62e76326 727
41462d93 728 comm_close(fd);
729}
730
b6b6f466 731void
732FwdState::connectStart()
41462d93 733{
b6b6f466 734 const char *url = storeUrl(entry);
cb928909 735 int fd = -1;
b6b6f466 736 FwdServer *fs = servers;
db1cd23c 737 const char *host;
738 unsigned short port;
bd0723ad 739 const char *domain = NULL;
777831e0 740 int ctimeout;
b6b6f466 741 int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
fc68f6b1 742#if LINUX_TPROXY
743
744 struct in_tproxy itp;
745#endif
62e76326 746
ddfcbc22 747 struct IN_ADDR outgoing;
d6827718 748 unsigned short tos;
fc68f6b1 749
750 struct IN_ADDR *client_addr = NULL;
db1cd23c 751 assert(fs);
b6b6f466 752 assert(server_fd == -1);
41462d93 753 debug(17, 3) ("fwdConnectStart: %s\n", url);
62e76326 754
29b8d8d6 755 if (fs->_peer) {
37181402 756 host = fs->_peer->host;
62e76326 757 port = fs->_peer->http_port;
758 ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout
759 : Config.Timeout.peer_connect;
bd0723ad 760
7171e7ee 761 if (fs->_peer->options.originserver)
b6b6f466 762 domain = request->host;
db1cd23c 763 } else {
b6b6f466 764 host = request->host;
765 port = request->port;
62e76326 766 ctimeout = Config.Timeout.connect;
db1cd23c 767 }
62e76326 768
fc68f6b1 769#if LINUX_TPROXY
770 if (request->flags.tproxy)
771 client_addr = &request->client_addr;
772
773#endif
774
777831e0 775 if (ftimeout < 0)
776 ftimeout = 5;
777
778 if (ftimeout < ctimeout)
779 ctimeout = ftimeout;
780
fc68f6b1 781 if ((fd = fwdPconnPool->pop(host, port, domain, client_addr)) >= 0) {
b6b6f466 782 if (checkRetriable()) {
cb928909 783 debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
b6b6f466 784 server_fd = fd;
785 n_tries++;
4ed0e075 786
787 if (!fs->_peer)
b6b6f466 788 origin_tries++;
4ed0e075 789
b6b6f466 790 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
4ed0e075 791
b6b6f466 792 dispatch();
4ed0e075 793
cb928909 794 return;
a93e38e5 795 } else {
796 /* Discard the persistent connection to not cause
57912861 797 * an imbalance in number of connections open if there
a93e38e5 798 * is a lot of POST requests
799 */
800 comm_close(fd);
cb928909 801 }
41462d93 802 }
62e76326 803
bc87dc25 804#if URL_CHECKSUM_DEBUG
b6b6f466 805 entry->mem_obj->checkUrlChecksum();
62e76326 806
bc87dc25 807#endif
62e76326 808
b6b6f466 809 outgoing = getOutgoingAddr(request);
62e76326 810
b6b6f466 811 tos = getOutgoingTOS(request);
d6827718 812
813 debug(17, 3) ("fwdConnectStart: got addr %s, tos %d\n",
62e76326 814 inet_ntoa(outgoing), tos);
815
d6827718 816 fd = comm_openex(SOCK_STREAM,
bdb741f4 817 IPPROTO_TCP,
62e76326 818 outgoing,
819 0,
820 COMM_NONBLOCKING,
821 tos,
822 url);
823
41462d93 824 if (fd < 0) {
62e76326 825 debug(50, 4) ("fwdConnectStart: %s\n", xstrerror());
2cc81f1f 826 ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
b6b6f466 827 anErr->xerrno = errno;
828 fail(anErr);
829 self = NULL; // refcounted
62e76326 830 return;
41462d93 831 }
62e76326 832
b6b6f466 833 server_fd = fd;
834 n_tries++;
4ed0e075 835
836 if (!fs->_peer)
b6b6f466 837 origin_tries++;
4ed0e075 838
c7f9eb6d 839 /*
840 * stats.conn_open is used to account for the number of
841 * connections that we have open to the peer, so we can limit
842 * based on the max-conn option. We need to increment here,
843 * even if the connection may fail.
844 */
62e76326 845
a4bd7d1f 846 if (fs->_peer) {
62e76326 847 fs->_peer->stats.conn_open++;
a4bd7d1f 848 comm_add_close_handler(fd, fwdPeerClosed, fs->_peer);
849 }
62e76326 850
b6b6f466 851 comm_add_close_handler(fd, fwdServerClosedWrapper, this);
62e76326 852
b6b6f466 853 commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this);
62e76326 854
fc68f6b1 855 if (fs->_peer) {
beed27a2 856 hierarchyNote(&request->hier, fs->code, fs->_peer->host);
fc68f6b1 857 } else {
858#if LINUX_TPROXY
859
860 if (request->flags.tproxy) {
861 itp.v.addr.faddr.s_addr = src.sin_addr.s_addr;
862 itp.v.addr.fport = 0;
863
864 /* If these syscalls fail then we just fallback to connecting
865 * normally by simply ignoring the errors...
866 */
867 itp.op = TPROXY_ASSIGN;
868
869 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
870 debug(20, 1) ("tproxy ip=%s,0x%x,port=%d ERROR ASSIGN\n",
871 inet_ntoa(itp.v.addr.faddr),
872 itp.v.addr.faddr.s_addr,
873 itp.v.addr.fport);
874 request->flags.tproxy = 0;
875 } else {
876 itp.op = TPROXY_FLAGS;
877 itp.v.flags = ITP_CONNECT;
878
879 if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp, sizeof(itp)) == -1) {
880 debug(20, 1) ("tproxy ip=%x,port=%d ERROR CONNECT\n",
881 itp.v.addr.faddr.s_addr,
882 itp.v.addr.fport);
883 request->flags.tproxy = 0;
884 }
885 }
886 }
887
888#endif
beed27a2 889 hierarchyNote(&request->hier, fs->code, request->host);
fc68f6b1 890 }
beed27a2 891
b6b6f466 892 commConnectStart(fd, host, port, fwdConnectDoneWrapper, this);
41462d93 893}
894
b6b6f466 895void
896FwdState::startComplete(FwdServer * theServers)
41462d93 897{
b6b6f466 898 debug(17, 3) ("fwdStartComplete: %s\n", storeUrl(entry));
62e76326 899
b6b6f466 900 if (theServers != NULL) {
901 servers = theServers;
902 connectStart();
41462d93 903 } else {
b6b6f466 904 startFail();
41462d93 905 }
41462d93 906}
907
b6b6f466 908void
909FwdState::startFail()
41462d93 910{
b6b6f466 911 debug(17, 3) ("fwdStartFail: %s\n", storeUrl(entry));
2cc81f1f 912 ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
b6b6f466 913 anErr->xerrno = errno;
914 fail(anErr);
915 self = NULL; // refcounted
41462d93 916}
910169e5 917
b6b6f466 918void
919FwdState::dispatch()
41462d93 920{
c7f9eb6d 921 peer *p = NULL;
b746bd8a 922 debug(17, 3) ("fwdDispatch: FD %d: Fetching '%s %s'\n",
b6b6f466 923 client_fd,
62e76326 924 RequestMethodStr[request->method],
925 storeUrl(entry));
e0ebe27c 926 /*
927 * Assert that server_fd is set. This is to guarantee that fwdState
928 * is attached to something and will be deallocated when server_fd
929 * is closed.
930 */
a7ad6e4e 931 assert(server_fd > -1);
62e76326 932
b6b6f466 933 fd_note(server_fd, storeUrl(entry));
62e76326 934
781ce8ff 935 fd_table[server_fd].noteUse(fwdPconnPool);
62e76326 936
a7ad6e4e 937 /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */
938 assert(entry->ping_status != PING_WAITING);
62e76326 939
a7ad6e4e 940 assert(entry->lock_count);
62e76326 941
a7ad6e4e 942 EBIT_SET(entry->flags, ENTRY_DISPATCHED);
62e76326 943
a7ad6e4e 944 netdbPingSite(request->host);
62e76326 945
b6b6f466 946 if (servers && (p = servers->_peer)) {
62e76326 947 p->stats.fetches++;
b6b6f466 948 request->peer_login = p->login;
949 request->peer_domain = p->domain;
950 httpStart(this);
41462d93 951 } else {
b6b6f466 952 request->peer_login = NULL;
953 request->peer_domain = NULL;
62e76326 954
955 switch (request->protocol) {
a7ad6e4e 956#if USE_SSL
62e76326 957
958 case PROTO_HTTPS:
b6b6f466 959 httpStart(this);
62e76326 960 break;
a7ad6e4e 961#endif
62e76326 962
963 case PROTO_HTTP:
b6b6f466 964 httpStart(this);
62e76326 965 break;
966
967 case PROTO_GOPHER:
b6b6f466 968 gopherStart(this);
62e76326 969 break;
970
971 case PROTO_FTP:
b6b6f466 972 ftpStart(this);
62e76326 973 break;
974
62e76326 975 case PROTO_CACHEOBJ:
976
977 case PROTO_INTERNAL:
978
979 case PROTO_URN:
980 fatal_dump("Should never get here");
981 break;
982
983 case PROTO_WHOIS:
b6b6f466 984 whoisStart(this);
62e76326 985 break;
986
db80e881 987 case PROTO_WAIS: /* Not implemented */
fc68f6b1 988
62e76326 989 default:
990 debug(17, 1) ("fwdDispatch: Cannot retrieve '%s'\n",
991 storeUrl(entry));
2cc81f1f 992 ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request);
b6b6f466 993 fail(anErr);
62e76326 994 /*
995 * Force a persistent connection to be closed because
996 * some Netscape browsers have a bug that sends CONNECT
997 * requests as GET's over persistent connections.
998 */
999 request->flags.proxy_keepalive = 0;
1000 /*
1001 * Set the dont_retry flag becuase this is not a
1002 * transient (network) error; its a bug.
1003 */
b6b6f466 1004 flags.dont_retry = 1;
1005 comm_close(server_fd);
62e76326 1006 break;
1007 }
41462d93 1008 }
1009}
1010
b6b6f466 1011int
1012FwdState::reforward()
db1cd23c 1013{
b6b6f466 1014 StoreEntry *e = entry;
1015 FwdServer *fs = servers;
db1cd23c 1016 http_status s;
1017 assert(e->store_status == STORE_PENDING);
1018 assert(e->mem_obj);
bc87dc25 1019#if URL_CHECKSUM_DEBUG
62e76326 1020
528b2c61 1021 e->mem_obj->checkUrlChecksum();
bc87dc25 1022#endif
62e76326 1023
b746bd8a 1024 debug(17, 3) ("fwdReforward: %s?\n", storeUrl(e));
62e76326 1025
d6eb18d6 1026 if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
62e76326 1027 debug(17, 3) ("fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set\n");
1028 return 0;
d6eb18d6 1029 }
62e76326 1030
b6b6f466 1031 if (n_tries > 9)
62e76326 1032 return 0;
1033
b6b6f466 1034 if (origin_tries > 1)
4ed0e075 1035 return 0;
1036
58217e94 1037 if (request->bodyNibbled())
62e76326 1038 return 0;
1039
db1cd23c 1040 assert(fs);
62e76326 1041
b6b6f466 1042 servers = fs->next;
62e76326 1043
db1cd23c 1044 fwdServerFree(fs);
62e76326 1045
b6b6f466 1046 if (servers == NULL) {
62e76326 1047 debug(17, 3) ("fwdReforward: No forward-servers left\n");
1048 return 0;
db1cd23c 1049 }
62e76326 1050
528b2c61 1051 s = e->getReply()->sline.status;
db1cd23c 1052 debug(17, 3) ("fwdReforward: status %d\n", (int) s);
b6b6f466 1053 return reforwardableStatus(s);
db1cd23c 1054}
1055
b6b6f466 1056static void
1057fwdStats(StoreEntry * s)
64d8034e 1058{
b6b6f466 1059 int i;
1060 int j;
1061 storeAppendPrintf(s, "Status");
62e76326 1062
b6b6f466 1063 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1064 storeAppendPrintf(s, "\ttry#%d", j + 1);
64d8034e 1065 }
64d8034e 1066
b6b6f466 1067 storeAppendPrintf(s, "\n");
0185bd6f 1068
b6b6f466 1069 for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
1070 if (FwdReplyCodes[0][i] == 0)
1071 continue;
0185bd6f 1072
b6b6f466 1073 storeAppendPrintf(s, "%3d", i);
0185bd6f 1074
b6b6f466 1075 for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
1076 storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]);
62e76326 1077 }
62e76326 1078
b6b6f466 1079 storeAppendPrintf(s, "\n");
e0ebe27c 1080 }
7197b20d 1081}
1082
a4bd7d1f 1083
b6b6f466 1084/**** STATIC MEMBER FUNCTIONS *************************************************/
db1cd23c 1085
b6b6f466 1086bool
1087FwdState::reforwardableStatus(http_status s)
db1cd23c 1088{
b6b6f466 1089 switch (s) {
62e76326 1090
b6b6f466 1091 case HTTP_BAD_GATEWAY:
62e76326 1092
b6b6f466 1093 case HTTP_GATEWAY_TIMEOUT:
1094 return true;
62e76326 1095
b6b6f466 1096 case HTTP_FORBIDDEN:
62e76326 1097
b6b6f466 1098 case HTTP_INTERNAL_SERVER_ERROR:
62e76326 1099
b6b6f466 1100 case HTTP_NOT_IMPLEMENTED:
62e76326 1101
b6b6f466 1102 case HTTP_SERVICE_UNAVAILABLE:
1103 return Config.retry.onerror;
62e76326 1104
b6b6f466 1105 default:
1106 return false;
db1cd23c 1107 }
b6b6f466 1108
1109 /* NOTREACHED */
db1cd23c 1110}
8ddcc35d 1111
781ce8ff 1112void
fc68f6b1 1113
1114FwdState::pconnPush(int fd, const char *host, int port, const char *domain, struct IN_ADDR *client_addr)
781ce8ff 1115{
fc68f6b1 1116 fwdPconnPool->push(fd, host, port, domain, client_addr);
781ce8ff 1117}
1118
8ddcc35d 1119void
b6b6f466 1120FwdState::initModule()
8ddcc35d 1121{
b6b6f466 1122 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
1123
225644d7 1124#if WIP_FWD_LOG
62e76326 1125
225644d7 1126 if (logfile)
62e76326 1127 (void) 0;
225644d7 1128 else if (NULL == Config.Log.forward)
62e76326 1129 (void) 0;
225644d7 1130 else
62e76326 1131 logfile = logfileOpen(Config.Log.forward, 0, 1);
1132
225644d7 1133#endif
8ddcc35d 1134}
1135
62ee09ca 1136void
1137FwdState::RegisterWithCacheManager(CacheManager & manager)
1138{
1139 manager.registerAction("forward",
1140 "Request Forwarding Statistics",
1141 fwdStats, 0, 1);
1142}
1143
b6b6f466 1144void
1145FwdState::logReplyStatus(int tries, http_status status)
8ddcc35d 1146{
1147 if (status > HTTP_INVALID_HEADER)
62e76326 1148 return;
1149
8ddcc35d 1150 assert(tries);
62e76326 1151
8ddcc35d 1152 tries--;
62e76326 1153
8ddcc35d 1154 if (tries > MAX_FWD_STATS_IDX)
62e76326 1155 tries = MAX_FWD_STATS_IDX;
1156
8ddcc35d 1157 FwdReplyCodes[tries][status]++;
1158}
1159
b6b6f466 1160void
1161FwdState::serversFree(FwdServer ** FSVR)
8ddcc35d 1162{
b6b6f466 1163 FwdServer *fs;
62e76326 1164
b6b6f466 1165 while ((fs = *FSVR)) {
1166 *FSVR = fs->next;
1167 fwdServerFree(fs);
9977e14b 1168 }
b6b6f466 1169}
62e76326 1170
b6b6f466 1171/**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/
62e76326 1172
b6b6f466 1173static void
1174fwdServerFree(FwdServer * fs)
1175{
1176 cbdataReferenceDone(fs->_peer);
1177 memFree(fs, MEM_FWD_SERVER);
1178}
62e76326 1179
b6b6f466 1180static struct IN_ADDR
1181 aclMapAddr(acl_address * head, ACLChecklist * ch)
1182{
1183 acl_address *l;
62e76326 1184
b6b6f466 1185 struct IN_ADDR addr;
62e76326 1186
b6b6f466 1187 for (l = head; l; l = l->next)
1188 {
1189 if (ch->matchAclListFast(l->aclList))
1190 return l->addr;
9977e14b 1191 }
b6b6f466 1192
1193 addr.s_addr = INADDR_ANY;
1194 return addr;
8ddcc35d 1195}
b6a2f15e 1196
b6b6f466 1197static int
1198aclMapTOS(acl_tos * head, ACLChecklist * ch)
b6a2f15e 1199{
b6b6f466 1200 acl_tos *l;
62e76326 1201
b6b6f466 1202 for (l = head; l; l = l->next) {
1203 if (ch->matchAclListFast(l->aclList))
1204 return l->tos;
1205 }
5894ad28 1206
b6b6f466 1207 return 0;
1208}
5894ad28 1209
b6b6f466 1210struct IN_ADDR
1211 getOutgoingAddr(HttpRequest * request)
1212{
1213 ACLChecklist ch;
62e76326 1214
b6b6f466 1215 if (request)
1216 {
1217 ch.src_addr = request->client_addr;
1218 ch.my_addr = request->my_addr;
1219 ch.my_port = request->my_port;
6dd9f4bd 1220 ch.request = HTTPMSGLOCK(request);
b6b6f466 1221 }
62e76326 1222
b6b6f466 1223 return aclMapAddr(Config.accessList.outgoing_address, &ch);
1224}
62e76326 1225
b6b6f466 1226unsigned long
1227getOutgoingTOS(HttpRequest * request)
1228{
1229 ACLChecklist ch;
62e76326 1230
b6b6f466 1231 if (request) {
1232 ch.src_addr = request->client_addr;
1233 ch.my_addr = request->my_addr;
1234 ch.my_port = request->my_port;
6dd9f4bd 1235 ch.request = HTTPMSGLOCK(request);
b6a2f15e 1236 }
62e76326 1237
b6b6f466 1238 return aclMapTOS(Config.accessList.outgoing_tos, &ch);
b6a2f15e 1239}
225644d7 1240
b6b6f466 1241
1242/**** WIP_FWD_LOG *************************************************************/
1243
225644d7 1244#if WIP_FWD_LOG
1245void
1246fwdUninit(void)
1247{
dab0cec3 1248 if (NULL == logfile)
62e76326 1249 return;
1250
225644d7 1251 logfileClose(logfile);
62e76326 1252
225644d7 1253 logfile = NULL;
1254}
1255
1256void
1257fwdLogRotate(void)
1258{
1259 if (logfile)
62e76326 1260 logfileRotate(logfile);
225644d7 1261}
1262
1263static void
b6b6f466 1264FwdState::log()
225644d7 1265{
1266 if (NULL == logfile)
62e76326 1267 return;
1268
225644d7 1269 logfilePrintf(logfile, "%9d.%03d %03d %s %s\n",
62e76326 1270 (int) current_time.tv_sec,
1271 (int) current_time.tv_usec / 1000,
b6b6f466 1272 last_status,
1273 RequestMethodStr[request->method],
1274 request->canonical);
225644d7 1275}
1276
1277void
b6b6f466 1278FwdState::status(http_status s)
225644d7 1279{
b6b6f466 1280 last_status = s;
225644d7 1281}
1282
1283#endif