|  | /* | 
|  | * 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); | 
|  | 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); | 
|  | } | 
|  | } | 
|  | } |