--- ProtoPort.h.org 2009-05-22 16:36:34.000000000 +0900 +++ ProtoPort.h 2009-05-22 16:37:04.000000000 +0900 @@ -23,6 +23,7 @@ unsigned int accel:1; /**< HTTP accelerator */ unsigned int vhost:1; /**< uses host header */ unsigned int sslBump:1; /**< intercepts CONNECT requests */ + unsigned int sslConnect:1; /**< sslConnect */ int vport; /* virtual port support, -1 for dynamic, >0 static*/ bool connection_auth_disabled; /* Don't support connection oriented auth */ --- cache_cf.cc.org 2009-05-22 16:32:34.000000000 +0900 +++ cache_cf.cc 2009-05-22 16:34:24.000000000 +0900 @@ -3062,6 +3062,9 @@ } else if (strncmp(token, "sslcontext=", 11) == 0) { safe_free(s->sslcontext); s->sslcontext = xstrdup(token + 11); + // sslConnect + } else if (strncmp(token, "sslConnect", 10) == 0) { + s->sslConnect = 1; } else if (strcmp(token, "sslBump") == 0) { s->sslBump = 1; // accelerated when bumped, otherwise not #endif --- client_side.cc.org 2009-05-23 22:44:21.000000000 +0900 +++ client_side.cc 2009-06-14 05:20:24.000000000 +0900 @@ -168,6 +168,9 @@ static ConnStateData *connStateCreate(const IpAddress &peer, const IpAddress &me, int fd, http_port_list *port); +extern "C" CSR clientGetMoreData; +extern "C" CSS clientReplyStatus; +extern "C" CSD clientReplyDetach; int ClientSocketContext::fd() const @@ -214,6 +217,99 @@ comm_read(fd, in.addressToReadInto(), getAvailableBufferLength(), call); } +void +ConnStateData::startSSLConnect() +{ + ConnStateData * conn = this; + ClientSocketContext *context; + ClientHttpRequest *http; + HttpVersion http_ver(1,0); + HttpRequestMethod method = METHOD_CONNECT; + protocol_t protocol = PROTO_NONE; + char *urlpath = NULL; + char ntoabuf[MAX_IPSTRLEN]; + StoreIOBuffer tempBuffer; + + debugs(95, 5, "ConnStateData::startSSLConnect: start"); + + /* Should not be needed anymore */ + /* Terminate the string */ + conn->in.buf[conn->in.notYetUsed] = '\0'; + + http = new ClientHttpRequest(conn); + context = ClientSocketContextNew(http); + + if (conn->transparent()) { + http->flags.intercepted = conn->port->intercepted; + http->flags.spoof_client_ip = conn->port->spoof_client_ip; + + // set url + int url_sz = 32 + Config.appendDomainLen; + http->uri = (char *)xcalloc(url_sz, 1); + snprintf(http->uri, url_sz, "%s:%d", + http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN), + http->getConn()->me.GetPort()); + debugs(95, 5, "ConnStateData::startSSLConnect: " << http->uri); + } + if (context) { + debugs(95, 5, "ConnStateData::startSSLConnect: set http request objects"); + commSetTimeout(conn->fd, Config.Timeout.lifetime, + clientLifetimeTimeout, context->http); + + // clientProcessRequest + HttpRequest *request = new HttpRequest(method, protocol, urlpath); + + /* We have an initial client stream in place should it be needed */ + /* setup our private context */ + context->registerWithConn(); + + if (http->flags.internal) { + request->protocol = PROTO_HTTP; + request->login[0] = '\0'; + } + if (conn->transparent()) { + request->flags.spoof_client_ip = conn->port->spoof_client_ip; + } + setLogUri (http, http->uri); + request->flags.sslConnect = conn->port->sslConnect; + request->flags.internal = http->flags.internal; + request->client_addr = conn->peer; + request->SetHost(http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN)); + request->port = http->getConn()->me.GetPort(); +#if FOLLOW_X_FORWARDED_FOR + request->indirect_client_addr = conn->peer; +#endif /* FOLLOW_X_FORWARDED_FOR */ + request->my_addr = conn->me; + request->http_ver = http_ver; + http->request = HTTPMSGLOCK(request); + http->req_sz = conn->in.notYetUsed; + clientSetKeepaliveFlag(http); + + tempBuffer.data = context->reqbuf; + tempBuffer.length = HTTP_REQBUF_SZ; + ClientStreamData newServer = new clientReplyContext(http); + ClientStreamData newClient = context; + clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach, + clientReplyStatus, newServer, clientSocketRecipient, + clientSocketDetach, newClient, tempBuffer); + + http->calloutContext = new ClientRequestContext(http); + + // check access list + if (Config.accessList.http) { + ACLFilledChecklist checklist(Config.accessList.http, request, NULL); + checklist.src_addr = request->client_addr; + checklist.my_addr = request->my_addr; + if (!checklist.fastCheck()) { + debugs(95, 5, "ConnStateData::startSSLConnect: access denied"); + comm_close(conn->fd); + return; + } + } + tunnelStart(http, &(http->out.size), &(http->al.http.code)); + } + debugs(95, 5, "ConnStateData::startSSLConnect: end"); +} void ClientSocketContext::removeFromConnectionList(ConnStateData * conn) @@ -2886,7 +2982,11 @@ commSetTcpKeepalive(newfd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout); } - connState->readSomeData(); + if (s->sslConnect) { + connState->startSSLConnect(); + } else { + connState->readSomeData(); + } clientdbEstablished(details->peer, 1); --- client_side.h.org 2009-05-23 22:55:28.000000000 +0900 +++ client_side.h 2009-05-23 22:57:32.000000000 +0900 @@ -132,6 +132,7 @@ ~ConnStateData(); void readSomeData(); + void startSSLConnect(); int getAvailableBufferLength() const; bool areAllContextsForThisConnection() const; void freeAllContexts(); --- structs.h.org 2009-05-23 22:33:02.000000000 +0900 +++ structs.h 2009-05-23 22:36:11.000000000 +0900 @@ -1034,6 +1034,7 @@ unsigned int connection_proxy_auth:1; /** Request wants connection oriented auth */ unsigned int pinned:1; /* Request sent on a pinned connection */ unsigned int auth_sent:1; /* Authentication forwarded */ + unsigned int sslConnect:1; // When adding new flags, please update cloneAdaptationImmune() as needed. --- tunnel.cc.org 2009-05-22 16:38:32.000000000 +0900 +++ tunnel.cc 2009-06-12 23:20:28.000000000 +0900 @@ -480,19 +480,17 @@ } else debugs(26, 1, "tunnelConnectTimeout(): tunnelState->servers is NULL"); - err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); - *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; - - err->xerrno = ETIMEDOUT; - - err->port = tunnelState->port; - - err->callback = tunnelErrorComplete; - - err->callback_data = tunnelState; - - errorSend(tunnelState->client.fd(), err); + if (request->flags.sslConnect) { + // nothing to do + } else { + err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); + err->xerrno = ETIMEDOUT; + err->port = tunnelState->port; + err->callback = tunnelErrorComplete; + err->callback_data = tunnelState; + errorSend(tunnelState->client.fd(), err); + } comm_close(fd); } @@ -522,13 +520,46 @@ } static void +tunnelProxyConnectedWriteDummyDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) +{ + // nothing to do +} + +static void +tunnelProxyConnectedReadDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data) +{ + TunnelStateData *tunnelState = (TunnelStateData *)data; + debugs(95, 5, "tunnelProxyConnectedReadDone: start"); + if (strncmp(buf, "\r\n", size) == 0 || + strncmp(buf, "\n", size) == 0 || + squid_strnstr(buf, "\r\n\r\n", size) || + squid_strnstr(buf, "\n\n", size)) { + debugs(95, 5, "tunnelProxyConnectedReadDone: detect the status code"); + tunnelConnectedWriteDone(fd, buf, size, flag, xerrno, data); + } else { + // retry + debugs(95, 5, "tunnelProxyConnectedReadDone: retry to get the status code"); + comm_read(tunnelState->server.fd(), tunnelState->server.buf, + tunnelState->server.bytesWanted(1, SQUID_TCP_SO_RCVBUF), + tunnelProxyConnectedReadDone, tunnelState); + } + debugs(95, 5, "tunnelProxyConnectedReadDone: end"); +} + +static void tunnelConnected(int fd, void *data) { TunnelStateData *tunnelState = (TunnelStateData *)data; debugs(26, 3, "tunnelConnected: FD " << fd << " tunnelState=" << tunnelState); *tunnelState->status_ptr = HTTP_OK; - comm_write(tunnelState->client.fd(), conn_established, strlen(conn_established), - tunnelConnectedWriteDone, tunnelState, NULL); + + if (tunnelState->request->flags.sslConnect) { + debugs(95, 5, "tunnelConnected: direct connected"); + tunnelConnectedWriteDone(fd, NULL, 0, COMM_OK, 0, tunnelState); + } else { + comm_write(tunnelState->client.fd(), conn_established, strlen(conn_established), + tunnelConnectedWriteDone, tunnelState, NULL); + } } static void @@ -568,20 +599,28 @@ if (status == COMM_ERR_DNS) { debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host); - err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request); *tunnelState->status_ptr = HTTP_NOT_FOUND; - err->dnsserver_msg = xstrdup(dns_error_message_safe()); - err->callback = tunnelErrorComplete; - err->callback_data = tunnelState; - errorSend(tunnelState->client.fd(), err); + if (request->flags.sslConnect) { + comm_close(tunnelState->client.fd()); + } else { + err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request); + err->dnsserver_msg = xstrdup(dns_error_message_safe()); + err->callback = tunnelErrorComplete; + err->callback_data = tunnelState; + errorSend(tunnelState->client.fd(), err); + } } else if (status != COMM_OK) { - err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; - err->xerrno = xerrno; - err->port = tunnelState->port; - err->callback = tunnelErrorComplete; - err->callback_data = tunnelState; - errorSend(tunnelState->client.fd(), err); + if (request->flags.sslConnect) { + comm_close(tunnelState->client.fd()); + } else { + err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); + err->xerrno = xerrno; + err->port = tunnelState->port; + err->callback = tunnelErrorComplete; + err->callback_data = tunnelState; + errorSend(tunnelState->client.fd(), err); + } } else { if (tunnelState->servers->_peer) tunnelProxyConnected(tunnelState->server.fd(), tunnelState); @@ -624,9 +663,13 @@ answer = ch.fastCheck(); if (answer == 0) { - err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request); *status_ptr = HTTP_FORBIDDEN; - errorSend(fd, err); + if (tunnelState->request->flags.sslConnect) { + comm_close(fd); + } else { + err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request); + errorSend(fd, err); + } return; } } @@ -636,19 +679,33 @@ statCounter.server.other.requests++; /* Create socket. */ IpAddress temp = getOutgoingAddr(request,NULL); - sock = comm_openex(SOCK_STREAM, - IPPROTO_TCP, - temp, - COMM_NONBLOCKING, - getOutgoingTOS(request), - url); + + if (request->flags.spoof_client_ip) { + sock = comm_openex(SOCK_STREAM, + IPPROTO_TCP, + temp, + (COMM_NONBLOCKING|COMM_TRANSPARENT), + getOutgoingTOS(request), + url); + } else { + sock = comm_openex(SOCK_STREAM, + IPPROTO_TCP, + temp, + COMM_NONBLOCKING, + getOutgoingTOS(request), + url); + } if (sock == COMM_ERROR) { debugs(26, 4, "tunnelStart: Failed because we're out of sockets."); - err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); *status_ptr = HTTP_INTERNAL_SERVER_ERROR; - err->xerrno = errno; - errorSend(fd, err); + if (tunnelState->request->flags.sslConnect) { + comm_close(fd); + } else { + err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); + err->xerrno = errno; + errorSend(fd, err); + } return; } @@ -713,7 +770,14 @@ packerClean(&p); mb.append("\r\n", 2); - comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDone, tunnelState); + if (tunnelState->request->flags.sslConnect) { + comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDummyDone, NULL); + comm_read(tunnelState->server.fd(), tunnelState->server.buf, + tunnelState->server.bytesWanted(1, SQUID_TCP_SO_RCVBUF), + tunnelProxyConnectedReadDone, tunnelState); + } else { + comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDone, tunnelState); + } commSetTimeout(tunnelState->server.fd(), Config.Timeout.read, tunnelTimeout, tunnelState); } @@ -725,12 +789,17 @@ peer *g = NULL; if (fs == NULL) { - ErrorState *err; - err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request); - *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; - err->callback = tunnelErrorComplete; - err->callback_data = tunnelState; - errorSend(tunnelState->client.fd(), err); + if (request->flags.sslConnect) { + *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; + comm_close(tunnelState->client.fd()); + } else { + ErrorState *err; + err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request); + *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE; + err->callback = tunnelErrorComplete; + err->callback_data = tunnelState; + errorSend(tunnelState->client.fd(), err); + } return; } @@ -806,3 +875,4 @@ } #endif +