| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | 2 // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 // found in the LICENSE file. | 
|  | 4 | 
|  | 5 #include "remoting/host/host_status_service.h" | 
|  | 6 | 
|  | 7 #include "base/json/json_reader.h" | 
|  | 8 #include "base/json/json_writer.h" | 
|  | 9 #include "base/stl_util.h" | 
|  | 10 #include "base/string_number_conversions.h" | 
|  | 11 #include "base/string_util.h" | 
|  | 12 #include "base/stringize_macros.h" | 
|  | 13 #include "base/values.h" | 
|  | 14 #include "net/base/ip_endpoint.h" | 
|  | 15 #include "net/base/net_util.h" | 
|  | 16 #include "remoting/host/websocket_connection.h" | 
|  | 17 #include "remoting/host/websocket_listener.h" | 
|  | 18 | 
|  | 19 namespace remoting { | 
|  | 20 | 
|  | 21 namespace { | 
|  | 22 | 
|  | 23 // HostStatusService uses the first port available in the following range. | 
|  | 24 const int kPortRangeMin = 12810; | 
|  | 25 const int kPortRangeMax = 12820; | 
|  | 26 | 
|  | 27 const char kChromeExtensionUrlSchemePrefix[] = "chrome-extension://"; | 
|  | 28 | 
|  | 29 #if defined(NDEBUG) | 
|  | 30 const char* const kAllowedWebApplicationIds[] = { | 
|  | 31   "gbchcmhmhahfdphkhkmpfmihenigjmpp",  // Chrome Remote Desktop | 
|  | 32   "kgngmbheleoaphbjbaiobfdepmghbfah",  // Pre-release Chrome Remote Desktop | 
|  | 33   "odkaodonbgfohohmklejpjiejmcipmib",  // Dogfood Chrome Remote Desktop | 
|  | 34   "ojoimpklfciegopdfgeenehpalipignm",  // Chromoting canary | 
|  | 35 }; | 
|  | 36 #endif | 
|  | 37 | 
|  | 38 // All messages we expect should be smaller than 64k. | 
|  | 39 const uint kMaximumIncomingMessageSize = 65536; | 
|  | 40 | 
|  | 41 }  // namespace | 
|  | 42 | 
|  | 43 class HostStatusService::Connection : public WebsocketConnection::Delegate { | 
|  | 44  public: | 
|  | 45   // |service| owns Connection connection objects and must outlive them. | 
|  | 46   Connection(HostStatusService* service, | 
|  | 47              scoped_ptr<WebsocketConnection> websocket); | 
|  | 48   virtual ~Connection(); | 
|  | 49 | 
|  | 50   // WebsocketConnection::Delegate interface. | 
|  | 51   virtual void OnWebsocketMessage(const std::string& message) OVERRIDE; | 
|  | 52   virtual void OnWebsocketClosed() OVERRIDE; | 
|  | 53 | 
|  | 54  private: | 
|  | 55   // Sends message with the specified |method| and |data|. | 
|  | 56   void SendMessage(const std::string& method, | 
|  | 57                    scoped_ptr<base::DictionaryValue> data); | 
|  | 58 | 
|  | 59    // Closes the connection and destroys this object. | 
|  | 60   void Close(); | 
|  | 61 | 
|  | 62   HostStatusService* service_; | 
|  | 63   scoped_ptr<WebsocketConnection> websocket_; | 
|  | 64 | 
|  | 65   DISALLOW_COPY_AND_ASSIGN(Connection); | 
|  | 66 }; | 
|  | 67 | 
|  | 68 HostStatusService::Connection::Connection( | 
|  | 69     HostStatusService* service, | 
|  | 70     scoped_ptr<WebsocketConnection> websocket) | 
|  | 71     : service_(service), | 
|  | 72       websocket_(websocket.Pass()) { | 
|  | 73   websocket_->Accept(this); | 
|  | 74   websocket_->set_maximum_message_size(kMaximumIncomingMessageSize); | 
|  | 75 } | 
|  | 76 | 
|  | 77 HostStatusService::Connection::~Connection() { | 
|  | 78 } | 
|  | 79 | 
|  | 80 void HostStatusService::Connection::OnWebsocketMessage( | 
|  | 81     const std::string& message) { | 
|  | 82   scoped_ptr<base::Value> json( | 
|  | 83       base::JSONReader::Read(message, base::JSON_ALLOW_TRAILING_COMMAS)); | 
|  | 84 | 
|  | 85   // Verify that we've received a valid JSON dictionary and extract |method| and | 
|  | 86   // |data| fields from it. | 
|  | 87   base::DictionaryValue* message_dict = NULL; | 
|  | 88   std::string method; | 
|  | 89   base::DictionaryValue* data = NULL; | 
|  | 90   if (!json.get() || | 
|  | 91       !json->GetAsDictionary(&message_dict) || | 
|  | 92       !message_dict->GetString("method", &method) || | 
|  | 93       !message_dict->GetDictionary("data", &data)) { | 
|  | 94     LOG(ERROR) << "Received invalid message: " << message; | 
|  | 95     Close(); | 
|  | 96     return; | 
|  | 97   } | 
|  | 98 | 
|  | 99   if (method == "getHostStatus") { | 
|  | 100     SendMessage("hostStatus", service_->GetStatusMessage()); | 
|  | 101   } else { | 
|  | 102     LOG(ERROR) << "Received message with unknown method: " << message; | 
|  | 103     scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); | 
|  | 104     response->SetString("method", method); | 
|  | 105     SendMessage("unsupportedMethod", response.Pass()); | 
|  | 106     return; | 
|  | 107   } | 
|  | 108 } | 
|  | 109 | 
|  | 110 void HostStatusService::Connection::OnWebsocketClosed() { | 
|  | 111   Close(); | 
|  | 112 } | 
|  | 113 | 
|  | 114 void HostStatusService::Connection::SendMessage( | 
|  | 115     const std::string& method, | 
|  | 116     scoped_ptr<base::DictionaryValue> data) { | 
|  | 117   scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue()); | 
|  | 118   message->SetString("method", method); | 
|  | 119   message->Set("data", data.release()); | 
|  | 120 | 
|  | 121   std::string message_json; | 
|  | 122   base::JSONWriter::Write(message.get(), &message_json); | 
|  | 123   websocket_->SendText(message_json); | 
|  | 124 } | 
|  | 125 | 
|  | 126 void HostStatusService::Connection::Close() { | 
|  | 127   websocket_.reset(); | 
|  | 128   service_->OnConnectionClosed(this); | 
|  | 129 } | 
|  | 130 | 
|  | 131 HostStatusService::HostStatusService() | 
|  | 132     : started_(false) { | 
|  | 133   // TODO(sergeyu): Do we need to listen on IPv6 port too? | 
|  | 134   char ip[] = {127, 0, 0, 1}; | 
|  | 135   net::IPAddressNumber localhost(ip, ip + sizeof(ip)); | 
|  | 136   for (int port = kPortRangeMin; port < kPortRangeMax; ++port) { | 
|  | 137     net::IPEndPoint endpoint(localhost, port); | 
|  | 138     // base::Unretained is safe because we own |websocket_listener_|. | 
|  | 139     if (websocket_listener_.Listen( | 
|  | 140             endpoint, | 
|  | 141             base::Bind(&HostStatusService::OnNewConnection, | 
|  | 142                        base::Unretained(this)))) { | 
|  | 143       service_host_name_ = "localhost:" + base::UintToString(port); | 
|  | 144       LOG(INFO) << "Listening for WebSocket connections on localhost:" << port; | 
|  | 145       break; | 
|  | 146     } | 
|  | 147   } | 
|  | 148 } | 
|  | 149 | 
|  | 150 HostStatusService::~HostStatusService() { | 
|  | 151   STLDeleteElements(&connections_); | 
|  | 152 } | 
|  | 153 | 
|  | 154 void HostStatusService::SetHostIsUp(const std::string& host_id) { | 
|  | 155   started_ = true; | 
|  | 156   host_id_ = host_id; | 
|  | 157 } | 
|  | 158 void HostStatusService::SetHostIsDown() { | 
|  | 159   started_ = false; | 
|  | 160   host_id_.clear(); | 
|  | 161 } | 
|  | 162 | 
|  | 163 // static | 
|  | 164 bool HostStatusService::IsAllowedOrigin(const std::string& origin) { | 
|  | 165 #ifndef NDEBUG | 
|  | 166   // Allow all chrome extensions in Debug builds. | 
|  | 167   return StartsWithASCII(origin, kChromeExtensionUrlSchemePrefix, false); | 
|  | 168 #else | 
|  | 169   // For Release builds allow only specific set of clients. | 
|  | 170   // | 
|  | 171   // TODO(sergeyu): Allow whitelisting of origins different from the ones in | 
|  | 172   // kAllowedWebApplicationIds (e.g. specify them in the host config). | 
|  | 173   std::string prefix(kChromeExtensionUrlSchemePrefix); | 
|  | 174   for (int i = 0; i < arraysize(kAllowedWebApplicationIds)) { | 
|  | 175     if (origin == prefix + kAllowedWebApplicationIds[i]) { | 
|  | 176       return true; | 
|  | 177     } | 
|  | 178   } | 
|  | 179   return false; | 
|  | 180 #endif | 
|  | 181 } | 
|  | 182 | 
|  | 183 void HostStatusService::OnNewConnection( | 
|  | 184     scoped_ptr<WebsocketConnection> connection) { | 
|  | 185   if (connection->request_host() != service_host_name_) { | 
|  | 186     LOG(ERROR) << "Received connection for invalid host: " | 
|  | 187                << connection->request_host() | 
|  | 188                << ". Expected " << service_host_name_; | 
|  | 189     connection->Reject(); | 
|  | 190     return; | 
|  | 191   } | 
|  | 192 | 
|  | 193   if (connection->request_path() != "/remoting_host_status") { | 
|  | 194     LOG(ERROR) << "Received connection for unknown path: " | 
|  | 195                << connection->request_path(); | 
|  | 196     connection->Reject(); | 
|  | 197     return; | 
|  | 198   } | 
|  | 199 | 
|  | 200   if (!IsAllowedOrigin(connection->origin())) { | 
|  | 201     LOG(ERROR) << "Rejecting connection from unknown origin: " | 
|  | 202                << connection->origin(); | 
|  | 203     connection->Reject(); | 
|  | 204     return; | 
|  | 205   } | 
|  | 206 | 
|  | 207   // Accept connection. | 
|  | 208   connections_.insert(new Connection(this, connection.Pass())); | 
|  | 209 } | 
|  | 210 | 
|  | 211 void HostStatusService::OnConnectionClosed(Connection* connection) { | 
|  | 212   connections_.erase(connection); | 
|  | 213   delete connection; | 
|  | 214 } | 
|  | 215 | 
|  | 216 scoped_ptr<base::DictionaryValue> HostStatusService::GetStatusMessage() { | 
|  | 217   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); | 
|  | 218   result->SetString("state", started_ ? "STARTED" : "STOPPED"); | 
|  | 219   result->SetString("version", STRINGIZE(VERSION)); | 
|  | 220   result->SetString("hostId", host_id_); | 
|  | 221   return result.Pass(); | 
|  | 222 } | 
|  | 223 | 
|  | 224 }  // namespace remoting | 
| OLD | NEW | 
|---|