1 // Copyright 2014 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/protocol/chromium_port_allocator.h" 6 7 #include "base/bind.h" 8 #include "base/stl_util.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "net/http/http_status_code.h" 11 #include "net/url_request/url_fetcher.h" 12 #include "net/url_request/url_fetcher_delegate.h" 13 #include "net/url_request/url_request_context_getter.h" 14 #include "remoting/protocol/chromium_socket_factory.h" 15 #include "remoting/protocol/network_settings.h" 16 #include "url/gurl.h" 17 18 namespace remoting { 19 namespace protocol { 20 21 namespace { 22 23 class ChromiumPortAllocatorSession 24 : public cricket::HttpPortAllocatorSessionBase, 25 public net::URLFetcherDelegate { 26 public: 27 ChromiumPortAllocatorSession( 28 cricket::HttpPortAllocatorBase* allocator, 29 const std::string& content_name, 30 int component, 31 const std::string& ice_username_fragment, 32 const std::string& ice_password, 33 const std::vector<rtc::SocketAddress>& stun_hosts, 34 const std::vector<std::string>& relay_hosts, 35 const std::string& relay, 36 const scoped_refptr<net::URLRequestContextGetter>& url_context); 37 virtual ~ChromiumPortAllocatorSession(); 38 39 // cricket::HttpPortAllocatorBase overrides. 40 virtual void ConfigReady(cricket::PortConfiguration* config) OVERRIDE; 41 virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE; 42 43 // net::URLFetcherDelegate interface. 44 virtual void OnURLFetchComplete(const net::URLFetcher* url_fetcher) OVERRIDE; 45 46 private: 47 scoped_refptr<net::URLRequestContextGetter> url_context_; 48 std::set<const net::URLFetcher*> url_fetchers_; 49 50 DISALLOW_COPY_AND_ASSIGN(ChromiumPortAllocatorSession); 51 }; 52 53 ChromiumPortAllocatorSession::ChromiumPortAllocatorSession( 54 cricket::HttpPortAllocatorBase* allocator, 55 const std::string& content_name, 56 int component, 57 const std::string& ice_username_fragment, 58 const std::string& ice_password, 59 const std::vector<rtc::SocketAddress>& stun_hosts, 60 const std::vector<std::string>& relay_hosts, 61 const std::string& relay, 62 const scoped_refptr<net::URLRequestContextGetter>& url_context) 63 : HttpPortAllocatorSessionBase(allocator, 64 content_name, 65 component, 66 ice_username_fragment, 67 ice_password, 68 stun_hosts, 69 relay_hosts, 70 relay, 71 std::string()), 72 url_context_(url_context) {} 73 74 ChromiumPortAllocatorSession::~ChromiumPortAllocatorSession() { 75 STLDeleteElements(&url_fetchers_); 76 } 77 78 void ChromiumPortAllocatorSession::ConfigReady( 79 cricket::PortConfiguration* config) { 80 // Filter out non-UDP relay ports, so that we don't try using TCP. 81 for (cricket::PortConfiguration::RelayList::iterator relay = 82 config->relays.begin(); relay != config->relays.end(); ++relay) { 83 cricket::PortList filtered_ports; 84 for (cricket::PortList::iterator port = 85 relay->ports.begin(); port != relay->ports.end(); ++port) { 86 if (port->proto == cricket::PROTO_UDP) { 87 filtered_ports.push_back(*port); 88 } 89 } 90 relay->ports = filtered_ports; 91 } 92 cricket::BasicPortAllocatorSession::ConfigReady(config); 93 } 94 95 void ChromiumPortAllocatorSession::SendSessionRequest( 96 const std::string& host, 97 int port) { 98 GURL url("https://" + host + ":" + base::IntToString(port) + 99 GetSessionRequestUrl() + "&sn=1"); 100 scoped_ptr<net::URLFetcher> url_fetcher( 101 net::URLFetcher::Create(url, net::URLFetcher::GET, this)); 102 url_fetcher->SetRequestContext(url_context_.get()); 103 url_fetcher->AddExtraRequestHeader("X-Talk-Google-Relay-Auth: " + 104 relay_token()); 105 url_fetcher->AddExtraRequestHeader("X-Google-Relay-Auth: " + relay_token()); 106 url_fetcher->AddExtraRequestHeader("X-Stream-Type: chromoting"); 107 url_fetcher->Start(); 108 url_fetchers_.insert(url_fetcher.release()); 109 } 110 111 void ChromiumPortAllocatorSession::OnURLFetchComplete( 112 const net::URLFetcher* source) { 113 int response_code = source->GetResponseCode(); 114 std::string response; 115 source->GetResponseAsString(&response); 116 117 url_fetchers_.erase(source); 118 delete source; 119 120 if (response_code != net::HTTP_OK) { 121 LOG(WARNING) << "Received error when allocating relay session: " 122 << response_code; 123 TryCreateRelaySession(); 124 return; 125 } 126 127 ReceiveSessionResponse(response); 128 } 129 130 } // namespace 131 132 // static 133 scoped_ptr<ChromiumPortAllocator> ChromiumPortAllocator::Create( 134 const scoped_refptr<net::URLRequestContextGetter>& url_context, 135 const NetworkSettings& network_settings) { 136 scoped_ptr<rtc::NetworkManager> network_manager( 137 new rtc::BasicNetworkManager()); 138 scoped_ptr<rtc::PacketSocketFactory> socket_factory( 139 new ChromiumPacketSocketFactory()); 140 scoped_ptr<ChromiumPortAllocator> result( 141 new ChromiumPortAllocator(url_context, network_manager.Pass(), 142 socket_factory.Pass())); 143 144 // We always use PseudoTcp to provide a reliable channel. It provides poor 145 // performance when combined with TCP-based transport, so we have to disable 146 // TCP ports. ENABLE_SHARED_UFRAG flag is specified so that the same username 147 // fragment is shared between all candidates for this channel. 148 int flags = cricket::PORTALLOCATOR_DISABLE_TCP | 149 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | 150 cricket::PORTALLOCATOR_ENABLE_IPV6; 151 152 if (!(network_settings.flags & NetworkSettings::NAT_TRAVERSAL_STUN)) 153 flags |= cricket::PORTALLOCATOR_DISABLE_STUN; 154 155 if (!(network_settings.flags & NetworkSettings::NAT_TRAVERSAL_RELAY)) 156 flags |= cricket::PORTALLOCATOR_DISABLE_RELAY; 157 158 result->set_flags(flags); 159 result->SetPortRange(network_settings.min_port, 160 network_settings.max_port); 161 162 return result.Pass(); 163 } 164 165 ChromiumPortAllocator::ChromiumPortAllocator( 166 const scoped_refptr<net::URLRequestContextGetter>& url_context, 167 scoped_ptr<rtc::NetworkManager> network_manager, 168 scoped_ptr<rtc::PacketSocketFactory> socket_factory) 169 : HttpPortAllocatorBase(network_manager.get(), 170 socket_factory.get(), 171 std::string()), 172 url_context_(url_context), 173 network_manager_(network_manager.Pass()), 174 socket_factory_(socket_factory.Pass()) {} 175 176 ChromiumPortAllocator::~ChromiumPortAllocator() { 177 } 178 179 cricket::PortAllocatorSession* ChromiumPortAllocator::CreateSessionInternal( 180 const std::string& content_name, 181 int component, 182 const std::string& ice_username_fragment, 183 const std::string& ice_password) { 184 return new ChromiumPortAllocatorSession( 185 this, content_name, component, ice_username_fragment, ice_password, 186 stun_hosts(), relay_hosts(), relay_token(), url_context_); 187 } 188 189 } // namespace protocol 190 } // namespace remoting 191