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