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 36 namespace cricket { 37 38 // TODO: Move these to a common place (used in relayport too) 39 const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts 40 const int RETRY_DELAY = 50; // 50ms, from ICE spec 41 const int RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs 42 43 // Handles a binding request sent to the STUN server. 44 class StunPortBindingRequest : public StunRequest { 45 public: 46 StunPortBindingRequest(StunPort* port, bool keep_alive, 47 const talk_base::SocketAddress& addr) 48 : port_(port), keep_alive_(keep_alive), server_addr_(addr) { 49 start_time_ = talk_base::Time(); 50 } 51 52 virtual ~StunPortBindingRequest() { 53 } 54 55 const talk_base::SocketAddress& server_addr() const { return server_addr_; } 56 57 virtual void Prepare(StunMessage* request) { 58 request->SetType(STUN_BINDING_REQUEST); 59 } 60 61 virtual void OnResponse(StunMessage* response) { 62 const StunAddressAttribute* addr_attr = 63 response->GetAddress(STUN_ATTR_MAPPED_ADDRESS); 64 if (!addr_attr) { 65 LOG(LS_ERROR) << "Binding response missing mapped address."; 66 } else if (addr_attr->family() != 1) { 67 LOG(LS_ERROR) << "Binding address has bad family"; 68 } else { 69 talk_base::SocketAddress addr(addr_attr->ip(), addr_attr->port()); 70 port_->AddAddress(addr, "udp", true); 71 } 72 73 // We will do a keep-alive regardless of whether this request suceeds. 74 // This should have almost no impact on network usage. 75 if (keep_alive_) { 76 port_->requests_.SendDelayed( 77 new StunPortBindingRequest(port_, true, server_addr_), 78 KEEPALIVE_DELAY); 79 } 80 } 81 82 virtual void OnErrorResponse(StunMessage* response) { 83 const StunErrorCodeAttribute* attr = response->GetErrorCode(); 84 if (!attr) { 85 LOG(LS_ERROR) << "Bad allocate response error code"; 86 } else { 87 LOG(LS_ERROR) << "Binding error response:" 88 << " class=" << attr->error_class() 89 << " number=" << attr->number() 90 << " reason='" << attr->reason() << "'"; 91 } 92 93 port_->SignalAddressError(port_); 94 95 if (keep_alive_ 96 && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) { 97 port_->requests_.SendDelayed( 98 new StunPortBindingRequest(port_, true, server_addr_), 99 KEEPALIVE_DELAY); 100 } 101 } 102 103 virtual void OnTimeout() { 104 LOG(LS_ERROR) << "Binding request timed out from " 105 << port_->socket_->GetLocalAddress(NULL).ToString() 106 << " (" << port_->network()->name() << ")"; 107 108 port_->SignalAddressError(port_); 109 110 if (keep_alive_ 111 && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) { 112 port_->requests_.SendDelayed( 113 new StunPortBindingRequest(port_, true, server_addr_), 114 RETRY_DELAY); 115 } 116 } 117 118 private: 119 StunPort* port_; 120 bool keep_alive_; 121 talk_base::SocketAddress server_addr_; 122 uint32 start_time_; 123 }; 124 125 const std::string STUN_PORT_TYPE("stun"); 126 127 StunPort::StunPort(talk_base::Thread* thread, 128 talk_base::PacketSocketFactory* factory, 129 talk_base::Network* network, 130 uint32 ip, int min_port, int max_port, 131 const talk_base::SocketAddress& server_addr) 132 : Port(thread, STUN_PORT_TYPE, factory, network, ip, min_port, max_port), 133 server_addr_(server_addr), 134 requests_(thread), 135 socket_(NULL), 136 error_(0), 137 resolver_(NULL) { 138 requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket); 139 } 140 141 bool StunPort::Init() { 142 socket_ = factory_->CreateUdpSocket( 143 talk_base::SocketAddress(ip_, 0), min_port_, max_port_); 144 if (!socket_) { 145 LOG_J(LS_WARNING, this) << "UDP socket creation failed"; 146 return false; 147 } 148 socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket); 149 return true; 150 } 151 152 StunPort::~StunPort() { 153 if (resolver_) { 154 resolver_->Destroy(false); 155 } 156 delete socket_; 157 } 158 159 void StunPort::PrepareAddress() { 160 // We will keep pinging the stun server to make sure our NAT pin-hole stays 161 // open during the call. 162 if (server_addr_.IsUnresolved()) { 163 ResolveStunAddress(); 164 } else { 165 requests_.Send(new StunPortBindingRequest(this, true, server_addr_)); 166 } 167 } 168 169 void StunPort::PrepareSecondaryAddress() { 170 // DNS resolution of the secondary address is not currently supported. 171 ASSERT(!server_addr2_.IsAny()); 172 requests_.Send(new StunPortBindingRequest(this, false, server_addr2_)); 173 } 174 175 Connection* StunPort::CreateConnection(const Candidate& address, 176 CandidateOrigin origin) { 177 if (address.protocol() != "udp") 178 return NULL; 179 180 Connection* conn = new ProxyConnection(this, 0, address); 181 AddConnection(conn); 182 return conn; 183 } 184 185 int StunPort::SendTo(const void* data, size_t size, 186 const talk_base::SocketAddress& addr, bool payload) { 187 int sent = socket_->SendTo(data, size, addr); 188 if (sent < 0) { 189 error_ = socket_->GetError(); 190 LOG_J(LS_ERROR, this) << "UDP send of " << size 191 << " bytes failed with error " << error_; 192 } 193 return sent; 194 } 195 196 int StunPort::SetOption(talk_base::Socket::Option opt, int value) { 197 return socket_->SetOption(opt, value); 198 } 199 200 int StunPort::GetError() { 201 return error_; 202 } 203 204 void StunPort::OnReadPacket(talk_base::AsyncPacketSocket* socket, 205 const char* data, size_t size, 206 const talk_base::SocketAddress& remote_addr) { 207 ASSERT(socket == socket_); 208 209 // Look for a response from the STUN server. 210 // Even if the response doesn't match one of our outstanding requests, we 211 // will eat it because it might be a response to a retransmitted packet, and 212 // we already cleared the request when we got the first response. 213 ASSERT(!server_addr_.IsUnresolved()); 214 if (remote_addr == server_addr_ || remote_addr == server_addr2_) { 215 requests_.CheckResponse(data, size); 216 return; 217 } 218 219 if (Connection* conn = GetConnection(remote_addr)) { 220 conn->OnReadPacket(data, size); 221 } else { 222 Port::OnReadPacket(data, size, remote_addr); 223 } 224 } 225 226 void StunPort::ResolveStunAddress() { 227 if (resolver_) 228 return; 229 230 resolver_ = new talk_base::AsyncResolver(); 231 resolver_->SignalWorkDone.connect(this, &StunPort::OnResolveResult); 232 resolver_->set_address(server_addr_); 233 resolver_->Start(); 234 } 235 236 void StunPort::OnResolveResult(talk_base::SignalThread* t) { 237 ASSERT(t == resolver_); 238 if (resolver_->error() != 0) { 239 LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error " 240 << resolver_->error(); 241 SignalAddressError(this); 242 } 243 244 server_addr_ = resolver_->address(); 245 PrepareAddress(); 246 } 247 248 // TODO: merge this with SendTo above. 249 void StunPort::OnSendPacket(const void* data, size_t size, StunRequest* req) { 250 StunPortBindingRequest* sreq = static_cast<StunPortBindingRequest*>(req); 251 if (socket_->SendTo(data, size, sreq->server_addr()) < 0) 252 PLOG(LERROR, socket_->GetError()) << "sendto"; 253 } 254 255 } // namespace cricket 256