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