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