summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/api/transport/stun.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/api/transport/stun.cc')
-rw-r--r--third_party/libwebrtc/api/transport/stun.cc1515
1 files changed, 1515 insertions, 0 deletions
diff --git a/third_party/libwebrtc/api/transport/stun.cc b/third_party/libwebrtc/api/transport/stun.cc
new file mode 100644
index 0000000000..1098c6720e
--- /dev/null
+++ b/third_party/libwebrtc/api/transport/stun.cc
@@ -0,0 +1,1515 @@
+/*
+ * 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 "api/transport/stun.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "rtc_base/byte_order.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/crc32.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/message_digest.h"
+#include "system_wrappers/include/metrics.h"
+
+using rtc::ByteBufferReader;
+using rtc::ByteBufferWriter;
+
+namespace cricket {
+
+namespace {
+
+const int k127Utf8CharactersLengthInBytes = 508;
+const int kMessageIntegrityAttributeLength = 20;
+const int kTheoreticalMaximumAttributeLength = 65535;
+
+uint32_t ReduceTransactionId(absl::string_view transaction_id) {
+ RTC_DCHECK(transaction_id.length() == cricket::kStunTransactionIdLength ||
+ transaction_id.length() == cricket::kStunLegacyTransactionIdLength)
+ << transaction_id.length();
+ ByteBufferReader reader(transaction_id.data(), transaction_id.size());
+ uint32_t result = 0;
+ uint32_t next;
+ while (reader.ReadUInt32(&next)) {
+ result ^= next;
+ }
+ return result;
+}
+
+// Check the maximum length of a BYTE_STRING attribute against specifications.
+bool LengthValid(int type, int length) {
+ // "Less than 509 bytes" is intended to indicate a maximum of 127
+ // UTF-8 characters, which may take up to 4 bytes per character.
+ switch (type) {
+ case STUN_ATTR_USERNAME:
+ return length <=
+ k127Utf8CharactersLengthInBytes; // RFC 8489 section 14.3
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ return length ==
+ kMessageIntegrityAttributeLength; // RFC 8489 section 14.5
+ case STUN_ATTR_REALM:
+ return length <=
+ k127Utf8CharactersLengthInBytes; // RFC 8489 section 14.9
+ case STUN_ATTR_NONCE:
+ return length <=
+ k127Utf8CharactersLengthInBytes; // RFC 8489 section 14.10
+ case STUN_ATTR_SOFTWARE:
+ return length <=
+ k127Utf8CharactersLengthInBytes; // RFC 8489 section 14.14
+ case STUN_ATTR_DATA:
+ // No length restriction in RFC; it's the content of an UDP datagram,
+ // which in theory can be up to 65.535 bytes.
+ // TODO(bugs.webrtc.org/12179): Write a test to find the real limit.
+ return length <= kTheoreticalMaximumAttributeLength;
+ default:
+ // Return an arbitrary restriction for all other types.
+ return length <= kTheoreticalMaximumAttributeLength;
+ }
+ RTC_DCHECK_NOTREACHED();
+ return true;
+}
+
+} // namespace
+
+const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[] = "Try Alternate Server";
+const char STUN_ERROR_REASON_BAD_REQUEST[] = "Bad Request";
+const char STUN_ERROR_REASON_UNAUTHORIZED[] = "Unauthorized";
+const char STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE[] = "Unknown Attribute";
+const char STUN_ERROR_REASON_FORBIDDEN[] = "Forbidden";
+const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[] = "Allocation Mismatch";
+const char STUN_ERROR_REASON_STALE_NONCE[] = "Stale Nonce";
+const char STUN_ERROR_REASON_WRONG_CREDENTIALS[] = "Wrong Credentials";
+const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[] = "Unsupported Protocol";
+const char STUN_ERROR_REASON_ROLE_CONFLICT[] = "Role Conflict";
+const char STUN_ERROR_REASON_SERVER_ERROR[] = "Server Error";
+
+const char TURN_MAGIC_COOKIE_VALUE[] = {'\x72', '\xC6', '\x4B', '\xC6'};
+const char EMPTY_TRANSACTION_ID[] = "0000000000000000";
+const uint32_t STUN_FINGERPRINT_XOR_VALUE = 0x5354554E;
+const int SERVER_NOT_REACHABLE_ERROR = 701;
+
+// StunMessage
+
+StunMessage::StunMessage()
+ : StunMessage(STUN_INVALID_MESSAGE_TYPE, EMPTY_TRANSACTION_ID) {}
+
+StunMessage::StunMessage(uint16_t type)
+ : StunMessage(type, GenerateTransactionId()) {}
+
+StunMessage::StunMessage(uint16_t type, absl::string_view transaction_id)
+ : type_(type),
+ transaction_id_(transaction_id),
+ reduced_transaction_id_(ReduceTransactionId(transaction_id_)) {
+ RTC_DCHECK(IsValidTransactionId(transaction_id_));
+}
+
+StunMessage::~StunMessage() = default;
+
+bool StunMessage::IsLegacy() const {
+ if (transaction_id_.size() == kStunLegacyTransactionIdLength)
+ return true;
+ RTC_DCHECK(transaction_id_.size() == kStunTransactionIdLength);
+ return false;
+}
+
+static bool DesignatedExpertRange(int attr_type) {
+ return (attr_type >= 0x4000 && attr_type <= 0x7FFF) ||
+ (attr_type >= 0xC000 && attr_type <= 0xFFFF);
+}
+
+void StunMessage::AddAttribute(std::unique_ptr<StunAttribute> attr) {
+ // Fail any attributes that aren't valid for this type of message,
+ // but allow any type for the range that in the RFC is reserved for
+ // the "designated experts".
+ if (!DesignatedExpertRange(attr->type())) {
+ RTC_DCHECK_EQ(attr->value_type(), GetAttributeValueType(attr->type()));
+ }
+
+ attr->SetOwner(this);
+ size_t attr_length = attr->length();
+ if (attr_length % 4 != 0) {
+ attr_length += (4 - (attr_length % 4));
+ }
+ length_ += static_cast<uint16_t>(attr_length + 4);
+
+ attrs_.push_back(std::move(attr));
+}
+
+std::unique_ptr<StunAttribute> StunMessage::RemoveAttribute(int type) {
+ std::unique_ptr<StunAttribute> attribute;
+ for (auto it = attrs_.rbegin(); it != attrs_.rend(); ++it) {
+ if ((*it)->type() == type) {
+ attribute = std::move(*it);
+ attrs_.erase(std::next(it).base());
+ break;
+ }
+ }
+ if (attribute) {
+ attribute->SetOwner(nullptr);
+ size_t attr_length = attribute->length();
+ if (attr_length % 4 != 0) {
+ attr_length += (4 - (attr_length % 4));
+ }
+ length_ -= static_cast<uint16_t>(attr_length + 4);
+ }
+ return attribute;
+}
+
+void StunMessage::ClearAttributes() {
+ for (auto it = attrs_.rbegin(); it != attrs_.rend(); ++it) {
+ (*it)->SetOwner(nullptr);
+ }
+ attrs_.clear();
+ length_ = 0;
+}
+
+std::vector<uint16_t> StunMessage::GetNonComprehendedAttributes() const {
+ std::vector<uint16_t> unknown_attributes;
+ for (auto& attr : attrs_) {
+ // "comprehension-required" range is 0x0000-0x7FFF.
+ if (attr->type() >= 0x0000 && attr->type() <= 0x7FFF &&
+ GetAttributeValueType(attr->type()) == STUN_VALUE_UNKNOWN) {
+ unknown_attributes.push_back(attr->type());
+ }
+ }
+ return unknown_attributes;
+}
+
+const StunAddressAttribute* StunMessage::GetAddress(int type) const {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS: {
+ // Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
+ // missing.
+ const StunAttribute* mapped_address =
+ GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
+ if (!mapped_address)
+ mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
+ return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
+ }
+
+ default:
+ return static_cast<const StunAddressAttribute*>(GetAttribute(type));
+ }
+}
+
+const StunUInt32Attribute* StunMessage::GetUInt32(int type) const {
+ return static_cast<const StunUInt32Attribute*>(GetAttribute(type));
+}
+
+const StunUInt64Attribute* StunMessage::GetUInt64(int type) const {
+ return static_cast<const StunUInt64Attribute*>(GetAttribute(type));
+}
+
+const StunByteStringAttribute* StunMessage::GetByteString(int type) const {
+ return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
+}
+
+const StunUInt16ListAttribute* StunMessage::GetUInt16List(int type) const {
+ return static_cast<const StunUInt16ListAttribute*>(GetAttribute(type));
+}
+
+const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
+ return static_cast<const StunErrorCodeAttribute*>(
+ GetAttribute(STUN_ATTR_ERROR_CODE));
+}
+
+int StunMessage::GetErrorCodeValue() const {
+ const StunErrorCodeAttribute* error_attribute = GetErrorCode();
+ return error_attribute ? error_attribute->code() : STUN_ERROR_GLOBAL_FAILURE;
+}
+
+const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
+ return static_cast<const StunUInt16ListAttribute*>(
+ GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
+}
+
+StunMessage::IntegrityStatus StunMessage::ValidateMessageIntegrity(
+ const std::string& password) {
+ RTC_DCHECK(integrity_ == IntegrityStatus::kNotSet)
+ << "Usage error: Verification should only be done once";
+ password_ = password;
+ if (GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) {
+ if (ValidateMessageIntegrityOfType(
+ STUN_ATTR_MESSAGE_INTEGRITY, kStunMessageIntegritySize,
+ buffer_.c_str(), buffer_.size(), password)) {
+ integrity_ = IntegrityStatus::kIntegrityOk;
+ } else {
+ integrity_ = IntegrityStatus::kIntegrityBad;
+ }
+ } else if (GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32)) {
+ if (ValidateMessageIntegrityOfType(
+ STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32, kStunMessageIntegrity32Size,
+ buffer_.c_str(), buffer_.size(), password)) {
+ integrity_ = IntegrityStatus::kIntegrityOk;
+ } else {
+ integrity_ = IntegrityStatus::kIntegrityBad;
+ }
+ } else {
+ integrity_ = IntegrityStatus::kNoIntegrity;
+ }
+ // Log the result of integrity checking. See crbug.com/1177125 for background.
+ // Convert args to integer for the benefit of the macros.
+ int bucket_count = static_cast<int>(IntegrityStatus::kMaxValue) + 1;
+ int integrity = static_cast<int>(integrity_);
+ if (IsStunRequestType(type_)) {
+ RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Request", integrity,
+ bucket_count);
+ } else if (IsStunSuccessResponseType(type_)) {
+ RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Response", integrity,
+ bucket_count);
+ } else if (IsStunIndicationType(type_)) {
+ RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Indication", integrity,
+ bucket_count);
+ } else {
+ RTC_DCHECK(IsStunErrorResponseType(type_));
+ auto* error_attribute = GetErrorCode();
+ if (!error_attribute) {
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.NoErrorAttribute", integrity,
+ bucket_count);
+ } else {
+ switch (error_attribute->code()) {
+ case STUN_ERROR_TRY_ALTERNATE:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.TryAlternate", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_BAD_REQUEST:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.BadRequest", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_UNAUTHORIZED:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.Unauthorized", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_UNKNOWN_ATTRIBUTE:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.UnknownAttribute", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_STALE_NONCE:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.StaleNonce", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_SERVER_ERROR:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.ServerError", integrity,
+ bucket_count);
+ break;
+ case STUN_ERROR_GLOBAL_FAILURE:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.GlobalFailure", integrity,
+ bucket_count);
+ break;
+ default:
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.Stun.Integrity.ErrorResponse.ErrorOther", integrity,
+ bucket_count);
+ break;
+ }
+ }
+ }
+ return integrity_;
+}
+
+StunMessage::IntegrityStatus StunMessage::RevalidateMessageIntegrity(
+ const std::string& password) {
+ RTC_LOG(LS_INFO) << "Message revalidation, old status was "
+ << static_cast<int>(integrity_);
+ integrity_ = IntegrityStatus::kNotSet;
+ return ValidateMessageIntegrity(password);
+}
+
+bool StunMessage::ValidateMessageIntegrityForTesting(
+ const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+ kStunMessageIntegritySize, data, size,
+ password);
+}
+
+bool StunMessage::ValidateMessageIntegrity32ForTesting(
+ const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
+ kStunMessageIntegrity32Size, data, size,
+ password);
+}
+
+// Deprecated
+bool StunMessage::ValidateMessageIntegrity(const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+ kStunMessageIntegritySize, data, size,
+ password);
+}
+
+// Deprecated
+bool StunMessage::ValidateMessageIntegrity32(const char* data,
+ size_t size,
+ const std::string& password) {
+ return ValidateMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
+ kStunMessageIntegrity32Size, data, size,
+ password);
+}
+
+// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
+// procedure outlined in RFC 5389, section 15.4.
+bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type,
+ size_t mi_attr_size,
+ const char* data,
+ size_t size,
+ const std::string& password) {
+ RTC_DCHECK(mi_attr_size <= kStunMessageIntegritySize);
+
+ // Verifying the size of the message.
+ if ((size % 4) != 0 || size < kStunHeaderSize) {
+ return false;
+ }
+
+ // Getting the message length from the STUN header.
+ uint16_t msg_length = rtc::GetBE16(&data[2]);
+ if (size != (msg_length + kStunHeaderSize)) {
+ return false;
+ }
+
+ // Finding Message Integrity attribute in stun message.
+ size_t current_pos = kStunHeaderSize;
+ bool has_message_integrity_attr = false;
+ while (current_pos + 4 <= size) {
+ uint16_t attr_type, attr_length;
+ // Getting attribute type and length.
+ attr_type = rtc::GetBE16(&data[current_pos]);
+ attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
+
+ // If M-I, sanity check it, and break out.
+ if (attr_type == mi_attr_type) {
+ if (attr_length != mi_attr_size ||
+ current_pos + sizeof(attr_type) + sizeof(attr_length) + attr_length >
+ size) {
+ return false;
+ }
+ has_message_integrity_attr = true;
+ break;
+ }
+
+ // Otherwise, skip to the next attribute.
+ current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
+ if ((attr_length % 4) != 0) {
+ current_pos += (4 - (attr_length % 4));
+ }
+ }
+
+ if (!has_message_integrity_attr) {
+ return false;
+ }
+
+ // Getting length of the message to calculate Message Integrity.
+ size_t mi_pos = current_pos;
+ std::unique_ptr<char[]> temp_data(new char[current_pos]);
+ memcpy(temp_data.get(), data, current_pos);
+ if (size > mi_pos + kStunAttributeHeaderSize + mi_attr_size) {
+ // Stun message has other attributes after message integrity.
+ // Adjust the length parameter in stun message to calculate HMAC.
+ size_t extra_offset =
+ size - (mi_pos + kStunAttributeHeaderSize + mi_attr_size);
+ size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
+
+ // Writing new length of the STUN message @ Message Length in temp buffer.
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |0 0| STUN Message Type | Message Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ rtc::SetBE16(temp_data.get() + 2, static_cast<uint16_t>(new_adjusted_len));
+ }
+
+ char hmac[kStunMessageIntegritySize];
+ size_t ret =
+ rtc::ComputeHmac(rtc::DIGEST_SHA_1, password.c_str(), password.size(),
+ temp_data.get(), mi_pos, hmac, sizeof(hmac));
+ RTC_DCHECK(ret == sizeof(hmac));
+ if (ret != sizeof(hmac)) {
+ return false;
+ }
+
+ // Comparing the calculated HMAC with the one present in the message.
+ return memcmp(data + current_pos + kStunAttributeHeaderSize, hmac,
+ mi_attr_size) == 0;
+}
+
+bool StunMessage::AddMessageIntegrity(absl::string_view password) {
+ return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+ kStunMessageIntegritySize, password);
+}
+
+bool StunMessage::AddMessageIntegrity32(absl::string_view password) {
+ return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
+ kStunMessageIntegrity32Size, password);
+}
+
+bool StunMessage::AddMessageIntegrityOfType(int attr_type,
+ size_t attr_size,
+ absl::string_view key) {
+ // Add the attribute with a dummy value. Since this is a known attribute, it
+ // can't fail.
+ RTC_DCHECK(attr_size <= kStunMessageIntegritySize);
+ auto msg_integrity_attr_ptr = std::make_unique<StunByteStringAttribute>(
+ attr_type, std::string(attr_size, '0'));
+ auto* msg_integrity_attr = msg_integrity_attr_ptr.get();
+ AddAttribute(std::move(msg_integrity_attr_ptr));
+
+ // Calculate the HMAC for the message.
+ ByteBufferWriter buf;
+ if (!Write(&buf))
+ return false;
+
+ int msg_len_for_hmac = static_cast<int>(
+ buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
+ char hmac[kStunMessageIntegritySize];
+ size_t ret =
+ rtc::ComputeHmac(rtc::DIGEST_SHA_1, key.data(), key.size(), buf.Data(),
+ msg_len_for_hmac, hmac, sizeof(hmac));
+ RTC_DCHECK(ret == sizeof(hmac));
+ if (ret != sizeof(hmac)) {
+ RTC_LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
+ "has dummy value.";
+ return false;
+ }
+
+ // Insert correct HMAC into the attribute.
+ msg_integrity_attr->CopyBytes(hmac, attr_size);
+ password_ = std::string(key);
+ integrity_ = IntegrityStatus::kIntegrityOk;
+ return true;
+}
+
+// Verifies a message is in fact a STUN message, by performing the checks
+// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
+// in section 15.5.
+bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
+ // Check the message length.
+ size_t fingerprint_attr_size =
+ kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
+ if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
+ return false;
+
+ // Skip the rest if the magic cookie isn't present.
+ const char* magic_cookie =
+ data + kStunTransactionIdOffset - kStunMagicCookieLength;
+ if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
+ return false;
+
+ // Check the fingerprint type and length.
+ const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
+ if (rtc::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
+ rtc::GetBE16(fingerprint_attr_data + sizeof(uint16_t)) !=
+ StunUInt32Attribute::SIZE)
+ return false;
+
+ // Check the fingerprint value.
+ uint32_t fingerprint =
+ rtc::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
+ return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
+ rtc::ComputeCrc32(data, size - fingerprint_attr_size));
+}
+
+// static
+std::string StunMessage::GenerateTransactionId() {
+ return rtc::CreateRandomString(kStunTransactionIdLength);
+}
+
+bool StunMessage::IsStunMethod(rtc::ArrayView<int> methods,
+ const char* data,
+ size_t size) {
+ // Check the message length.
+ if (size % 4 != 0 || size < kStunHeaderSize)
+ return false;
+
+ // Skip the rest if the magic cookie isn't present.
+ const char* magic_cookie =
+ data + kStunTransactionIdOffset - kStunMagicCookieLength;
+ if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
+ return false;
+
+ int method = rtc::GetBE16(data);
+ for (int m : methods) {
+ if (m == method) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool StunMessage::AddFingerprint() {
+ // Add the attribute with a dummy value. Since this is a known attribute,
+ // it can't fail.
+ auto fingerprint_attr_ptr =
+ std::make_unique<StunUInt32Attribute>(STUN_ATTR_FINGERPRINT, 0);
+ auto* fingerprint_attr = fingerprint_attr_ptr.get();
+ AddAttribute(std::move(fingerprint_attr_ptr));
+
+ // Calculate the CRC-32 for the message and insert it.
+ ByteBufferWriter buf;
+ if (!Write(&buf))
+ return false;
+
+ int msg_len_for_crc32 = static_cast<int>(
+ buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
+ uint32_t c = rtc::ComputeCrc32(buf.Data(), msg_len_for_crc32);
+
+ // Insert the correct CRC-32, XORed with a constant, into the attribute.
+ fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
+ return true;
+}
+
+bool StunMessage::Read(ByteBufferReader* buf) {
+ // Keep a copy of the buffer data around for later verification.
+ buffer_.assign(buf->Data(), buf->Length());
+
+ if (!buf->ReadUInt16(&type_)) {
+ return false;
+ }
+
+ if (type_ & 0x8000) {
+ // RTP and RTCP set the MSB of first byte, since first two bits are version,
+ // and version is always 2 (10). If set, this is not a STUN packet.
+ return false;
+ }
+
+ if (!buf->ReadUInt16(&length_)) {
+ return false;
+ }
+
+ std::string magic_cookie;
+ if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength)) {
+ return false;
+ }
+
+ std::string transaction_id;
+ if (!buf->ReadString(&transaction_id, kStunTransactionIdLength)) {
+ return false;
+ }
+
+ uint32_t magic_cookie_int;
+ static_assert(sizeof(magic_cookie_int) == kStunMagicCookieLength,
+ "Integer size mismatch: magic_cookie_int and kStunMagicCookie");
+ std::memcpy(&magic_cookie_int, magic_cookie.data(), sizeof(magic_cookie_int));
+ if (rtc::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
+ // If magic cookie is invalid it means that the peer implements
+ // RFC3489 instead of RFC5389.
+ transaction_id.insert(0, magic_cookie);
+ }
+ RTC_DCHECK(IsValidTransactionId(transaction_id));
+ transaction_id_ = transaction_id;
+ reduced_transaction_id_ = ReduceTransactionId(transaction_id_);
+
+ if (length_ != buf->Length()) {
+ return false;
+ }
+
+ attrs_.resize(0);
+
+ size_t rest = buf->Length() - length_;
+ while (buf->Length() > rest) {
+ uint16_t attr_type, attr_length;
+ if (!buf->ReadUInt16(&attr_type))
+ return false;
+ if (!buf->ReadUInt16(&attr_length))
+ return false;
+
+ std::unique_ptr<StunAttribute> attr(
+ CreateAttribute(attr_type, attr_length));
+ if (!attr) {
+ // Skip any unknown or malformed attributes.
+ if ((attr_length % 4) != 0) {
+ attr_length += (4 - (attr_length % 4));
+ }
+ if (!buf->Consume(attr_length)) {
+ return false;
+ }
+ } else {
+ if (!attr->Read(buf)) {
+ return false;
+ }
+ attrs_.push_back(std::move(attr));
+ }
+ }
+
+ RTC_DCHECK(buf->Length() == rest);
+ return true;
+}
+
+bool StunMessage::Write(ByteBufferWriter* buf) const {
+ buf->WriteUInt16(type_);
+ buf->WriteUInt16(length_);
+ if (!IsLegacy())
+ buf->WriteUInt32(stun_magic_cookie_);
+ buf->WriteString(transaction_id_);
+
+ for (const auto& attr : attrs_) {
+ buf->WriteUInt16(attr->type());
+ buf->WriteUInt16(static_cast<uint16_t>(attr->length()));
+ if (!attr->Write(buf)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+StunMessage* StunMessage::CreateNew() const {
+ return new StunMessage();
+}
+
+void StunMessage::SetStunMagicCookie(uint32_t val) {
+ stun_magic_cookie_ = val;
+}
+
+void StunMessage::SetTransactionIdForTesting(absl::string_view transaction_id) {
+ RTC_DCHECK(IsValidTransactionId(transaction_id));
+ transaction_id_ = std::string(transaction_id);
+ reduced_transaction_id_ = ReduceTransactionId(transaction_id_);
+}
+
+StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ return STUN_VALUE_ADDRESS;
+ case STUN_ATTR_USERNAME:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_ERROR_CODE:
+ return STUN_VALUE_ERROR_CODE;
+ case STUN_ATTR_UNKNOWN_ATTRIBUTES:
+ return STUN_VALUE_UINT16_LIST;
+ case STUN_ATTR_REALM:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_NONCE:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_XOR_MAPPED_ADDRESS:
+ return STUN_VALUE_XOR_ADDRESS;
+ case STUN_ATTR_SOFTWARE:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_ALTERNATE_SERVER:
+ return STUN_VALUE_ADDRESS;
+ case STUN_ATTR_FINGERPRINT:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_RETRANSMIT_COUNT:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_GOOG_MISC_INFO:
+ return STUN_VALUE_UINT16_LIST;
+ default:
+ return STUN_VALUE_UNKNOWN;
+ }
+}
+
+StunAttribute* StunMessage::CreateAttribute(int type, size_t length) /*const*/ {
+ StunAttributeValueType value_type = GetAttributeValueType(type);
+ if (value_type != STUN_VALUE_UNKNOWN) {
+ return StunAttribute::Create(value_type, type,
+ static_cast<uint16_t>(length), this);
+ } else if (DesignatedExpertRange(type)) {
+ // Read unknown attributes as STUN_VALUE_BYTE_STRING
+ return StunAttribute::Create(STUN_VALUE_BYTE_STRING, type,
+ static_cast<uint16_t>(length), this);
+ } else {
+ return NULL;
+ }
+}
+
+const StunAttribute* StunMessage::GetAttribute(int type) const {
+ for (const auto& attr : attrs_) {
+ if (attr->type() == type) {
+ return attr.get();
+ }
+ }
+ return NULL;
+}
+
+bool StunMessage::IsValidTransactionId(absl::string_view transaction_id) {
+ return transaction_id.size() == kStunTransactionIdLength ||
+ transaction_id.size() == kStunLegacyTransactionIdLength;
+}
+
+bool StunMessage::EqualAttributes(
+ const StunMessage* other,
+ std::function<bool(int type)> attribute_type_mask) const {
+ RTC_DCHECK(other != nullptr);
+ rtc::ByteBufferWriter tmp_buffer_ptr1;
+ rtc::ByteBufferWriter tmp_buffer_ptr2;
+ for (const auto& attr : attrs_) {
+ if (attribute_type_mask(attr->type())) {
+ const StunAttribute* other_attr = other->GetAttribute(attr->type());
+ if (other_attr == nullptr) {
+ return false;
+ }
+ tmp_buffer_ptr1.Clear();
+ tmp_buffer_ptr2.Clear();
+ attr->Write(&tmp_buffer_ptr1);
+ other_attr->Write(&tmp_buffer_ptr2);
+ if (tmp_buffer_ptr1.Length() != tmp_buffer_ptr2.Length()) {
+ return false;
+ }
+ if (memcmp(tmp_buffer_ptr1.Data(), tmp_buffer_ptr2.Data(),
+ tmp_buffer_ptr1.Length()) != 0) {
+ return false;
+ }
+ }
+ }
+
+ for (const auto& attr : other->attrs_) {
+ if (attribute_type_mask(attr->type())) {
+ const StunAttribute* own_attr = GetAttribute(attr->type());
+ if (own_attr == nullptr) {
+ return false;
+ }
+ // we have already compared all values...
+ }
+ }
+ return true;
+}
+
+// StunAttribute
+
+StunAttribute::StunAttribute(uint16_t type, uint16_t length)
+ : type_(type), length_(length) {}
+
+void StunAttribute::ConsumePadding(ByteBufferReader* buf) const {
+ int remainder = length_ % 4;
+ if (remainder > 0) {
+ buf->Consume(4 - remainder);
+ }
+}
+
+void StunAttribute::WritePadding(ByteBufferWriter* buf) const {
+ int remainder = length_ % 4;
+ if (remainder > 0) {
+ char zeroes[4] = {0};
+ buf->WriteBytes(zeroes, 4 - remainder);
+ }
+}
+
+StunAttribute* StunAttribute::Create(StunAttributeValueType value_type,
+ uint16_t type,
+ uint16_t length,
+ StunMessage* owner) {
+ switch (value_type) {
+ case STUN_VALUE_ADDRESS:
+ return new StunAddressAttribute(type, length);
+ case STUN_VALUE_XOR_ADDRESS:
+ return new StunXorAddressAttribute(type, length, owner);
+ case STUN_VALUE_UINT32:
+ return new StunUInt32Attribute(type);
+ case STUN_VALUE_UINT64:
+ return new StunUInt64Attribute(type);
+ case STUN_VALUE_BYTE_STRING:
+ return new StunByteStringAttribute(type, length);
+ case STUN_VALUE_ERROR_CODE:
+ return new StunErrorCodeAttribute(type, length);
+ case STUN_VALUE_UINT16_LIST:
+ return new StunUInt16ListAttribute(type, length);
+ default:
+ return NULL;
+ }
+}
+
+std::unique_ptr<StunAddressAttribute> StunAttribute::CreateAddress(
+ uint16_t type) {
+ return std::make_unique<StunAddressAttribute>(type, 0);
+}
+
+std::unique_ptr<StunXorAddressAttribute> StunAttribute::CreateXorAddress(
+ uint16_t type) {
+ return std::make_unique<StunXorAddressAttribute>(type, 0, nullptr);
+}
+
+std::unique_ptr<StunUInt64Attribute> StunAttribute::CreateUInt64(
+ uint16_t type) {
+ return std::make_unique<StunUInt64Attribute>(type);
+}
+
+std::unique_ptr<StunUInt32Attribute> StunAttribute::CreateUInt32(
+ uint16_t type) {
+ return std::make_unique<StunUInt32Attribute>(type);
+}
+
+std::unique_ptr<StunByteStringAttribute> StunAttribute::CreateByteString(
+ uint16_t type) {
+ return std::make_unique<StunByteStringAttribute>(type, 0);
+}
+
+std::unique_ptr<StunErrorCodeAttribute> StunAttribute::CreateErrorCode() {
+ return std::make_unique<StunErrorCodeAttribute>(
+ STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
+}
+
+std::unique_ptr<StunUInt16ListAttribute>
+StunAttribute::CreateUInt16ListAttribute(uint16_t type) {
+ return std::make_unique<StunUInt16ListAttribute>(type, 0);
+}
+
+std::unique_ptr<StunUInt16ListAttribute>
+StunAttribute::CreateUnknownAttributes() {
+ return std::make_unique<StunUInt16ListAttribute>(STUN_ATTR_UNKNOWN_ATTRIBUTES,
+ 0);
+}
+
+StunAddressAttribute::StunAddressAttribute(uint16_t type,
+ const rtc::SocketAddress& addr)
+ : StunAttribute(type, 0) {
+ SetAddress(addr);
+}
+
+StunAddressAttribute::StunAddressAttribute(uint16_t type, uint16_t length)
+ : StunAttribute(type, length) {}
+
+StunAttributeValueType StunAddressAttribute::value_type() const {
+ return STUN_VALUE_ADDRESS;
+}
+
+bool StunAddressAttribute::Read(ByteBufferReader* buf) {
+ uint8_t dummy;
+ if (!buf->ReadUInt8(&dummy))
+ return false;
+
+ uint8_t stun_family;
+ if (!buf->ReadUInt8(&stun_family)) {
+ return false;
+ }
+ uint16_t port;
+ if (!buf->ReadUInt16(&port))
+ return false;
+ if (stun_family == STUN_ADDRESS_IPV4) {
+ in_addr v4addr;
+ if (length() != SIZE_IP4) {
+ return false;
+ }
+ if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
+ return false;
+ }
+ rtc::IPAddress ipaddr(v4addr);
+ SetAddress(rtc::SocketAddress(ipaddr, port));
+ } else if (stun_family == STUN_ADDRESS_IPV6) {
+ in6_addr v6addr;
+ if (length() != SIZE_IP6) {
+ return false;
+ }
+ if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
+ return false;
+ }
+ rtc::IPAddress ipaddr(v6addr);
+ SetAddress(rtc::SocketAddress(ipaddr, port));
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool StunAddressAttribute::Write(ByteBufferWriter* buf) const {
+ StunAddressFamily address_family = family();
+ if (address_family == STUN_ADDRESS_UNDEF) {
+ RTC_LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
+ return false;
+ }
+ buf->WriteUInt8(0);
+ buf->WriteUInt8(address_family);
+ buf->WriteUInt16(address_.port());
+ switch (address_.family()) {
+ case AF_INET: {
+ in_addr v4addr = address_.ipaddr().ipv4_address();
+ buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
+ break;
+ }
+ case AF_INET6: {
+ in6_addr v6addr = address_.ipaddr().ipv6_address();
+ buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
+ break;
+ }
+ }
+ return true;
+}
+
+StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
+ const rtc::SocketAddress& addr)
+ : StunAddressAttribute(type, addr), owner_(NULL) {}
+
+StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
+ uint16_t length,
+ StunMessage* owner)
+ : StunAddressAttribute(type, length), owner_(owner) {}
+
+StunAttributeValueType StunXorAddressAttribute::value_type() const {
+ return STUN_VALUE_XOR_ADDRESS;
+}
+
+void StunXorAddressAttribute::SetOwner(StunMessage* owner) {
+ owner_ = owner;
+}
+
+rtc::IPAddress StunXorAddressAttribute::GetXoredIP() const {
+ if (owner_) {
+ rtc::IPAddress ip = ipaddr();
+ switch (ip.family()) {
+ case AF_INET: {
+ in_addr v4addr = ip.ipv4_address();
+ v4addr.s_addr =
+ (v4addr.s_addr ^ rtc::HostToNetwork32(kStunMagicCookie));
+ return rtc::IPAddress(v4addr);
+ }
+ case AF_INET6: {
+ in6_addr v6addr = ip.ipv6_address();
+ const std::string& transaction_id = owner_->transaction_id();
+ if (transaction_id.length() == kStunTransactionIdLength) {
+ uint32_t transactionid_as_ints[3];
+ memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
+ transaction_id.length());
+ uint32_t* ip_as_ints = reinterpret_cast<uint32_t*>(&v6addr.s6_addr);
+ // Transaction ID is in network byte order, but magic cookie
+ // is stored in host byte order.
+ ip_as_ints[0] =
+ (ip_as_ints[0] ^ rtc::HostToNetwork32(kStunMagicCookie));
+ ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
+ ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
+ ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
+ return rtc::IPAddress(v6addr);
+ }
+ break;
+ }
+ }
+ }
+ // Invalid ip family or transaction ID, or missing owner.
+ // Return an AF_UNSPEC address.
+ return rtc::IPAddress();
+}
+
+bool StunXorAddressAttribute::Read(ByteBufferReader* buf) {
+ if (!StunAddressAttribute::Read(buf))
+ return false;
+ uint16_t xoredport = port() ^ (kStunMagicCookie >> 16);
+ rtc::IPAddress xored_ip = GetXoredIP();
+ SetAddress(rtc::SocketAddress(xored_ip, xoredport));
+ return true;
+}
+
+bool StunXorAddressAttribute::Write(ByteBufferWriter* buf) const {
+ StunAddressFamily address_family = family();
+ if (address_family == STUN_ADDRESS_UNDEF) {
+ RTC_LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
+ return false;
+ }
+ rtc::IPAddress xored_ip = GetXoredIP();
+ if (xored_ip.family() == AF_UNSPEC) {
+ return false;
+ }
+ buf->WriteUInt8(0);
+ buf->WriteUInt8(family());
+ buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
+ switch (xored_ip.family()) {
+ case AF_INET: {
+ in_addr v4addr = xored_ip.ipv4_address();
+ buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
+ break;
+ }
+ case AF_INET6: {
+ in6_addr v6addr = xored_ip.ipv6_address();
+ buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
+ break;
+ }
+ }
+ return true;
+}
+
+StunUInt32Attribute::StunUInt32Attribute(uint16_t type, uint32_t value)
+ : StunAttribute(type, SIZE), bits_(value) {}
+
+StunUInt32Attribute::StunUInt32Attribute(uint16_t type)
+ : StunAttribute(type, SIZE), bits_(0) {}
+
+StunAttributeValueType StunUInt32Attribute::value_type() const {
+ return STUN_VALUE_UINT32;
+}
+
+bool StunUInt32Attribute::GetBit(size_t index) const {
+ RTC_DCHECK(index < 32);
+ return static_cast<bool>((bits_ >> index) & 0x1);
+}
+
+void StunUInt32Attribute::SetBit(size_t index, bool value) {
+ RTC_DCHECK(index < 32);
+ bits_ &= ~(1 << index);
+ bits_ |= value ? (1 << index) : 0;
+}
+
+bool StunUInt32Attribute::Read(ByteBufferReader* buf) {
+ if (length() != SIZE || !buf->ReadUInt32(&bits_))
+ return false;
+ return true;
+}
+
+bool StunUInt32Attribute::Write(ByteBufferWriter* buf) const {
+ buf->WriteUInt32(bits_);
+ return true;
+}
+
+StunUInt64Attribute::StunUInt64Attribute(uint16_t type, uint64_t value)
+ : StunAttribute(type, SIZE), bits_(value) {}
+
+StunUInt64Attribute::StunUInt64Attribute(uint16_t type)
+ : StunAttribute(type, SIZE), bits_(0) {}
+
+StunAttributeValueType StunUInt64Attribute::value_type() const {
+ return STUN_VALUE_UINT64;
+}
+
+bool StunUInt64Attribute::Read(ByteBufferReader* buf) {
+ if (length() != SIZE || !buf->ReadUInt64(&bits_))
+ return false;
+ return true;
+}
+
+bool StunUInt64Attribute::Write(ByteBufferWriter* buf) const {
+ buf->WriteUInt64(bits_);
+ return true;
+}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16_t type)
+ : StunAttribute(type, 0), bytes_(NULL) {}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
+ absl::string_view str)
+ : StunAttribute(type, 0), bytes_(NULL) {
+ CopyBytes(str);
+}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
+ const void* bytes,
+ size_t length)
+ : StunAttribute(type, 0), bytes_(NULL) {
+ CopyBytes(bytes, length);
+}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16_t type, uint16_t length)
+ : StunAttribute(type, length), bytes_(NULL) {}
+
+StunByteStringAttribute::~StunByteStringAttribute() {
+ delete[] bytes_;
+}
+
+StunAttributeValueType StunByteStringAttribute::value_type() const {
+ return STUN_VALUE_BYTE_STRING;
+}
+
+void StunByteStringAttribute::CopyBytes(absl::string_view bytes) {
+ char* new_bytes = new char[bytes.size()];
+ memcpy(new_bytes, bytes.data(), bytes.size());
+ SetBytes(new_bytes, bytes.size());
+}
+
+void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
+ char* new_bytes = new char[length];
+ memcpy(new_bytes, bytes, length);
+ SetBytes(new_bytes, length);
+}
+
+uint8_t StunByteStringAttribute::GetByte(size_t index) const {
+ RTC_DCHECK(bytes_ != NULL);
+ RTC_DCHECK(index < length());
+ return static_cast<uint8_t>(bytes_[index]);
+}
+
+void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
+ RTC_DCHECK(bytes_ != NULL);
+ RTC_DCHECK(index < length());
+ bytes_[index] = value;
+}
+
+bool StunByteStringAttribute::Read(ByteBufferReader* buf) {
+ bytes_ = new char[length()];
+ if (!buf->ReadBytes(bytes_, length())) {
+ return false;
+ }
+
+ ConsumePadding(buf);
+ return true;
+}
+
+bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const {
+ // Check that length is legal according to specs
+ if (!LengthValid(type(), length())) {
+ return false;
+ }
+ buf->WriteBytes(bytes_, length());
+ WritePadding(buf);
+ return true;
+}
+
+void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
+ delete[] bytes_;
+ bytes_ = bytes;
+ SetLength(static_cast<uint16_t>(length));
+}
+
+const uint16_t StunErrorCodeAttribute::MIN_SIZE = 4;
+
+StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type,
+ int code,
+ const std::string& reason)
+ : StunAttribute(type, 0) {
+ SetCode(code);
+ SetReason(reason);
+}
+
+StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type, uint16_t length)
+ : StunAttribute(type, length), class_(0), number_(0) {}
+
+StunErrorCodeAttribute::~StunErrorCodeAttribute() {}
+
+StunAttributeValueType StunErrorCodeAttribute::value_type() const {
+ return STUN_VALUE_ERROR_CODE;
+}
+
+int StunErrorCodeAttribute::code() const {
+ return class_ * 100 + number_;
+}
+
+void StunErrorCodeAttribute::SetCode(int code) {
+ class_ = static_cast<uint8_t>(code / 100);
+ number_ = static_cast<uint8_t>(code % 100);
+}
+
+void StunErrorCodeAttribute::SetReason(const std::string& reason) {
+ SetLength(MIN_SIZE + static_cast<uint16_t>(reason.size()));
+ reason_ = reason;
+}
+
+bool StunErrorCodeAttribute::Read(ByteBufferReader* buf) {
+ uint32_t val;
+ if (length() < MIN_SIZE || !buf->ReadUInt32(&val))
+ return false;
+
+ if ((val >> 11) != 0)
+ RTC_LOG(LS_ERROR) << "error-code bits not zero";
+
+ class_ = ((val >> 8) & 0x7);
+ number_ = (val & 0xff);
+
+ if (!buf->ReadString(&reason_, length() - 4))
+ return false;
+
+ ConsumePadding(buf);
+ return true;
+}
+
+bool StunErrorCodeAttribute::Write(ByteBufferWriter* buf) const {
+ buf->WriteUInt32(class_ << 8 | number_);
+ buf->WriteString(reason_);
+ WritePadding(buf);
+ return true;
+}
+
+StunUInt16ListAttribute::StunUInt16ListAttribute(uint16_t type, uint16_t length)
+ : StunAttribute(type, length) {
+ attr_types_ = new std::vector<uint16_t>();
+}
+
+StunUInt16ListAttribute::~StunUInt16ListAttribute() {
+ delete attr_types_;
+}
+
+StunAttributeValueType StunUInt16ListAttribute::value_type() const {
+ return STUN_VALUE_UINT16_LIST;
+}
+
+size_t StunUInt16ListAttribute::Size() const {
+ return attr_types_->size();
+}
+
+uint16_t StunUInt16ListAttribute::GetType(int index) const {
+ return (*attr_types_)[index];
+}
+
+void StunUInt16ListAttribute::SetType(int index, uint16_t value) {
+ (*attr_types_)[index] = value;
+}
+
+void StunUInt16ListAttribute::AddType(uint16_t value) {
+ attr_types_->push_back(value);
+ SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
+}
+
+void StunUInt16ListAttribute::AddTypeAtIndex(uint16_t index, uint16_t value) {
+ if (attr_types_->size() < static_cast<size_t>(index + 1)) {
+ attr_types_->resize(index + 1);
+ }
+ (*attr_types_)[index] = value;
+ SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
+}
+
+bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
+ if (length() % 2) {
+ return false;
+ }
+
+ for (size_t i = 0; i < length() / 2; i++) {
+ uint16_t attr;
+ if (!buf->ReadUInt16(&attr))
+ return false;
+ attr_types_->push_back(attr);
+ }
+ // Padding of these attributes is done in RFC 5389 style. This is
+ // slightly different from RFC3489, but it shouldn't be important.
+ // RFC3489 pads out to a 32 bit boundary by duplicating one of the
+ // entries in the list (not necessarily the last one - it's unspecified).
+ // RFC5389 pads on the end, and the bytes are always ignored.
+ ConsumePadding(buf);
+ return true;
+}
+
+bool StunUInt16ListAttribute::Write(ByteBufferWriter* buf) const {
+ for (size_t i = 0; i < attr_types_->size(); ++i) {
+ buf->WriteUInt16((*attr_types_)[i]);
+ }
+ WritePadding(buf);
+ return true;
+}
+
+std::string StunMethodToString(int msg_type) {
+ switch (msg_type) {
+ case STUN_BINDING_REQUEST:
+ return "STUN BINDING request";
+ case STUN_BINDING_INDICATION:
+ return "STUN BINDING indication";
+ case STUN_BINDING_RESPONSE:
+ return "STUN BINDING response";
+ case STUN_BINDING_ERROR_RESPONSE:
+ return "STUN BINDING error response";
+ case GOOG_PING_REQUEST:
+ return "GOOG PING request";
+ case GOOG_PING_RESPONSE:
+ return "GOOG PING response";
+ case GOOG_PING_ERROR_RESPONSE:
+ return "GOOG PING error response";
+ case STUN_ALLOCATE_REQUEST:
+ return "TURN ALLOCATE request";
+ case STUN_ALLOCATE_RESPONSE:
+ return "TURN ALLOCATE response";
+ case STUN_ALLOCATE_ERROR_RESPONSE:
+ return "TURN ALLOCATE error response";
+ case TURN_REFRESH_REQUEST:
+ return "TURN REFRESH request";
+ case TURN_REFRESH_RESPONSE:
+ return "TURN REFRESH response";
+ case TURN_REFRESH_ERROR_RESPONSE:
+ return "TURN REFRESH error response";
+ case TURN_SEND_INDICATION:
+ return "TURN SEND INDICATION";
+ case TURN_DATA_INDICATION:
+ return "TURN DATA INDICATION";
+ case TURN_CREATE_PERMISSION_REQUEST:
+ return "TURN CREATE PERMISSION request";
+ case TURN_CREATE_PERMISSION_RESPONSE:
+ return "TURN CREATE PERMISSION response";
+ case TURN_CREATE_PERMISSION_ERROR_RESPONSE:
+ return "TURN CREATE PERMISSION error response";
+ case TURN_CHANNEL_BIND_REQUEST:
+ return "TURN CHANNEL BIND request";
+ case TURN_CHANNEL_BIND_RESPONSE:
+ return "TURN CHANNEL BIND response";
+ case TURN_CHANNEL_BIND_ERROR_RESPONSE:
+ return "TURN CHANNEL BIND error response";
+ default:
+ return "UNKNOWN<" + std::to_string(msg_type) + ">";
+ }
+}
+
+int GetStunSuccessResponseType(int req_type) {
+ return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
+}
+
+int GetStunErrorResponseType(int req_type) {
+ return IsStunRequestType(req_type) ? (req_type | 0x110) : -1;
+}
+
+bool IsStunRequestType(int msg_type) {
+ return ((msg_type & kStunTypeMask) == 0x000);
+}
+
+bool IsStunIndicationType(int msg_type) {
+ return ((msg_type & kStunTypeMask) == 0x010);
+}
+
+bool IsStunSuccessResponseType(int msg_type) {
+ return ((msg_type & kStunTypeMask) == 0x100);
+}
+
+bool IsStunErrorResponseType(int msg_type) {
+ return ((msg_type & kStunTypeMask) == 0x110);
+}
+
+bool ComputeStunCredentialHash(const std::string& username,
+ const std::string& realm,
+ const std::string& password,
+ std::string* hash) {
+ // http://tools.ietf.org/html/rfc5389#section-15.4
+ // long-term credentials will be calculated using the key and key is
+ // key = MD5(username ":" realm ":" SASLprep(password))
+ std::string input = username;
+ input += ':';
+ input += realm;
+ input += ':';
+ input += password;
+
+ char digest[rtc::MessageDigest::kMaxSize];
+ size_t size = rtc::ComputeDigest(rtc::DIGEST_MD5, input.c_str(), input.size(),
+ digest, sizeof(digest));
+ if (size == 0) {
+ return false;
+ }
+
+ *hash = std::string(digest, size);
+ return true;
+}
+
+std::unique_ptr<StunAttribute> CopyStunAttribute(
+ const StunAttribute& attribute,
+ rtc::ByteBufferWriter* tmp_buffer_ptr) {
+ ByteBufferWriter tmpBuffer;
+ if (tmp_buffer_ptr == nullptr) {
+ tmp_buffer_ptr = &tmpBuffer;
+ }
+
+ std::unique_ptr<StunAttribute> copy(StunAttribute::Create(
+ attribute.value_type(), attribute.type(),
+ static_cast<uint16_t>(attribute.length()), nullptr));
+
+ if (!copy) {
+ return nullptr;
+ }
+ tmp_buffer_ptr->Clear();
+ if (!attribute.Write(tmp_buffer_ptr)) {
+ return nullptr;
+ }
+ rtc::ByteBufferReader reader(*tmp_buffer_ptr);
+ if (!copy->Read(&reader)) {
+ return nullptr;
+ }
+
+ return copy;
+}
+
+StunAttributeValueType RelayMessage::GetAttributeValueType(int type) const {
+ switch (type) {
+ case STUN_ATTR_LIFETIME:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_MAGIC_COOKIE:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_BANDWIDTH:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ return STUN_VALUE_ADDRESS;
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ return STUN_VALUE_ADDRESS;
+ case STUN_ATTR_DATA:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_OPTIONS:
+ return STUN_VALUE_UINT32;
+ default:
+ return StunMessage::GetAttributeValueType(type);
+ }
+}
+
+StunMessage* RelayMessage::CreateNew() const {
+ return new RelayMessage();
+}
+
+StunAttributeValueType TurnMessage::GetAttributeValueType(int type) const {
+ switch (type) {
+ case STUN_ATTR_CHANNEL_NUMBER:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_TURN_LIFETIME:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_XOR_PEER_ADDRESS:
+ return STUN_VALUE_XOR_ADDRESS;
+ case STUN_ATTR_DATA:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_XOR_RELAYED_ADDRESS:
+ return STUN_VALUE_XOR_ADDRESS;
+ case STUN_ATTR_EVEN_PORT:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_REQUESTED_TRANSPORT:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_DONT_FRAGMENT:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_RESERVATION_TOKEN:
+ return STUN_VALUE_BYTE_STRING;
+ default:
+ return StunMessage::GetAttributeValueType(type);
+ }
+}
+
+StunMessage* TurnMessage::CreateNew() const {
+ return new TurnMessage();
+}
+
+StunAttributeValueType IceMessage::GetAttributeValueType(int type) const {
+ switch (type) {
+ case STUN_ATTR_PRIORITY:
+ case STUN_ATTR_GOOG_NETWORK_INFO:
+ case STUN_ATTR_NOMINATION:
+ return STUN_VALUE_UINT32;
+ case STUN_ATTR_USE_CANDIDATE:
+ return STUN_VALUE_BYTE_STRING;
+ case STUN_ATTR_ICE_CONTROLLED:
+ return STUN_VALUE_UINT64;
+ case STUN_ATTR_ICE_CONTROLLING:
+ return STUN_VALUE_UINT64;
+ default:
+ return StunMessage::GetAttributeValueType(type);
+ }
+}
+
+StunMessage* IceMessage::CreateNew() const {
+ return new IceMessage();
+}
+
+std::unique_ptr<StunMessage> StunMessage::Clone() const {
+ std::unique_ptr<StunMessage> copy(CreateNew());
+ if (!copy) {
+ return nullptr;
+ }
+ rtc::ByteBufferWriter buf;
+ if (!Write(&buf)) {
+ return nullptr;
+ }
+ rtc::ByteBufferReader reader(buf);
+ if (!copy->Read(&reader)) {
+ return nullptr;
+ }
+ return copy;
+}
+
+} // namespace cricket