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