1 // Copyright 2011 Google Inc. All Rights Reserved.
2
3 #include <string>
4
5 #include "talk/base/asynchttprequest.h"
6 #include "talk/base/basicpacketsocketfactory.h"
7 #include "talk/base/gunit.h"
8 #include "talk/base/fakenetwork.h"
9 #include "talk/base/scoped_ptr.h"
10 #include "talk/base/socketaddress.h"
11 #include "talk/p2p/base/relayport.h"
12 #include "talk/p2p/base/stunport.h"
13 #include "talk/p2p/client/connectivitychecker.h"
14 #include "talk/p2p/client/httpportallocator.h"
15
16 namespace cricket {
17
18 static const talk_base::SocketAddress kClientAddr1("11.11.11.11", 0);
19 static const talk_base::SocketAddress kClientAddr2("22.22.22.22", 0);
20 static const talk_base::SocketAddress kExternalAddr("33.33.33.33", 3333);
21 static const talk_base::SocketAddress kStunAddr("44.44.44.44", 4444);
22 static const talk_base::SocketAddress kRelayAddr("55.55.55.55", 5555);
23 static const talk_base::SocketAddress kProxyAddr("66.66.66.66", 6666);
24 static const talk_base::ProxyType kProxyType = talk_base::PROXY_HTTPS;
25 static const char kSessionName[] = "rtp_test";
26 static const char kSessionType[] = "http://www.google.com/session/test";
27 static const char kRelayHost[] = "relay.google.com";
28 static const char kRelayToken[] =
29 "CAESFwoOb2phQGdvb2dsZS5jb20Q043h47MmGhBTB1rbfIXkhuarDCZe+xF6";
30 static const char kBrowserAgent[] = "browser_test";
31 static const char kJid[] = "a.b@c";
32 static const char kUserName[] = "testuser";
33 static const char kPassword[] = "testpassword";
34 static const char kMagicCookie[] = "testcookie";
35 static const char kRelayUdpPort[] = "4444";
36 static const char kRelayTcpPort[] = "5555";
37 static const char kRelaySsltcpPort[] = "6666";
38 static const char kSessionId[] = "testsession";
39 static const char kConnection[] = "testconnection";
40 static const int kMinPort = 1000;
41 static const int kMaxPort = 2000;
42
43 // Fake implementation to mock away real network usage.
44 class FakeRelayPort : public RelayPort {
45 public:
FakeRelayPort(talk_base::Thread * thread,talk_base::PacketSocketFactory * factory,talk_base::Network * network,const talk_base::IPAddress & ip,int min_port,int max_port,const std::string & username,const std::string & password,const std::string & magic_cookie)46 FakeRelayPort(talk_base::Thread* thread,
47 talk_base::PacketSocketFactory* factory,
48 talk_base::Network* network,
49 const talk_base::IPAddress& ip,
50 int min_port,
51 int max_port,
52 const std::string& username,
53 const std::string& password,
54 const std::string& magic_cookie)
55 : RelayPort(thread,
56 factory,
57 network,
58 ip,
59 min_port,
60 max_port,
61 username,
62 password,
63 magic_cookie) {
64 }
65
66 // Just signal that we are done.
PrepareAddress()67 virtual void PrepareAddress() {
68 SignalAddressReady(this);
69 }
70 };
71
72 // Fake implementation to mock away real network usage.
73 class FakeStunPort : public StunPort {
74 public:
FakeStunPort(talk_base::Thread * thread,talk_base::PacketSocketFactory * factory,talk_base::Network * network,const talk_base::IPAddress & ip,int min_port,int max_port,const talk_base::SocketAddress & server_addr)75 FakeStunPort(talk_base::Thread* thread,
76 talk_base::PacketSocketFactory* factory,
77 talk_base::Network* network,
78 const talk_base::IPAddress& ip,
79 int min_port,
80 int max_port,
81 const talk_base::SocketAddress& server_addr)
82 : StunPort(thread,
83 factory,
84 network,
85 ip,
86 min_port,
87 max_port,
88 server_addr) {
89 }
90
91 // Just set external address and signal that we are done.
PrepareAddress()92 virtual void PrepareAddress() {
93 AddAddress(kExternalAddr, "udp", true);
94 SignalAddressReady(this);
95 }
96 };
97
98 // Fake implementation to mock away real network usage by responding
99 // to http requests immediately.
100 class FakeHttpPortAllocatorSession : public TestHttpPortAllocatorSession {
101 public:
FakeHttpPortAllocatorSession(HttpPortAllocator * allocator,const std::string & name,const std::string & session_type,const std::vector<talk_base::SocketAddress> & stun_hosts,const std::vector<std::string> & relay_hosts,const std::string & relay_token,const std::string & agent)102 FakeHttpPortAllocatorSession(
103 HttpPortAllocator* allocator,
104 const std::string& name,
105 const std::string& session_type,
106 const std::vector<talk_base::SocketAddress>& stun_hosts,
107 const std::vector<std::string>& relay_hosts,
108 const std::string& relay_token,
109 const std::string& agent)
110 : TestHttpPortAllocatorSession(allocator,
111 name,
112 session_type,
113 stun_hosts,
114 relay_hosts,
115 relay_token,
116 agent) {
117 }
SendSessionRequest(const std::string & host,int port)118 virtual void SendSessionRequest(const std::string& host, int port) {
119 FakeReceiveSessionResponse(host, port);
120 }
121
122 // Pass results to the real implementation.
FakeReceiveSessionResponse(const std::string & host,int port)123 void FakeReceiveSessionResponse(const std::string& host, int port) {
124 talk_base::AsyncHttpRequest* response = CreateAsyncHttpResponse(port);
125 TestHttpPortAllocatorSession::OnRequestDone(response);
126 response->Destroy(true);
127 }
128
129 private:
130 // Helper method for creating a response to a relay session request.
CreateAsyncHttpResponse(int port)131 talk_base::AsyncHttpRequest* CreateAsyncHttpResponse(int port) {
132 talk_base::AsyncHttpRequest* request =
133 new talk_base::AsyncHttpRequest(kBrowserAgent);
134 std::stringstream ss;
135 ss << "username=" << kUserName << std::endl
136 << "password=" << kPassword << std::endl
137 << "magic_cookie=" << kMagicCookie << std::endl
138 << "relay.ip=" << kRelayAddr.IPAsString() << std::endl
139 << "relay.udp_port=" << kRelayUdpPort << std::endl
140 << "relay.tcp_port=" << kRelayTcpPort << std::endl
141 << "relay.ssltcp_port=" << kRelaySsltcpPort << std::endl;
142 request->response().document.reset(
143 new talk_base::MemoryStream(ss.str().c_str()));
144 request->response().set_success();
145 request->set_port(port);
146 request->set_secure(port == talk_base::HTTP_SECURE_PORT);
147 return request;
148 }
149 };
150
151 // Fake implementation for creating fake http sessions.
152 class FakeHttpPortAllocator : public HttpPortAllocator {
153 public:
FakeHttpPortAllocator(talk_base::NetworkManager * network_manager,const std::string & user_agent)154 FakeHttpPortAllocator(talk_base::NetworkManager* network_manager,
155 const std::string& user_agent)
156 : HttpPortAllocator(network_manager, user_agent) {
157 }
158
CreateSession(const std::string & name,const std::string & session_type)159 virtual PortAllocatorSession* CreateSession(const std::string& name,
160 const std::string& session_type) {
161 std::vector<talk_base::SocketAddress> stun_hosts;
162 stun_hosts.push_back(kStunAddr);
163 std::vector<std::string> relay_hosts;
164 relay_hosts.push_back(kRelayHost);
165 return new FakeHttpPortAllocatorSession(this,
166 kSessionName,
167 kSessionType,
168 stun_hosts,
169 relay_hosts,
170 kRelayToken,
171 kBrowserAgent);
172 }
173 };
174
175 class ConnectivityCheckerForTest : public ConnectivityChecker {
176 public:
ConnectivityCheckerForTest(talk_base::Thread * worker,const std::string & jid,const std::string & session_id,const std::string & user_agent,const std::string & relay_token,const std::string & connection)177 ConnectivityCheckerForTest(talk_base::Thread* worker,
178 const std::string& jid,
179 const std::string& session_id,
180 const std::string& user_agent,
181 const std::string& relay_token,
182 const std::string& connection)
183 : ConnectivityChecker(worker,
184 jid,
185 session_id,
186 user_agent,
187 relay_token,
188 connection),
189 proxy_initiated_(false) {
190 }
191
network_manager() const192 talk_base::FakeNetworkManager* network_manager() const {
193 return network_manager_;
194 }
195
port_allocator() const196 FakeHttpPortAllocator* port_allocator() const {
197 return fake_port_allocator_;
198 }
199
200 protected:
201 // Overridden methods for faking a real network.
CreateNetworkManager()202 virtual talk_base::NetworkManager* CreateNetworkManager() {
203 network_manager_ = new talk_base::FakeNetworkManager();
204 return network_manager_;
205 }
CreateSocketFactory(talk_base::Thread * thread)206 virtual talk_base::BasicPacketSocketFactory* CreateSocketFactory(
207 talk_base::Thread* thread) {
208 // Create socket factory, for simplicity, let it run on the current thread.
209 socket_factory_ =
210 new talk_base::BasicPacketSocketFactory(talk_base::Thread::Current());
211 return socket_factory_;
212 }
CreatePortAllocator(talk_base::NetworkManager * network_manager,const std::string & user_agent,const std::string & relay_token)213 virtual HttpPortAllocator* CreatePortAllocator(
214 talk_base::NetworkManager* network_manager,
215 const std::string& user_agent,
216 const std::string& relay_token) {
217 fake_port_allocator_ =
218 new FakeHttpPortAllocator(network_manager, user_agent);
219 return fake_port_allocator_;
220 }
CreateStunPort(const PortConfiguration * config,talk_base::Network * network)221 virtual StunPort* CreateStunPort(const PortConfiguration* config,
222 talk_base::Network* network) {
223 return new FakeStunPort(worker(),
224 socket_factory_,
225 network,
226 network->ip(),
227 kMinPort,
228 kMaxPort,
229 config->stun_address);
230 }
CreateRelayPort(const PortConfiguration * config,talk_base::Network * network)231 virtual RelayPort* CreateRelayPort(const PortConfiguration* config,
232 talk_base::Network* network) {
233 return new FakeRelayPort(worker(),
234 socket_factory_,
235 network,
236 network->ip(),
237 kMinPort,
238 kMaxPort,
239 config->username,
240 config->password,
241 config->magic_cookie);
242 }
InitiateProxyDetection()243 virtual void InitiateProxyDetection() {
244 if (!proxy_initiated_) {
245 proxy_initiated_ = true;
246 proxy_info_.address = kProxyAddr;
247 proxy_info_.type = kProxyType;
248 SetProxyInfo(proxy_info_);
249 }
250 }
251
GetProxyInfo() const252 virtual talk_base::ProxyInfo GetProxyInfo() const {
253 return proxy_info_;
254 }
255
256 private:
257 talk_base::BasicPacketSocketFactory* socket_factory_;
258 FakeHttpPortAllocator* fake_port_allocator_;
259 talk_base::FakeNetworkManager* network_manager_;
260 talk_base::ProxyInfo proxy_info_;
261 bool proxy_initiated_;
262 };
263
264 class ConnectivityCheckerTest : public testing::Test {
265 protected:
VerifyNic(const NicInfo & info,const talk_base::SocketAddress & local_address)266 void VerifyNic(const NicInfo& info,
267 const talk_base::SocketAddress& local_address) {
268 // Verify that the external address has been set.
269 EXPECT_EQ(kExternalAddr, info.external_address);
270
271 // Verify that the stun server address has been set.
272 EXPECT_EQ(kStunAddr, info.stun_server_address);
273
274 // Verify that the media server address has been set. Don't care
275 // about port since it is different for different protocols.
276 EXPECT_EQ(kRelayAddr.ipaddr(), info.media_server_address.ipaddr());
277
278 // Verify that local ip matches.
279 EXPECT_EQ(local_address.ipaddr(), info.ip);
280
281 // Verify that we have received responses for our
282 // pings. Unsuccessful ping has rtt value -1, successful >= 0.
283 EXPECT_GE(info.stun.rtt, 0);
284 EXPECT_GE(info.udp.rtt, 0);
285 EXPECT_GE(info.tcp.rtt, 0);
286 EXPECT_GE(info.ssltcp.rtt, 0);
287
288 // If proxy has been set, verify address and type.
289 if (!info.proxy_info.address.IsNil()) {
290 EXPECT_EQ(kProxyAddr, info.proxy_info.address);
291 EXPECT_EQ(kProxyType, info.proxy_info.type);
292 }
293 }
294 };
295
296 // Tests a configuration with two network interfaces. Verifies that 4
297 // combinations of ip/proxy are created and that all protocols are
298 // tested on each combination.
TEST_F(ConnectivityCheckerTest,TestStart)299 TEST_F(ConnectivityCheckerTest, TestStart) {
300 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(),
301 kJid,
302 kSessionId,
303 kBrowserAgent,
304 kRelayToken,
305 kConnection);
306 connectivity_checker.Initialize();
307 connectivity_checker.set_stun_address(kStunAddr);
308 connectivity_checker.network_manager()->AddInterface(kClientAddr1);
309 connectivity_checker.network_manager()->AddInterface(kClientAddr2);
310
311 connectivity_checker.Start();
312 talk_base::Thread::Current()->ProcessMessages(1000);
313
314 NicMap nics = connectivity_checker.GetResults();
315
316 // There should be 4 nics in our map. 2 for each interface added,
317 // one with proxy set and one without.
318 EXPECT_EQ(4U, nics.size());
319
320 // First verify interfaces without proxy.
321 talk_base::SocketAddress nilAddress;
322
323 // First lookup the address of the first nic combined with no proxy.
324 NicMap::iterator i = nics.find(NicId(kClientAddr1.ipaddr(), nilAddress));
325 ASSERT(i != nics.end());
326 NicInfo info = i->second;
327 VerifyNic(info, kClientAddr1);
328
329 // Then make sure the second device has been tested without proxy.
330 i = nics.find(NicId(kClientAddr2.ipaddr(), nilAddress));
331 ASSERT(i != nics.end());
332 info = i->second;
333 VerifyNic(info, kClientAddr2);
334
335 // Now verify both interfaces with proxy.
336 i = nics.find(NicId(kClientAddr1.ipaddr(), kProxyAddr));
337 ASSERT(i != nics.end());
338 info = i->second;
339 VerifyNic(info, kClientAddr1);
340
341 i = nics.find(NicId(kClientAddr2.ipaddr(), kProxyAddr));
342 ASSERT(i != nics.end());
343 info = i->second;
344 VerifyNic(info, kClientAddr2);
345 };
346
347 // Tests that nothing bad happens if thera are no network interfaces
348 // available to check.
TEST_F(ConnectivityCheckerTest,TestStartNoNetwork)349 TEST_F(ConnectivityCheckerTest, TestStartNoNetwork) {
350 ConnectivityCheckerForTest connectivity_checker(talk_base::Thread::Current(),
351 kJid,
352 kSessionId,
353 kBrowserAgent,
354 kRelayToken,
355 kConnection);
356 connectivity_checker.Initialize();
357 connectivity_checker.Start();
358 talk_base::Thread::Current()->ProcessMessages(1000);
359
360 NicMap nics = connectivity_checker.GetResults();
361
362 // Verify that no nics where checked.
363 EXPECT_EQ(0U, nics.size());
364 }
365
366 } // namespace cricket
367