// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_web_socket_proxy_private_api.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/internal_auth.h" #include "chrome/browser/io_thread.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_details.h" #include "net/base/escape.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/base/single_request_host_resolver.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/web_socket_proxy_controller.h" #endif namespace { const char kPermissionDeniedError[] = "Extension does not have permission to use this method."; } WebSocketProxyPrivate::WebSocketProxyPrivate() : listening_port_(-1), do_tls_(false), is_finalized_(false){ } WebSocketProxyPrivate::~WebSocketProxyPrivate() { } void WebSocketProxyPrivate::Finalize() { CustomFinalize(); if (is_finalized_) return; is_finalized_ = true; SendResponse(listening_port_ > 0); Release(); } void WebSocketProxyPrivate::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { #if defined(OS_CHROMEOS) DCHECK_EQ(chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED, type); #else NOTREACHED(); #endif timer_.Stop(); // Cancel timeout timer. ResolveHost(); } void WebSocketProxyPrivate::ResolveHost() { #if defined(OS_CHROMEOS) IOThread* io_thread = g_browser_process->io_thread(); content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&WebSocketProxyPrivate::ResolveHostIOPart, this, io_thread)); #endif } void WebSocketProxyPrivate::ResolveHostIOPart(IOThread* io_thread) { #if defined(OS_CHROMEOS) DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); DCHECK(resolver_ == NULL); if (io_thread && io_thread->globals()) { net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get(); if (host_resolver) { resolver_.reset(new net::SingleRequestHostResolver(host_resolver)); net::HostResolver::RequestInfo info(net::HostPortPair( hostname_, port_)); int result = resolver_->Resolve(info, &addr_, base::Bind(&WebSocketProxyPrivate::OnHostResolution, this), net::BoundNetLog()); if (result != net::ERR_IO_PENDING) OnHostResolution(result); return; } } NOTREACHED(); OnHostResolution(net::ERR_UNEXPECTED); #endif } bool WebSocketProxyPrivate::RunImpl() { AddRef(); result_.reset(Value::CreateStringValue("")); #if defined(OS_CHROMEOS) bool delay_response = false; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &hostname_)); EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &port_)); if (chromeos::WebSocketProxyController::CheckCredentials( extension_id(), hostname_, port_, do_tls_ ? chromeos::WebSocketProxyController::TLS_OVER_TCP : chromeos::WebSocketProxyController::PLAIN_TCP)) { listening_port_ = chromeos::WebSocketProxyController::GetPort(); if (listening_port_ < 1) { delay_response = true; registrar_.Add( this, chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED, content::NotificationService::AllSources()); } map_["hostname"] = hostname_; map_["port"] = base::IntToString(port_); map_["extension_id"] = extension_id(); } else { error_ = kPermissionDeniedError; return false; } if (delay_response) { const int kTimeout = 12; timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeout), this, &WebSocketProxyPrivate::ResolveHost); } else { ResolveHost(); } #else Finalize(); #endif return true; } void WebSocketProxyPrivate::OnHostResolution(int result) { #if defined(OS_CHROMEOS) if (result == 0 && addr_.head() != NULL) { std::string ip = net::NetAddressToString(addr_.head()); if (!ip.empty() && ip.find(':') != std::string::npos && ip[0] != '[') ip = '[' + ip + ']'; map_["addr"] = ip; } content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&WebSocketProxyPrivate::Finalize, this)); #endif } WebSocketProxyPrivateGetURLForTCPFunction:: WebSocketProxyPrivateGetURLForTCPFunction() { } WebSocketProxyPrivateGetURLForTCPFunction:: ~WebSocketProxyPrivateGetURLForTCPFunction() { } void WebSocketProxyPrivateGetURLForTCPFunction::CustomFinalize() { #if defined(OS_CHROMEOS) std::string passport = browser::InternalAuthGeneration::GeneratePassport( "web_socket_proxy", map_); std::string query = std::string("hostname=") + net::EscapeQueryParamValue(hostname_, false) + "&port=" + map_["port"] + "&tls=" + map_["tls"] + "&passport=" + net::EscapeQueryParamValue(passport, false); if (ContainsKey(map_, "addr")) query += std::string("&addr=") + map_["addr"]; if (listening_port_ < 1) listening_port_ = chromeos::WebSocketProxyController::GetPort(); StringValue* url = Value::CreateStringValue(std::string( "ws://127.0.0.1:" + base::IntToString(listening_port_) + "/tcpproxy?" + query)); result_.reset(url); #endif } bool WebSocketProxyPrivateGetURLForTCPFunction::RunImpl() { #if defined(OS_CHROMEOS) DictionaryValue* qualification = NULL; if (args_->GetDictionary(2, &qualification)) { const char kTlsOption[] = "tls"; if (qualification->HasKey(kTlsOption)) { EXTENSION_FUNCTION_VALIDATE(qualification->GetBoolean( kTlsOption, &do_tls_)); } } map_["tls"] = do_tls_ ? "true" : "false"; #endif return WebSocketProxyPrivate::RunImpl(); } WebSocketProxyPrivateGetPassportForTCPFunction:: WebSocketProxyPrivateGetPassportForTCPFunction() { // This obsolete API uses fixed port to listen websocket connections. listening_port_ = 10101; } WebSocketProxyPrivateGetPassportForTCPFunction:: ~WebSocketProxyPrivateGetPassportForTCPFunction() { } void WebSocketProxyPrivateGetPassportForTCPFunction::CustomFinalize() { #if defined(OS_CHROMEOS) std::string passport = browser::InternalAuthGeneration::GeneratePassport( "web_socket_proxy", map_) + std::string(":"); if (ContainsKey(map_, "addr")) passport += map_["addr"]; result_.reset(Value::CreateStringValue(passport)); #endif }