/* * Copyright 2015 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.
*/
// A requester tracks the requests and responses from a single socket to many // STUN servers class StunProber::Requester : public sigslot::has_slots<> { public: // Each Request maps to a request and response. struct Request { // Actual time the STUN bind request was sent.
int64_t sent_time_ms = 0; // Time the response was received.
int64_t received_time_ms = 0;
// Server reflexive address from STUN response for this given request.
rtc::SocketAddress srflx_addr;
// StunProber provides `server_ips` for Requester to probe. For shared // socket mode, it'll be all the resolved IP addresses. For non-shared mode, // it'll just be a single address.
Requester(StunProber* prober,
rtc::AsyncPacketSocket* socket, const std::vector<rtc::SocketAddress>& server_ips);
~Requester() override;
// There is no callback for SendStunRequest as the underneath socket send is // expected to be completed immediately. Otherwise, it'll skip this request // and move to the next one. void SendStunRequest();
std::unique_ptr<rtc::ByteBufferWriter> request_packet( new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)); if (!message.Write(request_packet.get())) {
prober_->ReportOnFinished(WRITE_FAILED); return;
}
auto addr = server_ips_[num_request_sent_];
request.server_addr = addr.ipaddr();
// The write must succeed immediately. Otherwise, the calculating of the STUN // request timing could become too complicated. Callback is ignored by passing // empty AsyncCallback.
rtc::PacketOptions options; int rv = socket_->SendTo(request_packet->Data(), request_packet->Length(),
addr, options); if (rv < 0) {
prober_->ReportOnFinished(WRITE_FAILED); return;
}
void StunProber::Requester::Request::ProcessResponse(
rtc::ArrayView<const uint8_t> payload) {
int64_t now = rtc::TimeMillis();
rtc::ByteBufferReader message(payload);
cricket::StunMessage stun_response; if (!stun_response.Read(&message)) { // Invalid or incomplete STUN packet.
received_time_ms = 0; return;
}
// Get external address of the socket. const cricket::StunAddressAttribute* addr_attr =
stun_response.GetAddress(cricket::STUN_ATTR_MAPPED_ADDRESS); if (addr_attr == nullptr) { // Addresses not available to detect whether or not behind a NAT. return;
}
StunProber::~StunProber() {
RTC_DCHECK(thread_checker_.IsCurrent()); for (auto* req : requesters_) { if (req) { delete req;
}
} for (auto* s : sockets_) { if (s) { delete s;
}
}
}
bool StunProber::Start(const std::vector<rtc::SocketAddress>& servers, bool shared_socket_mode, int interval_ms, int num_request_per_ip, int timeout_ms, const AsyncCallback callback) {
observer_adapter_.set_callback(callback); return Prepare(servers, shared_socket_mode, interval_ms, num_request_per_ip,
timeout_ms, &observer_adapter_);
}
bool StunProber::Prepare(const std::vector<rtc::SocketAddress>& servers, bool shared_socket_mode, int interval_ms, int num_request_per_ip, int timeout_ms,
StunProber::Observer* observer) {
RTC_DCHECK(thread_checker_.IsCurrent());
interval_ms_ = interval_ms;
shared_socket_mode_ = shared_socket_mode;
void StunProber::OnServerResolved( const webrtc::AsyncDnsResolverResult& result) {
RTC_DCHECK(thread_checker_.IsCurrent());
rtc::SocketAddress received_address; if (result.GetResolvedAddress(AF_INET, &received_address)) { // Construct an address without the name in it.
rtc::SocketAddress addr(received_address.ipaddr(), received_address.port());
all_servers_addrs_.push_back(addr);
}
resolver_.reset();
servers_.pop_back(); if (servers_.size()) { if (!ResolveServerName(servers_.back())) {
ReportOnPrepared(RESOLVE_FAILED);
} return;
}
if (all_servers_addrs_.size() == 0) {
ReportOnPrepared(RESOLVE_FAILED); return;
}
// Prepare all the sockets beforehand. All of them will bind to "any" address. while (sockets_.size() < total_socket_required()) {
std::unique_ptr<rtc::AsyncPacketSocket> socket(
socket_factory_->CreateUdpSocket(rtc::SocketAddress(INADDR_ANY, 0), 0,
0)); if (!socket) {
ReportOnPrepared(GENERIC_FAILURE); return;
} // Chrome and WebRTC behave differently in terms of the state of a socket // once returned from PacketSocketFactory::CreateUdpSocket. if (socket->GetState() == rtc::AsyncPacketSocket::STATE_BINDING) {
socket->SignalAddressReady.connect(this, &StunProber::OnSocketReady);
} else {
OnSocketReady(socket.get(), rtc::SocketAddress(INADDR_ANY, 0));
}
sockets_.push_back(socket.release());
}
}
StunProber::Requester* StunProber::CreateRequester() {
RTC_DCHECK(thread_checker_.IsCurrent()); if (!sockets_.size()) { return nullptr;
}
StunProber::Requester* requester; if (shared_socket_mode_) {
requester = new Requester(this, sockets_.back(), all_servers_addrs_);
} else {
std::vector<rtc::SocketAddress> server_ip;
server_ip.push_back(
all_servers_addrs_[(num_request_sent_ % all_servers_addrs_.size())]);
requester = new Requester(this, sockets_.back(), server_ip);
}
// Track of how many srflx IP that we have seen.
std::set<rtc::IPAddress> srflx_ips;
// If we're not receiving any response on a given IP, all requests sent to // that IP should be ignored as this could just be an DNS error.
std::map<rtc::IPAddress, int> num_response_per_server;
std::map<rtc::IPAddress, int> num_request_per_server;
for (auto* requester : requesters_) {
std::map<rtc::SocketAddress, int> num_response_per_srflx_addr; for (auto* request : requester->requests()) { if (request->sent_time_ms <= 0) { continue;
}
// If we're using shared mode and seeing >1 srflx addresses for a single // requester, it's symmetric NAT. if (shared_socket_mode_ && num_response_per_srflx_addr.size() > 1) {
nat_type = NATTYPE_SYMMETRIC;
}
}
// We're probably not behind a regular NAT. We have more than 1 distinct // server reflexive IPs. if (srflx_ips.size() > 1) { returnfalse;
}
int num_sent = 0; int num_received = 0; int num_server_ip_with_response = 0;
// Shared mode is only true if we use the shared socket and there are more // than 1 responding servers.
stats.shared_socket_mode =
shared_socket_mode_ && (num_server_ip_with_response > 1);
// If we could find a local IP matching srflx, we're not behind a NAT.
rtc::SocketAddress srflx_addr; if (stats.srflx_addrs.size() &&
!srflx_addr.FromString(*(stats.srflx_addrs.begin()))) { returnfalse;
} for (constauto* net : networks_) { if (srflx_addr.ipaddr() == net->GetBestIP()) {
nat_type = stunprober::NATTYPE_NONE;
stats.host_ip = net->GetBestIP().ToString(); break;
}
}
// Finally, we know we're behind a NAT but can't determine which type it is. if (nat_type == NATTYPE_INVALID) {
nat_type = NATTYPE_UNKNOWN;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.