1/* 2 * libjingle 3 * Copyright 2004--2005, 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/stunport.h" 29 30#include "talk/base/common.h" 31#include "talk/base/logging.h" 32#include "talk/base/helpers.h" 33#include "talk/base/nethelpers.h" 34#include "talk/p2p/base/common.h" 35#include "talk/p2p/base/stun.h" 36 37namespace cricket { 38 39// TODO: Move these to a common place (used in relayport too) 40const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts 41const int RETRY_DELAY = 50; // 50ms, from ICE spec 42const int RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs 43 44// Handles a binding request sent to the STUN server. 45class StunBindingRequest : public StunRequest { 46 public: 47 StunBindingRequest(UDPPort* port, bool keep_alive, 48 const talk_base::SocketAddress& addr) 49 : port_(port), keep_alive_(keep_alive), server_addr_(addr) { 50 start_time_ = talk_base::Time(); 51 } 52 53 virtual ~StunBindingRequest() { 54 } 55 56 const talk_base::SocketAddress& server_addr() const { return server_addr_; } 57 58 virtual void Prepare(StunMessage* request) { 59 request->SetType(STUN_BINDING_REQUEST); 60 } 61 62 virtual void OnResponse(StunMessage* response) { 63 const StunAddressAttribute* addr_attr = 64 response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); 65 if (!addr_attr) { 66 LOG(LS_ERROR) << "Binding response missing mapped address."; 67 } else if (addr_attr->family() != STUN_ADDRESS_IPV4 && 68 addr_attr->family() != STUN_ADDRESS_IPV6) { 69 LOG(LS_ERROR) << "Binding address has bad family"; 70 } else { 71 talk_base::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port()); 72 port_->OnStunBindingRequestSucceeded(addr); 73 } 74 75 // We will do a keep-alive regardless of whether this request suceeds. 76 // This should have almost no impact on network usage. 77 if (keep_alive_) { 78 port_->requests_.SendDelayed( 79 new StunBindingRequest(port_, true, server_addr_), 80 port_->stun_keepalive_delay()); 81 } 82 } 83 84 virtual void OnErrorResponse(StunMessage* response) { 85 const StunErrorCodeAttribute* attr = response->GetErrorCode(); 86 if (!attr) { 87 LOG(LS_ERROR) << "Bad allocate response error code"; 88 } else { 89 LOG(LS_ERROR) << "Binding error response:" 90 << " class=" << attr->eclass() 91 << " number=" << attr->number() 92 << " reason='" << attr->reason() << "'"; 93 } 94 95 port_->OnStunBindingOrResolveRequestFailed(); 96 97 if (keep_alive_ 98 && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) { 99 port_->requests_.SendDelayed( 100 new StunBindingRequest(port_, true, server_addr_), 101 port_->stun_keepalive_delay()); 102 } 103 } 104 105 virtual void OnTimeout() { 106 LOG(LS_ERROR) << "Binding request timed out from " 107 << port_->GetLocalAddress().ToSensitiveString() 108 << " (" << port_->Network()->name() << ")"; 109 110 port_->OnStunBindingOrResolveRequestFailed(); 111 112 if (keep_alive_ 113 && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) { 114 port_->requests_.SendDelayed( 115 new StunBindingRequest(port_, true, server_addr_), 116 RETRY_DELAY); 117 } 118 } 119 120 private: 121 UDPPort* port_; 122 bool keep_alive_; 123 talk_base::SocketAddress server_addr_; 124 uint32 start_time_; 125}; 126 127UDPPort::UDPPort(talk_base::Thread* thread, 128 talk_base::PacketSocketFactory* factory, 129 talk_base::Network* network, 130 talk_base::AsyncPacketSocket* socket, 131 const std::string& username, const std::string& password) 132 : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(), 133 username, password), 134 requests_(thread), 135 socket_(socket), 136 error_(0), 137 resolver_(NULL), 138 ready_(false), 139 stun_keepalive_delay_(KEEPALIVE_DELAY) { 140} 141 142UDPPort::UDPPort(talk_base::Thread* thread, 143 talk_base::PacketSocketFactory* factory, 144 talk_base::Network* network, 145 const talk_base::IPAddress& ip, int min_port, int max_port, 146 const std::string& username, const std::string& password) 147 : Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port, 148 username, password), 149 requests_(thread), 150 socket_(NULL), 151 error_(0), 152 resolver_(NULL), 153 ready_(false), 154 stun_keepalive_delay_(KEEPALIVE_DELAY) { 155} 156 157bool UDPPort::Init() { 158 if (!SharedSocket()) { 159 ASSERT(socket_ == NULL); 160 socket_ = socket_factory()->CreateUdpSocket( 161 talk_base::SocketAddress(ip(), 0), min_port(), max_port()); 162 if (!socket_) { 163 LOG_J(LS_WARNING, this) << "UDP socket creation failed"; 164 return false; 165 } 166 socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket); 167 } 168 socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend); 169 socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady); 170 requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket); 171 return true; 172} 173 174UDPPort::~UDPPort() { 175 if (resolver_) { 176 resolver_->Destroy(false); 177 } 178 if (!SharedSocket()) 179 delete socket_; 180} 181 182void UDPPort::PrepareAddress() { 183 ASSERT(requests_.empty()); 184 if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) { 185 OnLocalAddressReady(socket_, socket_->GetLocalAddress()); 186 } 187} 188 189void UDPPort::MaybePrepareStunCandidate() { 190 // Sending binding request to the STUN server if address is available to 191 // prepare STUN candidate. 192 if (!server_addr_.IsNil()) { 193 SendStunBindingRequest(); 194 } else { 195 // Processing host candidate address. 196 SetResult(true); 197 } 198} 199 200Connection* UDPPort::CreateConnection(const Candidate& address, 201 CandidateOrigin origin) { 202 if (address.protocol() != "udp") 203 return NULL; 204 205 if (!IsCompatibleAddress(address.address())) { 206 return NULL; 207 } 208 209 if (SharedSocket() && Candidates()[0].type() != LOCAL_PORT_TYPE) { 210 ASSERT(false); 211 return NULL; 212 } 213 214 Connection* conn = new ProxyConnection(this, 0, address); 215 AddConnection(conn); 216 return conn; 217} 218 219int UDPPort::SendTo(const void* data, size_t size, 220 const talk_base::SocketAddress& addr, 221 talk_base::DiffServCodePoint dscp, 222 bool payload) { 223 int sent = socket_->SendTo(data, size, addr, dscp); 224 if (sent < 0) { 225 error_ = socket_->GetError(); 226 LOG_J(LS_ERROR, this) << "UDP send of " << size 227 << " bytes failed with error " << error_; 228 } 229 return sent; 230} 231 232int UDPPort::SetOption(talk_base::Socket::Option opt, int value) { 233 // TODO(mallinath) - After we have the support on socket, 234 // remove this specialization. 235 if (opt == talk_base::Socket::OPT_DSCP) { 236 SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value)); 237 return 0; 238 } 239 return socket_->SetOption(opt, value); 240} 241 242int UDPPort::GetOption(talk_base::Socket::Option opt, int* value) { 243 return socket_->GetOption(opt, value); 244} 245 246int UDPPort::GetError() { 247 return error_; 248} 249 250void UDPPort::OnLocalAddressReady(talk_base::AsyncPacketSocket* socket, 251 const talk_base::SocketAddress& address) { 252 AddAddress(address, address, UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE, 253 ICE_TYPE_PREFERENCE_HOST, false); 254 MaybePrepareStunCandidate(); 255} 256 257void UDPPort::OnReadPacket( 258 talk_base::AsyncPacketSocket* socket, const char* data, size_t size, 259 const talk_base::SocketAddress& remote_addr, 260 const talk_base::PacketTime& packet_time) { 261 ASSERT(socket == socket_); 262 263 // Look for a response from the STUN server. 264 // Even if the response doesn't match one of our outstanding requests, we 265 // will eat it because it might be a response to a retransmitted packet, and 266 // we already cleared the request when we got the first response. 267 if (!server_addr_.IsUnresolved() && remote_addr == server_addr_) { 268 requests_.CheckResponse(data, size); 269 return; 270 } 271 272 if (Connection* conn = GetConnection(remote_addr)) { 273 conn->OnReadPacket(data, size, packet_time); 274 } else { 275 Port::OnReadPacket(data, size, remote_addr, PROTO_UDP); 276 } 277} 278 279void UDPPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { 280 Port::OnReadyToSend(); 281} 282 283void UDPPort::SendStunBindingRequest() { 284 // We will keep pinging the stun server to make sure our NAT pin-hole stays 285 // open during the call. 286 // TODO: Support multiple stun servers, or make ResolveStunAddress find a 287 // server with the correct family, or something similar. 288 ASSERT(requests_.empty()); 289 if (server_addr_.IsUnresolved()) { 290 ResolveStunAddress(); 291 } else if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) { 292 // Check if |server_addr_| is compatible with the port's ip. 293 if (IsCompatibleAddress(server_addr_)) { 294 requests_.Send(new StunBindingRequest(this, true, server_addr_)); 295 } else { 296 // Since we can't send stun messages to the server, we should mark this 297 // port ready. 298 OnStunBindingOrResolveRequestFailed(); 299 } 300 } 301} 302 303void UDPPort::ResolveStunAddress() { 304 if (resolver_) 305 return; 306 307 resolver_ = socket_factory()->CreateAsyncResolver(); 308 resolver_->SignalDone.connect(this, &UDPPort::OnResolveResult); 309 resolver_->Start(server_addr_); 310} 311 312void UDPPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { 313 ASSERT(resolver == resolver_); 314 if (resolver_->GetError() != 0 || 315 !resolver_->GetResolvedAddress(ip().family(), &server_addr_)) { 316 LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error " 317 << resolver_->GetError(); 318 OnStunBindingOrResolveRequestFailed(); 319 return; 320 } 321 322 SendStunBindingRequest(); 323} 324 325void UDPPort::OnStunBindingRequestSucceeded( 326 const talk_base::SocketAddress& stun_addr) { 327 if (ready_) // Discarding the binding response if port is already enabled. 328 return; 329 330 if (!SharedSocket() || stun_addr != socket_->GetLocalAddress()) { 331 // If socket is shared and |stun_addr| is equal to local socket 332 // address then discarding the stun address. 333 // Setting related address before STUN candidate is added. For STUN 334 // related address is local socket address. 335 set_related_address(socket_->GetLocalAddress()); 336 AddAddress(stun_addr, socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, 337 STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_PRFLX, false); 338 } 339 SetResult(true); 340} 341 342void UDPPort::OnStunBindingOrResolveRequestFailed() { 343 if (ready_) // Discarding failure response if port is already enabled. 344 return; 345 346 // If socket is shared, we should process local udp candidate. 347 SetResult(SharedSocket()); 348} 349 350void UDPPort::SetResult(bool success) { 351 // Setting ready status. 352 ready_ = true; 353 if (success) { 354 SignalPortComplete(this); 355 } else { 356 SignalPortError(this); 357 } 358} 359 360// TODO: merge this with SendTo above. 361void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) { 362 StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req); 363 if (socket_->SendTo(data, size, sreq->server_addr(), DefaultDscpValue()) < 0) 364 PLOG(LERROR, socket_->GetError()) << "sendto"; 365} 366 367} // namespace cricket 368