Home | History | Annotate | Download | only in p2p
      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 "content/renderer/p2p/port_allocator.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/strings/string_util.h"
     12 #include "content/public/common/content_switches.h"
     13 #include "content/renderer/p2p/host_address_request.h"
     14 #include "jingle/glue/utils.h"
     15 #include "net/base/escape.h"
     16 #include "net/base/ip_endpoint.h"
     17 #include "third_party/WebKit/public/platform/WebURLError.h"
     18 #include "third_party/WebKit/public/platform/WebURLLoader.h"
     19 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     20 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     21 #include "third_party/WebKit/public/web/WebFrame.h"
     22 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
     23 
     24 using blink::WebString;
     25 using blink::WebURL;
     26 using blink::WebURLLoader;
     27 using blink::WebURLLoaderOptions;
     28 using blink::WebURLRequest;
     29 using blink::WebURLResponse;
     30 
     31 namespace content {
     32 
     33 namespace {
     34 
     35 // URL used to create a relay session.
     36 const char kCreateRelaySessionURL[] = "/create_session";
     37 
     38 // Number of times we will try to request relay session.
     39 const int kRelaySessionRetries = 3;
     40 
     41 // Manimum relay server size we would try to parse.
     42 const int kMaximumRelayResponseSize = 102400;
     43 
     44 bool ParsePortNumber(
     45     const std::string& string, int* value) {
     46   if (!base::StringToInt(string, value) || *value <= 0 || *value >= 65536) {
     47     LOG(ERROR) << "Received invalid port number from relay server: " << string;
     48     return false;
     49   }
     50   return true;
     51 }
     52 
     53 }  // namespace
     54 
     55 P2PPortAllocator::Config::Config()
     56     : stun_server_port(0),
     57       legacy_relay(true),
     58       disable_tcp_transport(false) {
     59 }
     60 
     61 P2PPortAllocator::Config::~Config() {
     62 }
     63 
     64 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
     65     : port(0) {
     66 }
     67 
     68 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
     69 }
     70 
     71 P2PPortAllocator::P2PPortAllocator(
     72     blink::WebFrame* web_frame,
     73     P2PSocketDispatcher* socket_dispatcher,
     74     talk_base::NetworkManager* network_manager,
     75     talk_base::PacketSocketFactory* socket_factory,
     76     const Config& config)
     77     : cricket::BasicPortAllocator(network_manager, socket_factory),
     78       web_frame_(web_frame),
     79       socket_dispatcher_(socket_dispatcher),
     80       config_(config) {
     81   uint32 flags = 0;
     82   if (config_.disable_tcp_transport)
     83     flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
     84   set_flags(flags);
     85   // TODO(ronghuawu): crbug/138185 add ourselves to the firewall list in browser
     86   // process and then remove below line.
     87   if (!CommandLine::ForCurrentProcess()->HasSwitch(
     88           switches::kEnableWebRtcTcpServerSocket)) {
     89     set_allow_tcp_listen(false);
     90   }
     91 }
     92 
     93 P2PPortAllocator::~P2PPortAllocator() {
     94 }
     95 
     96 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal(
     97     const std::string& content_name,
     98     int component,
     99     const std::string& ice_username_fragment,
    100     const std::string& ice_password) {
    101   return new P2PPortAllocatorSession(
    102       this, content_name, component, ice_username_fragment, ice_password);
    103 }
    104 
    105 P2PPortAllocatorSession::RelayServer::RelayServer() {
    106 }
    107 
    108 P2PPortAllocatorSession::RelayServer::~RelayServer() {
    109 }
    110 
    111 P2PPortAllocatorSession::P2PPortAllocatorSession(
    112     P2PPortAllocator* allocator,
    113     const std::string& content_name,
    114     int component,
    115     const std::string& ice_username_fragment,
    116     const std::string& ice_password)
    117     : cricket::BasicPortAllocatorSession(
    118         allocator, content_name, component,
    119         ice_username_fragment, ice_password),
    120       allocator_(allocator),
    121       relay_session_attempts_(0),
    122       relay_udp_port_(0),
    123       relay_tcp_port_(0),
    124       relay_ssltcp_port_(0),
    125       pending_relay_requests_(0) {
    126 }
    127 
    128 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
    129   if (stun_address_request_.get())
    130     stun_address_request_->Cancel();
    131 
    132   for (size_t i = 0; i < relay_info_.size(); ++i) {
    133     if (relay_info_[i].relay_address_request.get())
    134       relay_info_[i].relay_address_request->Cancel();
    135   }
    136 }
    137 
    138 void P2PPortAllocatorSession::didReceiveData(
    139     WebURLLoader* loader, const char* data,
    140     int data_length, int encoded_data_length) {
    141   DCHECK_EQ(loader, relay_session_request_.get());
    142   if (static_cast<int>(relay_session_response_.size()) + data_length >
    143       kMaximumRelayResponseSize) {
    144     LOG(ERROR) << "Response received from the server is too big.";
    145     loader->cancel();
    146     return;
    147   }
    148   relay_session_response_.append(data, data + data_length);
    149 }
    150 
    151 void P2PPortAllocatorSession::didFinishLoading(WebURLLoader* loader,
    152                                                double finish_time) {
    153   ParseRelayResponse();
    154 }
    155 
    156 void P2PPortAllocatorSession::didFail(blink::WebURLLoader* loader,
    157                                       const blink::WebURLError& error) {
    158   DCHECK_EQ(loader, relay_session_request_.get());
    159   DCHECK_NE(error.reason, 0);
    160 
    161   LOG(ERROR) << "Relay session request failed.";
    162 
    163   // Retry the request.
    164   AllocateLegacyRelaySession();
    165 }
    166 
    167 void P2PPortAllocatorSession::GetPortConfigurations() {
    168   // Resolve Stun and Relay server addresses.
    169   if (!allocator_->config_.stun_server.empty() &&
    170       stun_server_address_.IsNil()) {
    171     ResolveStunServerAddress();
    172   } else {
    173     AddConfig();
    174   }
    175 
    176   if (allocator_->config_.legacy_relay) {
    177     AllocateLegacyRelaySession();
    178   } else {
    179     ResolveRelayServerAddresses();
    180   }
    181 }
    182 
    183 void P2PPortAllocatorSession::ResolveStunServerAddress() {
    184   if (stun_address_request_.get())
    185     return;
    186 
    187   stun_address_request_ =
    188       new P2PHostAddressRequest(allocator_->socket_dispatcher_);
    189   stun_address_request_->Request(allocator_->config_.stun_server, base::Bind(
    190       &P2PPortAllocatorSession::OnStunServerAddress,
    191       base::Unretained(this)));
    192 }
    193 
    194 void P2PPortAllocatorSession::OnStunServerAddress(
    195     const net::IPAddressNumber& address) {
    196   if (address.empty()) {
    197     LOG(ERROR) << "Failed to resolve STUN server address "
    198                << allocator_->config_.stun_server;
    199     // Allocating local ports on stun failure.
    200     AddConfig();
    201     return;
    202   }
    203 
    204   if (!jingle_glue::IPEndPointToSocketAddress(
    205           net::IPEndPoint(address, allocator_->config_.stun_server_port),
    206           &stun_server_address_)) {
    207     return;
    208   }
    209   AddConfig();
    210 }
    211 
    212 void P2PPortAllocatorSession::ResolveRelayServerAddresses() {
    213   for (size_t i = 0; i < allocator_->config_.relays.size(); ++i) {
    214     scoped_refptr<P2PHostAddressRequest> relay_request =
    215         new P2PHostAddressRequest(allocator_->socket_dispatcher_);
    216     relay_request->Request(
    217         allocator_->config_.relays[i].server_address,
    218         base::Bind(&P2PPortAllocatorSession::OnRelayServerAddressResolved,
    219                    base::Unretained(this), i));
    220     // Copy relay configuration from alloctor and keeping it in a map.
    221     RelayServer relay;
    222     relay.config = allocator_->config_.relays[i];
    223     relay.relay_address_request = relay_request;
    224     relay_info_.push_back(relay);
    225     ++pending_relay_requests_;
    226   }
    227 }
    228 
    229 void P2PPortAllocatorSession::OnRelayServerAddressResolved(
    230     size_t index, const net::IPAddressNumber& address) {
    231   // Let's first decrement the pending requests count.
    232   --pending_relay_requests_;
    233   if (index > relay_info_.size()) {
    234     NOTREACHED();
    235     return;
    236   }
    237 
    238   if (address.empty()) {
    239     LOG(ERROR) << "Failed to resolve Relay server address "
    240                << relay_info_.at(index).config.server_address;
    241   } else {
    242     // Getting relay server info for which this resolved address belongs.
    243     RelayServer& relay_server = relay_info_.at(index);
    244 
    245     talk_base::SocketAddress socket_address;
    246     if (!jingle_glue::IPEndPointToSocketAddress(
    247             net::IPEndPoint(address, relay_server.config.port),
    248             &socket_address)) {
    249       NOTREACHED();
    250     }
    251     relay_server.resolved_relay_address = socket_address;
    252   }
    253 
    254   if (!pending_relay_requests_)
    255     AddConfig();
    256 }
    257 
    258 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
    259   if (allocator_->config_.relays.empty())
    260     return;
    261   // If we are using legacy relay, we will have only one entry in relay server
    262   // list.
    263   P2PPortAllocator::Config::RelayServerConfig relay_config =
    264       allocator_->config_.relays[0];
    265 
    266   if (relay_session_attempts_ > kRelaySessionRetries)
    267     return;
    268   relay_session_attempts_++;
    269 
    270   relay_session_response_.clear();
    271 
    272   WebURLLoaderOptions options;
    273   options.allowCredentials = false;
    274 
    275   options.crossOriginRequestPolicy =
    276       WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
    277 
    278   relay_session_request_.reset(
    279       allocator_->web_frame_->createAssociatedURLLoader(options));
    280   if (!relay_session_request_) {
    281     LOG(ERROR) << "Failed to create URL loader.";
    282     return;
    283   }
    284 
    285   std::string url = "https://" + relay_config.server_address +
    286       kCreateRelaySessionURL +
    287       "?username=" + net::EscapeUrlEncodedData(username(), true) +
    288       "&password=" + net::EscapeUrlEncodedData(password(), true);
    289 
    290   WebURLRequest request;
    291   request.initialize();
    292   request.setURL(WebURL(GURL(url)));
    293   request.setAllowStoredCredentials(false);
    294   request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData);
    295   request.setHTTPMethod("GET");
    296   request.addHTTPHeaderField(
    297       WebString::fromUTF8("X-Talk-Google-Relay-Auth"),
    298       WebString::fromUTF8(relay_config.password));
    299   request.addHTTPHeaderField(
    300       WebString::fromUTF8("X-Google-Relay-Auth"),
    301       WebString::fromUTF8(relay_config.username));
    302   request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"),
    303                              WebString::fromUTF8("chromoting"));
    304 
    305   relay_session_request_->loadAsynchronously(request, this);
    306 }
    307 
    308 void P2PPortAllocatorSession::ParseRelayResponse() {
    309   std::vector<std::pair<std::string, std::string> > value_pairs;
    310   if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n',
    311                                           &value_pairs)) {
    312     LOG(ERROR) << "Received invalid response from relay server";
    313     return;
    314   }
    315 
    316   relay_ip_.Clear();
    317   relay_udp_port_ = 0;
    318   relay_tcp_port_ = 0;
    319   relay_ssltcp_port_ = 0;
    320 
    321   for (std::vector<std::pair<std::string, std::string> >::iterator
    322            it = value_pairs.begin();
    323        it != value_pairs.end(); ++it) {
    324     std::string key;
    325     std::string value;
    326     TrimWhitespaceASCII(it->first, TRIM_ALL, &key);
    327     TrimWhitespaceASCII(it->second, TRIM_ALL, &value);
    328 
    329     if (key == "username") {
    330       if (value != username()) {
    331         LOG(ERROR) << "When creating relay session received user name "
    332             " that was different from the value specified in the query.";
    333         return;
    334       }
    335     } else if (key == "password") {
    336       if (value != password()) {
    337         LOG(ERROR) << "When creating relay session received password "
    338             "that was different from the value specified in the query.";
    339         return;
    340       }
    341     } else if (key == "relay.ip") {
    342       relay_ip_.SetIP(value);
    343       if (relay_ip_.ip() == 0) {
    344         LOG(ERROR) << "Received unresolved relay server address: " << value;
    345         return;
    346       }
    347     } else if (key == "relay.udp_port") {
    348       if (!ParsePortNumber(value, &relay_udp_port_))
    349         return;
    350     } else if (key == "relay.tcp_port") {
    351       if (!ParsePortNumber(value, &relay_tcp_port_))
    352         return;
    353     } else if (key == "relay.ssltcp_port") {
    354       if (!ParsePortNumber(value, &relay_ssltcp_port_))
    355         return;
    356     }
    357   }
    358 
    359   AddConfig();
    360 }
    361 
    362 void P2PPortAllocatorSession::AddConfig() {
    363   cricket::PortConfiguration* port_config = new cricket::PortConfiguration(
    364       stun_server_address_, std::string(), std::string());
    365 
    366   if (!pending_relay_requests_) {
    367     // Push all resolved addresses and transport port type to allocator.
    368     for (size_t i = 0; i < relay_info_.size(); ++i) {
    369       if (relay_info_[i].resolved_relay_address.IsNil())
    370         continue;
    371 
    372       RelayServer relay_info = relay_info_[i];
    373       cricket::RelayCredentials credentials(relay_info.config.username,
    374                                             relay_info.config.password);
    375       cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
    376       cricket::ProtocolType protocol;
    377       if (!cricket::StringToProto(relay_info.config.transport_type.c_str(),
    378                                   &protocol)) {
    379         DLOG(WARNING) << "Ignoring TURN server "
    380                       << relay_info.config.server_address << ". "
    381                       << "Reason= Incorrect "
    382                       << relay_info.config.transport_type
    383                       << " transport parameter.";
    384         continue;
    385       }
    386 
    387       relay_server.ports.push_back(cricket::ProtocolAddress(
    388           relay_info.resolved_relay_address,
    389           protocol,
    390           relay_info.config.secure));
    391       relay_server.credentials = credentials;
    392       port_config->AddRelay(relay_server);
    393     }
    394   }
    395   ConfigReady(port_config);
    396 }
    397 
    398 }  // namespace content
    399