Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/net/HttpAuthUtils.h"
#include "mozilla/Tokenizer.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsUnicharUtils.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
namespace mozilla {
namespace net {
namespace auth {
namespace detail {
bool MatchesBaseURI(const nsACString& matchScheme, const nsACString& matchHost,
int32_t matchPort, nsDependentCSubstring const& url) {
// check if scheme://host:port matches baseURI
// parse the base URI
mozilla::Tokenizer t(url);
mozilla::Tokenizer::Token token;
t.SkipWhites();
// We don't know if the url to check against starts with scheme
// or a host name. Start recording here.
t.Record();
mozilla::Unused << t.Next(token);
// The ipv6 literals MUST be enclosed with [] in the preference.
bool ipv6 = false;
if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
nsDependentCSubstring ipv6BareLiteral;
if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) {
// Broken ipv6 literal
return false;
}
nsDependentCSubstring ipv6Literal;
t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST);
if (!matchHost.Equals(ipv6Literal, nsCaseInsensitiveUTF8StringComparator) &&
!matchHost.Equals(ipv6BareLiteral,
nsCaseInsensitiveUTF8StringComparator)) {
return false;
}
ipv6 = true;
} else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) {
if (!matchScheme.Equals(token.Fragment())) {
return false;
}
// Re-start recording the hostname from the point after scheme://.
t.Record();
}
while (t.Next(token)) {
bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile());
bool port = token.Equals(mozilla::Tokenizer::Token::Char(':'));
if (eof || port) {
if (!ipv6) { // Match already performed above.
nsDependentCSubstring hostName;
t.Claim(hostName);
// An empty hostname means to accept everything for the schema
if (!hostName.IsEmpty()) {
/*
host: bar.com foo.bar.com foobar.com foo.bar.com bar.com
pref: bar.com bar.com bar.com .bar.com .bar.com
result: accept accept reject accept reject
*/
if (!StringEndsWith(matchHost, hostName,
nsCaseInsensitiveUTF8StringComparator)) {
return false;
}
if (matchHost.Length() > hostName.Length() &&
matchHost[matchHost.Length() - hostName.Length() - 1] != '.' &&
hostName[0] != '.') {
return false;
}
}
}
if (port) {
uint16_t portNumber;
if (!t.ReadInteger(&portNumber)) {
// Missing port number
return false;
}
if (matchPort != portNumber) {
return false;
}
if (!t.CheckEOF()) {
return false;
}
}
} else if (ipv6) {
// After an ipv6 literal there can only be EOF or :port. Everything else
// must be treated as non-match/broken input.
return false;
}
}
// All negative checks has passed positively.
return true;
}
} // namespace detail
bool URIMatchesPrefPattern(nsIURI* uri, const char* pref) {
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs) {
return false;
}
nsAutoCString scheme, host;
int32_t port;
if (NS_FAILED(uri->GetScheme(scheme))) {
return false;
}
if (NS_FAILED(uri->GetAsciiHost(host))) {
return false;
}
port = NS_GetRealPort(uri);
if (port == -1) {
return false;
}
nsAutoCString hostList;
if (NS_FAILED(prefs->GetCharPref(pref, hostList))) {
return false;
}
// pseudo-BNF
// ----------
//
// url-list base-url ( base-url "," LWS )*
// base-url ( scheme-part | host-part | scheme-part host-part )
// scheme-part scheme "://"
// host-part host [":" port]
//
// for example:
// "https://, http://office.foo.com"
//
mozilla::Tokenizer t(hostList);
while (!t.CheckEOF()) {
t.SkipWhites();
nsDependentCSubstring url;
mozilla::Unused << t.ReadUntil(mozilla::Tokenizer::Token::Char(','), url);
if (url.IsEmpty()) {
continue;
}
if (detail::MatchesBaseURI(scheme, host, port, url)) {
return true;
}
}
return false;
}
} // namespace auth
} // namespace net
} // namespace mozilla