/* * Copyright 2012 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.
*/
// TODO(mallinath) - Move these to a common place. bool IsTurnChannelData(uint16_t msg_type) { // The first two bits of a channel data message are 0b01. return ((msg_type & 0xC000) == 0x4000);
}
void TurnServer::AcceptConnection(rtc::Socket* server_socket) { // Check if someone is trying to connect to us.
rtc::SocketAddress accept_addr;
rtc::Socket* accepted_socket = server_socket->Accept(&accept_addr); if (accepted_socket != NULL) { const ServerSocketInfo& info = server_listen_sockets_[server_socket]; if (info.ssl_adapter_factory) {
rtc::SSLAdapter* ssl_adapter =
info.ssl_adapter_factory->CreateAdapter(accepted_socket);
ssl_adapter->StartSSL("");
accepted_socket = ssl_adapter;
}
cricket::AsyncStunTCPSocket* tcp_socket = new cricket::AsyncStunTCPSocket(accepted_socket);
tcp_socket->SubscribeCloseEvent(this,
[this](rtc::AsyncPacketSocket* s, int err) {
OnInternalSocketClose(s, err);
}); // Finally add the socket so it can start communicating with the client.
AddInternalSocket(tcp_socket, info.proto);
}
}
void TurnServer::OnInternalSocketClose(rtc::AsyncPacketSocket* socket, int err) {
RTC_DCHECK_RUN_ON(thread_);
DestroyInternalSocket(socket);
}
void TurnServer::OnInternalPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
RTC_DCHECK_RUN_ON(thread_); // Fail if the packet is too small to even contain a channel header. if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) { return;
}
InternalSocketMap::iterator iter = server_sockets_.find(socket);
RTC_DCHECK(iter != server_sockets_.end());
TurnServerConnection conn(packet.source_address(), iter->second, socket);
uint16_t msg_type = rtc::GetBE16(packet.payload().data()); if (!IsTurnChannelData(msg_type)) { // This is a STUN message.
HandleStunMessage(&conn, packet.payload());
} else { // This is a channel message; let the allocation handle it.
TurnServerAllocation* allocation = FindAllocation(&conn); if (allocation) {
allocation->HandleChannelData(packet.payload());
} if (stun_message_observer_ != nullptr) {
stun_message_observer_->ReceivedChannelData(packet.payload());
}
}
}
// Look up the key that we'll use to validate the M-I. If we have an // existing allocation, the key will already be cached.
TurnServerAllocation* allocation = FindAllocation(conn);
std::string key; if (!allocation) {
GetKey(&msg, &key);
} else {
key = allocation->key();
}
// Ensure the message is authorized; only needed for requests. if (IsStunRequestType(msg.type())) { if (!CheckAuthorization(conn, &msg, key)) { return;
}
}
if (!allocation && msg.type() == STUN_ALLOCATE_REQUEST) {
HandleAllocateRequest(conn, &msg, key);
} elseif (allocation &&
(msg.type() != STUN_ALLOCATE_REQUEST ||
msg.transaction_id() == allocation->transaction_id())) { // This is a non-allocate request, or a retransmit of an allocate. // Check that the username matches the previous username used. if (IsStunRequestType(msg.type()) &&
msg.GetByteString(STUN_ATTR_USERNAME)->string_view() !=
allocation->username()) {
SendErrorResponse(conn, &msg, STUN_ERROR_WRONG_CREDENTIALS,
STUN_ERROR_REASON_WRONG_CREDENTIALS); return;
}
allocation->HandleTurnMessage(&msg);
} else { // Allocation mismatch.
SendErrorResponse(conn, &msg, STUN_ERROR_ALLOCATION_MISMATCH,
STUN_ERROR_REASON_ALLOCATION_MISMATCH);
}
}
// Fail if no MESSAGE_INTEGRITY. if (!mi_attr) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
STUN_ERROR_REASON_UNAUTHORIZED); returnfalse;
}
// Fail if there is MESSAGE_INTEGRITY but no username, nonce, or realm. if (!username_attr || !realm_attr || !nonce_attr) {
SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST,
STUN_ERROR_REASON_BAD_REQUEST); returnfalse;
}
// Fail if bad nonce. if (!ValidateNonce(nonce_attr->string_view())) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
STUN_ERROR_REASON_STALE_NONCE); returnfalse;
}
// Fail if bad MESSAGE_INTEGRITY. if (key.empty() || msg->ValidateMessageIntegrity(std::string(key)) !=
StunMessage::IntegrityStatus::kIntegrityOk) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
STUN_ERROR_REASON_UNAUTHORIZED); returnfalse;
}
// Fail if one-time-use nonce feature is enabled.
TurnServerAllocation* allocation = FindAllocation(conn); if (enable_otu_nonce_ && allocation &&
allocation->last_nonce() == nonce_attr->string_view()) {
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
STUN_ERROR_REASON_STALE_NONCE); returnfalse;
}
if (allocation) {
allocation->set_last_nonce(nonce_attr->string_view());
} // Success. returntrue;
}
void TurnServer::HandleBindingRequest(TurnServerConnection* conn, const StunMessage* req) {
StunMessage response(GetStunSuccessResponseTypeOrZero(*req),
req->transaction_id()); // Tell the user the address that we received their request from. auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>(
STUN_ATTR_XOR_MAPPED_ADDRESS, conn->src());
response.AddAttribute(std::move(mapped_addr_attr));
SendStun(conn, &response);
}
void TurnServer::HandleAllocateRequest(TurnServerConnection* conn, const TurnMessage* msg,
absl::string_view key) { // Check the parameters in the request. const StunUInt32Attribute* transport_attr =
msg->GetUInt32(STUN_ATTR_REQUESTED_TRANSPORT); if (!transport_attr) {
SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST,
STUN_ERROR_REASON_BAD_REQUEST); return;
}
// Only UDP is supported right now. int proto = transport_attr->value() >> 24; if (proto != IPPROTO_UDP) {
SendErrorResponse(conn, msg, STUN_ERROR_UNSUPPORTED_PROTOCOL,
STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL); return;
}
// Create the allocation and let it send the success response. // If the actual socket allocation fails, send an internal error.
TurnServerAllocation* alloc = CreateAllocation(conn, proto, key); if (alloc) {
alloc->HandleTurnMessage(msg);
} else {
SendErrorResponse(conn, msg, STUN_ERROR_SERVER_ERROR, "Failed to allocate socket");
}
}
std::string TurnServer::GenerateNonce(int64_t now) const { // Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now))
std::string input(reinterpret_cast<constchar*>(&now), sizeof(now));
std::string nonce = rtc::hex_encode(input);
nonce += rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_, input);
RTC_DCHECK(nonce.size() == kNonceSize);
return nonce;
}
bool TurnServer::ValidateNonce(absl::string_view nonce) const { // Check the size. if (nonce.size() != kNonceSize) { returnfalse;
}
// Decode the timestamp.
int64_t then; char* p = reinterpret_cast<char*>(&then);
size_t len = rtc::hex_decode(rtc::ArrayView<char>(p, sizeof(then)),
nonce.substr(0, sizeof(then) * 2)); if (len != sizeof(then)) { returnfalse;
}
// Verify the HMAC. if (nonce.substr(sizeof(then) * 2) !=
rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_,
std::string(p, sizeof(then)))) { returnfalse;
}
void TurnServer::DestroyAllocation(TurnServerAllocation* allocation) { // Removing the internal socket if the connection is not udp.
rtc::AsyncPacketSocket* socket = allocation->conn()->socket();
InternalSocketMap::iterator iter = server_sockets_.find(socket); // Skip if the socket serving this allocation is UDP, as this will be shared // by all allocations. // Note: We may not find a socket if it's a TCP socket that was closed, and // the allocation is only now timing out. if (iter != server_sockets_.end() && iter->second != cricket::PROTO_UDP) {
DestroyInternalSocket(socket);
}
allocations_.erase(*(allocation->conn()));
}
void TurnServer::DestroyInternalSocket(rtc::AsyncPacketSocket* socket) {
InternalSocketMap::iterator iter = server_sockets_.find(socket); if (iter != server_sockets_.end()) {
rtc::AsyncPacketSocket* socket = iter->first;
socket->UnsubscribeCloseEvent(this);
socket->DeregisterReceivedPacketCallback();
server_sockets_.erase(iter);
std::unique_ptr<rtc::AsyncPacketSocket> socket_to_delete =
absl::WrapUnique(socket); // We must destroy the socket async to avoid invalidating the sigslot // callback list iterator inside a sigslot callback. (In other words, // deleting an object from within a callback from that object).
thread_->PostTask([socket_to_delete = std::move(socket_to_delete)] {});
}
}
void TurnServerAllocation::HandleTurnMessage(const TurnMessage* msg) {
RTC_DCHECK(msg != NULL); switch (msg->type()) { case STUN_ALLOCATE_REQUEST:
HandleAllocateRequest(msg); break; case TURN_REFRESH_REQUEST:
HandleRefreshRequest(msg); break; case TURN_SEND_INDICATION:
HandleSendIndication(msg); break; case TURN_CREATE_PERMISSION_REQUEST:
HandleCreatePermissionRequest(msg); break; case TURN_CHANNEL_BIND_REQUEST:
HandleChannelBindRequest(msg); break; default: // Not sure what to do with this, just eat it.
RTC_LOG(LS_WARNING) << ToString()
<< ": Invalid TURN message type received: "
<< msg->type();
}
}
void TurnServerAllocation::HandleAllocateRequest(const TurnMessage* msg) { // Copy the important info from the allocate request.
transaction_id_ = msg->transaction_id(); const StunByteStringAttribute* username_attr =
msg->GetByteString(STUN_ATTR_USERNAME);
RTC_DCHECK(username_attr != NULL);
username_ = std::string(username_attr->string_view());
// Figure out the lifetime and start the allocation timer.
TimeDelta lifetime = ComputeLifetime(*msg);
PostDeleteSelf(lifetime);
RTC_LOG(LS_INFO) << ToString() << ": Created allocation with lifetime="
<< lifetime.seconds();
// We've already validated all the important bits; just send a response here.
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
msg->transaction_id());
auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>(
STUN_ATTR_XOR_MAPPED_ADDRESS, conn_.src()); auto relayed_addr_attr = std::make_unique<StunXorAddressAttribute>(
STUN_ATTR_XOR_RELAYED_ADDRESS, external_socket_->GetLocalAddress()); auto lifetime_attr = std::make_unique<StunUInt32Attribute>(
STUN_ATTR_LIFETIME, lifetime.seconds());
response.AddAttribute(std::move(mapped_addr_attr));
response.AddAttribute(std::move(relayed_addr_attr));
response.AddAttribute(std::move(lifetime_attr));
SendResponse(&response);
}
void TurnServerAllocation::HandleRefreshRequest(const TurnMessage* msg) { // Figure out the new lifetime.
TimeDelta lifetime = ComputeLifetime(*msg);
// Reset the expiration timer.
safety_.reset();
PostDeleteSelf(lifetime);
// If a permission exists, send the data on to the peer. if (HasPermission(peer_attr->GetAddress().ipaddr())) {
SendExternal(reinterpret_cast<char*>(data_attr->array_view().data()),
data_attr->length(), peer_attr->GetAddress());
} else {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received send indication without permission" " peer="
<< peer_attr->GetAddress().ToSensitiveString();
}
}
// Check that channel id is valid. int channel_id = channel_attr->value() >> 16; if (channel_id < kMinTurnChannelNumber ||
channel_id > kMaxTurnChannelNumber) {
SendBadRequestResponse(msg); return;
}
// Check that this channel id isn't bound to another transport address, and // that this transport address isn't bound to another channel id. auto channel1 = FindChannel(channel_id); auto channel2 = FindChannel(peer_attr->GetAddress()); if (channel1 != channel2) {
SendBadRequestResponse(msg); return;
}
// Send a success response.
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
msg->transaction_id());
SendResponse(&response);
}
void TurnServerAllocation::HandleChannelData(
rtc::ArrayView<const uint8_t> payload) { // Extract the channel number from the data.
uint16_t channel_id = rtc::GetBE16(payload.data()); auto channel = FindChannel(channel_id); if (channel != channels_.end()) { // Send the data to the peer address.
SendExternal(payload.data() + TURN_CHANNEL_HEADER_SIZE,
payload.size() - TURN_CHANNEL_HEADER_SIZE, channel->peer);
} else {
RTC_LOG(LS_WARNING) << ToString()
<< ": Received channel data for invalid channel, id="
<< channel_id;
}
}
void TurnServerAllocation::OnExternalPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
RTC_DCHECK(external_socket_.get() == socket); auto channel = FindChannel(packet.source_address()); if (channel != channels_.end()) { // There is a channel bound to this address. Send as a channel message.
rtc::ByteBufferWriter buf;
buf.WriteUInt16(channel->id);
buf.WriteUInt16(static_cast<uint16_t>(packet.payload().size()));
buf.WriteBytes(packet.payload().data(), packet.payload().size());
server_->Send(&conn_, buf);
} elseif (!server_->enable_permission_checks_ ||
HasPermission(packet.source_address().ipaddr())) { // No channel, but a permission exists. Send as a data indication.
TurnMessage msg(TURN_DATA_INDICATION);
msg.AddAttribute(std::make_unique<StunXorAddressAttribute>(
STUN_ATTR_XOR_PEER_ADDRESS, packet.source_address()));
msg.AddAttribute(std::make_unique<StunByteStringAttribute>(
STUN_ATTR_DATA, packet.payload().data(), packet.payload().size()));
server_->SendStun(&conn_, &msg);
} else {
RTC_LOG(LS_WARNING)
<< ToString() << ": Received external packet without permission, peer="
<< packet.source_address().ToSensitiveString();
}
}
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.