mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-07-07 12:18:12 +08:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user