text: /*
   *  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 "p2p/base/stun.h"
  
  #include <string.h>
  
  #include <algorithm>
  #include <memory>
  #include <utility>
  
  #include "absl/memory/memory.h"
  #include "rtc_base/byte_order.h"
  #include "rtc_base/checks.h"
  #include "rtc_base/crc32.h"
  #include "rtc_base/logging.h"
  #include "rtc_base/message_digest.h"
  
  using rtc::ByteBufferReader;
  using rtc::ByteBufferWriter;
  
  namespace {
  
  uint32_t ReduceTransactionId(const std::string& transaction_id) {
    RTC_DCHECK(transaction_id.length() == cricket::kStunTransactionIdLength ||
               transaction_id.length() ==
                   cricket::kStunLegacyTransactionIdLength);
    ByteBufferReader reader(transaction_id.c_str(), transaction_id.length(),
                            rtc::ByteBuffer::ORDER_NETWORK);
    uint32_t result = 0;
    uint32_t next;
    while (reader.ReadUInt32(&next)) {
      result ^= next;
    }
    return result;
  }
  
  }  // namespace
  
  namespace cricket {
  
  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_FORBIDDEN[] = "Forbidden";
  const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "Stale Credentials";
  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;
  
  // StunMessage
  
  StunMessage::StunMessage()
      : type_(0),
        length_(0),
        transaction_id_(EMPTY_TRANSACTION_ID),
        stun_magic_cookie_(kStunMagicCookie) {
    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;
  }
  
  bool StunMessage::SetTransactionID(const std::string& str) {
    if (!IsValidTransactionId(str)) {
      return false;
    }
    transaction_id_ = str;
    reduced_transaction_id_ = ReduceTransactionId(transaction_id_);
    return true;
  }
  
  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;
  }
  
  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 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));
  }
  
  // Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
  // procedure outlined in RFC 5389, section 15.4.
  bool StunMessage::ValidateMessageIntegrity(const char* data,
                                             size_t size,
                                             const std::string& password) {
    // 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 == STUN_ATTR_MESSAGE_INTEGRITY) {
        if (attr_length != kStunMessageIntegritySize ||
            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 + kStunMessageIntegritySize) {
      // 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 + kStunMessageIntegritySize);
      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,
                  sizeof(hmac)) == 0;
  }
  
  bool StunMessage::AddMessageIntegrity(const std::string& password) {
    return AddMessageIntegrity(password.c_str(), password.size());
  }
  
  bool StunMessage::AddMessageIntegrity(const char* key, size_t keylen) {
    // Add the attribute with a dummy value. Since this is a known attribute, it
    // can't fail.
    auto msg_integrity_attr_ptr = absl::make_unique<StunByteStringAttribute>(
        STUN_ATTR_MESSAGE_INTEGRITY, std::string(kStunMessageIntegritySize, '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, keylen, 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, sizeof(hmac));
    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));
  }
  
  bool StunMessage::AddFingerprint() {
    // Add the attribute with a dummy value. Since this is a known attribute,
    // it can't fail.
    auto fingerprint_attr_ptr =
        absl::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) {
    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;
  }
  
  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_ORIGIN:
        return STUN_VALUE_BYTE_STRING;
      case STUN_ATTR_RETRANSMIT_COUNT:
        return STUN_VALUE_UINT32;
      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(const std::string& transaction_id) {
    return transaction_id.size() == kStunTransactionIdLength ||
           transaction_id.size() == kStunLegacyTransactionIdLength;
  }
  
  // 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 absl::make_unique<StunAddressAttribute>(type, 0);
  }
  
  std::unique_ptr<StunXorAddressAttribute> StunAttribute::CreateXorAddress(
      uint16_t type) {
    return absl::make_unique<StunXorAddressAttribute>(type, 0, nullptr);
  }
  
  std::unique_ptr<StunUInt64Attribute> StunAttribute::CreateUInt64(
      uint16_t type) {
    return absl::make_unique<StunUInt64Attribute>(type);
  }
  
  std::unique_ptr<StunUInt32Attribute> StunAttribute::CreateUInt32(
      uint16_t type) {
    return absl::make_unique<StunUInt32Attribute>(type);
  }
  
  std::unique_ptr<StunByteStringAttribute> StunAttribute::CreateByteString(
      uint16_t type) {
    return absl::make_unique<StunByteStringAttribute>(type, 0);
  }
  
  std::unique_ptr<StunErrorCodeAttribute> StunAttribute::CreateErrorCode() {
    return absl::make_unique<StunErrorCodeAttribute>(
        STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
  }
  
  std::unique_ptr<StunUInt16ListAttribute>
  StunAttribute::CreateUnknownAttributes() {
    return absl::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,
                                                   const std::string& str)
      : StunAttribute(type, 0), bytes_(NULL) {
    CopyBytes(str.c_str(), str.size());
  }
  
  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(const char* bytes) {
    CopyBytes(bytes, strlen(bytes));
  }
  
  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 {
    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));
  }
  
  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;
  }
  
  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_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();
  }
  
  }  // namespace cricket
  