Here is a patch which implements Alex proposal.
Regards,
Christos
On 06/08/2012 05:52 PM, Alex Rousskov wrote:
> On 06/08/2012 06:00 AM, Amos Jeffries wrote:
>> On 8/06/2012 9:23 p.m., Alexander Komyagin wrote:
>>> Hello Alex!
>>>
>>> On Thu, 2012-06-07 at 17:22 -0600, Alex Rousskov wrote:
>>>> On 06/07/2012 10:02 AM, Alexander Komyagin wrote:
>>>>> Hi! I've found that Squid (as of 3.2.0.16) is still overwriting clients
>>>>> requests to HTTP if they are intercepted:
>>>>>
>>>>> src/client_side.cc:prepareTransparentURL():
>>>>>
>>>>> snprintf(http->uri, url_sz, "http://%s%s", /*conn->port->protocol,*/
>>>>> host, url);
>>>>>
>>>>> When I want to intercept https traffic this one really breaks the
>>>>> things
>>>>> down. I have not deepened into the details, but it seems that
>>>>> everything
>>>>> works fine (simultaneous http/https) when I use conn->port->protocol
>>>>> and
>>>>> force protocol to http if it is NULL.
>>>>>
>>>>> Patch is attached.
>>>> Hello Alexander,
>>>>
>>>> It looks like your patch is changing more than just the lines that
>>>> format the URL. We made a similar but more compact change for the
>>>> bump-server-first project:
>>>>
>>>>> static void
>>>>> prepareTransparentURL(ConnStateData * conn, ClientHttpRequest
>>>>> *http, char *url,
>>>>> const char *req_hdr)
>>>>> {
>>>>> char *host;
>>>>> char ipbuf[MAX_IPSTRLEN];
>>>>>
>>>>> if (*url != '/')
>>>>> return; /* already in good shape */
>>>>>
>>>>> /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
>>>>> // BUG 2976: Squid only accepts intercepted HTTP.
>>>>>
>>>>> if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
>>>>> int url_sz = strlen(url) + 32 + Config.appendDomainLen +
>>>>> strlen(host);
>>>>> http->uri = (char *)xcalloc(url_sz, 1);
>>>>> - snprintf(http->uri, url_sz, "http://%s%s",
>>>>> /*conn->port->protocol,*/ host, url);
>>>>> + snprintf(http->uri, url_sz, "%s://%s%s",
>>>>> conn->port->protocol, host, url);
>>>>> debugs(33, 5, "TRANSPARENT HOST REWRITE: '"<<
>>>>> http->uri<<"'");
>>>>> } else {
>>>>> /* Put the local socket IP address as the hostname. */
>>>>> int url_sz = strlen(url) + 32 + Config.appendDomainLen;
>>>>> http->uri = (char *)xcalloc(url_sz, 1);
>>>>>
>>>>> http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN),
>>>>>
>>>>> - snprintf(http->uri, url_sz, "http://%s:%d%s",
>>>>> - // http->getConn()->port->protocol,
>>>>> + snprintf(http->uri, url_sz, "%s://%s:%d%s",
>>>>> + http->getConn()->port->protocol,
>>>>> ipbuf,
>>>>> http->getConn()->clientConnection->local.GetPort(), url);
>>>>> debugs(33, 5, "TRANSPARENT REWRITE: '"<< http->uri<< "'");
>>>>> }
>>>>> }
>>>>
>>>> Have you seen the port protocol being NULL? It feels like that should
>>>> not be possible. If it is never NULL, I think the above changes are
>>>> preferred.
>>> Nope. Never seen. But the guy who reported *BUG 2976* certainly did. See
>>> http://www.squid-cache.org/mail-archive/squid-dev/201103/0020.html
>>
>> As did several other people around that period. Enough to make me add
>> that workaround to stable releases. It happens whenever Squid is
>> reconfigured. The "conn->port" is a pointer to the SquidConfig
>> http(s)_port state which gets erasesd and rebuilt, problem is the object
>> is reallocated in a different location and the old object being pointed
>> to by this fixed pointer has its string unset. NOTE: when MemPools is
>> disabled this is an invalid pointer dereference crash rather than a
>> silent "NULL".
>>
>> The easiest proper fix is to copy the http_port protocol= string into
>> ConnStateData member for use. ie conn->transportProtocol.
>>
>> The harder one I was working towards is to RefCount the new
>> AnyP::PortCfg objects at which point they will persist as long as that
>> ConnStateData needs to use them. Which is tricky since they are
>> apparently CBDATA which is not compatible with RefCounting.
>
>
> If they are cbdata, let's just lock/release them properly, without ever
> freeing/deleting them explicitly. My understanding is that the following
> is necessary to fix the underlying problem:
>
> 1. add_http_port() needs to lock the new port pointer before linking it.
> 2. parsePortCfg() needs to lock the new port pointer before linking it.
> 3. free_PortCfg() needs to unlock the old port pointer before unlinking
> it. It should not delete the old pointer.
>
> Anything else?
>
>
> Thank you,
>
> Alex.
>
>
>
>
=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2012-05-24 00:23:52 +0000
+++ src/cache_cf.cc 2012-06-13 16:23:15 +0000
@@ -3752,84 +3752,85 @@
s->generateHostCertificates = true;
} else if (strcmp(token, "generate-host-certificates=on") == 0) {
s->generateHostCertificates = true;
} else if (strcmp(token, "generate-host-certificates=off") == 0) {
s->generateHostCertificates = false;
} else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28);
#endif
} else {
self_destruct();
}
}
void
add_http_port(char *portspec)
{
AnyP::PortCfg *s = new AnyP::PortCfg("http_port");
parsePortSpecification(s, portspec);
// we may need to merge better if the above returns a list with clones
assert(s->next == NULL);
- s->next = Config.Sockaddr.http;
- Config.Sockaddr.http = s;
+ s->next = cbdataReference(Config.Sockaddr.http);
+ cbdataReferenceDone(Config.Sockaddr.http);
+ Config.Sockaddr.http = cbdataReference(s);
}
static void
parsePortCfg(AnyP::PortCfg ** head, const char *optionName)
{
const char *protocol = NULL;
if (strcmp(optionName, "http_port") == 0 ||
strcmp(optionName, "ascii_port") == 0)
protocol = "http";
else if (strcmp(optionName, "https_port") == 0)
protocol = "https";
if (!protocol) {
self_destruct();
return;
}
char *token = strtok(NULL, w_space);
if (!token) {
self_destruct();
return;
}
AnyP::PortCfg *s = new AnyP::PortCfg(protocol);
parsePortSpecification(s, token);
/* parse options ... */
while ((token = strtok(NULL, w_space))) {
parse_port_option(s, token);
}
if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.IsAnyAddr()) {
// clone the port options from *s to *(s->next)
- s->next = s->clone();
+ s->next = cbdataReference(s->clone());
s->next->s.SetIPv4();
debugs(3, 3, protocol << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
}
while (*head)
head = &(*head)->next;
- *head = s;
+ *head = cbdataReference(s);
}
static void
dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
{
char buf[MAX_IPSTRLEN];
storeAppendPrintf(e, "%s %s",
n,
s->s.ToURL(buf,MAX_IPSTRLEN));
// MODES and specific sub-options.
if (s->intercepted)
storeAppendPrintf(e, " intercept");
else if (s->spoof_client_ip)
storeAppendPrintf(e, " tproxy");
else if (s->accel) {
storeAppendPrintf(e, " accel");
@@ -3938,41 +3939,41 @@
#endif
}
static void
dump_PortCfg(StoreEntry * e, const char *n, const AnyP::PortCfg * s)
{
while (s) {
dump_generic_port(e, n, s);
storeAppendPrintf(e, "\n");
s = s->next;
}
}
static void
free_PortCfg(AnyP::PortCfg ** head)
{
AnyP::PortCfg *s;
while ((s = *head) != NULL) {
*head = s->next;
- delete s;
+ cbdataReferenceDone(s);
}
}
void
configFreeMemory(void)
{
free_all();
#if USE_SSL
SSL_CTX_free(Config.ssl_client.sslContext);
#endif
}
void
requirePathnameExists(const char *name, const char *path)
{
struct stat sb;
char pathbuf[BUFSIZ];
assert(path != NULL);
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2012-04-25 05:29:20 +0000
+++ src/client_side.cc 2012-06-13 20:51:00 +0000
@@ -2076,49 +2076,49 @@
debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
}
}
static void
prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
{
char *host;
char ipbuf[MAX_IPSTRLEN];
if (*url != '/')
return; /* already in good shape */
/* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
// BUG 2976: Squid only accepts intercepted HTTP.
if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
int url_sz = strlen(url) + 32 + Config.appendDomainLen +
strlen(host);
http->uri = (char *)xcalloc(url_sz, 1);
- snprintf(http->uri, url_sz, "http://%s%s", /*conn->port->protocol,*/ host, url);
+ snprintf(http->uri, url_sz, "%s://%s%s", conn->port->protocol, host, url);
debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
} else {
/* Put the local socket IP address as the hostname. */
int url_sz = strlen(url) + 32 + Config.appendDomainLen;
http->uri = (char *)xcalloc(url_sz, 1);
http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN),
- snprintf(http->uri, url_sz, "http://%s:%d%s",
- // http->getConn()->port->protocol,
+ snprintf(http->uri, url_sz, "%s://%s:%d%s",
+ http->getConn()->port->protocol,
ipbuf, http->getConn()->clientConnection->local.GetPort(), url);
debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
}
}
/**
* parseHttpRequest()
*
* Returns
* NULL on incomplete requests
* a ClientSocketContext structure on success or failure.
* Sets result->flags.parsed_ok to 0 if failed to parse the request.
* Sets result->flags.parsed_ok to 1 if we have a good request.
*/
static ClientSocketContext *
parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, HttpVersion *http_ver)
{
char *req_hdr = NULL;
char *end;
size_t req_sz;