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