# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: chtsanti@users.sourceforge.net-20080920172016-\ # 2khd7117cp125tnf # target_branch: file:///home/projects/squid/squid3-bzr/squid-\ # repo/trunk/ # testament_sha1: c79755cca30b877e873083896f61ad63a641ae88 # timestamp: 2008-09-20 20:27:37 +0300 # base_revision_id: squid3@treenet.co.nz-20080920100327-\ # h7e6i88s7t5lieme # # Begin patch === modified file 'src/HttpHeader.cc' --- src/HttpHeader.cc 2008-07-13 08:37:43 +0000 +++ src/HttpHeader.cc 2008-07-17 18:33:07 +0000 @@ -112,6 +112,7 @@ {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr}, {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr}, {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr}, + {"Proxy-support", HDR_PROXY_SUPPORT, ftStr}, {"Public", HDR_PUBLIC, ftStr}, {"Range", HDR_RANGE, ftPRange}, {"Referer", HDR_REFERER, ftStr}, @@ -172,6 +173,7 @@ HDR_IF_MATCH, HDR_IF_NONE_MATCH, HDR_LINK, HDR_PRAGMA, HDR_PROXY_CONNECTION, + HDR_PROXY_SUPPORT, HDR_TRANSFER_ENCODING, HDR_UPGRADE, HDR_VARY, === modified file 'src/HttpHeader.h' --- src/HttpHeader.h 2008-07-13 08:37:43 +0000 +++ src/HttpHeader.h 2008-07-17 18:33:07 +0000 @@ -83,6 +83,7 @@ HDR_PROXY_AUTHENTICATION_INFO, HDR_PROXY_AUTHORIZATION, HDR_PROXY_CONNECTION, + HDR_PROXY_SUPPORT, HDR_PUBLIC, HDR_RANGE, HDR_REQUEST_RANGE, /**< some clients use this, sigh */ === modified file 'src/HttpReply.cc' --- src/HttpReply.cc 2008-07-02 03:49:07 +0000 +++ src/HttpReply.cc 2008-09-02 08:03:35 +0000 @@ -563,5 +563,6 @@ rep->pstate = pstate; rep->protocol = protocol; rep->sline = sline; + rep->keep_alive = keep_alive; return rep; } === modified file 'src/HttpRequest.cc' --- src/HttpRequest.cc 2008-06-07 05:20:05 +0000 +++ src/HttpRequest.cc 2008-06-23 18:02:37 +0000 @@ -74,6 +74,7 @@ login[0] = '\0'; host[0] = '\0'; auth_user_request = NULL; + pinned_connection = NULL; port = 0; canonical = NULL; memset(&flags, '\0', sizeof(flags)); @@ -127,6 +128,10 @@ range = NULL; } + if(pinned_connection) + cbdataReferenceDone(pinned_connection); + pinned_connection = NULL; + tag.clean(); extacl_user.clean(); === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2008-06-07 05:20:05 +0000 +++ src/HttpRequest.h 2008-06-23 18:02:37 +0000 @@ -148,6 +148,8 @@ String extacl_log; /* String to be used for access.log purposes */ + ConnStateData *pinned_connection; + #if FOLLOW_X_FORWARDED_FOR String x_forwarded_for_iterator; /* XXX a list of IP addresses */ #endif /* FOLLOW_X_FORWARDED_FOR */ === modified file 'src/ICAP/ICAPModXact.cc' --- src/ICAP/ICAPModXact.cc 2008-09-19 17:26:31 +0000 +++ src/ICAP/ICAPModXact.cc 2008-09-20 15:15:50 +0000 @@ -786,9 +786,12 @@ HttpRequest *newR = new HttpRequest; inheritVirginProperties(*newR, *oldR); newHead = newR; - } else - if (dynamic_cast(oldHead)) - newHead = new HttpReply; + } + else if (const HttpReply *oldRep = dynamic_cast(oldHead)) { + HttpReply *newRep = new HttpReply; + inheritVirginReplyProperties(*newRep, *oldRep); + newHead = newRep; + } Must(newHead); adapted.setHeader(newHead); @@ -854,6 +857,10 @@ // http://www.squid-cache.org/mail-archive/squid-dev/200703/0040.html inheritVirginProperties(*newHead, *oldR); } + else if (HttpReply *newRep = dynamic_cast(adapted.header)){ + HttpReply *oldRep = dynamic_cast(virgin.header); + inheritVirginReplyProperties(*newRep, *oldRep); + } } decideOnParsingBody(); @@ -895,6 +902,15 @@ newR.auth_user_request = oldR.auth_user_request; AUTHUSERREQUESTLOCK(newR.auth_user_request, "newR in ICAPModXact"); } + + if(oldR.pinned_connection) { + newR.pinned_connection = cbdataReference(oldR.pinned_connection); + } + +} + +void ICAPModXact::inheritVirginReplyProperties(HttpReply &newR, const HttpReply &oldR) { + newR.keep_alive = oldR.keep_alive; } void ICAPModXact::decideOnParsingBody() { === modified file 'src/ICAP/ICAPModXact.h' --- src/ICAP/ICAPModXact.h 2008-03-31 01:06:13 +0000 +++ src/ICAP/ICAPModXact.h 2008-09-02 08:12:29 +0000 @@ -209,6 +209,7 @@ void parseHttpHead(); bool parseHead(HttpMsg *head); void inheritVirginProperties(HttpRequest &newR, const HttpRequest &oldR); + void inheritVirginReplyProperties(HttpReply &newR, const HttpReply &oldR); void decideOnParsingBody(); void parseBody(); === modified file 'src/ProtoPort.h' --- src/ProtoPort.h 2008-04-09 10:17:28 +0000 +++ src/ProtoPort.h 2008-05-18 20:15:06 +0000 @@ -26,6 +26,7 @@ unsigned int sslBump:1; /**< intercepts CONNECT requests */ int vport; /* virtual port support, -1 for dynamic, >0 static*/ + int no_connection_auth; /* Don't support connection oriented auth */ int disable_pmtu_discovery; struct { === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2008-08-10 05:05:45 +0000 +++ src/cache_cf.cc 2008-09-02 08:32:52 +0000 @@ -1692,6 +1692,7 @@ self_destruct(); p->icp.port = GetUdpService(); + p->connection_auth = 2; /* auto */ while ((token = strtok(NULL, w_space))) { if (!strcasecmp(token, "proxy-only")) { @@ -1829,6 +1830,14 @@ p->front_end_https = 1; } else if (strcmp(token, "front-end-https=auto") == 0) { p->front_end_https = 2; + }else if (strcmp(token, "connection-auth=off") == 0) { + p->connection_auth = 0; + } else if (strcmp(token, "connection-auth") == 0) { + p->connection_auth = 1; + } else if (strcmp(token, "connection-auth=on") == 0) { + p->connection_auth = 1; + } else if (strcmp(token, "connection-auth=auto") == 0) { + p->connection_auth = 2; } else { debugs(3, 0, "parse_peer: token='" << token << "'"); self_destruct(); @@ -2936,6 +2945,8 @@ s->accel = 1; } else if (strcmp(token, "accel") == 0) { s->accel = 1; + } else if (strcmp(token, "no-connection-auth") == 0) { + s->no_connection_auth = 1; } else if (strncmp(token, "disable-pmtu-discovery=", 23) == 0) { if (!strcasecmp(token + 23, "off")) s->disable_pmtu_discovery = DISABLE_PMTU_OFF; @@ -3108,6 +3119,9 @@ if (s->vport) storeAppendPrintf(e, " vport"); + if (s->no_connection_auth) + storeAppendPrintf(e, " no-connection-auth"); + if (s->disable_pmtu_discovery != DISABLE_PMTU_OFF) { const char *pmtu; === modified file 'src/cf.data.pre' --- src/cf.data.pre 2008-09-20 09:43:40 +0000 +++ src/cf.data.pre 2008-09-20 15:15:50 +0000 @@ -1023,6 +1023,10 @@ protocol= Protocol to reconstruct accelerated requests with. Defaults to http. + no-connection-auth + Prevent forwarding of Microsoft connection oriented + authentication (NTLM, Negotiate and Kerberos) + disable-pmtu-discovery= Control Path-MTU discovery usage: off lets OS decide on what to do (default). @@ -1588,6 +1592,7 @@ sslcipher=... ssloptions=... front-end-https[=on|auto] + connection-auth[=on|off|auto] use 'proxy-only' to specify objects fetched from this cache should not be saved locally. @@ -1796,6 +1801,12 @@ on this header. If set to auto the header will only be added if the request is forwarded as a https:// URL. + + use connection-auth=off to tell Squid that this peer does + not support Microsoft connection oriented authentication, + and any such challenges received from there should be + ignored. Default is auto to automatically determine the + status of the peer. DOC_END NAME: cache_peer_domain cache_host_domain === modified file 'src/client_side.cc' --- src/client_side.cc 2008-09-11 04:54:34 +0000 +++ src/client_side.cc 2008-09-14 13:22:13 +0000 @@ -644,6 +644,9 @@ if (!flags.swanSang) debugs(33, 1, "BUG: ConnStateData was not destroyed properly; FD " << fd); + if (pinning.fd >= 0) + comm_close(pinning.fd); + AUTHUSERREQUESTUNLOCK(auth_user_request, "~conn"); cbdataReferenceDone(port); @@ -1368,6 +1371,12 @@ debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: FD " << conn->fd); connIsFinished(); + if (conn->pinning.pinned && conn->pinning.fd == -1) { + debug(33, 2) ("clientKeepaliveNextRequest: FD %d Connection was pinned but server side gone. Terminating client connection\n", conn->fd); + comm_close(conn->fd); + return; + } + /** \par * Attempt to parse a request from the request buffer. * If we've been fed a pipelined request it may already @@ -3336,6 +3345,7 @@ ConnStateData::ConnStateData() :AsyncJob("ConnStateData"), transparent_ (false), reading_ (false), closing_ (false) { + pinning.fd = -1; } bool @@ -3415,3 +3425,108 @@ if (allocatedSize) memFreeBuf(allocatedSize, buf); } + +/* This is a handler normally called by comm_close() */ +static void +clientPinnedConnectionClosed(int fd, void *data) +{ + ConnStateData *conn = (ConnStateData *)data; + conn->pinning.fd = -1; + if (conn->pinning.peer) { + cbdataReferenceDone(conn->pinning.peer); + conn->pinning.peer = NULL; + } + safe_free(conn->pinning.host); + /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host + * connection has gone away */ +} + +/** + * Correlate the current ConnStateData object with the pinning_fd socket descriptor. + */ +void ConnStateData::pinConnection(int pinning_fd, HttpRequest *request, struct peer *peer, int auth){ + fde *f; + char desc[FD_DESC_SZ]; + + if (pinning.fd == pinning_fd) + return; + else if (pinning.fd != -1) + comm_close(pinning.fd); + + if(pinning.host) + safe_free(pinning.host); + + pinning.fd = pinning_fd; + pinning.host = xstrdup(request->GetHost()); + pinning.port = request->port; + pinning.pinned = 1; + if (pinning.peer) + cbdataReferenceDone(pinning.peer); + if (peer) + pinning.peer = cbdataReference(peer); + pinning.auth = auth; + f = &fd_table[fd]; + snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s:%d (%d)", + (auth || !peer) ? request->GetHost() : peer->name, f->ipaddr, (int) f->remote_port, fd); + fd_note(pinning_fd, desc); + comm_add_close_handler(pinning_fd, clientPinnedConnectionClosed, this); +} + +/** + * If the current ConnStateData has pinned connection returns the socket descriptor of + * pinned connection and the peer object if exists +*/ +int ConnStateData::getPinnedInfo(const HttpRequest * request, struct peer * &peer){ + + if (pinning.fd < 0) + return -1; + + if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) { + comm_close(pinning.fd); + return -1; + } + if (request && pinning.port != request->port){ + comm_close(pinning.fd); + return -1; + } + if (pinning.peer && !cbdataReferenceValid(pinning.peer)){ + comm_close(pinning.fd); + return -1; + } + + /* + Maybe the peer should be a cbdataReference of pinning.peer (and the caller + use cbdataReferenceDone). + Now the peer is a single pointer... + */ + peer = pinning.peer; + return pinning.fd; +} + + +/** + * Returns the pinned socked descriptor if exists and decorrelate the current ConnStateData + * object with this socket descriptor. + */ +int ConnStateData::getPinnedConnection(const HttpRequest * request, const struct peer * peer, int &auth){ + int pinning_fd = pinning.fd; + if (pinning_fd < 0) + return -1; + + if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) { + comm_close(pinning_fd); + return -1; + } + auth = pinning.auth; + if (peer != pinning.peer){ + comm_close(pinning_fd); + return -1; + } + cbdataReferenceDone(pinning.peer); + pinning.peer = NULL; + pinning.fd = -1; + comm_remove_close_handler(pinning_fd, clientPinnedConnectionClosed, this); + return pinning_fd; +} + + === modified file 'src/client_side.h' --- src/client_side.h 2008-09-11 04:54:34 +0000 +++ src/client_side.h 2008-09-14 13:22:13 +0000 @@ -189,6 +189,15 @@ bool readMoreRequests; bool swanSang; // XXX: temporary flag to check proper cleanup } flags; + struct { + int fd; /* pinned server side connection */ + char *host; /* host name of pinned connection */ + int port; /* port of pinned connection */ + int pinned; /* this connection was pinned */ + int auth; /* pinned for www authentication */ + struct peer *peer; /* peer the connection goes via */ + } pinning; + http_port_list *port; bool transparent() const; @@ -205,6 +214,9 @@ void handleReadData(char *buf, size_t size); void handleRequestBodyData(); + void pinConnection(int fd, HttpRequest *request, struct peer *peer, int auth); + int getPinnedInfo(const HttpRequest *request, struct peer * &peer); + int getPinnedConnection(const HttpRequest *request, const struct peer *peer, int &auth); // comm callbacks void clientReadRequest(const CommIoCbParams &io); === modified file 'src/client_side_reply.cc' --- src/client_side_reply.cc 2008-09-20 05:00:47 +0000 +++ src/client_side_reply.cc 2008-09-20 17:20:16 +0000 @@ -1249,24 +1249,60 @@ /* Filter unproxyable authentication types */ if (http->logType != LOG_TCP_DENIED && - (hdr->has(HDR_WWW_AUTHENTICATE) || hdr->has(HDR_PROXY_AUTHENTICATE))) { + hdr->has(HDR_WWW_AUTHENTICATE)) { HttpHeaderPos pos = HttpHeaderInitPos; HttpHeaderEntry *e; - int headers_deleted = 0; + int connection_auth_blocked = 0; while ((e = hdr->getEntry(&pos))) { - if (e->id == HDR_WWW_AUTHENTICATE || e->id == HDR_PROXY_AUTHENTICATE) { + if (e->id == HDR_WWW_AUTHENTICATE) { const char *value = e->value.buf(); if ((strncasecmp(value, "NTLM", 4) == 0 && (value[4] == '\0' || value[4] == ' ')) || (strncasecmp(value, "Negotiate", 9) == 0 && - (value[9] == '\0' || value[9] == ' '))) - hdr->delAt(pos, headers_deleted); + (value[9] == '\0' || value[9] == ' ')) + || + (strncasecmp(value, "Kerberos", 8) == 0 && + (value[8] == '\0' || value[8] == ' '))) + { + if (request->flags.no_connection_auth) { + hdr->delAt(pos, connection_auth_blocked); + continue; + } + request->flags.must_keepalive = 1; + if (!request->flags.accelerated && !request->flags.intercepted) { + /* The use of the Proxy-Support header is documented in the RFC4559. + I was not able to find documentation about the use of the header + "Connection: Proxy-Support" and how other http proxies use this header. + The only I found is a Henrik's comment: + + "[...] However, this alone is not sufficient. You must also add a + "Connection: Proxy-support" header to mark the extension header as a + hop-by-hop header to protect from other proxies inbetween this proxy + and the client. + + Also, you should also keep in mind that transparently intercepting + proxies are quite widely deployed on the Internet today, so to really + have a reasonable chance of working the server initiating the + "Negotiate" scheme needs to add a extension hop-by-hop header + signalling that this connection needs to be kept end-to-end and + negotiate (SPNEGO) only accepted by the client if this extension + header is seen in the reply. [...]" + + (http://osdir.com/ml/ietf.krb-wg/2002-11/msg00024.html) + + */ + httpHeaderPutStrf(hdr, HDR_PROXY_SUPPORT, "Session-Based-Authentication"); + httpHeaderPutStrf(hdr, HDR_CONNECTION, "Proxy-support"); + } + break; + } } } - if (headers_deleted) + + if (connection_auth_blocked) hdr->refreshMask(); } @@ -1324,6 +1360,12 @@ debugs(88, 3, "clientBuildReplyHeader: Shutting down, don't keep-alive."); request->flags.proxy_keepalive = 0; } + + if (request->flags.connection_auth && !reply->keep_alive) { + debug(33, 2) ("clientBuildReplyHeader: Connection oriented auth but server side non-persistent\n"); + request->flags.proxy_keepalive = 0; + } + /* Append VIA */ if (Config.onoff.via) { === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2008-09-11 11:14:39 +0000 +++ src/client_side_request.cc 2008-09-14 13:22:13 +0000 @@ -828,6 +828,55 @@ if (req_hdr->has(HDR_AUTHORIZATION)) request->flags.auth = 1; + + if (!request->flags.no_connection_auth) { + ConnStateData *http_conn = http->getConn(); + if (http_conn->pinning.fd != -1) { + if (http_conn->pinning.auth) { + request->flags.connection_auth = 1; + request->flags.auth = 1; + } else { + request->flags.connection_proxy_auth = 1; + } + request->pinned_connection = cbdataReference(http_conn); + } + } + + /* check if connection auth is used, and flag as candidate for pinning + * in such case. + * Note: we may need to set flags.connection_auth even if the connection + * is already pinned if it was pinned earlier due to proxy auth + */ + if (!request->flags.connection_auth) { + if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) { + HttpHeaderPos pos = HttpHeaderInitPos; + HttpHeaderEntry *e; + int may_pin = 0; + while ((e = req_hdr->getEntry(&pos))) { + if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) { + const char *value = e->value.buf(); + if (strncasecmp(value, "NTLM ", 5) == 0 + || + strncasecmp(value, "Negotiate ", 10) == 0 + || + strncasecmp(value, "Kerberos ", 9) == 0) { + if (e->id == HDR_AUTHORIZATION) { + request->flags.connection_auth = 1; + may_pin = 1; + } else { + request->flags.connection_proxy_auth = 1; + may_pin = 1; + } + } + } + } + if (may_pin && !request->pinned_connection) { + request->pinned_connection = cbdataReference(http->getConn()); + } + } + } + + if (request->login[0] != '\0') request->flags.auth = 1; === modified file 'src/enums.h' --- src/enums.h 2008-07-11 20:43:43 +0000 +++ src/enums.h 2008-07-17 18:33:07 +0000 @@ -177,7 +177,8 @@ ANY_OLD_PARENT, USERHASH_PARENT, SOURCEHASH_PARENT, - HIER_MAX + HIER_MAX, + PINNED } hier_code; /// \ingroup ServerProtocolICPAPI === modified file 'src/forward.cc' --- src/forward.cc 2008-09-18 09:46:56 +0000 +++ src/forward.cc 2008-09-20 15:15:50 +0000 @@ -804,6 +804,35 @@ if (ftimeout < ctimeout) ctimeout = ftimeout; + + request->flags.pinned = 0; + if (fs->code == PINNED) { + int auth; + assert(request->pinned_connection); + fd = request->pinned_connection->getPinnedConnection(request, fs->_peer, auth); + if (fd >= 0) { +#if 0 + if (!fs->_peer) + fs->code = HIER_DIRECT; +#endif + server_fd = fd; + n_tries++; + request->flags.pinned = 1; + if (auth) + request->flags.auth = 1; + comm_add_close_handler(fd, fwdServerClosedWrapper, this); + connectDone(fd, COMM_OK, 0); + return; + } + /* Failure. Fall back on next path */ + cbdataReferenceDone(request->pinned_connection); + request->pinned_connection = NULL; + servers = fs->next; + fwdServerFree(fs); + connectStart(); + return; + } + fd = fwdPconnPool->pop(host, port, domain, client_addr, checkRetriable()); if (fd >= 0) { debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd); === modified file 'src/http.cc' --- src/http.cc 2008-09-13 13:43:00 +0000 +++ src/http.cc 2008-09-14 13:22:13 +0000 @@ -377,7 +377,7 @@ } } - if (request->flags.auth) { + if (request->flags.auth || request->flags.auth_sent) { /* * Responses to requests with authorization may be cached * only if a Cache-Control: public reply header is present. @@ -709,6 +709,9 @@ httpChunkDecoder = new ChunkedCodingParser; } + if(!peerSupportsConnectionPinning()) + orig_request->flags.no_connection_auth = 1; + HttpReply *vrep = setVirginReply(newrep); flags.headers_parsed = 1; @@ -726,6 +729,67 @@ } +/** + * returns true if the peer can support connection pinning +*/ +bool HttpStateData::peerSupportsConnectionPinning() +{ + const HttpReply *rep = entry->mem_obj->getReply(); + const HttpHeader *hdr = &rep->header; + bool rc; + String header; + + if (!_peer) + return true; + + /*If this peer does not support connection pinning (authenticated + connections) return false + */ + if (!_peer->connection_auth) + return false; + + /*The peer supports connection pinning and the http reply status + is not unauthorized, so the related connection can be pinned + */ + if (rep->sline.status != HTTP_UNAUTHORIZED) + return true; + + /*The server respond with HTTP_UNAUTHORIZED and the peer configured + with "connection-auth=on" we know that the peer supports pinned + connections + */ + if (_peer->connection_auth == 1) + return true; + + /*At this point peer has configured with "connection-auth=auto" + parameter so we need some extra checks to decide if we are going + to allow pinned connections or not + */ + + /*if the peer configured with originserver just allow connection + pinning (squid 2.6 behaviour) + */ + if (_peer->options.originserver) + return true; + + /*if the connections it is already pinned it is OK*/ + if (request->flags.pinned) + return true; + + /*Allow pinned connections only if the Proxy-support header exists in + reply and has in its list the "Session-Based-Authentication" + which means that the peer supports connection pinning. + */ + if (!hdr->has(HDR_PROXY_SUPPORT)) + return false; + + header = hdr->getStrOrList(HDR_PROXY_SUPPORT); + /* XXX This ought to be done in a case-insensitive manner */ + rc = (strstr(header.buf(), "Session-Based-Authentication") != NULL); + + return rc; +} + // Called when we parsed (and possibly adapted) the headers but // had not starting storing (a.k.a., sending) the body yet. void @@ -1135,6 +1199,7 @@ { AsyncCall::Pointer call; IPAddress client_addr; + bool ispinned = false; if (!flags.headers_parsed) { flags.do_next_read = 1; @@ -1200,7 +1265,16 @@ if (orig_request->flags.spoof_client_ip) client_addr = orig_request->client_addr; - if (_peer) { + + if (request->flags.pinned) { + ispinned = true; + } else if (request->flags.connection_auth && request->flags.auth_sent) { + ispinned = true; + } + + if (orig_request->pinned_connection && ispinned) { + orig_request->pinned_connection->pinConnection(fd, orig_request, _peer, request->flags.connection_auth); + } else if (_peer) { if (_peer->options.originserver) fwd->pconnPush(fd, _peer->name, orig_request->port, orig_request->GetHost(), client_addr); else @@ -1709,7 +1783,7 @@ */ if (NULL == orig_request->range || !orig_request->flags.cachable - || orig_request->range->offsetLimitExceeded()) + || orig_request->range->offsetLimitExceeded() || orig_request->flags.connection_auth) result = false; debugs(11, 8, "decideIfWeDoRanges: range specs: " << @@ -1739,6 +1813,12 @@ HttpHeader hdr(hoRequest); Packer p; httpBuildRequestHeader(request, orig_request, entry, &hdr, flags); + + if (request->flags.pinned && request->flags.connection_auth) + request->flags.auth_sent = 1; + else if (hdr.has(HDR_AUTHORIZATION)) + request->flags.auth_sent = 1; + packerToMemInit(&p, mb); hdr.packInto(&p); hdr.clean(); @@ -1792,7 +1872,9 @@ /* * Is keep-alive okay for all request methods? */ - if (!Config.onoff.server_pconns) + if (orig_request->flags.must_keepalive) + flags.keepalive = 1; + else if (!Config.onoff.server_pconns) flags.keepalive = 0; else if (_peer == NULL) flags.keepalive = 1; === modified file 'src/http.h' --- src/http.h 2008-09-13 13:43:00 +0000 +++ src/http.h 2008-09-14 13:22:13 +0000 @@ -118,6 +118,7 @@ MemBuf * mb, http_state_flags flags); static bool decideIfWeDoRanges (HttpRequest * orig_request); + bool peerSupportsConnectionPinning(); ChunkedCodingParser *httpChunkDecoder; private: === modified file 'src/neighbors.cc' --- src/neighbors.cc 2008-07-18 11:24:16 +0000 +++ src/neighbors.cc 2008-09-20 15:39:41 +0000 @@ -51,7 +51,7 @@ /* count mcast group peers every 15 minutes */ #define MCAST_COUNT_RATE 900 -static int peerAllowedToUse(const peer *, HttpRequest *); +int peerAllowedToUse(const peer *, HttpRequest *); static int peerWouldBePinged(const peer *, HttpRequest *); static void neighborRemove(peer *); static void neighborAlive(peer *, const MemObject *, const icp_common_t *); @@ -136,7 +136,7 @@ * this function figures out if it is appropriate to fetch REQUEST * from PEER. */ -static int +int peerAllowedToUse(const peer * p, HttpRequest * request) { @@ -1635,6 +1635,13 @@ if (p->domain) storeAppendPrintf(sentry, " forceddomain=%s", p->domain); + if(p->connection_auth == 0) + storeAppendPrintf(sentry, " connection-auth=off"); + else if(p->connection_auth == 1) + storeAppendPrintf(sentry, " connection-auth=on"); + else if(p->connection_auth == 2) + storeAppendPrintf(sentry, " connection-auth=auto"); + storeAppendPrintf(sentry, "\n"); } === modified file 'src/peer_select.cc' --- src/peer_select.cc 2008-07-11 20:43:43 +0000 +++ src/peer_select.cc 2008-09-07 14:20:04 +0000 @@ -100,6 +100,7 @@ static void peerGetSomeParent(ps_state *); static void peerGetAllParents(ps_state *); static void peerAddFwdServer(FwdServer **, peer *, hier_code); +static void peerGetPinned(ps_state * ps); CBDATA_CLASS_INIT(ps_state); @@ -322,6 +323,8 @@ debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]); } + if (!entry || entry->ping_status == PING_NONE) + peerGetPinned(ps); if (entry == NULL) { (void) 0; } else if (entry->ping_status == PING_NONE) { @@ -363,6 +366,32 @@ } /* + * peerGetPinned + * + * Selects a pinned connection + */ +int peerAllowedToUse(const peer * p, HttpRequest * request); +static void +peerGetPinned(ps_state * ps) +{ + HttpRequest *request = ps->request; + peer *peer; + if (!request->pinned_connection) + return; + if (request->pinned_connection->getPinnedInfo(request, peer) != -1) { + if (peer && peerAllowedToUse(peer, request)) { + peerAddFwdServer(&ps->servers, peer, PINNED); + if (ps->entry) + ps->entry->ping_status = PING_DONE; /* Skip ICP */ + } else if (!peer && ps->direct != DIRECT_NO) { + peerAddFwdServer(&ps->servers, NULL, PINNED); + if (ps->entry) + ps->entry->ping_status = PING_DONE; /* Skip ICP */ + } + } +} + +/* * peerGetSomeNeighbor * * Selects a neighbor (parent or sibling) based on one of the === modified file 'src/structs.h' --- src/structs.h 2008-08-09 06:24:33 +0000 +++ src/structs.h 2008-09-02 08:32:52 +0000 @@ -1011,6 +1011,7 @@ #endif int front_end_https; + int connection_auth; }; struct _net_db_name @@ -1095,6 +1096,11 @@ unsigned int internal:1; unsigned int internalclient:1; unsigned int must_keepalive:1; + unsigned int connection_auth:1; /** Request wants connection oriented auth */ + unsigned int no_connection_auth:1; /** Connection oriented auth can not be supported */ + unsigned int connection_proxy_auth:1; /** Request wants connection oriented auth */ + unsigned int pinned:1; /* Request seont on a pinned connection */ + unsigned int auth_sent:1; /* Authentication forwarded */ // When adding new flags, please update cloneAdaptationImmune() as needed. # Begin bundle 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 l3MyB06tBvvCb6Q57QPRxnYR0d0MESTT0+Tx+L7w4jCoKP8n48BpoVL8JrZkMOGaf6ajW4jm226k LMExr9lOSfmn6b65PD0UdSBtPU0l+bObR0rgmEj0wXXX3/D8HxUDu9/QCtPOZSkiZ+l9ybVo/Wfr UWUbKs2+uM5Z5LfxmbJhoozWZP2pNn8jVmTbvmwscNGzZdw/Us82a9LJ3ZJNlGrhdZk4Wf092q7J q1YdNHCpBkcufg+3QySYODgg0WsxQyKSd0Ekmf0Es97vTDZo6cJNl1lXCaaijzEEdtEnho8m0Giy aTVOyirV7MK57pTyWXcuGzhs8t26jNm1ZLpGvOqzJyzZmyzNuku8+bsLuDV9mrxCMkmTKEUezDp2 k5WSeSxd08Mnlh2Zr1S6XWbqrskkmzdagHk5tSQwff0xSagoJTW8gPLCB3P7qywibduhIXEIQKhi owLty56KGozExcPNScgUFxKbTaXhpMZQ0hr5vdUAieFw09D9ng7VbQtE9QBtAf7+BunQ8Rwa0cHm PkXIXZ5YedHzpsLUAA1QVE5vYlgC8XTxSmf4N4qFwoPOcReYjHcewcTmvZmgVkDh1GpLSE8WqHHe QO8tKCY/IkuyatogzZp3bKNFlGyyTJNy+tqybtHDZ11k2KtWzlJmk/TGjwydOV3hhmizw7ZvDMsk 4RtZKZJyycqqMmrVo3auFSGjx4qq0dumFlHDNu8NkzDN0q333VbMnh01KunhZ011ybMJs2jRNyo2 bJLsnzQptylHDNVow7bpOmzwVdsPT2zVhElCJQVlyDJH+aGRwxLjuHlfNOBwJi48g7KAjsJVlhoa DjaVmJtIjxwbzUiDy0+pg/EdWGb/t/12G8nPMAEanIxIBgXFx0GCg7dqvtYaVXaPtRw+z7830KtH 0OFVnCaar6oR+CImZPLp0qdO27w2axGSbNm1u3bPw+0VQcGUoZzlV3F6Fi6spOoyBkMBUtMZnCoY cRNpIMYmnTE6h66AJ3IKW5t1+bBAnnjP+0Cec85AZJ7ABLBKCWCUEoNiUGxKDYlIywSwiQpBKAws iUGxKDYlBsSwiQsBKDRLBLEsBJSJSMsEoNEsEoJYJQSwEliWRCxKDRLHuJ06uuE/EQyFnZS0B7Kg WGZxHoo0QxoRGBBIMGJBKHIAej2CXnOkDqOwVfSYKLP/3BTb3JGXAhJ+31F9VM4xozO22kwggJAy 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