mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-01-12 00:04:03 +08:00
- 增加客户端模式,支持主动拉流、推流: - 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>
690 lines
25 KiB
C++
690 lines
25 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef ZLMEDIAKIT_WEBRTC_STUN_PACKET_HPP
|
|
#define ZLMEDIAKIT_WEBRTC_STUN_PACKET_HPP
|
|
|
|
#include <string>
|
|
#include "Util/Byte.hpp"
|
|
#include "Network/Buffer.h"
|
|
#include "Network/sockutil.h"
|
|
|
|
namespace RTC {
|
|
// reference https://rcf-editor.org/rfc/rfc8489
|
|
// reference https://rcf-editor.org/rfc/rfc8656
|
|
// reference https://rcf-editor.org/rfc/rfc8445
|
|
|
|
//////////// 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,
|
|
|
|
//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
|