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 "talk/p2p/base/basicpacketsocketfactory.h" 29 30 #include "talk/base/asyncudpsocket.h" 31 #include "talk/base/asynctcpsocket.h" 32 #include "talk/base/logging.h" 33 #include "talk/base/nethelpers.h" 34 #include "talk/base/physicalsocketserver.h" 35 #include "talk/base/scoped_ptr.h" 36 #include "talk/base/socketadapters.h" 37 #include "talk/base/thread.h" 38 #include "talk/p2p/base/asyncstuntcpsocket.h" 39 #include "talk/p2p/base/stun.h" 40 41 namespace talk_base { 42 43 BasicPacketSocketFactory::BasicPacketSocketFactory() 44 : thread_(Thread::Current()), 45 socket_factory_(NULL) { 46 } 47 48 BasicPacketSocketFactory::BasicPacketSocketFactory(Thread* thread) 49 : thread_(thread), 50 socket_factory_(NULL) { 51 } 52 53 BasicPacketSocketFactory::BasicPacketSocketFactory( 54 SocketFactory* socket_factory) 55 : thread_(NULL), 56 socket_factory_(socket_factory) { 57 } 58 59 BasicPacketSocketFactory::~BasicPacketSocketFactory() { 60 } 61 62 AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket( 63 const SocketAddress& address, int min_port, int max_port) { 64 // UDP sockets are simple. 65 talk_base::AsyncSocket* socket = 66 socket_factory()->CreateAsyncSocket( 67 address.family(), SOCK_DGRAM); 68 if (!socket) { 69 return NULL; 70 } 71 if (BindSocket(socket, address, min_port, max_port) < 0) { 72 LOG(LS_ERROR) << "UDP bind failed with error " 73 << socket->GetError(); 74 delete socket; 75 return NULL; 76 } 77 return new talk_base::AsyncUDPSocket(socket); 78 } 79 80 AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket( 81 const SocketAddress& local_address, int min_port, int max_port, int opts) { 82 83 // Fail if TLS is required. 84 if (opts & PacketSocketFactory::OPT_TLS) { 85 LOG(LS_ERROR) << "TLS support currently is not available."; 86 return NULL; 87 } 88 89 talk_base::AsyncSocket* socket = 90 socket_factory()->CreateAsyncSocket(local_address.family(), 91 SOCK_STREAM); 92 if (!socket) { 93 return NULL; 94 } 95 96 if (BindSocket(socket, local_address, min_port, max_port) < 0) { 97 LOG(LS_ERROR) << "TCP bind failed with error " 98 << socket->GetError(); 99 delete socket; 100 return NULL; 101 } 102 103 // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. 104 if (opts & PacketSocketFactory::OPT_SSLTCP) { 105 ASSERT(!(opts & PacketSocketFactory::OPT_TLS)); 106 socket = new talk_base::AsyncSSLSocket(socket); 107 } 108 109 // Set TCP_NODELAY (via OPT_NODELAY) for improved performance. 110 // See http://go/gtalktcpnodelayexperiment 111 socket->SetOption(talk_base::Socket::OPT_NODELAY, 1); 112 113 if (opts & PacketSocketFactory::OPT_STUN) 114 return new cricket::AsyncStunTCPSocket(socket, true); 115 116 return new talk_base::AsyncTCPSocket(socket, true); 117 } 118 119 AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( 120 const SocketAddress& local_address, const SocketAddress& remote_address, 121 const ProxyInfo& proxy_info, const std::string& user_agent, int opts) { 122 123 // Fail if TLS is required. 124 if (opts & PacketSocketFactory::OPT_TLS) { 125 LOG(LS_ERROR) << "TLS support currently is not available."; 126 return NULL; 127 } 128 129 talk_base::AsyncSocket* socket = 130 socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM); 131 if (!socket) { 132 return NULL; 133 } 134 135 if (BindSocket(socket, local_address, 0, 0) < 0) { 136 LOG(LS_ERROR) << "TCP bind failed with error " 137 << socket->GetError(); 138 delete socket; 139 return NULL; 140 } 141 142 // If using a proxy, wrap the socket in a proxy socket. 143 if (proxy_info.type == talk_base::PROXY_SOCKS5) { 144 socket = new talk_base::AsyncSocksProxySocket( 145 socket, proxy_info.address, proxy_info.username, proxy_info.password); 146 } else if (proxy_info.type == talk_base::PROXY_HTTPS) { 147 socket = new talk_base::AsyncHttpsProxySocket( 148 socket, user_agent, proxy_info.address, 149 proxy_info.username, proxy_info.password); 150 } 151 152 // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. 153 if (opts & PacketSocketFactory::OPT_SSLTCP) { 154 ASSERT(!(opts & PacketSocketFactory::OPT_TLS)); 155 socket = new talk_base::AsyncSSLSocket(socket); 156 } 157 158 if (socket->Connect(remote_address) < 0) { 159 LOG(LS_ERROR) << "TCP connect failed with error " 160 << socket->GetError(); 161 delete socket; 162 return NULL; 163 } 164 165 // Finally, wrap that socket in a TCP or STUN TCP packet socket. 166 AsyncPacketSocket* tcp_socket; 167 if (opts & PacketSocketFactory::OPT_STUN) { 168 tcp_socket = new cricket::AsyncStunTCPSocket(socket, false); 169 } else { 170 tcp_socket = new talk_base::AsyncTCPSocket(socket, false); 171 } 172 173 // Set TCP_NODELAY (via OPT_NODELAY) for improved performance. 174 // See http://go/gtalktcpnodelayexperiment 175 tcp_socket->SetOption(talk_base::Socket::OPT_NODELAY, 1); 176 177 return tcp_socket; 178 } 179 180 AsyncResolverInterface* BasicPacketSocketFactory::CreateAsyncResolver() { 181 return new talk_base::AsyncResolver(); 182 } 183 184 int BasicPacketSocketFactory::BindSocket( 185 AsyncSocket* socket, const SocketAddress& local_address, 186 int min_port, int max_port) { 187 int ret = -1; 188 if (min_port == 0 && max_port == 0) { 189 // If there's no port range, let the OS pick a port for us. 190 ret = socket->Bind(local_address); 191 } else { 192 // Otherwise, try to find a port in the provided range. 193 for (int port = min_port; ret < 0 && port <= max_port; ++port) { 194 ret = socket->Bind(talk_base::SocketAddress(local_address.ipaddr(), 195 port)); 196 } 197 } 198 return ret; 199 } 200 201 SocketFactory* BasicPacketSocketFactory::socket_factory() { 202 if (thread_) { 203 ASSERT(thread_ == Thread::Current()); 204 return thread_->socketserver(); 205 } else { 206 return socket_factory_; 207 } 208 } 209 210 } // namespace talk_base 211