1 /* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/p2p/client/httpportallocator.h" 12 13 #include <algorithm> 14 #include <map> 15 16 #include "webrtc/base/common.h" 17 #include "webrtc/base/helpers.h" 18 #include "webrtc/base/httpcommon.h" 19 #include "webrtc/base/logging.h" 20 #include "webrtc/base/nethelpers.h" 21 #include "webrtc/base/signalthread.h" 22 #include "webrtc/base/stringencode.h" 23 24 namespace { 25 26 // Helper routine to remove whitespace from the ends of a string. 27 void Trim(std::string& str) { 28 size_t first = str.find_first_not_of(" \t\r\n"); 29 if (first == std::string::npos) { 30 str.clear(); 31 return; 32 } 33 34 ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos); 35 } 36 37 // Parses the lines in the result of the HTTP request that are of the form 38 // 'a=b' and returns them in a map. 39 typedef std::map<std::string, std::string> StringMap; 40 void ParseMap(const std::string& string, StringMap& map) { 41 size_t start_of_line = 0; 42 size_t end_of_line = 0; 43 44 for (;;) { // for each line 45 start_of_line = string.find_first_not_of("\r\n", end_of_line); 46 if (start_of_line == std::string::npos) 47 break; 48 49 end_of_line = string.find_first_of("\r\n", start_of_line); 50 if (end_of_line == std::string::npos) { 51 end_of_line = string.length(); 52 } 53 54 size_t equals = string.find('=', start_of_line); 55 if ((equals >= end_of_line) || (equals == std::string::npos)) 56 continue; 57 58 std::string key(string, start_of_line, equals - start_of_line); 59 std::string value(string, equals + 1, end_of_line - equals - 1); 60 61 Trim(key); 62 Trim(value); 63 64 if ((key.size() > 0) && (value.size() > 0)) 65 map[key] = value; 66 } 67 } 68 69 } // namespace 70 71 namespace cricket { 72 73 // HttpPortAllocatorBase 74 75 const int HttpPortAllocatorBase::kNumRetries = 5; 76 77 const char HttpPortAllocatorBase::kCreateSessionURL[] = "/create_session"; 78 79 HttpPortAllocatorBase::HttpPortAllocatorBase( 80 rtc::NetworkManager* network_manager, 81 rtc::PacketSocketFactory* socket_factory, 82 const std::string &user_agent) 83 : BasicPortAllocator(network_manager, socket_factory), agent_(user_agent) { 84 relay_hosts_.push_back("relay.google.com"); 85 stun_hosts_.push_back( 86 rtc::SocketAddress("stun.l.google.com", 19302)); 87 } 88 89 HttpPortAllocatorBase::HttpPortAllocatorBase( 90 rtc::NetworkManager* network_manager, 91 const std::string &user_agent) 92 : BasicPortAllocator(network_manager), agent_(user_agent) { 93 relay_hosts_.push_back("relay.google.com"); 94 stun_hosts_.push_back( 95 rtc::SocketAddress("stun.l.google.com", 19302)); 96 } 97 98 HttpPortAllocatorBase::~HttpPortAllocatorBase() { 99 } 100 101 // HttpPortAllocatorSessionBase 102 103 HttpPortAllocatorSessionBase::HttpPortAllocatorSessionBase( 104 HttpPortAllocatorBase* allocator, 105 const std::string& content_name, 106 int component, 107 const std::string& ice_ufrag, 108 const std::string& ice_pwd, 109 const std::vector<rtc::SocketAddress>& stun_hosts, 110 const std::vector<std::string>& relay_hosts, 111 const std::string& relay_token, 112 const std::string& user_agent) 113 : BasicPortAllocatorSession(allocator, content_name, component, 114 ice_ufrag, ice_pwd), 115 relay_hosts_(relay_hosts), stun_hosts_(stun_hosts), 116 relay_token_(relay_token), agent_(user_agent), attempts_(0) { 117 } 118 119 HttpPortAllocatorSessionBase::~HttpPortAllocatorSessionBase() {} 120 121 void HttpPortAllocatorSessionBase::GetPortConfigurations() { 122 // Creating relay sessions can take time and is done asynchronously. 123 // Creating stun sessions could also take time and could be done aysnc also, 124 // but for now is done here and added to the initial config. Note any later 125 // configs will have unresolved stun ips and will be discarded by the 126 // AllocationSequence. 127 ServerAddresses hosts; 128 for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin(); 129 it != stun_hosts_.end(); ++it) { 130 hosts.insert(*it); 131 } 132 133 PortConfiguration* config = new PortConfiguration(hosts, 134 username(), 135 password()); 136 ConfigReady(config); 137 TryCreateRelaySession(); 138 } 139 140 void HttpPortAllocatorSessionBase::TryCreateRelaySession() { 141 if (allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) { 142 LOG(LS_VERBOSE) << "HttpPortAllocator: Relay ports disabled, skipping."; 143 return; 144 } 145 146 if (attempts_ == HttpPortAllocatorBase::kNumRetries) { 147 LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; " 148 << "giving up on relay."; 149 return; 150 } 151 152 if (relay_hosts_.size() == 0) { 153 LOG(LS_ERROR) << "HttpPortAllocator: no relay hosts configured."; 154 return; 155 } 156 157 // Choose the next host to try. 158 std::string host = relay_hosts_[attempts_ % relay_hosts_.size()]; 159 attempts_++; 160 LOG(LS_INFO) << "HTTPPortAllocator: sending to relay host " << host; 161 if (relay_token_.empty()) { 162 LOG(LS_WARNING) << "No relay auth token found."; 163 } 164 165 SendSessionRequest(host, rtc::HTTP_SECURE_PORT); 166 } 167 168 std::string HttpPortAllocatorSessionBase::GetSessionRequestUrl() { 169 std::string url = std::string(HttpPortAllocatorBase::kCreateSessionURL); 170 ASSERT(!username().empty()); 171 ASSERT(!password().empty()); 172 url = url + "?username=" + rtc::s_url_encode(username()) + 173 "&password=" + rtc::s_url_encode(password()); 174 return url; 175 } 176 177 void HttpPortAllocatorSessionBase::ReceiveSessionResponse( 178 const std::string& response) { 179 180 StringMap map; 181 ParseMap(response, map); 182 183 if (!username().empty() && map["username"] != username()) { 184 LOG(LS_WARNING) << "Received unexpected username value from relay server."; 185 } 186 if (!password().empty() && map["password"] != password()) { 187 LOG(LS_WARNING) << "Received unexpected password value from relay server."; 188 } 189 190 std::string relay_ip = map["relay.ip"]; 191 std::string relay_udp_port = map["relay.udp_port"]; 192 std::string relay_tcp_port = map["relay.tcp_port"]; 193 std::string relay_ssltcp_port = map["relay.ssltcp_port"]; 194 195 ServerAddresses hosts; 196 for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin(); 197 it != stun_hosts_.end(); ++it) { 198 hosts.insert(*it); 199 } 200 201 PortConfiguration* config = new PortConfiguration(hosts, 202 map["username"], 203 map["password"]); 204 205 RelayServerConfig relay_config(RELAY_GTURN); 206 if (!relay_udp_port.empty()) { 207 rtc::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str())); 208 relay_config.ports.push_back(ProtocolAddress(address, PROTO_UDP)); 209 } 210 if (!relay_tcp_port.empty()) { 211 rtc::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str())); 212 relay_config.ports.push_back(ProtocolAddress(address, PROTO_TCP)); 213 } 214 if (!relay_ssltcp_port.empty()) { 215 rtc::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str())); 216 relay_config.ports.push_back(ProtocolAddress(address, PROTO_SSLTCP)); 217 } 218 config->AddRelay(relay_config); 219 ConfigReady(config); 220 } 221 222 } // namespace cricket 223