blob: ffe64413f4cca8a433ef91619d4686cb32509451 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
#include "Debug.h"
#include <string>
#include <cstring>
#include <vector>
#include "AllowedConnections.h"
// TODO(jat): do we need to protect against parallel access to static state?
// current browsers only use one thread, but Chrome is multithreaded, though
// it isn't clear if plugins see parallel execution. For now, we will
// assume the caller takes responsibility for making sure there are no
// concurrent calls into AllowedConnections.
std::vector<AllowedConnections::Rule> AllowedConnections::rules;
/**
* Get the host portion of the URL, not including the port.
*
* @return the host portion of the URL, or the unmodified URL if it does not appear
* to be valid
*/
std::string AllowedConnections::getHostFromUrl(const std::string& url) {
int protoEnd = url.find("://");
if (protoEnd == std::string::npos) {
Debug::log(Debug::Debugging) << "getHostFromUrl(" << url
<< ") - no :// in URL" << Debug::flush;
return url;
}
protoEnd += 3; // skip over "://"
int hostEnd = url.find('/', protoEnd);
if (hostEnd == std::string::npos) {
hostEnd = url.length();
}
//skip over user:passwd@ if it exists
int userPassword = url.find( '@', protoEnd );
if (userPassword == std::string::npos || userPassword > hostEnd)
{
userPassword = protoEnd;
}
int colon = url.find(':', userPassword);
if (colon == std::string::npos || colon > hostEnd) {
colon = hostEnd;
}
std::string host = url.substr(userPassword, colon - userPassword);
return host;
}
std::string AllowedConnections::getCodeServerFromUrl(const std::string& url) {
int queryStart = url.find("?");
if (queryStart == std::string::npos) {
Debug::log(Debug::Debugging) << "getCodeServerFromUrl(" << url
<< ") - no ? in URL" << Debug::flush;
return "";
}
++queryStart; //skip the ?
int paramStart = url.find("gwt.codesvr=", queryStart);
if (paramStart == std::string::npos) {
Debug::log(Debug::Debugging) << "getCodeServerFromUrl(" << url
<< ") - missing gwt.codesvr in URL" << Debug::flush;
return "";
}
paramStart += 12;
int colon = url.find(':', paramStart);
// After navigation, the URL parameter could be encoded.
// ex: gwt.codesvr=127.0.0.1:9997 -> gwt.codesvr=127.0.0.1%3A9997.
int colonEncoded = url.find("%3A", paramStart);
if (colonEncoded != std::string::npos
&& (colon == std::string::npos || colonEncoded < colon)) {
colon = colonEncoded;
}
int variableEnd = url.find('&', paramStart);
if ( variableEnd == std::string::npos || colon < variableEnd) {
variableEnd = colon; //could be std::string::npos!
}
Debug::log(Debug::Spam) << "getCodeServerFromUrl(" << url
<< ") - gwt.codesvr=" <<
url.substr(paramStart, variableEnd-paramStart) << " in URL"
<< Debug::flush;
return url.substr(paramStart, variableEnd-paramStart);
}
bool AllowedConnections::matchesRule(const std::string& webHost,
const std::string& codeServer,
bool* allowed) {
std::string host = webHost;
std::string server = codeServer;
//Remap variants of localhost
if (host.find("localhost.") == 0 || host == "127.0.0.1") {
host = "localhost";
}
if (server.find("localhost.") == 0 || server == "127.0.0.1" )
{
server = "localhost";
}
// always allow localhost
// TODO(jat): try and get IP addresses of local interfaces?
if (host == "localhost" && server == "localhost") {
*allowed = true;
return true;
}
Debug::log(Debug::Spam) << "Checking webHost(" << webHost
<< "), codeServer(" << codeServer << ") " << Debug::flush;
for (std::vector<AllowedConnections::Rule>::const_iterator it = rules.begin();
it != rules.end(); ++it) {
Debug::log(Debug::Spam) << " comparing to webHost=(" << it->getWebHost()
<< ") codeServer=(" << it->getCodeServer() << ")" << Debug::flush;
// TODO(jat): add support for regexes
if (webHost == it->getWebHost() && codeServer == it->getCodeServer()) {
*allowed = !it->isExcluded();
Debug::log(Debug::Spam) << " found! allowed=" << *allowed
<< Debug::flush;
return true;
}
}
Debug::log(Debug::Info)
<< "GWT Development Mode connection requested by unknown web server "
<< webHost << ", code server " << codeServer << Debug::flush;
return false;
}
void AllowedConnections::addRule(const std::string& webHost,
const std::string& codeServer,
bool exclude) {
Debug::log(Debug::Spam) << "AllowedConnections::addRule(webHost=" << webHost
<< ", codeServer=" << codeServer << ", excl=" << exclude << ")"
<< Debug::flush;
rules.push_back(AllowedConnections::Rule(webHost, codeServer, exclude));
}
void AllowedConnections::clearRules() {
rules.clear();
}
void AllowedConnections::initFromAccessList(const std::string& accessList) {
Debug::log(Debug::Spam) << "initFromAccessList() accessList="
<< accessList << Debug::flush;
clearRules();
int n = accessList.length();
for (int i = 0; i < n; ) {
bool exclude = false;
if (accessList[i] == '!') {
exclude = true;
++i;
}
int comma = i - 1; // for pre-increment below
while (++comma < n && accessList[comma] != ','); // empty
std::string option = accessList.substr(i, comma - i);
i = comma + 1;
//parse the [/codeserver] optional element
int slash = option.find( '/');
if( slash == std::string::npos ) {
addRule(option, "localhost", exclude);
} else {
addRule(option.substr(0, slash), option.substr(slash+1), exclude);
}
}
}