• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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