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