feat: 增加webrtc代理拉流 (#4389)

- 增加客户端模式,支持主动拉流、推流:
   - addStreamProxy接口新增支持whep主动拉流,拉流地址目前只兼容zlm的whep url。
   - addStreamPusherProxy接口新增支持whip主动推流,推流地址目前只兼容zlm的whip url。
   - 以上推流url格式为webrtc[s]://server_host:server_port/app/stream_id?key=value, 内部会自动转换为http[s]://server_host:server_port/index/api/[whip/whep]?app=app&stream=stream_id&key=value。

- 增加WebRtc p2p 模式:
  - 增加 ICE FULL模式。
  - 增加STUN/TURN 服务器。
  - 增加websocket 信令。
  - 增加P2P代理拉流。

---------

Co-authored-by: xia-chu <771730766@qq.com>
Co-authored-by: mtdxc <mtdxc@126.com>
Co-authored-by: cqm <cqm@97kid.com>
This commit is contained in:
baigao-X
2025-09-20 16:23:30 +08:00
committed by GitHub
parent 97d2a1fb08
commit 3fb43c5fef
72 changed files with 16912 additions and 10319 deletions

View File

@@ -1,213 +1,689 @@
/**
ISC License
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
Copyright © 2015, Iñaki Baz Castillo <ibc@aliax.net>
#ifndef ZLMEDIAKIT_WEBRTC_STUN_PACKET_HPP
#define ZLMEDIAKIT_WEBRTC_STUN_PACKET_HPP
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef MS_RTC_STUN_PACKET_HPP
#define MS_RTC_STUN_PACKET_HPP
#include "logger.h"
#include "Utils.hpp"
#include <string>
#include "Util/Byte.hpp"
#include "Network/Buffer.h"
#include "Network/sockutil.h"
namespace RTC
{
class StunPacket
{
public:
// STUN message class.
enum class Class : uint16_t
{
REQUEST = 0,
INDICATION = 1,
SUCCESS_RESPONSE = 2,
ERROR_RESPONSE = 3
};
namespace RTC {
// reference https://rcf-editor.org/rfc/rfc8489
// reference https://rcf-editor.org/rfc/rfc8656
// reference https://rcf-editor.org/rfc/rfc8445
// STUN message method.
enum class Method : uint16_t
{
BINDING = 1
};
//////////// Attribute //////////////////////////
// reference https://rcf-editor.org/rfc/rfc8489
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value (variable) ....
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 4: Format of STUN Attributes
reference https://www.rfc-editor.org/rfc/rfc8489.html#section-14
*/
class StunAttribute {
public:
// Attribute type.
enum class Type : uint16_t {
MAPPED_ADDRESS = 0x0001,
RESPONSE_ADDRESS = 0x0002, // Reserved; was RESPONSE-ADDRESS prior to [RFC5389]
CHANGE_REQUEST = 0x0003, // Reserved; was CHANGE-REQUEST prior to [RFC5389]
CHANGED_ADDRESS = 0x0005, // Reserved; was CHANGED-ADDRESS prior to [RFC5389]
USERNAME = 0x0006,
PASSWORD = 0x0005, // Reserved; was PASSWORD prior to [RFC5389]
MESSAGE_INTEGRITY = 0x0008,
ERROR_CODE = 0x0009,
UNKNOWN_ATTRIBUTES = 0x000A,
REFLECTED_FROM = 0x000B, // Reserved; was REFLECTED-FROM prior to [RFC5389]
CHANNEL_NUMBER = 0x000C, // [RFC5766]
LIFETIME = 0x000D, // [RFC5766]
BANDWIDTH = 0x0010, // Reserved; [RFC5766]
XOR_PEER_ADDRESS = 0x0012, // [RFC5766]
DATA = 0x0013, // [RFC5766]
REALM = 0x0014,
NONCE = 0x0015,
XOR_RELAYED_ADDRESS = 0x0016, // [RFC5766]
EVEN_PORT = 0x0018, // [RFC5766]
REQUESTED_TRANSPORT = 0x0019, // [RFC5766]
DONT_FRAGMENT = 0x001A, // [RFC5766]
MESSAGE_INTEGRITY_SHA256 = 0x001C,
USERHASH = 0x001E,
PASSWORD_ALGORITHM = 0x001D,
XOR_MAPPED_ADDRESS = 0x0020,
TIMER_VAL = 0x0021, // Reserved; [RFC5766]
RESERVATION_TOKEN = 0x0022, // [RFC5766]
PRIORITY = 0x0024,
USE_CANDIDATE = 0x0025,
// Attribute type.
enum class Attribute : uint16_t
{
MAPPED_ADDRESS = 0x0001,
USERNAME = 0x0006,
MESSAGE_INTEGRITY = 0x0008,
ERROR_CODE = 0x0009,
UNKNOWN_ATTRIBUTES = 0x000A,
REALM = 0x0014,
NONCE = 0x0015,
XOR_MAPPED_ADDRESS = 0x0020,
PRIORITY = 0x0024,
USE_CANDIDATE = 0x0025,
SOFTWARE = 0x8022,
ALTERNATE_SERVER = 0x8023,
FINGERPRINT = 0x8028,
ICE_CONTROLLED = 0x8029,
ICE_CONTROLLING = 0x802A
};
// Authentication result.
enum class Authentication
{
OK = 0,
UNAUTHORIZED = 1,
BAD_REQUEST = 2
};
public:
static bool IsStun(const uint8_t* data, size_t len)
{
// clang-format off
return (
// STUN headers are 20 bytes.
(len >= 20) &&
// DOC: https://tools.ietf.org/html/draft-ietf-avtcore-rfc5764-mux-fixes
(data[0] < 3) &&
// Magic cookie must match.
(data[4] == StunPacket::magicCookie[0]) && (data[5] == StunPacket::magicCookie[1]) &&
(data[6] == StunPacket::magicCookie[2]) && (data[7] == StunPacket::magicCookie[3])
);
// clang-format on
}
static StunPacket* Parse(const uint8_t* data, size_t len);
private:
static const uint8_t magicCookie[];
public:
StunPacket(
Class klass, Method method, const uint8_t* transactionId, const uint8_t* data, size_t size);
~StunPacket();
void Dump() const;
Class GetClass() const
{
return this->klass;
}
Method GetMethod() const
{
return this->method;
}
const uint8_t* GetData() const
{
return this->data;
}
size_t GetSize() const
{
return this->size;
}
void SetUsername(const char* username, size_t len)
{
this->username.assign(username, len);
}
void SetPriority(uint32_t priority)
{
this->priority = priority;
}
void SetIceControlling(uint64_t iceControlling)
{
this->iceControlling = iceControlling;
}
void SetIceControlled(uint64_t iceControlled)
{
this->iceControlled = iceControlled;
}
void SetUseCandidate()
{
this->hasUseCandidate = true;
}
void SetXorMappedAddress(const struct sockaddr* xorMappedAddress)
{
this->xorMappedAddress = xorMappedAddress;
}
void SetErrorCode(uint16_t errorCode)
{
this->errorCode = errorCode;
}
void SetMessageIntegrity(const uint8_t* messageIntegrity)
{
this->messageIntegrity = messageIntegrity;
}
void SetFingerprint()
{
this->hasFingerprint = true;
}
const std::string& GetUsername() const
{
return this->username;
}
uint32_t GetPriority() const
{
return this->priority;
}
uint64_t GetIceControlling() const
{
return this->iceControlling;
}
uint64_t GetIceControlled() const
{
return this->iceControlled;
}
bool HasUseCandidate() const
{
return this->hasUseCandidate;
}
uint16_t GetErrorCode() const
{
return this->errorCode;
}
bool HasMessageIntegrity() const
{
return (this->messageIntegrity ? true : false);
}
bool HasFingerprint() const
{
return this->hasFingerprint;
}
Authentication CheckAuthentication(
const std::string& localUsername, const std::string& localPassword);
StunPacket* CreateSuccessResponse();
StunPacket* CreateErrorResponse(uint16_t errorCode);
void Authenticate(const std::string& password);
void Serialize(uint8_t* buffer);
private:
// Passed by argument.
Class klass; // 2 bytes.
Method method; // 2 bytes.
const uint8_t* transactionId{ nullptr }; // 12 bytes.
uint8_t* data{ nullptr }; // Pointer to binary data.
size_t size{ 0u }; // The full message size (including header).
// STUN attributes.
std::string username; // Less than 513 bytes.
uint32_t priority{ 0u }; // 4 bytes unsigned integer.
uint64_t iceControlling{ 0u }; // 8 bytes unsigned integer.
uint64_t iceControlled{ 0u }; // 8 bytes unsigned integer.
bool hasUseCandidate{ false }; // 0 bytes.
const uint8_t* messageIntegrity{ nullptr }; // 20 bytes.
bool hasFingerprint{ false }; // 4 bytes.
const struct sockaddr* xorMappedAddress{ nullptr }; // 8 or 20 bytes.
uint16_t errorCode{ 0u }; // 4 bytes (no reason phrase).
std::string password;
//Comprehension-optional range (0x8000-0xFFFF)
PASSWORD_ALGORITHMS = 0x8002,
ALTERNATE_DOMAIN = 0x8003,
SOFTWARE = 0x8022,
ALTERNATE_SERVER = 0x8023,
FINGERPRINT = 0x8028,
ICE_CONTROLLED = 0x8029,
ICE_CONTROLLING = 0x802A,
GOOG_NETWORK_INFO = 0xC057,
};
static const size_t ATTR_HEADER_SIZE = 4;
static bool isComprehensionRequired(const uint8_t *data, size_t len);
using Ptr = std::shared_ptr<StunAttribute>;
StunAttribute(StunAttribute::Type type) : _type(type) {}
virtual ~StunAttribute() = default;
char *data() { return _data ? _data->data() : nullptr; }
char *body() { return _data ? _data->data() + ATTR_HEADER_SIZE : nullptr; }
size_t size() const { return _data ? _data->size() : 0; }
Type type() const { return _type; }
virtual bool loadFromData(const uint8_t *buf, size_t len) = 0;
virtual bool storeToData() = 0;
// virtual std::string dump() = 0;
protected:
const uint8_t * loadHeader(const uint8_t *buf);
uint8_t * storeHeader();
protected:
Type _type;
uint16_t _length;
toolkit::BufferRaw::Ptr _data;
};
/*
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 0 0 0 0 0 0| Family | Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Address (32 bits or 128 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 5: Format of MAPPED-ADDRESS Attribute
reference https://www.rfc-editor.org/rfc/rfc8489.html#page-37
*/
class StunAttrMappedAddress : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrMappedAddress>;
static constexpr Type TYPE = StunAttribute::Type::MAPPED_ADDRESS;
StunAttrMappedAddress() : StunAttribute(TYPE) {};
virtual ~StunAttrMappedAddress() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
};
class StunAttrUserName : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrUserName>;
static constexpr Type TYPE = StunAttribute::Type::USERNAME;
StunAttrUserName() : StunAttribute(TYPE) {};
virtual ~StunAttrUserName() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setUsername(std::string username) { _username = std::move(username); }
const std::string& getUsername() const { return _username; }
private:
std::string _username;
};
class StunAttrMessageIntegrity : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrMessageIntegrity>;
static constexpr Type TYPE = StunAttribute::Type::MESSAGE_INTEGRITY;
StunAttrMessageIntegrity() : StunAttribute(TYPE) {};
virtual ~StunAttrMessageIntegrity() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setHmac(std::string hmac) { _hmac = std::move(hmac); }
const std::string &getHmac() const { return _hmac; }
private:
std::string _hmac;
};
class StunAttrErrorCode : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrErrorCode>;
static constexpr Type TYPE = StunAttribute::Type::ERROR_CODE;
StunAttrErrorCode() : StunAttribute(TYPE) {};
virtual ~StunAttrErrorCode() = default;
enum class Code : uint16_t {
Invalid = 0, //
TryAlternate = 300, //尝试备用服务器
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403, //禁止
RequestTimedOut = 408, //请求超时(客户端认为此事务已经失败)
UnknownAttribute = 420,
AllocationMismatch = 438,
StaleNonce = 438, //NONCE 不再有效,客户端应使用响应中的NONCE重试
AddressFamilyNotSupported = 440, //不支持的协议簇
WrongCredentials = 441, //凭据错误
UnsupportedTransportAddress = 442, //不支持的传输地址
AllocationQuotaReached = 486, //alloction 达到上限,客户端应该至少等待一分钟后重新尝试创建
RoleConflict = 487, //角色冲突
ServerError = 500, //服务器临时错误,客户端应重试
InsuficientCapacity = 508, //容量不足,没有更多可用的中继传输地址
};
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setErrorCode(Code error_code) { _error_code = error_code; }
Code getErrorCode() const { return _error_code; }
private:
Code _error_code;
};
class StunAttrChannelNumber : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrChannelNumber>;
static constexpr Type TYPE = StunAttribute::Type::CHANNEL_NUMBER;
StunAttrChannelNumber() : StunAttribute(TYPE) {};
virtual ~StunAttrChannelNumber() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
void setChannelNumber(uint16_t channel_number) { _channel_number = channel_number; }
uint16_t getChannelNumber() const { return _channel_number; }
private:
uint16_t _channel_number;
};
class StunAttrLifeTime : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrLifeTime>;
static constexpr Type TYPE = StunAttribute::Type::LIFETIME;
StunAttrLifeTime() : StunAttribute(TYPE) {};
~StunAttrLifeTime() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setLifetime(uint32_t lifetime) { _lifetime = lifetime; }
uint32_t getLifetime() const { return _lifetime; }
private:
uint32_t _lifetime;
};
/*
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 0 0 0 0 0 0| Family | X-Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| X-Address (Variable)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 6: Format of XOR-MAPPED-ADDRESS Attribute
reference https://www.rfc-editor.org/rfc/rfc8489.html#page-38
*/
class StunAttrXorPeerAddress : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrXorPeerAddress>;
static constexpr Type TYPE = StunAttribute::Type::XOR_PEER_ADDRESS;
StunAttrXorPeerAddress(std::string transaction_id)
: StunAttribute(TYPE)
, _transaction_id(std::move(transaction_id)) {}
virtual ~StunAttrXorPeerAddress() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setAddr(const struct sockaddr_storage &addr) { _addr = addr; }
const struct sockaddr_storage& getAddr() const { return _addr; }
std::string getIp() const { return toolkit::SockUtil::inet_ntoa((struct sockaddr *)&_addr); }
uint16_t getPort() const { return toolkit::SockUtil::inet_port((struct sockaddr *)&_addr); }
protected:
struct sockaddr_storage _addr;
std::string _transaction_id;
};
class StunAttrData : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrData>;
static constexpr Type TYPE = StunAttribute::Type::DATA;
StunAttrData() : StunAttribute(TYPE) {};
virtual ~StunAttrData() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
void setData(std::string data) { _data_content = std::move(data); }
void setData(const char *data, int size) { _data_content.assign(data, size); }
const std::string &getData() const { return _data_content; }
private:
std::string _data_content;
};
class StunAttrRealm : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrRealm>;
static constexpr Type TYPE = StunAttribute::Type::REALM;
StunAttrRealm() : StunAttribute(TYPE) {};
virtual ~StunAttrRealm() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setRealm(std::string realm) { _realm = std::move(realm); }
const std::string &getRealm() const { return _realm; }
private:
// 长度小于128字符
std::string _realm;
};
class StunAttrNonce : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrNonce>;
static constexpr Type TYPE = StunAttribute::Type::NONCE;
StunAttrNonce() : StunAttribute(TYPE) {};
virtual ~StunAttrNonce() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setNonce(std::string nonce) { _nonce = std::move(nonce); }
const std::string& getNonce() const { return _nonce; }
private:
// 长度小于128字符
std::string _nonce;
};
class StunAttrXorRelayedAddress : public StunAttrXorPeerAddress {
public:
using Ptr = std::shared_ptr<StunAttrXorRelayedAddress>;
static constexpr Type TYPE = StunAttribute::Type::XOR_RELAYED_ADDRESS;
StunAttrXorRelayedAddress(std::string transaction_id) : StunAttrXorPeerAddress(std::move(transaction_id)) {
_type = TYPE;
}
virtual ~StunAttrXorRelayedAddress() = default;
};
class StunAttrXorMappedAddress : public StunAttrXorPeerAddress {
public:
using Ptr = std::shared_ptr<StunAttrXorPeerAddress>;
static constexpr Type TYPE = StunAttribute::Type::XOR_MAPPED_ADDRESS;
StunAttrXorMappedAddress(std::string transaction_id) : StunAttrXorPeerAddress(std::move(transaction_id)) {
_type = TYPE;
}
virtual ~StunAttrXorMappedAddress() = default;
};
/*
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Protocol | RFFU |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
reference https://www.rfc-editor.org/rfc/rfc5766.html#section-14.7
*/
class StunAttrRequestedTransport : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrRequestedTransport>;
static constexpr Type TYPE = StunAttribute::Type::REQUESTED_TRANSPORT;
StunAttrRequestedTransport() : StunAttribute(TYPE) {};
virtual ~StunAttrRequestedTransport() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
enum class Protocol : uint8_t {
// This specification only allows the use of codepoint 17 (User Datagram Protocol).
UDP = 0x11,
};
void setProtocol(Protocol protocol) { _protocol = protocol; }
Protocol getProtocol() const { return _protocol; }
private:
Protocol _protocol = Protocol::UDP;
};
class StunAttrPriority : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrPriority>;
static constexpr Type TYPE = StunAttribute::Type::PRIORITY;
StunAttrPriority() : StunAttribute(TYPE) {};
virtual ~StunAttrPriority() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setPriority(uint64_t priority) { _priority = priority; }
uint64_t getPriority() const { return _priority; }
private:
uint32_t _priority;
};
class StunAttrUseCandidate : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrUseCandidate>;
static constexpr Type TYPE = StunAttribute::Type::USE_CANDIDATE;
StunAttrUseCandidate() : StunAttribute(TYPE) {};
virtual ~StunAttrUseCandidate() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
};
class StunAttrFingerprint : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrFingerprint>;
static constexpr Type TYPE = StunAttribute::Type::FINGERPRINT;
StunAttrFingerprint() : StunAttribute(TYPE) {};
virtual ~StunAttrFingerprint() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setFingerprint(uint32_t fingerprint) { _fingerprint = fingerprint; }
uint32_t getFingerprint() const { return _fingerprint; }
private:
uint32_t _fingerprint;
};
class StunAttrIceControlled : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrIceControlled>;
static constexpr Type TYPE = StunAttribute::Type::ICE_CONTROLLED;
StunAttrIceControlled() : StunAttribute(TYPE) {};
virtual ~StunAttrIceControlled() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setTiebreaker(uint64_t tiebreaker) { _tiebreaker = tiebreaker; }
uint64_t getTiebreaker() const { return _tiebreaker; }
private:
uint64_t _tiebreaker = 0; // 8 bytes unsigned integer.
};
class StunAttrIceControlling : public StunAttribute {
public:
using Ptr = std::shared_ptr<StunAttrIceControlling>;
static constexpr Type TYPE = StunAttribute::Type::ICE_CONTROLLING;
StunAttrIceControlling() : StunAttribute(TYPE) {};
virtual ~StunAttrIceControlling() = default;
bool loadFromData(const uint8_t *buf, size_t len) override;
bool storeToData() override;
// std::string dump() override;
void setTiebreaker(uint64_t tiebreaker) { _tiebreaker = tiebreaker; }
uint64_t getTiebreaker() const { return _tiebreaker; }
private:
uint64_t _tiebreaker = 0; // 8 bytes unsigned integer.
};
//////////// STUN //////////////////////////
/*
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Transaction ID (96 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2: Format of STUN Message Header
reference https://www.rfc-editor.org/rfc/rfc8489.html#section-5 */
class StunPacket : public toolkit::Buffer {
public:
using Ptr = std::shared_ptr<StunPacket>;
// STUN message class.
enum class Class : uint8_t {
REQUEST = 0,
INDICATION = 1,
SUCCESS_RESPONSE = 2,
ERROR_RESPONSE = 3
};
// STUN message method.
enum class Method : uint16_t {
BINDING = 0x001,
//TURN Extended
//https://www.rfc-editor.org/rfc/rfc5766.html#section-13
ALLOCATE = 0x003, // (only request/response semantics defined)
REFRESH = 0x004, // (only request/response semantics defined)
SEND = 0x006, // (only indication semantics defined)
DATA = 0x007, // (only indication semantics defined)
CREATEPERMISSION = 0x008, // (only request/response semantics defined
CHANNELBIND = 0x009, // (only request/response semantics defined)
};
// Authentication result.
enum class Authentication {
OK = 0,
UNAUTHORIZED = 1,
BAD_REQUEST = 2
};
struct EnumClassHash {
template <typename T>
std::size_t operator()(T t) const {
return static_cast<std::size_t>(t);
}
};
struct ClassMethodHash {
bool operator()(std::pair<StunPacket::Class, StunPacket::Method> key) const {
std::size_t h = 0;
h ^= std::hash<uint8_t>()((uint8_t)key.first) << 1;
h ^= std::hash<uint8_t>()((uint8_t)key.second) << 2;
return h;
}
};
static const size_t HEADER_SIZE = 20;
static const uint8_t _magicCookie[];
static bool isStun(const uint8_t *data, size_t len);
static Class getClass(const uint8_t *data, size_t len);
static Method getMethod(const uint8_t *data, size_t len);
static StunPacket::Ptr parse(const uint8_t *data, size_t len);
static std::string mappingClassEnum2Str(Class klass);
static std::string mappingMethodEnum2Str(Method method);
StunPacket(Class klass, Method method, const char* transId = nullptr);
virtual ~StunPacket();
Class getClass() const { return _klass; }
Method getMethod() const { return _method; }
std::string getClassStr() const { return StrPrinter << mappingClassEnum2Str(_klass) << "(" << (uint32_t)_klass << ")"; }
std::string getMethodStr() const { return StrPrinter << mappingMethodEnum2Str(_method) << "(" << (uint32_t)_method << ")"; }
std::string dumpString(bool transId = false) const;
const std::string& getTransactionId() const { return _transaction_id; }
void setUfrag(std::string ufrag) { _ufrag = std::move(ufrag); }
const std::string& getUfrag() const { return _ufrag; }
void setPassword(std::string password) { _password = std::move(password); }
const std::string& getPassword() const { return _password; }
void setPeerUfrag(std::string peer_ufrag) { _peer_ufrag = std::move(peer_ufrag); }
const std::string& getPeerUfrag() const { return _peer_ufrag; }
void setPeerPassword(std::string peer_password) { _peer_password = std::move(peer_password); }
const std::string& getPeerPassword() const { return _peer_password; }
void setNeedMessageIntegrity(bool flag) { _need_message_integrity = flag; }
bool getNeedMessageIntegrity() const { return _need_message_integrity; }
void setNeedFingerprint(bool flag) { _need_fingerprint = flag; }
bool getNeedFingerprint() const { return _need_fingerprint; }
void refreshTransactionId() { _transaction_id = toolkit::makeRandStr(12, false); }
void addAttribute(StunAttribute::Ptr attr);
void removeAttribute(StunAttribute::Type type);
bool hasAttribute(StunAttribute::Type type) const;
StunAttribute::Ptr getAttribute(StunAttribute::Type type) const;
template <typename T>
std::shared_ptr<T> getAttribute() const {
auto attr = getAttribute(T::TYPE);
if (attr) {
return std::dynamic_pointer_cast<T>(attr);
}
return nullptr;
}
std::string getUsername() const;
uint64_t getPriority() const;
StunAttrErrorCode::Code getErrorCode() const;
Authentication checkAuthentication(const std::string &ufrag, const std::string &password) const;
void serialize();
StunPacket::Ptr createSuccessResponse() const;
StunPacket::Ptr createErrorResponse(StunAttrErrorCode::Code errorCode) const;
///////Buffer override///////
char *data() const override;
size_t size() const override;
private:
bool loadFromData(const uint8_t *buf, size_t len);
// attribute
bool loadAttrMessage(const uint8_t *buf, size_t len);
bool storeAttrMessage();
size_t getAttrSize() const;
protected:
Class _klass;
Method _method;
std::string _transaction_id; // 12 bytes/96bits.
std::map<StunAttribute::Type, StunAttribute::Ptr> _attribute_map;
toolkit::BufferRaw::Ptr _data;
std::string _ufrag;
std::string _password;
std::string _peer_ufrag;
std::string _peer_password;
size_t _message_integrity_data_len = 0; //MESSAGE_INTEGRITY属性之前的字段
bool _need_message_integrity = true;
bool _need_fingerprint = true;
};
class BindingPacket : public StunPacket {
public:
BindingPacket() : StunPacket(Class::REQUEST, Method::BINDING) {};
virtual ~BindingPacket() {};
};
class SuccessResponsePacket : public StunPacket {
public:
SuccessResponsePacket(Method method, const std::string& transaction_id);
virtual ~SuccessResponsePacket() {};
};
class ErrorResponsePacket : public StunPacket {
public:
ErrorResponsePacket(Method method, const std::string& transaction_id, StunAttrErrorCode::Code error_code);
virtual ~ErrorResponsePacket() {};
};
//////////// TURN //////////////////////////
class TurnPacket : public StunPacket {
public:
TurnPacket(Class klass, Method method) : StunPacket(klass, method) {}
virtual ~TurnPacket() {};
};
class AllocatePacket : public TurnPacket {
public:
AllocatePacket() : TurnPacket(Class::REQUEST, Method::ALLOCATE) {};
virtual ~AllocatePacket() {};
};
class RefreshPacket : public TurnPacket {
public:
RefreshPacket() : TurnPacket(Class::REQUEST, Method::REFRESH) {};
virtual ~RefreshPacket() {};
};
class CreatePermissionPacket : public TurnPacket {
public:
CreatePermissionPacket() : TurnPacket(Class::REQUEST, Method::CREATEPERMISSION) {};
virtual ~CreatePermissionPacket() {};
};
class ChannelBindPacket : public TurnPacket {
public:
ChannelBindPacket() : TurnPacket(Class::REQUEST, Method::CHANNELBIND) {};
virtual ~ChannelBindPacket() {};
};
class SendIndicationPacket : public TurnPacket {
public:
SendIndicationPacket() : TurnPacket(Class::INDICATION, Method::SEND) {};
virtual ~SendIndicationPacket() {};
};
class DataIndicationPacket : public TurnPacket {
public:
DataIndicationPacket() : TurnPacket(Class::INDICATION, Method::DATA) {};
virtual ~DataIndicationPacket() {};
};
class DataPacket : public TurnPacket {
public:
DataPacket() : TurnPacket(Class::INDICATION, Method::DATA) {};
virtual ~DataPacket() {};
};
} // namespace RTC
#endif