📄 port.cc
字号:
/* * libjingle * Copyright 2004--2005, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#if defined(_MSC_VER) && _MSC_VER < 1300#pragma warning(disable:4786)#endif#include <errno.h>#include <algorithm>#include <iostream>#include <vector>#include "talk/base/asyncudpsocket.h"#include "talk/base/asynctcpsocket.h"#include "talk/base/helpers.h"#include "talk/base/logging.h"#include "talk/base/scoped_ptr.h"#include "talk/base/socketadapters.h"#include "talk/p2p/base/common.h"#include "talk/p2p/base/port.h"#if defined(_MSC_VER) && _MSC_VER < 1300namespace std { using ::memcmp;}#endifnamespace {// The length of time we wait before timing out readability on a connection.const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds// The length of time we wait before timing out writability on a connection.const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds// The length of time we wait before we become unwritable.const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds// The number of pings that must fail to respond before we become unwritable.const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5;// This is the length of time that we wait for a ping response to come back.const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds// Determines whether we have seen at least the given maximum number of// pings fail to have a response.inline bool TooManyFailures( const std::vector<uint32>& pings_since_last_response, uint32 maximum_failures, uint32 rtt_estimate, uint32 now) { // If we haven't sent that many pings, then we can't have failed that many. if (pings_since_last_response.size() < maximum_failures) return false; // Check if the window in which we would expect a response to the ping has // already elapsed. return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now;}// Determines whether we have gone too long without seeing any response.inline bool TooLongWithoutResponse( const std::vector<uint32>& pings_since_last_response, uint32 maximum_time, uint32 now) { if (pings_since_last_response.size() == 0) return false; return pings_since_last_response[0] + maximum_time < now;}// We will restrict RTT estimates (when used for determining state) to be// within a reasonable range.const uint32 MINIMUM_RTT = 100; // 0.1 secondsconst uint32 MAXIMUM_RTT = 3000; // 3 seconds// When we don't have any RTT data, we have to pick something reasonable. We// use a large value just in case the connection is really slow.const uint32 DEFAULT_RTT = MAXIMUM_RTT;// Computes our estimate of the RTT given the current estimate and the number// of data points on which it is based.inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) { if (rtt_data_points == 0) return DEFAULT_RTT; else return talk_base::_max(MINIMUM_RTT, talk_base::_min(MAXIMUM_RTT, 2 * rtt));}// Weighting of the old rtt value to new data.const int RTT_RATIO = 3; // 3 : 1// The delay before we begin checking if this port is useless.const int kPortTimeoutDelay = 30 * 1000; // 30 secondsconst uint32 MSG_CHECKTIMEOUT = 1;const uint32 MSG_DELETE = 1;}namespace cricket {static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" };const char * ProtoToString(ProtocolType proto) { return PROTO_NAMES[proto];}bool StringToProto(const char * value, ProtocolType& proto) { for (size_t i=0; i<=PROTO_LAST; ++i) { if (strcmp(PROTO_NAMES[i], value) == 0) { proto = static_cast<ProtocolType>(i); return true; } } return false;}std::string Port::agent_;talk_base::ProxyInfo Port::proxy_;Port::Port(talk_base::Thread* thread, const std::string& type, talk_base::SocketFactory* factory, talk_base::Network* network) : thread_(thread), factory_(factory), type_(type), network_(network), preference_(-1), lifetime_(LT_PRESTART), enable_port_packets_(false) { if (factory_ == NULL) factory_ = thread_->socketserver(); set_username_fragment(CreateRandomString(16)); set_password(CreateRandomString(16));}Port::~Port() { // Delete all of the remaining connections. We copy the list up front // because each deletion will cause it to be modified. std::vector<Connection*> list; AddressMap::iterator iter = connections_.begin(); while (iter != connections_.end()) { list.push_back(iter->second); ++iter; } for (uint32 i = 0; i < list.size(); i++) delete list[i];}Connection* Port::GetConnection(const talk_base::SocketAddress& remote_addr) { AddressMap::const_iterator iter = connections_.find(remote_addr); if (iter != connections_.end()) return iter->second; else return NULL;}void Port::AddAddress(const talk_base::SocketAddress& address, const std::string& protocol, bool final) { Candidate c; c.set_name(name_); c.set_type(type_); c.set_protocol(protocol); c.set_address(address); c.set_preference(preference_); c.set_username(username_frag_); c.set_password(password_); c.set_network_name(network_->name()); c.set_generation(generation_); candidates_.push_back(c); if (final) SignalAddressReady(this);}void Port::AddConnection(Connection* conn) { connections_[conn->remote_candidate().address()] = conn; conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); SignalConnectionCreated(this, conn);}void Port::OnReadPacket( const char* data, size_t size, const talk_base::SocketAddress& addr) { // If the user has enabled port packets, just hand this over. if (enable_port_packets_) { SignalReadPacket(this, data, size, addr); return; } // If this is an authenticated STUN request, then signal unknown address and // send back a proper binding response. StunMessage* msg; std::string remote_username; if (!GetStunMessage(data, size, addr, msg, remote_username)) { LOG_J(LS_ERROR, this) << "Received non-STUN packet from unknown address (" << addr.ToString() << ")"; } else if (!msg) { // STUN message handled already } else if (msg->type() == STUN_BINDING_REQUEST) { SignalUnknownAddress(this, addr, msg, remote_username); } else { LOG_J(LS_ERROR, this) << "Received unexpected STUN message type (" << msg->type() << ") from unknown address (" << addr.ToString() << ")"; delete msg; }}void Port::SendBindingRequest(Connection* conn) { // Construct the request message. StunMessage request; request.SetType(STUN_BINDING_REQUEST); request.SetTransactionID(CreateRandomString(16)); StunByteStringAttribute* username_attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME); std::string username = conn->remote_candidate().username(); username.append(username_frag_); username_attr->CopyBytes(username.c_str(), (uint16)username.size()); request.AddAttribute(username_attr); // Send the request message. // NOTE: If we wanted to, this is where we would add the HMAC. talk_base::ByteBuffer buf; request.Write(&buf); SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false);}bool Port::GetStunMessage(const char* data, size_t size, const talk_base::SocketAddress& addr, StunMessage *& msg, std::string& remote_username) { // NOTE: This could clearly be optimized to avoid allocating any memory. // However, at the data rates we'll be looking at on the client side, // this probably isn't worth worrying about. msg = 0; // Parse the request message. If the packet is not a complete and correct // STUN message, then ignore it. talk_base::scoped_ptr<StunMessage> stun_msg(new StunMessage()); talk_base::ByteBuffer buf(data, size); if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { return false; } // The packet must include a username that either begins or ends with our // fragment. It should begin with our fragment if it is a request and it // should end with our fragment if it is a response. const StunByteStringAttribute* username_attr = stun_msg->GetByteString(STUN_ATTR_USERNAME); int remote_frag_len = (username_attr ? username_attr->length() : 0); remote_frag_len -= static_cast<int>(username_frag_.size()); if (stun_msg->type() == STUN_BINDING_REQUEST) { if ((remote_frag_len < 0) || (std::memcmp(username_attr->bytes(), username_frag_.c_str(), username_frag_.size()) != 0)) { LOG_J(LS_ERROR, this) << "Received STUN request with bad username"; SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); return true; } remote_username.assign(username_attr->bytes() + username_frag_.size(), username_attr->bytes() + username_attr->length()); } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { if ((remote_frag_len < 0) || (std::memcmp(username_attr->bytes() + remote_frag_len, username_frag_.c_str(), username_frag_.size()) != 0)) { LOG_J(LS_ERROR, this) << "Received STUN response with bad username"; // Do not send error response to a response return true; } remote_username.assign(username_attr->bytes(), username_attr->bytes() + remote_frag_len);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -