webrtc_m130/webrtc/p2p/base/relayport.cc
skvlad c309e0e3ea Don't stop sending media on EWOULDBLOCK
This change makes WebRTC no longer stop sending video when we receive an
EWOULDBLOCK error from the operating system. This was previously
causing calls on a slow link (where the first hop is slow) to rapidly
oscillate between starting and stopping video.

We still do need to stop sending packets if there is no known good
connection we can use for that. We used to generate a synthetic
EWOULDBLOCK error in that case. This CL replaces it with a different
code (ENOTCONN); EWOULDBLOCK no longer stops the stream but ENOTCONN
does.

I've updated all the places where we seemed to be generating EWOULDBLOCK
for reasons other than some buffer been full; please give it a thorough
look in case I missed something.

R=pthatcher@webrtc.org

Review URL: https://codereview.webrtc.org/2192963002 .

Cr-Commit-Position: refs/heads/master@{#13566}
2016-07-29 00:15:30 +00:00

847 lines
27 KiB
C++

/*
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include "webrtc/p2p/base/relayport.h"
#include "webrtc/base/asyncpacketsocket.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/logging.h"
namespace cricket {
static const int kMessageConnectTimeout = 1;
static const int kKeepAliveDelay = 10 * 60 * 1000;
static const int kRetryTimeout = 50 * 1000; // ICE says 50 secs
// How long to wait for a socket to connect to remote host in milliseconds
// before trying another connection.
static const int kSoftConnectTimeoutMs = 3 * 1000;
// Handles a connection to one address/port/protocol combination for a
// particular RelayEntry.
class RelayConnection : public sigslot::has_slots<> {
public:
RelayConnection(const ProtocolAddress* protocol_address,
rtc::AsyncPacketSocket* socket,
rtc::Thread* thread);
~RelayConnection();
rtc::AsyncPacketSocket* socket() const { return socket_; }
const ProtocolAddress* protocol_address() {
return protocol_address_;
}
rtc::SocketAddress GetAddress() const {
return protocol_address_->address;
}
ProtocolType GetProtocol() const {
return protocol_address_->proto;
}
int SetSocketOption(rtc::Socket::Option opt, int value);
// Validates a response to a STUN allocate request.
bool CheckResponse(StunMessage* msg);
// Sends data to the relay server.
int Send(const void* pv, size_t cb, const rtc::PacketOptions& options);
// Sends a STUN allocate request message to the relay server.
void SendAllocateRequest(RelayEntry* entry, int delay);
// Return the latest error generated by the socket.
int GetError() { return socket_->GetError(); }
// Called on behalf of a StunRequest to write data to the socket. This is
// already STUN intended for the server, so no wrapping is necessary.
void OnSendPacket(const void* data, size_t size, StunRequest* req);
private:
rtc::AsyncPacketSocket* socket_;
const ProtocolAddress* protocol_address_;
StunRequestManager *request_manager_;
};
// Manages a number of connections to the relayserver, one for each
// available protocol. We aim to use each connection for only a
// specific destination address so that we can avoid wrapping every
// packet in a STUN send / data indication.
class RelayEntry : public rtc::MessageHandler,
public sigslot::has_slots<> {
public:
RelayEntry(RelayPort* port, const rtc::SocketAddress& ext_addr);
~RelayEntry();
RelayPort* port() { return port_; }
const rtc::SocketAddress& address() const { return ext_addr_; }
void set_address(const rtc::SocketAddress& addr) { ext_addr_ = addr; }
bool connected() const { return connected_; }
bool locked() const { return locked_; }
// Returns the last error on the socket of this entry.
int GetError();
// Returns the most preferred connection of the given
// ones. Connections are rated based on protocol in the order of:
// UDP, TCP and SSLTCP, where UDP is the most preferred protocol
static RelayConnection* GetBestConnection(RelayConnection* conn1,
RelayConnection* conn2);
// Sends the STUN requests to the server to initiate this connection.
void Connect();
// Called when this entry becomes connected. The address given is the one
// exposed to the outside world on the relay server.
void OnConnect(const rtc::SocketAddress& mapped_addr,
RelayConnection* socket);
// Sends a packet to the given destination address using the socket of this
// entry. This will wrap the packet in STUN if necessary.
int SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options);
// Schedules a keep-alive allocate request.
void ScheduleKeepAlive();
void SetServerIndex(size_t sindex) { server_index_ = sindex; }
// Sets this option on the socket of each connection.
int SetSocketOption(rtc::Socket::Option opt, int value);
size_t ServerIndex() const { return server_index_; }
// Try a different server address
void HandleConnectFailure(rtc::AsyncPacketSocket* socket);
// Implementation of the MessageHandler Interface.
virtual void OnMessage(rtc::Message *pmsg);
private:
RelayPort* port_;
rtc::SocketAddress ext_addr_;
size_t server_index_;
bool connected_;
bool locked_;
RelayConnection* current_connection_;
// Called when a TCP connection is established or fails
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
// Called when a packet is received on this socket.
void OnReadPacket(
rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time);
void OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet);
// Called when the socket is currently able to send.
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
// Sends the given data on the socket to the server with no wrapping. This
// returns the number of bytes written or -1 if an error occurred.
int SendPacket(const void* data, size_t size,
const rtc::PacketOptions& options);
};
// Handles an allocate request for a particular RelayEntry.
class AllocateRequest : public StunRequest {
public:
AllocateRequest(RelayEntry* entry, RelayConnection* connection);
virtual ~AllocateRequest() {}
void Prepare(StunMessage* request) override;
void OnSent() override;
int resend_delay() override;
void OnResponse(StunMessage* response) override;
void OnErrorResponse(StunMessage* response) override;
void OnTimeout() override;
private:
RelayEntry* entry_;
RelayConnection* connection_;
int64_t start_time_;
};
RelayPort::RelayPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
rtc::Network* network,
const rtc::IPAddress& ip,
uint16_t min_port,
uint16_t max_port,
const std::string& username,
const std::string& password)
: Port(thread,
RELAY_PORT_TYPE,
factory,
network,
ip,
min_port,
max_port,
username,
password),
ready_(false),
error_(0) {
entries_.push_back(
new RelayEntry(this, rtc::SocketAddress()));
// TODO: set local preference value for TCP based candidates.
}
RelayPort::~RelayPort() {
for (size_t i = 0; i < entries_.size(); ++i)
delete entries_[i];
thread()->Clear(this);
}
void RelayPort::AddServerAddress(const ProtocolAddress& addr) {
// Since HTTP proxies usually only allow 443,
// let's up the priority on PROTO_SSLTCP
if (addr.proto == PROTO_SSLTCP &&
(proxy().type == rtc::PROXY_HTTPS ||
proxy().type == rtc::PROXY_UNKNOWN)) {
server_addr_.push_front(addr);
} else {
server_addr_.push_back(addr);
}
}
void RelayPort::AddExternalAddress(const ProtocolAddress& addr) {
std::string proto_name = ProtoToString(addr.proto);
for (std::vector<ProtocolAddress>::iterator it = external_addr_.begin();
it != external_addr_.end(); ++it) {
if ((it->address == addr.address) && (it->proto == addr.proto)) {
LOG(INFO) << "Redundant relay address: " << proto_name
<< " @ " << addr.address.ToSensitiveString();
return;
}
}
external_addr_.push_back(addr);
}
void RelayPort::SetReady() {
if (!ready_) {
std::vector<ProtocolAddress>::iterator iter;
for (iter = external_addr_.begin();
iter != external_addr_.end(); ++iter) {
std::string proto_name = ProtoToString(iter->proto);
// In case of Gturn, related address is set to null socket address.
// This is due to as mapped address stun attribute is used for allocated
// address.
AddAddress(iter->address, iter->address, rtc::SocketAddress(), proto_name,
proto_name, "", RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, 0,
false);
}
ready_ = true;
SignalPortComplete(this);
}
}
const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
if (index < server_addr_.size())
return &server_addr_[index];
return NULL;
}
bool RelayPort::HasMagicCookie(const char* data, size_t size) {
if (size < 24 + sizeof(TURN_MAGIC_COOKIE_VALUE)) {
return false;
} else {
return memcmp(data + 24,
TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE)) == 0;
}
}
void RelayPort::PrepareAddress() {
// We initiate a connect on the first entry. If this completes, it will fill
// in the server address as the address of this port.
ASSERT(entries_.size() == 1);
entries_[0]->Connect();
ready_ = false;
}
Connection* RelayPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
// We only create conns to non-udp sockets if they are incoming on this port
if ((address.protocol() != UDP_PROTOCOL_NAME) &&
(origin != ORIGIN_THIS_PORT)) {
return 0;
}
// We don't support loopback on relays
if (address.type() == Type()) {
return 0;
}
if (!IsCompatibleAddress(address.address())) {
return 0;
}
size_t index = 0;
for (size_t i = 0; i < Candidates().size(); ++i) {
const Candidate& local = Candidates()[i];
if (local.protocol() == address.protocol()) {
index = i;
break;
}
}
Connection * conn = new ProxyConnection(this, index, address);
AddOrReplaceConnection(conn);
return conn;
}
int RelayPort::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options,
bool payload) {
// Try to find an entry for this specific address. Note that the first entry
// created was not given an address initially, so it can be set to the first
// address that comes along.
RelayEntry* entry = 0;
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->address().IsNil() && payload) {
entry = entries_[i];
entry->set_address(addr);
break;
} else if (entries_[i]->address() == addr) {
entry = entries_[i];
break;
}
}
// If we did not find one, then we make a new one. This will not be useable
// until it becomes connected, however.
if (!entry && payload) {
entry = new RelayEntry(this, addr);
if (!entries_.empty()) {
entry->SetServerIndex(entries_[0]->ServerIndex());
}
entry->Connect();
entries_.push_back(entry);
}
// If the entry is connected, then we can send on it (though wrapping may
// still be necessary). Otherwise, we can't yet use this connection, so we
// default to the first one.
if (!entry || !entry->connected()) {
ASSERT(!entries_.empty());
entry = entries_[0];
if (!entry->connected()) {
error_ = ENOTCONN;
return SOCKET_ERROR;
}
}
// Send the actual contents to the server using the usual mechanism.
int sent = entry->SendTo(data, size, addr, options);
if (sent <= 0) {
ASSERT(sent < 0);
error_ = entry->GetError();
return SOCKET_ERROR;
}
// The caller of the function is expecting the number of user data bytes,
// rather than the size of the packet.
return static_cast<int>(size);
}
int RelayPort::SetOption(rtc::Socket::Option opt, int value) {
int result = 0;
for (size_t i = 0; i < entries_.size(); ++i) {
if (entries_[i]->SetSocketOption(opt, value) < 0) {
result = -1;
error_ = entries_[i]->GetError();
}
}
options_.push_back(OptionValue(opt, value));
return result;
}
int RelayPort::GetOption(rtc::Socket::Option opt, int* value) {
std::vector<OptionValue>::iterator it;
for (it = options_.begin(); it < options_.end(); ++it) {
if (it->first == opt) {
*value = it->second;
return 0;
}
}
return SOCKET_ERROR;
}
int RelayPort::GetError() {
return error_;
}
void RelayPort::OnReadPacket(
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
ProtocolType proto,
const rtc::PacketTime& packet_time) {
if (Connection* conn = GetConnection(remote_addr)) {
conn->OnReadPacket(data, size, packet_time);
} else {
Port::OnReadPacket(data, size, remote_addr, proto);
}
}
RelayConnection::RelayConnection(const ProtocolAddress* protocol_address,
rtc::AsyncPacketSocket* socket,
rtc::Thread* thread)
: socket_(socket),
protocol_address_(protocol_address) {
request_manager_ = new StunRequestManager(thread);
request_manager_->SignalSendPacket.connect(this,
&RelayConnection::OnSendPacket);
}
RelayConnection::~RelayConnection() {
delete request_manager_;
delete socket_;
}
int RelayConnection::SetSocketOption(rtc::Socket::Option opt,
int value) {
if (socket_) {
return socket_->SetOption(opt, value);
}
return 0;
}
bool RelayConnection::CheckResponse(StunMessage* msg) {
return request_manager_->CheckResponse(msg);
}
void RelayConnection::OnSendPacket(const void* data, size_t size,
StunRequest* req) {
// TODO(mallinath) Find a way to get DSCP value from Port.
rtc::PacketOptions options; // Default dscp set to NO_CHANGE.
int sent = socket_->SendTo(data, size, GetAddress(), options);
if (sent <= 0) {
LOG(LS_VERBOSE) << "OnSendPacket: failed sending to " << GetAddress() <<
strerror(socket_->GetError());
ASSERT(sent < 0);
}
}
int RelayConnection::Send(const void* pv, size_t cb,
const rtc::PacketOptions& options) {
return socket_->SendTo(pv, cb, GetAddress(), options);
}
void RelayConnection::SendAllocateRequest(RelayEntry* entry, int delay) {
request_manager_->SendDelayed(new AllocateRequest(entry, this), delay);
}
RelayEntry::RelayEntry(RelayPort* port,
const rtc::SocketAddress& ext_addr)
: port_(port), ext_addr_(ext_addr),
server_index_(0), connected_(false), locked_(false),
current_connection_(NULL) {
}
RelayEntry::~RelayEntry() {
// Remove all RelayConnections and dispose sockets.
delete current_connection_;
current_connection_ = NULL;
}
void RelayEntry::Connect() {
// If we're already connected, return.
if (connected_)
return;
// If we've exhausted all options, bail out.
const ProtocolAddress* ra = port()->ServerAddress(server_index_);
if (!ra) {
LOG(LS_WARNING) << "No more relay addresses left to try";
return;
}
// Remove any previous connection.
if (current_connection_) {
port()->thread()->Dispose(current_connection_);
current_connection_ = NULL;
}
// Try to set up our new socket.
LOG(LS_INFO) << "Connecting to relay via " << ProtoToString(ra->proto) <<
" @ " << ra->address.ToSensitiveString();
rtc::AsyncPacketSocket* socket = NULL;
if (ra->proto == PROTO_UDP) {
// UDP sockets are simple.
socket = port_->socket_factory()->CreateUdpSocket(
rtc::SocketAddress(port_->ip(), 0),
port_->min_port(), port_->max_port());
} else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) {
int opts = (ra->proto == PROTO_SSLTCP) ?
rtc::PacketSocketFactory::OPT_SSLTCP : 0;
socket = port_->socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(port_->ip(), 0), ra->address,
port_->proxy(), port_->user_agent(), opts);
} else {
LOG(LS_WARNING) << "Unknown protocol (" << ra->proto << ")";
}
if (!socket) {
LOG(LS_WARNING) << "Socket creation failed";
}
// If we failed to get a socket, move on to the next protocol.
if (!socket) {
port()->thread()->Post(RTC_FROM_HERE, this, kMessageConnectTimeout);
return;
}
// Otherwise, create the new connection and configure any socket options.
socket->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
socket->SignalSentPacket.connect(this, &RelayEntry::OnSentPacket);
socket->SignalReadyToSend.connect(this, &RelayEntry::OnReadyToSend);
current_connection_ = new RelayConnection(ra, socket, port()->thread());
for (size_t i = 0; i < port_->options().size(); ++i) {
current_connection_->SetSocketOption(port_->options()[i].first,
port_->options()[i].second);
}
// If we're trying UDP, start binding requests.
// If we're trying TCP, wait for connection with a fixed timeout.
if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
socket->SignalClose.connect(this, &RelayEntry::OnSocketClose);
socket->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
port()->thread()->PostDelayed(RTC_FROM_HERE, kSoftConnectTimeoutMs, this,
kMessageConnectTimeout);
} else {
current_connection_->SendAllocateRequest(this, 0);
}
}
int RelayEntry::GetError() {
if (current_connection_ != NULL) {
return current_connection_->GetError();
}
return 0;
}
RelayConnection* RelayEntry::GetBestConnection(RelayConnection* conn1,
RelayConnection* conn2) {
return conn1->GetProtocol() <= conn2->GetProtocol() ? conn1 : conn2;
}
void RelayEntry::OnConnect(const rtc::SocketAddress& mapped_addr,
RelayConnection* connection) {
// We are connected, notify our parent.
ProtocolType proto = PROTO_UDP;
LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto)
<< " @ " << mapped_addr.ToSensitiveString();
connected_ = true;
port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto));
port_->SetReady();
}
int RelayEntry::SendTo(const void* data, size_t size,
const rtc::SocketAddress& addr,
const rtc::PacketOptions& options) {
// If this connection is locked to the address given, then we can send the
// packet with no wrapper.
if (locked_ && (ext_addr_ == addr))
return SendPacket(data, size, options);
// Otherwise, we must wrap the given data in a STUN SEND request so that we
// can communicate the destination address to the server.
//
// Note that we do not use a StunRequest here. This is because there is
// likely no reason to resend this packet. If it is late, we just drop it.
// The next send to this address will try again.
RelayMessage request;
request.SetType(STUN_SEND_REQUEST);
StunByteStringAttribute* magic_cookie_attr =
StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
magic_cookie_attr->CopyBytes(TURN_MAGIC_COOKIE_VALUE,
sizeof(TURN_MAGIC_COOKIE_VALUE));
VERIFY(request.AddAttribute(magic_cookie_attr));
StunByteStringAttribute* username_attr =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
username_attr->CopyBytes(port_->username_fragment().c_str(),
port_->username_fragment().size());
VERIFY(request.AddAttribute(username_attr));
StunAddressAttribute* addr_attr =
StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
addr_attr->SetIP(addr.ipaddr());
addr_attr->SetPort(addr.port());
VERIFY(request.AddAttribute(addr_attr));
// Attempt to lock
if (ext_addr_ == addr) {
StunUInt32Attribute* options_attr =
StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS);
options_attr->SetValue(0x1);
VERIFY(request.AddAttribute(options_attr));
}
StunByteStringAttribute* data_attr =
StunAttribute::CreateByteString(STUN_ATTR_DATA);
data_attr->CopyBytes(data, size);
VERIFY(request.AddAttribute(data_attr));
// TODO: compute the HMAC.
rtc::ByteBufferWriter buf;
request.Write(&buf);
return SendPacket(buf.Data(), buf.Length(), options);
}
void RelayEntry::ScheduleKeepAlive() {
if (current_connection_) {
current_connection_->SendAllocateRequest(this, kKeepAliveDelay);
}
}
int RelayEntry::SetSocketOption(rtc::Socket::Option opt, int value) {
// Set the option on all available sockets.
int socket_error = 0;
if (current_connection_) {
socket_error = current_connection_->SetSocketOption(opt, value);
}
return socket_error;
}
void RelayEntry::HandleConnectFailure(
rtc::AsyncPacketSocket* socket) {
// Make sure it's the current connection that has failed, it might
// be an old socked that has not yet been disposed.
if (!socket ||
(current_connection_ && socket == current_connection_->socket())) {
if (current_connection_)
port()->SignalConnectFailure(current_connection_->protocol_address());
// Try to connect to the next server address.
server_index_ += 1;
Connect();
}
}
void RelayEntry::OnMessage(rtc::Message *pmsg) {
ASSERT(pmsg->message_id == kMessageConnectTimeout);
if (current_connection_) {
const ProtocolAddress* ra = current_connection_->protocol_address();
LOG(LS_WARNING) << "Relay " << ra->proto << " connection to " <<
ra->address << " timed out";
// Currently we connect to each server address in sequence. If we
// have more addresses to try, treat this is an error and move on to
// the next address, otherwise give this connection more time and
// await the real timeout.
//
// TODO: Connect to servers in parallel to speed up connect time
// and to avoid giving up too early.
port_->SignalSoftTimeout(ra);
HandleConnectFailure(current_connection_->socket());
} else {
HandleConnectFailure(NULL);
}
}
void RelayEntry::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
LOG(INFO) << "relay tcp connected to " <<
socket->GetRemoteAddress().ToSensitiveString();
if (current_connection_ != NULL) {
current_connection_->SendAllocateRequest(this, 0);
}
}
void RelayEntry::OnSocketClose(rtc::AsyncPacketSocket* socket,
int error) {
PLOG(LERROR, error) << "Relay connection failed: socket closed";
HandleConnectFailure(socket);
}
void RelayEntry::OnReadPacket(
rtc::AsyncPacketSocket* socket,
const char* data, size_t size,
const rtc::SocketAddress& remote_addr,
const rtc::PacketTime& packet_time) {
// ASSERT(remote_addr == port_->server_addr());
// TODO: are we worried about this?
if (current_connection_ == NULL || socket != current_connection_->socket()) {
// This packet comes from an unknown address.
LOG(WARNING) << "Dropping packet: unknown address";
return;
}
// If the magic cookie is not present, then this is an unwrapped packet sent
// by the server, The actual remote address is the one we recorded.
if (!port_->HasMagicCookie(data, size)) {
if (locked_) {
port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP, packet_time);
} else {
LOG(WARNING) << "Dropping packet: entry not locked";
}
return;
}
rtc::ByteBufferReader buf(data, size);
RelayMessage msg;
if (!msg.Read(&buf)) {
LOG(INFO) << "Incoming packet was not STUN";
return;
}
// The incoming packet should be a STUN ALLOCATE response, SEND response, or
// DATA indication.
if (current_connection_->CheckResponse(&msg)) {
return;
} else if (msg.type() == STUN_SEND_RESPONSE) {
if (const StunUInt32Attribute* options_attr =
msg.GetUInt32(STUN_ATTR_OPTIONS)) {
if (options_attr->value() & 0x1) {
locked_ = true;
}
}
return;
} else if (msg.type() != STUN_DATA_INDICATION) {
LOG(INFO) << "Received BAD stun type from server: " << msg.type();
return;
}
// This must be a data indication.
const StunAddressAttribute* addr_attr =
msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
if (!addr_attr) {
LOG(INFO) << "Data indication has no source address";
return;
} else if (addr_attr->family() != 1) {
LOG(INFO) << "Source address has bad family";
return;
}
rtc::SocketAddress remote_addr2(addr_attr->ipaddr(), addr_attr->port());
const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA);
if (!data_attr) {
LOG(INFO) << "Data indication has no data";
return;
}
// Process the actual data and remote address in the normal manner.
port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2,
PROTO_UDP, packet_time);
}
void RelayEntry::OnSentPacket(rtc::AsyncPacketSocket* socket,
const rtc::SentPacket& sent_packet) {
port_->OnSentPacket(socket, sent_packet);
}
void RelayEntry::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
if (connected()) {
port_->OnReadyToSend();
}
}
int RelayEntry::SendPacket(const void* data, size_t size,
const rtc::PacketOptions& options) {
int sent = 0;
if (current_connection_) {
// We are connected, no need to send packets anywere else than to
// the current connection.
sent = current_connection_->Send(data, size, options);
}
return sent;
}
AllocateRequest::AllocateRequest(RelayEntry* entry,
RelayConnection* connection)
: StunRequest(new RelayMessage()),
entry_(entry),
connection_(connection) {
start_time_ = rtc::TimeMillis();
}
void AllocateRequest::Prepare(StunMessage* request) {
request->SetType(STUN_ALLOCATE_REQUEST);
StunByteStringAttribute* username_attr =
StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
username_attr->CopyBytes(
entry_->port()->username_fragment().c_str(),
entry_->port()->username_fragment().size());
VERIFY(request->AddAttribute(username_attr));
}
void AllocateRequest::OnSent() {
count_ += 1;
if (count_ == 5)
timeout_ = true;
}
int AllocateRequest::resend_delay() {
if (count_ == 0) {
return 0;
}
return 100 * std::max(1 << (count_-1), 2);
}
void AllocateRequest::OnResponse(StunMessage* response) {
const StunAddressAttribute* addr_attr =
response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
if (!addr_attr) {
LOG(INFO) << "Allocate response missing mapped address.";
} else if (addr_attr->family() != 1) {
LOG(INFO) << "Mapped address has bad family";
} else {
rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
entry_->OnConnect(addr, connection_);
}
// We will do a keep-alive regardless of whether this request suceeds.
// This should have almost no impact on network usage.
entry_->ScheduleKeepAlive();
}
void AllocateRequest::OnErrorResponse(StunMessage* response) {
const StunErrorCodeAttribute* attr = response->GetErrorCode();
if (!attr) {
LOG(INFO) << "Bad allocate response error code";
} else {
LOG(INFO) << "Allocate error response:"
<< " code=" << attr->code()
<< " reason='" << attr->reason() << "'";
}
if (rtc::TimeMillis() - start_time_ <= kRetryTimeout)
entry_->ScheduleKeepAlive();
}
void AllocateRequest::OnTimeout() {
LOG(INFO) << "Allocate request timed out";
entry_->HandleConnectFailure(connection_->socket());
}
} // namespace cricket