});
}
-unsigned short
+AnyP::Port
FindListeningPortNumber(const HttpRequest *callerRequest, const AccessLogEntry *ale)
{
const auto ip = FindGoodListeningPortAddress(callerRequest, ale, [](const Ip::Address &address) {
return address.port() > 0;
});
- return ip ? ip->port() : 0;
+
+ if (!ip)
+ return std::nullopt;
+
+ Assure(ip->port() > 0);
+ return ip->port();
}
/// nil parameter(s) indicate missing caller information and are handled safely
const Ip::Address *FindListeningPortAddress(const HttpRequest *, const AccessLogEntry *);
-/// \returns listening/*_port port number used by the client connection (or zero)
+/// \returns listening/*_port port number used by the client connection (or nothing)
/// nil parameter(s) indicate missing caller information and are handled safely
-unsigned short FindListeningPortNumber(const HttpRequest *, const AccessLogEntry *);
+AnyP::Port FindListeningPortNumber(const HttpRequest *, const AccessLogEntry *);
#endif /* SQUID_HTTPREQUEST_H */
int
ACLUrlPortStrategy::match(ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
{
- return data->match(checklist->request->url.port());
+ return data->match(checklist->request->url.port().value_or(0));
}
return false;
*dst = '\0';
- foundPort = scheme.defaultPort(); // may be reset later
+ // If the parsed scheme has no (known) default port, and there is no
+ // explicit port, then we will reject the zero port during foundPort
+ // validation, often resulting in a misleading 400/ERR_INVALID_URL.
+ // TODO: Remove this hack when switching to Tokenizer-based parsing.
+ foundPort = scheme.defaultPort().value_or(0); // may be reset later
/* Is there any login information? (we should eventually parse it above) */
t = strrchr(foundHost, '@');
authorityWithPort_.append(host());
authorityHttp_ = authorityWithPort_;
- // authorityForm_ only has :port if it is non-default
- authorityWithPort_.appendf(":%u",port());
- if (port() != getScheme().defaultPort())
- authorityHttp_ = authorityWithPort_;
+ if (port().has_value()) {
+ authorityWithPort_.appendf(":%hu", *port());
+ // authorityHttp_ only has :port for known non-default ports
+ if (port() != getScheme().defaultPort())
+ authorityHttp_ = authorityWithPort_;
+ }
+ // else XXX: We made authorityWithPort_ that does not have a port.
+ // TODO: Audit callers and refuse to give out broken authorityWithPort_.
}
return requirePort ? authorityWithPort_ : authorityHttp_;
AnyP::Uri::Uri(AnyP::UriScheme const &aScheme) :
scheme_(aScheme),
- hostIsNumeric_(false),
- port_(0)
+ hostIsNumeric_(false)
{
*host_=0;
}
MEMPROXY_CLASS(Uri);
public:
- Uri() : hostIsNumeric_(false), port_(0) {*host_=0;}
+ Uri(): hostIsNumeric_(false) { *host_ = 0; }
Uri(AnyP::UriScheme const &aScheme);
Uri(const Uri &other) {
this->operator =(other);
hostIsNumeric_ = false;
*host_ = 0;
hostAddr_.setEmpty();
- port_ = 0;
+ port_ = std::nullopt;
touch();
}
void touch(); ///< clear the cached URI display forms
/// [brackets]. See RFC 3986 Section 3.2.2.
SBuf hostOrIp() const;
- void port(unsigned short p) {port_=p; touch();}
- unsigned short port() const {return port_;}
+ /// reset authority port subcomponent
+ void port(const Port p) { port_ = p; touch(); }
+ /// \copydoc port_
+ Port port() const { return port_; }
/// reset the port to the default port number for the current scheme
void defaultPort() { port(getScheme().defaultPort()); }
bool hostIsNumeric_; ///< whether the authority 'host' is a raw-IP
Ip::Address hostAddr_; ///< binary representation of the URI authority if it is a raw-IP
- unsigned short port_; ///< URL port
+ Port port_; ///< authority port subcomponent
// XXX: for now includes query-string.
SBuf path_; ///< URI path segment
return AnyP::PROTO_UNKNOWN;
}
-unsigned short
+AnyP::Port
AnyP::UriScheme::defaultPort() const
{
switch (theScheme_) {
return 43;
default:
- return 0;
+ return std::nullopt;
}
}
#include "sbuf/SBuf.h"
#include <iosfwd>
+#include <optional>
#include <vector>
namespace AnyP
{
+/// validated/supported port number; these values are never zero
+using KnownPort = uint16_t;
+
+/// validated/supported port number (if any)
+using Port = std::optional<KnownPort>;
+
/** This class represents a URI Scheme such as http:// https://, wais://, urn: etc.
* It does not represent the PROTOCOL that such schemes refer to.
*/
*/
SBuf image() const {return image_;}
- unsigned short defaultPort() const;
+ Port defaultPort() const;
/// initializes down-cased protocol scheme names array
static void Init();
key.append(request->url.host());
}
if (tp->options.carp_key.port) {
- key.appendf(":%u", request->url.port());
+ key.appendf(":%hu", request->url.port().value_or(0));
}
if (tp->options.carp_key.path) {
// XXX: fix when path and query are separate
acl aclname urlpath_regex [-i] \.gif$ ...
# regex matching on URL path [fast]
- acl aclname port 80 70 21 0-1024... # destination TCP port [fast]
- # ranges are allowed
+ acl aclname port 80 70 21 0-1024 ...
+ # destination TCP port (or port range) of the request [fast]
+ #
+ # Port 0 matches requests that have no explicit and no default destination
+ # ports (e.g., HTTP requests with URN targets)
+
acl aclname localport 3128 ... # TCP port the client connected to [fast]
# NP: for interception mode this is usually '80'
const SBuf &scheme = AnyP::UriScheme(transferProtocol.protocol).image();
const int url_sz = scheme.length() + useHost.length() + hp->requestUri().length() + 32;
uri = static_cast<char *>(xcalloc(url_sz, 1));
- snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH ":%d" SQUIDSBUFPH,
+ snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH ":%hu" SQUIDSBUFPH,
SQUIDSBUFPRINT(scheme),
SQUIDSBUFPRINT(useHost),
- tlsConnectPort,
+ *tlsConnectPort,
SQUIDSBUFPRINT(hp->requestUri()));
}
#endif
{
// fake a CONNECT request to force connState to tunnel
SBuf connectHost;
- unsigned short connectPort = 0;
+ AnyP::Port connectPort;
if (pinning.serverConnection != nullptr) {
static char ip[MAX_IPSTRLEN];
connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
- connectPort = pinning.serverConnection->remote.port();
+ if (const auto remotePort = pinning.serverConnection->remote.port())
+ connectPort = remotePort;
} else if (cause) {
connectHost = cause->url.hostOrIp();
connectPort = cause->url.port();
static char ip[MAX_IPSTRLEN];
connectHost = clientConnection->local.toStr(ip, sizeof(ip));
connectPort = clientConnection->local.port();
- } else {
+ }
+
+ if (!connectPort) {
// Typical cases are malformed HTTP requests on http_port and malformed
// TLS handshakes on non-bumping https_port. TODO: Discover these
// problems earlier so that they can be classified/detailed better.
}
debugs(33, 2, "Request tunneling for " << reason);
- ClientHttpRequest *http = buildFakeRequest(connectHost, connectPort, payload);
+ const auto http = buildFakeRequest(connectHost, *connectPort, payload);
HttpRequest::Pointer request = http->request;
request->flags.forceTunnel = true;
http->calloutContext = new ClientRequestContext(http);
}
ClientHttpRequest *
-ConnStateData::buildFakeRequest(SBuf &useHost, unsigned short usePort, const SBuf &payload)
+ConnStateData::buildFakeRequest(SBuf &useHost, const AnyP::KnownPort usePort, const SBuf &payload)
{
ClientHttpRequest *http = new ClientHttpRequest(this);
Http::Stream *stream = new Http::Stream(clientConnection, http);
struct {
Comm::ConnectionPointer serverConnection; /* pinned server side connection */
char *host = nullptr; ///< host name of pinned connection
- int port = -1; ///< port of pinned connection
+ AnyP::Port port; ///< destination port of the request that caused serverConnection
bool pinned = false; ///< this connection was pinned
bool auth = false; ///< pinned for www authentication
bool reading = false; ///< we are monitoring for peer connection closure
bool shouldPreserveClientData() const;
/// build a fake http request
- ClientHttpRequest *buildFakeRequest(SBuf &useHost, unsigned short usePort, const SBuf &payload);
+ ClientHttpRequest *buildFakeRequest(SBuf &useHost, AnyP::KnownPort usePort, const SBuf &payload);
/// From-client handshake bytes (including bytes at the beginning of a
/// CONNECT tunnel) which we may need to forward as-is if their syntax does
/// The number of parsed HTTP requests headers on a bumped client connection
uint64_t parsedBumpedRequestCount = 0;
+ // TODO: Replace tlsConnectHostOrIp and tlsConnectPort with CONNECT request AnyP::Uri
/// The TLS server host name appears in CONNECT request or the server ip address for the intercepted requests
SBuf tlsConnectHostOrIp; ///< The TLS server host name as passed in the CONNECT request
- unsigned short tlsConnectPort = 0; ///< The TLS server port number as passed in the CONNECT request
+ AnyP::Port tlsConnectPort; ///< The TLS server port number as passed in the CONNECT request
SBuf sslCommonName_; ///< CN name for SSL certificate generation
/// TLS client delivered SNI value. Empty string if none has been received.
return;
}
+ // TODO: Unify Host value parsing below with AnyP::Uri authority parsing
// Locate if there is a port attached, strip ready for IP lookup
char *portStr = nullptr;
char *hostB = xstrdup(host);
// Verify forward-proxy requested URL domain matches the Host: header
debugs(85, 3, "FAIL on validate URL domain " << http->request->url.host() << " matches Host: " << host);
hostHeaderVerifyFailed(host, http->request->url.host());
- } else if (portStr && port != http->request->url.port()) {
+ } else if (portStr && !http->request->url.port()) {
+ debugs(85, 3, "FAIL on validate portless URI matches Host: " << portStr);
+ hostHeaderVerifyFailed("portless URI", portStr);
+ } else if (portStr && port != *http->request->url.port()) {
// Verify forward-proxy requested URL domain matches the Host: header
- debugs(85, 3, "FAIL on validate URL port " << http->request->url.port() << " matches Host: port " << portStr);
+ debugs(85, 3, "FAIL on validate URL port " << *http->request->url.port() << " matches Host: port " << portStr);
hostHeaderVerifyFailed("URL port", portStr);
} else if (!portStr && http->request->method != Http::METHOD_CONNECT && http->request->url.port() != http->request->url.getScheme().defaultPort()) {
// Verify forward-proxy requested URL domain matches the Host: header
// Special case: we don't have a default-port to check for CONNECT. Assume URL is correct.
- debugs(85, 3, "FAIL on validate URL port " << http->request->url.port() << " matches Host: default port " << http->request->url.getScheme().defaultPort());
+ debugs(85, 3, "FAIL on validate URL port " << http->request->url.port().value_or(0) << " matches Host: default port " << http->request->url.getScheme().defaultPort().value_or(0));
hostHeaderVerifyFailed("URL port", "default port");
} else {
// Okay no problem.
realm.append("unknown", 7);
else {
realm.append(request->url.host());
- if (request->url.port() != 21)
- realm.appendf(" port %d", request->url.port());
+ const auto &rport = request->url.port();
+ if (rport && *rport != 21)
+ realm.appendf(" port %hu", *rport);
}
return realm;
}
break;
case 'p':
- if (request) {
- mb.appendf("%u", request->url.port());
+ if (request && request->url.port()) {
+ mb.appendf("%hu", *request->url.port());
} else if (!building_deny_info_url) {
p = "[unknown port]";
}
HttpRequestPointer request;
char *url = nullptr;
int xerrno = 0;
- unsigned short port = 0;
std::optional<SBuf> dnsError; ///< DNS lookup error message
time_t ttl = 0;
case LFT_LOCAL_LISTENING_PORT:
if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
- outint = port;
+ outint = *port;
doint = 1;
}
break;
break;
case LFT_CLIENT_REQ_URLPORT:
- if (al->request) {
- outint = al->request->url.port();
+ if (al->request && al->request->url.port()) {
+ outint = *al->request->url.port();
doint = 1;
}
break;
break;
case LFT_SERVER_REQ_URLPORT:
- if (al->adapted_request) {
- outint = al->adapted_request->url.port();
+ if (al->adapted_request && al->adapted_request->url.port()) {
+ outint = *al->adapted_request->url.port();
doint = 1;
}
break;
Comm::ConnectionPointer p = new Comm::Connection();
p->remote = ip;
- p->remote.port(peer ? peer->http_port : request->url.port());
+ // XXX: We return a (non-peer) destination with a zero port if the selection
+ // initiator supplied a request target without a port. If there are no valid
+ // use cases for this behavior, stop _selecting_ such destinations.
+ p->remote.port(peer ? peer->http_port : request->url.port().value_or(0));
handlePath(p, *servers);
}
TestHttpRequest::testCreateFromUrl()
{
/* vanilla url, implicit method */
- unsigned short expected_port;
SBuf url("http://foo:90/bar");
const auto mx = MasterXaction::MakePortless<XactionInitiator::initHtcp>();
HttpRequest *aRequest = HttpRequest::FromUrl(url, mx);
- expected_port = 90;
+ AnyP::KnownPort expected_port = 90;
CPPUNIT_ASSERT(aRequest != nullptr);
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_GET);
expected_port = 90;
CPPUNIT_ASSERT(aRequest != nullptr);
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_PUT);
expected_port = 80;
CPPUNIT_ASSERT(aRequest != nullptr);
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_PUT);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_CONNECT);
expected_port = 45;
CPPUNIT_ASSERT(aRequest != nullptr);
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_CONNECT);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf(), aRequest->url.path());
void
TestHttpRequest::testIPv6HostColonBug()
{
- unsigned short expected_port;
HttpRequest *aRequest = nullptr;
/* valid IPv6 address without port */
SBuf url("http://[2000:800::45]/foo");
const auto mx = MasterXaction::MakePortless<XactionInitiator::initHtcp>();
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_GET);
- expected_port = 80;
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ AnyP::KnownPort expected_port = 80;
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
url = "http://[2000:800::45]:90/foo";
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_GET);
expected_port = 90;
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
url = "http://2000:800::45/foo";
aRequest = HttpRequest::FromUrl(url, mx, Http::METHOD_GET);
expected_port = 80;
- CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
+ CPPUNIT_ASSERT_EQUAL(expected_port, *aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
int router_len;
int bucket_counter;
uint32_t service_flags;
- unsigned short port = WCCP_PORT;
-
/* Packet segments */
struct wccp2_message_header_t *main_header;
router_len = sizeof(router);
memset(&router, '\0', router_len);
router.sin_family = AF_INET;
- router.sin_port = htons(port);
+ router.sin_port = htons(WCCP_PORT);
/* Start main header - fill in length later */
offset = 0;