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.
Trim(std::string & str)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;
ParseMap(const std::string & string,StringMap & map)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
HttpPortAllocatorBase(rtc::NetworkManager * network_manager,rtc::PacketSocketFactory * socket_factory,const std::string & user_agent)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
HttpPortAllocatorBase(rtc::NetworkManager * network_manager,const std::string & user_agent)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
~HttpPortAllocatorBase()98 HttpPortAllocatorBase::~HttpPortAllocatorBase() {
99 }
100
101 // HttpPortAllocatorSessionBase
102
HttpPortAllocatorSessionBase(HttpPortAllocatorBase * allocator,const std::string & content_name,int component,const std::string & ice_ufrag,const std::string & ice_pwd,const std::vector<rtc::SocketAddress> & stun_hosts,const std::vector<std::string> & relay_hosts,const std::string & relay_token,const std::string & user_agent)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
~HttpPortAllocatorSessionBase()119 HttpPortAllocatorSessionBase::~HttpPortAllocatorSessionBase() {}
120
GetPortConfigurations()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
TryCreateRelaySession()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
GetSessionRequestUrl()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
ReceiveSessionResponse(const std::string & response)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