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