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 "net/base/escape.h"
     14 #include "net/base/ip_endpoint.h"
     15 #include "third_party/WebKit/public/platform/WebURLError.h"
     16 #include "third_party/WebKit/public/platform/WebURLLoader.h"
     17 #include "third_party/WebKit/public/platform/WebURLRequest.h"
     18 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     19 #include "third_party/WebKit/public/web/WebFrame.h"
     20 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
     21 
     22 using blink::WebString;
     23 using blink::WebURL;
     24 using blink::WebURLLoader;
     25 using blink::WebURLLoaderOptions;
     26 using blink::WebURLRequest;
     27 using blink::WebURLResponse;
     28 
     29 namespace content {
     30 
     31 namespace {
     32 
     33 // URL used to create a relay session.
     34 const char kCreateRelaySessionURL[] = "/create_session";
     35 
     36 // Number of times we will try to request relay session.
     37 const int kRelaySessionRetries = 3;
     38 
     39 // Manimum relay server size we would try to parse.
     40 const int kMaximumRelayResponseSize = 102400;
     41 
     42 bool ParsePortNumber(
     43     const std::string& string, int* value) {
     44   if (!base::StringToInt(string, value) || *value <= 0 || *value >= 65536) {
     45     LOG(ERROR) << "Received invalid port number from relay server: " << string;
     46     return false;
     47   }
     48   return true;
     49 }
     50 
     51 }  // namespace
     52 
     53 P2PPortAllocator::Config::Config()
     54     : legacy_relay(true),
     55       disable_tcp_transport(false) {
     56 }
     57 
     58 P2PPortAllocator::Config::~Config() {
     59 }
     60 
     61 P2PPortAllocator::Config::RelayServerConfig::RelayServerConfig()
     62     : port(0) {
     63 }
     64 
     65 P2PPortAllocator::Config::RelayServerConfig::~RelayServerConfig() {
     66 }
     67 
     68 P2PPortAllocator::P2PPortAllocator(
     69     blink::WebFrame* web_frame,
     70     P2PSocketDispatcher* socket_dispatcher,
     71     rtc::NetworkManager* network_manager,
     72     rtc::PacketSocketFactory* socket_factory,
     73     const Config& config)
     74     : cricket::BasicPortAllocator(network_manager, socket_factory),
     75       web_frame_(web_frame),
     76       socket_dispatcher_(socket_dispatcher),
     77       config_(config) {
     78   uint32 flags = 0;
     79   if (config_.disable_tcp_transport)
     80     flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
     81   set_flags(flags);
     82   set_allow_tcp_listen(false);
     83 }
     84 
     85 P2PPortAllocator::~P2PPortAllocator() {
     86 }
     87 
     88 cricket::PortAllocatorSession* P2PPortAllocator::CreateSessionInternal(
     89     const std::string& content_name,
     90     int component,
     91     const std::string& ice_username_fragment,
     92     const std::string& ice_password) {
     93   return new P2PPortAllocatorSession(
     94       this, content_name, component, ice_username_fragment, ice_password);
     95 }
     96 
     97 P2PPortAllocatorSession::P2PPortAllocatorSession(
     98     P2PPortAllocator* allocator,
     99     const std::string& content_name,
    100     int component,
    101     const std::string& ice_username_fragment,
    102     const std::string& ice_password)
    103     : cricket::BasicPortAllocatorSession(
    104         allocator, content_name, component,
    105         ice_username_fragment, ice_password),
    106       allocator_(allocator),
    107       relay_session_attempts_(0),
    108       relay_udp_port_(0),
    109       relay_tcp_port_(0),
    110       relay_ssltcp_port_(0),
    111       pending_relay_requests_(0) {
    112 }
    113 
    114 P2PPortAllocatorSession::~P2PPortAllocatorSession() {
    115 }
    116 
    117 void P2PPortAllocatorSession::didReceiveData(
    118     WebURLLoader* loader, const char* data,
    119     int data_length, int encoded_data_length) {
    120   DCHECK_EQ(loader, relay_session_request_.get());
    121   if (static_cast<int>(relay_session_response_.size()) + data_length >
    122       kMaximumRelayResponseSize) {
    123     LOG(ERROR) << "Response received from the server is too big.";
    124     loader->cancel();
    125     return;
    126   }
    127   relay_session_response_.append(data, data + data_length);
    128 }
    129 
    130 void P2PPortAllocatorSession::didFinishLoading(
    131     WebURLLoader* loader, double finish_time,
    132     int64_t total_encoded_data_length) {
    133   ParseRelayResponse();
    134 }
    135 
    136 void P2PPortAllocatorSession::didFail(blink::WebURLLoader* loader,
    137                                       const blink::WebURLError& error) {
    138   DCHECK_EQ(loader, relay_session_request_.get());
    139   DCHECK_NE(error.reason, 0);
    140 
    141   LOG(ERROR) << "Relay session request failed.";
    142 
    143   // Retry the request.
    144   AllocateLegacyRelaySession();
    145 }
    146 
    147 void P2PPortAllocatorSession::GetPortConfigurations() {
    148   if (allocator_->config_.legacy_relay) {
    149     AllocateLegacyRelaySession();
    150   }
    151   AddConfig();
    152 }
    153 
    154 void P2PPortAllocatorSession::AllocateLegacyRelaySession() {
    155   if (allocator_->config_.relays.empty())
    156     return;
    157   // If we are using legacy relay, we will have only one entry in relay server
    158   // list.
    159   P2PPortAllocator::Config::RelayServerConfig relay_config =
    160       allocator_->config_.relays[0];
    161 
    162   if (relay_session_attempts_ > kRelaySessionRetries)
    163     return;
    164   relay_session_attempts_++;
    165 
    166   relay_session_response_.clear();
    167 
    168   WebURLLoaderOptions options;
    169   options.allowCredentials = false;
    170 
    171   options.crossOriginRequestPolicy =
    172       WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
    173 
    174   relay_session_request_.reset(
    175       allocator_->web_frame_->createAssociatedURLLoader(options));
    176   if (!relay_session_request_) {
    177     LOG(ERROR) << "Failed to create URL loader.";
    178     return;
    179   }
    180 
    181   std::string url = "https://" + relay_config.server_address +
    182       kCreateRelaySessionURL +
    183       "?username=" + net::EscapeUrlEncodedData(username(), true) +
    184       "&password=" + net::EscapeUrlEncodedData(password(), true);
    185 
    186   WebURLRequest request;
    187   request.initialize();
    188   request.setURL(WebURL(GURL(url)));
    189   request.setAllowStoredCredentials(false);
    190   request.setCachePolicy(WebURLRequest::ReloadIgnoringCacheData);
    191   request.setHTTPMethod("GET");
    192   request.addHTTPHeaderField(
    193       WebString::fromUTF8("X-Talk-Google-Relay-Auth"),
    194       WebString::fromUTF8(relay_config.password));
    195   request.addHTTPHeaderField(
    196       WebString::fromUTF8("X-Google-Relay-Auth"),
    197       WebString::fromUTF8(relay_config.username));
    198   request.addHTTPHeaderField(WebString::fromUTF8("X-Stream-Type"),
    199                              WebString::fromUTF8("chromoting"));
    200 
    201   relay_session_request_->loadAsynchronously(request, this);
    202 }
    203 
    204 void P2PPortAllocatorSession::ParseRelayResponse() {
    205   base::StringPairs value_pairs;
    206   if (!base::SplitStringIntoKeyValuePairs(relay_session_response_, '=', '\n',
    207                                           &value_pairs)) {
    208     LOG(ERROR) << "Received invalid response from relay server";
    209     return;
    210   }
    211 
    212   relay_ip_.Clear();
    213   relay_udp_port_ = 0;
    214   relay_tcp_port_ = 0;
    215   relay_ssltcp_port_ = 0;
    216 
    217   for (base::StringPairs::iterator it = value_pairs.begin();
    218        it != value_pairs.end(); ++it) {
    219     std::string key;
    220     std::string value;
    221     base::TrimWhitespaceASCII(it->first, base::TRIM_ALL, &key);
    222     base::TrimWhitespaceASCII(it->second, base::TRIM_ALL, &value);
    223 
    224     if (key == "username") {
    225       if (value != username()) {
    226         LOG(ERROR) << "When creating relay session received user name "
    227             " that was different from the value specified in the query.";
    228         return;
    229       }
    230     } else if (key == "password") {
    231       if (value != password()) {
    232         LOG(ERROR) << "When creating relay session received password "
    233             "that was different from the value specified in the query.";
    234         return;
    235       }
    236     } else if (key == "relay.ip") {
    237       relay_ip_.SetIP(value);
    238       if (relay_ip_.ip() == 0) {
    239         LOG(ERROR) << "Received unresolved relay server address: " << value;
    240         return;
    241       }
    242     } else if (key == "relay.udp_port") {
    243       if (!ParsePortNumber(value, &relay_udp_port_))
    244         return;
    245     } else if (key == "relay.tcp_port") {
    246       if (!ParsePortNumber(value, &relay_tcp_port_))
    247         return;
    248     } else if (key == "relay.ssltcp_port") {
    249       if (!ParsePortNumber(value, &relay_ssltcp_port_))
    250         return;
    251     }
    252   }
    253 
    254   AddConfig();
    255 }
    256 
    257 void P2PPortAllocatorSession::AddConfig() {
    258   const P2PPortAllocator::Config& config = allocator_->config_;
    259   cricket::PortConfiguration* port_config = new cricket::PortConfiguration(
    260       config.stun_servers, std::string(), std::string());
    261 
    262   for (size_t i = 0; i < config.relays.size(); ++i) {
    263     cricket::RelayCredentials credentials(config.relays[i].username,
    264                                           config.relays[i].password);
    265     cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
    266     cricket::ProtocolType protocol;
    267     if (!cricket::StringToProto(config.relays[i].transport_type.c_str(),
    268                                 &protocol)) {
    269       DLOG(WARNING) << "Ignoring TURN server "
    270                     << config.relays[i].server_address << ". "
    271                     << "Reason= Incorrect "
    272                     << config.relays[i].transport_type
    273                     << " transport parameter.";
    274       continue;
    275     }
    276 
    277     relay_server.ports.push_back(cricket::ProtocolAddress(
    278         rtc::SocketAddress(config.relays[i].server_address,
    279                                  config.relays[i].port),
    280         protocol,
    281         config.relays[i].secure));
    282     relay_server.credentials = credentials;
    283     port_config->AddRelay(relay_server);
    284   }
    285   ConfigReady(port_config);
    286 }
    287 
    288 }  // namespace content
    289