• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2004--2008, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/p2p/client/httpportallocator.h"
29 
30 #include <algorithm>
31 #include <map>
32 
33 #include "webrtc/base/asynchttprequest.h"
34 #include "webrtc/base/basicdefs.h"
35 #include "webrtc/base/common.h"
36 #include "webrtc/base/helpers.h"
37 #include "webrtc/base/logging.h"
38 #include "webrtc/base/nethelpers.h"
39 #include "webrtc/base/signalthread.h"
40 #include "webrtc/base/stringencode.h"
41 
42 namespace {
43 
44 // Helper routine to remove whitespace from the ends of a string.
Trim(std::string & str)45 void Trim(std::string& str) {
46   size_t first = str.find_first_not_of(" \t\r\n");
47   if (first == std::string::npos) {
48     str.clear();
49     return;
50   }
51 
52   ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
53 }
54 
55 // Parses the lines in the result of the HTTP request that are of the form
56 // 'a=b' and returns them in a map.
57 typedef std::map<std::string, std::string> StringMap;
ParseMap(const std::string & string,StringMap & map)58 void ParseMap(const std::string& string, StringMap& map) {
59   size_t start_of_line = 0;
60   size_t end_of_line = 0;
61 
62   for (;;) {  // for each line
63     start_of_line = string.find_first_not_of("\r\n", end_of_line);
64     if (start_of_line == std::string::npos)
65       break;
66 
67     end_of_line = string.find_first_of("\r\n", start_of_line);
68     if (end_of_line == std::string::npos) {
69       end_of_line = string.length();
70     }
71 
72     size_t equals = string.find('=', start_of_line);
73     if ((equals >= end_of_line) || (equals == std::string::npos))
74       continue;
75 
76     std::string key(string, start_of_line, equals - start_of_line);
77     std::string value(string, equals + 1, end_of_line - equals - 1);
78 
79     Trim(key);
80     Trim(value);
81 
82     if ((key.size() > 0) && (value.size() > 0))
83       map[key] = value;
84   }
85 }
86 
87 }  // namespace
88 
89 namespace cricket {
90 
91 // HttpPortAllocatorBase
92 
93 const int HttpPortAllocatorBase::kNumRetries = 5;
94 
95 const char HttpPortAllocatorBase::kCreateSessionURL[] = "/create_session";
96 
HttpPortAllocatorBase(rtc::NetworkManager * network_manager,rtc::PacketSocketFactory * socket_factory,const std::string & user_agent)97 HttpPortAllocatorBase::HttpPortAllocatorBase(
98     rtc::NetworkManager* network_manager,
99     rtc::PacketSocketFactory* socket_factory,
100     const std::string &user_agent)
101     : BasicPortAllocator(network_manager, socket_factory), agent_(user_agent) {
102   relay_hosts_.push_back("relay.google.com");
103   stun_hosts_.push_back(
104       rtc::SocketAddress("stun.l.google.com", 19302));
105 }
106 
HttpPortAllocatorBase(rtc::NetworkManager * network_manager,const std::string & user_agent)107 HttpPortAllocatorBase::HttpPortAllocatorBase(
108     rtc::NetworkManager* network_manager,
109     const std::string &user_agent)
110     : BasicPortAllocator(network_manager), agent_(user_agent) {
111   relay_hosts_.push_back("relay.google.com");
112   stun_hosts_.push_back(
113       rtc::SocketAddress("stun.l.google.com", 19302));
114 }
115 
~HttpPortAllocatorBase()116 HttpPortAllocatorBase::~HttpPortAllocatorBase() {
117 }
118 
119 // HttpPortAllocatorSessionBase
120 
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)121 HttpPortAllocatorSessionBase::HttpPortAllocatorSessionBase(
122     HttpPortAllocatorBase* allocator,
123     const std::string& content_name,
124     int component,
125     const std::string& ice_ufrag,
126     const std::string& ice_pwd,
127     const std::vector<rtc::SocketAddress>& stun_hosts,
128     const std::vector<std::string>& relay_hosts,
129     const std::string& relay_token,
130     const std::string& user_agent)
131     : BasicPortAllocatorSession(allocator, content_name, component,
132                                 ice_ufrag, ice_pwd),
133       relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
134       relay_token_(relay_token), agent_(user_agent), attempts_(0) {
135 }
136 
~HttpPortAllocatorSessionBase()137 HttpPortAllocatorSessionBase::~HttpPortAllocatorSessionBase() {}
138 
GetPortConfigurations()139 void HttpPortAllocatorSessionBase::GetPortConfigurations() {
140   // Creating relay sessions can take time and is done asynchronously.
141   // Creating stun sessions could also take time and could be done aysnc also,
142   // but for now is done here and added to the initial config.  Note any later
143   // configs will have unresolved stun ips and will be discarded by the
144   // AllocationSequence.
145   ServerAddresses hosts;
146   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
147       it != stun_hosts_.end(); ++it) {
148     hosts.insert(*it);
149   }
150 
151   PortConfiguration* config = new PortConfiguration(hosts,
152                                                     username(),
153                                                     password());
154   ConfigReady(config);
155   TryCreateRelaySession();
156 }
157 
TryCreateRelaySession()158 void HttpPortAllocatorSessionBase::TryCreateRelaySession() {
159   if (allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) {
160     LOG(LS_VERBOSE) << "HttpPortAllocator: Relay ports disabled, skipping.";
161     return;
162   }
163 
164   if (attempts_ == HttpPortAllocator::kNumRetries) {
165     LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; "
166                   << "giving up on relay.";
167     return;
168   }
169 
170   if (relay_hosts_.size() == 0) {
171     LOG(LS_ERROR) << "HttpPortAllocator: no relay hosts configured.";
172     return;
173   }
174 
175   // Choose the next host to try.
176   std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
177   attempts_++;
178   LOG(LS_INFO) << "HTTPPortAllocator: sending to relay host " << host;
179   if (relay_token_.empty()) {
180     LOG(LS_WARNING) << "No relay auth token found.";
181   }
182 
183   SendSessionRequest(host, rtc::HTTP_SECURE_PORT);
184 }
185 
GetSessionRequestUrl()186 std::string HttpPortAllocatorSessionBase::GetSessionRequestUrl() {
187   std::string url = std::string(HttpPortAllocator::kCreateSessionURL);
188   if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHARED_UFRAG) {
189     ASSERT(!username().empty());
190     ASSERT(!password().empty());
191     url = url + "?username=" + rtc::s_url_encode(username()) +
192         "&password=" + rtc::s_url_encode(password());
193   }
194   return url;
195 }
196 
ReceiveSessionResponse(const std::string & response)197 void HttpPortAllocatorSessionBase::ReceiveSessionResponse(
198     const std::string& response) {
199 
200   StringMap map;
201   ParseMap(response, map);
202 
203   if (!username().empty() && map["username"] != username()) {
204     LOG(LS_WARNING) << "Received unexpected username value from relay server.";
205   }
206   if (!password().empty() && map["password"] != password()) {
207     LOG(LS_WARNING) << "Received unexpected password value from relay server.";
208   }
209 
210   std::string relay_ip = map["relay.ip"];
211   std::string relay_udp_port = map["relay.udp_port"];
212   std::string relay_tcp_port = map["relay.tcp_port"];
213   std::string relay_ssltcp_port = map["relay.ssltcp_port"];
214 
215   ServerAddresses hosts;
216   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
217       it != stun_hosts_.end(); ++it) {
218     hosts.insert(*it);
219   }
220 
221   PortConfiguration* config = new PortConfiguration(hosts,
222                                                     map["username"],
223                                                     map["password"]);
224 
225   RelayServerConfig relay_config(RELAY_GTURN);
226   if (!relay_udp_port.empty()) {
227     rtc::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
228     relay_config.ports.push_back(ProtocolAddress(address, PROTO_UDP));
229   }
230   if (!relay_tcp_port.empty()) {
231     rtc::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
232     relay_config.ports.push_back(ProtocolAddress(address, PROTO_TCP));
233   }
234   if (!relay_ssltcp_port.empty()) {
235     rtc::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
236     relay_config.ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
237   }
238   config->AddRelay(relay_config);
239   ConfigReady(config);
240 }
241 
242 // HttpPortAllocator
243 
HttpPortAllocator(rtc::NetworkManager * network_manager,rtc::PacketSocketFactory * socket_factory,const std::string & user_agent)244 HttpPortAllocator::HttpPortAllocator(
245     rtc::NetworkManager* network_manager,
246     rtc::PacketSocketFactory* socket_factory,
247     const std::string &user_agent)
248     : HttpPortAllocatorBase(network_manager, socket_factory, user_agent) {
249 }
250 
HttpPortAllocator(rtc::NetworkManager * network_manager,const std::string & user_agent)251 HttpPortAllocator::HttpPortAllocator(
252     rtc::NetworkManager* network_manager,
253     const std::string &user_agent)
254     : HttpPortAllocatorBase(network_manager, user_agent) {
255 }
~HttpPortAllocator()256 HttpPortAllocator::~HttpPortAllocator() {}
257 
CreateSessionInternal(const std::string & content_name,int component,const std::string & ice_ufrag,const std::string & ice_pwd)258 PortAllocatorSession* HttpPortAllocator::CreateSessionInternal(
259     const std::string& content_name,
260     int component,
261     const std::string& ice_ufrag, const std::string& ice_pwd) {
262   return new HttpPortAllocatorSession(this, content_name, component,
263                                       ice_ufrag, ice_pwd, stun_hosts(),
264                                       relay_hosts(), relay_token(),
265                                       user_agent());
266 }
267 
268 // HttpPortAllocatorSession
269 
HttpPortAllocatorSession(HttpPortAllocator * 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,const std::string & agent)270 HttpPortAllocatorSession::HttpPortAllocatorSession(
271     HttpPortAllocator* allocator,
272     const std::string& content_name,
273     int component,
274     const std::string& ice_ufrag,
275     const std::string& ice_pwd,
276     const std::vector<rtc::SocketAddress>& stun_hosts,
277     const std::vector<std::string>& relay_hosts,
278     const std::string& relay,
279     const std::string& agent)
280     : HttpPortAllocatorSessionBase(allocator, content_name, component,
281                                    ice_ufrag, ice_pwd, stun_hosts,
282                                    relay_hosts, relay, agent) {
283 }
284 
~HttpPortAllocatorSession()285 HttpPortAllocatorSession::~HttpPortAllocatorSession() {
286   for (std::list<rtc::AsyncHttpRequest*>::iterator it = requests_.begin();
287        it != requests_.end(); ++it) {
288     (*it)->Destroy(true);
289   }
290 }
291 
SendSessionRequest(const std::string & host,int port)292 void HttpPortAllocatorSession::SendSessionRequest(const std::string& host,
293                                                   int port) {
294   // Initiate an HTTP request to create a session through the chosen host.
295   rtc::AsyncHttpRequest* request =
296       new rtc::AsyncHttpRequest(user_agent());
297   request->SignalWorkDone.connect(this,
298       &HttpPortAllocatorSession::OnRequestDone);
299 
300   request->set_secure(port == rtc::HTTP_SECURE_PORT);
301   request->set_proxy(allocator()->proxy());
302   request->response().document.reset(new rtc::MemoryStream);
303   request->request().verb = rtc::HV_GET;
304   request->request().path = GetSessionRequestUrl();
305   request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token(), true);
306   request->request().addHeader("X-Stream-Type", "video_rtp", true);
307   request->set_host(host);
308   request->set_port(port);
309   request->Start();
310   request->Release();
311 
312   requests_.push_back(request);
313 }
314 
OnRequestDone(rtc::SignalThread * data)315 void HttpPortAllocatorSession::OnRequestDone(rtc::SignalThread* data) {
316   rtc::AsyncHttpRequest* request =
317       static_cast<rtc::AsyncHttpRequest*>(data);
318 
319   // Remove the request from the list of active requests.
320   std::list<rtc::AsyncHttpRequest*>::iterator it =
321       std::find(requests_.begin(), requests_.end(), request);
322   if (it != requests_.end()) {
323     requests_.erase(it);
324   }
325 
326   if (request->response().scode != 200) {
327     LOG(LS_WARNING) << "HTTPPortAllocator: request "
328                     << " received error " << request->response().scode;
329     TryCreateRelaySession();
330     return;
331   }
332   LOG(LS_INFO) << "HTTPPortAllocator: request succeeded";
333 
334   rtc::MemoryStream* stream =
335       static_cast<rtc::MemoryStream*>(request->response().document.get());
336   stream->Rewind();
337   size_t length;
338   stream->GetSize(&length);
339   std::string resp = std::string(stream->GetBuffer(), length);
340   ReceiveSessionResponse(resp);
341 }
342 
343 }  // namespace cricket
344