IPv4 $_SERVER['SERVER_ADDR'] is IPv6 encoded when QUIC/HTTP3 is enabled
How to reproduce:
- Enable HTTP3/QUIC on a VHost
- create a php file, let's say
h.phpthat dumps$_SERVER, like:
<?php print(json_encode($_SERVER, JSON_PRETTY_PRINT)); ?>
- Access the file at
https://vhost.domain.com/h.php, with a browser that support QUIC/HTTP3, preferably in Incognito/Private mode, OVER IPv4 - Notice the values of:
"SERVER_ADDR": "A.B.C.D",
"X_SPDY": "HTTP2",
"SSL_PROTOCOL": "TLSv1.3",
- Refresh page, so it reconnects over HTTP3/QUIC (so it picks the
alt-svcheader from previous request) - Notice the values of:
"SERVER_ADDR": "::ffff:A.B.C.D",
"X_SPDY": "HTTP3",
"SSL_PROTOCOL": "QUIC",
Notice that, obviously, when accessing the server over IPv6, this SERVER_ADDR will reflect the proper IPv6 address.
Disabling IPv6 on the machine -- via sysctl net.ipv6.conf.all.disable_ipv6=0 - and restarting lsws, does not seem to change the way the IPv4 address is encapsulated.
The only thing that reports the correct SERVER_ADDRESS is turning off QUIC/HTTP3.
I've tested on a LiteSpeed Enterprise version, to compare, and I wasn't able to reproduce.
This has been an issue for us for over a year now, but we just recently tracked it down to QUIC/HTTP3. It was easily reproduced on Openlitespeed 1.7.16.
To my uninformed eye, it seems that the UDP socket in src/quic/quicengine.cpp is only called with AF_INET6, but I'm no C++ expert.
This is an issue with web apps like WHMCS and others that depend on a remote Licensing server, because the REMOTE_ADDR will no longer match what the licenser will have whitelisted.
Furthermore, it's terrible to debug, because for some browsers this will seemingly work -- browsers like Safari with no QUIC/HTTP3 support will connect over HTTP2, thus the license will suddenly be valid.
Hi,
Thanks the bug report, you can the follow patch to address this.
diff --git a/src/quic/quicengine.cpp b/src/quic/quicengine.cpp
index 370da9780..7a0325018 100644
--- a/src/quic/quicengine.cpp
+++ b/src/quic/quicengine.cpp
@@ -415,7 +415,18 @@ lsquic_stream_ctx_t *QuicEngine::onNewStream(void *stream_if_ctx,
pConnInfo->m_pServerAddrInfo->getAddr());
pConnInfo->m_pCrypto = pStream;
pConnInfo->m_pClientInfo = pClientInfo;
- pConnInfo->m_pServerAddrInfo = ServerAddrRegistry::getInstance().get(
+ if ((AF_INET6 == pLocal->sa_family) &&
+ (IN6_IS_ADDR_V4MAPPED(&((sockaddr_in6 *)pLocal)->sin6_addr)))
+ {
+ char serverAddr[28] = {0};
+ struct sockaddr *pAddr = (sockaddr *)serverAddr;
+ pAddr->sa_family = AF_INET;
+ memmove(&((sockaddr_in *)pAddr)->sin_addr.s_addr, &((char *)pLocal)[20], 4);
+ pConnInfo->m_pServerAddrInfo = ServerAddrRegistry::getInstance().get(
+ pLocal, pUdpListener->getTcpPeer());
+ }
+ else
+ pConnInfo->m_pServerAddrInfo = ServerAddrRegistry::getInstance().get(
pLocal, pUdpListener->getTcpPeer());
pConnInfo->m_remotePort = GSockAddr::getPort(pPeer);
//pStream->setConnInfo(getStream()->getConnInfo());