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