Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
2d71e5d3
Commit
2d71e5d3
authored
7 years ago
by
xiongziliang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
支持rtmp complex handshark
parent
acc6106f
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
214 行增加
和
16 行删除
+214
-16
src/Rtmp/Rtmp.h
+0
-1
src/Rtmp/RtmpProtocol.cpp
+206
-15
src/Rtmp/RtmpProtocol.h
+8
-0
没有找到文件。
src/Rtmp/Rtmp.h
查看文件 @
2d71e5d3
...
...
@@ -65,7 +65,6 @@ public:
uint8_t
timeStamp
[
4
];
uint8_t
zero
[
4
]
=
{
0
};
uint8_t
random
[
RANDOM_LEN
];
private
:
void
random_generate
(
char
*
bytes
,
int
size
)
{
static
char
cdata
[]
=
{
0x73
,
0x69
,
0x6d
,
0x70
,
0x6c
,
0x65
,
0x2d
,
0x72
,
0x74
,
0x6d
,
0x70
,
0x2d
,
0x73
,
0x65
,
0x72
,
0x76
,
0x65
,
0x72
,
...
...
This diff is collapsed.
Click to expand it.
src/Rtmp/RtmpProtocol.cpp
查看文件 @
2d71e5d3
...
...
@@ -11,9 +11,34 @@
#include "Util/util.h"
#include "Util/onceToken.h"
#include "Thread/ThreadPool.h"
using
namespace
ZL
::
Util
;
#ifdef ENABLE_OPENSSL
#include <openssl/hmac.h>
static
string
openssl_HMACsha256
(
const
void
*
key
,
unsigned
int
key_len
,
const
void
*
data
,
unsigned
int
data_len
){
char
out
[
48
];
unsigned
int
out_len
;
HMAC_CTX
ctx
;
HMAC_CTX_init
(
&
ctx
);
HMAC_Init_ex
(
&
ctx
,
key
,
key_len
,
EVP_sha256
(),
NULL
);
HMAC_Update
(
&
ctx
,
(
unsigned
char
*
)
data
,
data_len
);
HMAC_Final
(
&
ctx
,
(
unsigned
char
*
)
out
,
&
out_len
);
HMAC_CTX_cleanup
(
&
ctx
);
return
string
(
out
,
out_len
);
}
#endif //ENABLE_OPENSSL
#define C1_DIGEST_SIZE 32
#define C1_KEY_SIZE 128
#define C1_SCHEMA_SIZE 764
#define C1_HANDSHARK_SIZE (RANDOM_LEN + 8)
#define C1_FPKEY_SIZE 30
#define S1_FMS_KEY_SIZE 36
#define S2_FMS_KEY_SIZE 68
#define C1_OFFSET_SIZE 4
namespace
ZL
{
namespace
Rtmp
{
...
...
@@ -146,7 +171,7 @@ void RtmpProtocol::sendRtmp(uint8_t ui8Type, uint32_t ui32StreamId,
set_be24
(
header
.
bodySize
,
strBuf
.
size
());
set_le32
(
header
.
streamId
,
ui32StreamId
);
std
::
string
strSend
;
strSend
.
append
((
char
*
)
&
header
,
sizeof
header
);
strSend
.
append
((
char
*
)
&
header
,
sizeof
(
header
)
);
char
acExtStamp
[
4
];
if
(
bExtStamp
)
{
//扩展时间戳
...
...
@@ -185,15 +210,15 @@ void RtmpProtocol::startClientSession(const function<void()> &callBack) {
//发送 C0C1
char
handshake_head
=
HANDSHAKE_PLAINTEXT
;
onSendRawData
(
&
handshake_head
,
1
);
RtmpHandshake
c
0c
1
(
::
time
(
NULL
));
onSendRawData
((
char
*
)
(
&
c
0c1
),
sizeof
(
RtmpHandshake
));
RtmpHandshake
c1
(
::
time
(
NULL
));
onSendRawData
((
char
*
)
(
&
c
1
),
sizeof
(
c1
));
m_nextHandle
=
[
this
,
callBack
]()
{
//等待 S0+S1+S2
handle_S0S1S2
(
callBack
);
};
}
void
RtmpProtocol
::
handle_S0S1S2
(
const
function
<
void
()
>
&
callBack
)
{
if
(
m_strRcvBuf
.
size
()
<
1
+
2
*
sizeof
(
RtmpHandshake
)
)
{
if
(
m_strRcvBuf
.
size
()
<
1
+
2
*
C1_HANDSHARK_SIZE
)
{
//数据不够
return
;
}
...
...
@@ -202,8 +227,8 @@ void RtmpProtocol::handle_S0S1S2(const function<void()> &callBack) {
}
//发送 C2
const
char
*
pcC2
=
m_strRcvBuf
.
data
()
+
1
;
onSendRawData
(
pcC2
,
sizeof
(
RtmpHandshake
)
);
m_strRcvBuf
.
erase
(
0
,
1
+
2
*
sizeof
(
RtmpHandshake
)
);
onSendRawData
(
pcC2
,
C1_HANDSHARK_SIZE
);
m_strRcvBuf
.
erase
(
0
,
1
+
2
*
C1_HANDSHARK_SIZE
);
//握手结束
m_nextHandle
=
[
this
]()
{
//握手结束并且开始进入解析命令模式
...
...
@@ -213,34 +238,200 @@ void RtmpProtocol::handle_S0S1S2(const function<void()> &callBack) {
}
////for server ////
void
RtmpProtocol
::
handle_C0C1
()
{
if
(
m_strRcvBuf
.
size
()
<
1
+
sizeof
(
RtmpHandshake
)
)
{
if
(
m_strRcvBuf
.
size
()
<
1
+
C1_HANDSHARK_SIZE
)
{
//need more data!
return
;
}
if
(
m_strRcvBuf
[
0
]
!=
HANDSHAKE_PLAINTEXT
)
{
throw
std
::
runtime_error
(
"only plaintext[0x03] handshake supported"
);
}
char
handshake_head
=
HANDSHAKE_PLAINTEXT
;
if
(
memcmp
(
m_strRcvBuf
.
c_str
()
+
5
,
"
\x00\x00\x00\x00
"
,
4
)
==
0
){
//simple handsharke
handle_C1_simple
();
}
else
{
#ifdef ENABLE_OPENSSL
//complex handsharke
handle_C1_complex
();
#else
WarnL
<<
"未打开ENABLE_OPENSSL宏,复杂握手采用简单方式处理!"
;
handle_C1_simple
();
#endif//ENABLE_OPENSSL
}
m_strRcvBuf
.
erase
(
0
,
1
+
C1_HANDSHARK_SIZE
);
}
void
RtmpProtocol
::
handle_C1_simple
(){
//发送S0
char
handshake_head
=
HANDSHAKE_PLAINTEXT
;
onSendRawData
(
&
handshake_head
,
1
);
//发送S1
RtmpHandshake
s
2
(
0
);
onSendRawData
((
char
*
)
&
s
2
,
sizeof
(
RtmpHandshake
)
);
RtmpHandshake
s
1
(
0
);
onSendRawData
((
char
*
)
&
s
1
,
C1_HANDSHARK_SIZE
);
//发送S2
onSendRawData
(
m_strRcvBuf
.
c_str
()
+
1
,
sizeof
(
RtmpHandshake
));
m_strRcvBuf
.
erase
(
0
,
1
+
sizeof
(
RtmpHandshake
));
onSendRawData
(
m_strRcvBuf
.
c_str
()
+
1
,
C1_HANDSHARK_SIZE
);
//等待C2
m_nextHandle
=
[
this
]()
{
handle_C2
();
};
}
void
RtmpProtocol
::
handle_C1_complex
(){
//参考自:http://blog.csdn.net/win_lin/article/details/13006803
//skip c0,time,version
const
char
*
c1_start
=
m_strRcvBuf
.
data
()
+
1
;
const
char
*
schema_start
=
c1_start
+
8
;
char
*
digest_start
;
try
{
/* c1s1 schema0
time: 4bytes
version: 4bytes
key: 764bytes
digest: 764bytes
*/
auto
digest
=
get_C1_digest
((
uint8_t
*
)
schema_start
+
C1_SCHEMA_SIZE
,
&
digest_start
);
string
c1_joined
(
c1_start
,
C1_HANDSHARK_SIZE
);
c1_joined
.
erase
(
digest_start
-
c1_start
,
C1_DIGEST_SIZE
);
check_C1_Digest
(
digest
,
c1_joined
);
send_complex_S0S1S2
(
0
,
digest
);
InfoL
<<
"schema0"
;
}
catch
(
std
::
exception
&
ex
){
//貌似flash从来都不用schema1
WarnL
<<
"try rtmp complex schema0 failed:"
<<
ex
.
what
();
try
{
/* c1s1 schema1
time: 4bytes
version: 4bytes
digest: 764bytes
key: 764bytes
*/
auto
digest
=
get_C1_digest
((
uint8_t
*
)
schema_start
,
&
digest_start
);
string
c1_joined
(
c1_start
,
C1_HANDSHARK_SIZE
);
c1_joined
.
erase
(
digest_start
-
c1_start
,
C1_DIGEST_SIZE
);
check_C1_Digest
(
digest
,
c1_joined
);
send_complex_S0S1S2
(
1
,
digest
);
InfoL
<<
"schema1"
;
}
catch
(
std
::
exception
&
ex
){
WarnL
<<
"try rtmp complex schema1 failed:"
<<
ex
.
what
();
handle_C1_simple
();
}
}
}
static
u_int8_t
FMSKey
[]
=
{
0x47
,
0x65
,
0x6e
,
0x75
,
0x69
,
0x6e
,
0x65
,
0x20
,
0x41
,
0x64
,
0x6f
,
0x62
,
0x65
,
0x20
,
0x46
,
0x6c
,
0x61
,
0x73
,
0x68
,
0x20
,
0x4d
,
0x65
,
0x64
,
0x69
,
0x61
,
0x20
,
0x53
,
0x65
,
0x72
,
0x76
,
0x65
,
0x72
,
0x20
,
0x30
,
0x30
,
0x31
,
// Genuine Adobe Flash Media Server 001
0xf0
,
0xee
,
0xc2
,
0x4a
,
0x80
,
0x68
,
0xbe
,
0xe8
,
0x2e
,
0x00
,
0xd0
,
0xd1
,
0x02
,
0x9e
,
0x7e
,
0x57
,
0x6e
,
0xec
,
0x5d
,
0x2d
,
0x29
,
0x80
,
0x6f
,
0xab
,
0x93
,
0xb8
,
0xe6
,
0x36
,
0xcf
,
0xeb
,
0x31
,
0xae
};
// 68
static
u_int8_t
FPKey
[]
=
{
0x47
,
0x65
,
0x6E
,
0x75
,
0x69
,
0x6E
,
0x65
,
0x20
,
0x41
,
0x64
,
0x6F
,
0x62
,
0x65
,
0x20
,
0x46
,
0x6C
,
0x61
,
0x73
,
0x68
,
0x20
,
0x50
,
0x6C
,
0x61
,
0x79
,
0x65
,
0x72
,
0x20
,
0x30
,
0x30
,
0x31
,
// Genuine Adobe Flash Player 001
0xF0
,
0xEE
,
0xC2
,
0x4A
,
0x80
,
0x68
,
0xBE
,
0xE8
,
0x2E
,
0x00
,
0xD0
,
0xD1
,
0x02
,
0x9E
,
0x7E
,
0x57
,
0x6E
,
0xEC
,
0x5D
,
0x2D
,
0x29
,
0x80
,
0x6F
,
0xAB
,
0x93
,
0xB8
,
0xE6
,
0x36
,
0xCF
,
0xEB
,
0x31
,
0xAE
};
// 62
void
RtmpProtocol
::
check_C1_Digest
(
const
string
&
digest
,
const
string
&
data
){
auto
sha256
=
openssl_HMACsha256
(
FPKey
,
C1_FPKEY_SIZE
,
data
.
data
(),
data
.
size
());
if
(
sha256
!=
digest
){
throw
std
::
runtime_error
(
"digest不匹配"
);
}
else
{
InfoL
<<
"check rtmp complex handshark success!"
;
}
}
string
RtmpProtocol
::
get_C1_digest
(
const
uint8_t
*
ptr
,
char
**
digestPos
){
/* 764bytes digest结构
offset: 4bytes
random-data: (offset)bytes
digest-data: 32bytes
random-data: (764-4-offset-32)bytes
*/
int
offset
=
0
;
for
(
int
i
=
0
;
i
<
C1_OFFSET_SIZE
;
++
i
){
offset
+=
ptr
[
i
];
}
offset
%=
(
C1_SCHEMA_SIZE
-
C1_DIGEST_SIZE
-
C1_OFFSET_SIZE
);
*
digestPos
=
(
char
*
)
ptr
+
C1_OFFSET_SIZE
+
offset
;
string
digest
(
*
digestPos
,
C1_DIGEST_SIZE
);
//DebugL << "digest offset:" << offset << ",digest:" << hexdump(digest.data(),digest.size());
return
digest
;
}
string
RtmpProtocol
::
get_C1_key
(
const
uint8_t
*
ptr
){
/* 764bytes key结构
random-data: (offset)bytes
key-data: 128bytes
random-data: (764-offset-128-4)bytes
offset: 4bytes
*/
int
offset
=
0
;
for
(
int
i
=
C1_SCHEMA_SIZE
-
C1_OFFSET_SIZE
;
i
<
C1_SCHEMA_SIZE
;
++
i
){
offset
+=
ptr
[
i
];
}
offset
%=
(
C1_SCHEMA_SIZE
-
C1_KEY_SIZE
-
C1_OFFSET_SIZE
);
string
key
((
char
*
)
ptr
+
offset
,
C1_KEY_SIZE
);
//DebugL << "key offset:" << offset << ",key:" << hexdump(key.data(),key.size());
return
key
;
}
void
RtmpProtocol
::
send_complex_S0S1S2
(
int
schemeType
,
const
string
&
digest
){
//S1S2计算参考自:https://github.com/hitYangfei/golang/blob/master/rtmpserver.go
//发送S0
char
handshake_head
=
HANDSHAKE_PLAINTEXT
;
onSendRawData
(
&
handshake_head
,
1
);
//S1
RtmpHandshake
s1
(
time
(
NULL
));
memcpy
(
s1
.
zero
,
"
\x04\x05\x00\x01
"
,
4
);
char
*
digestPos
;
if
(
schemeType
==
0
){
/* c1s1 schema0
time: 4bytes
version: 4bytes
key: 764bytes
digest: 764bytes
*/
get_C1_digest
(
s1
.
random
+
C1_SCHEMA_SIZE
,
&
digestPos
);
}
else
{
/* c1s1 schema1
time: 4bytes
version: 4bytes
digest: 764bytes
key: 764bytes
*/
get_C1_digest
(
s1
.
random
,
&
digestPos
);
}
char
*
s1_start
=
(
char
*
)
&
s1
;
string
s1_joined
(
s1_start
,
sizeof
(
s1
));
s1_joined
.
erase
(
digestPos
-
s1_start
,
C1_DIGEST_SIZE
);
string
s1_digest
=
openssl_HMACsha256
(
FMSKey
,
S1_FMS_KEY_SIZE
,
s1_joined
.
data
(),
s1_joined
.
size
());
memcpy
(
digestPos
,
s1_digest
.
data
(),
s1_digest
.
size
());
onSendRawData
((
char
*
)
&
s1
,
sizeof
(
s1
));
//S2
string
s2_key
=
openssl_HMACsha256
(
FMSKey
,
S2_FMS_KEY_SIZE
,
digest
.
data
(),
digest
.
size
());
RtmpHandshake
s2
(
0
);
s2
.
random_generate
((
char
*
)
&
s2
,
8
);
string
s2_digest
=
openssl_HMACsha256
(
s2_key
.
data
(),
s2_key
.
size
(),
&
s2
,
sizeof
(
s2
)
-
C1_DIGEST_SIZE
);
memcpy
((
char
*
)
&
s2
+
C1_HANDSHARK_SIZE
-
C1_DIGEST_SIZE
,
s2_digest
.
data
(),
C1_DIGEST_SIZE
);
onSendRawData
((
char
*
)
&
s2
,
sizeof
(
s2
));
//等待C2
m_nextHandle
=
[
this
]()
{
handle_C2
();
};
}
void
RtmpProtocol
::
handle_C2
()
{
if
(
m_strRcvBuf
.
size
()
<
sizeof
(
RtmpHandshake
)
)
{
if
(
m_strRcvBuf
.
size
()
<
C1_HANDSHARK_SIZE
)
{
//need more data!
return
;
}
m_strRcvBuf
.
erase
(
0
,
sizeof
(
RtmpHandshake
)
);
m_strRcvBuf
.
erase
(
0
,
C1_HANDSHARK_SIZE
);
//握手结束,进入命令模式
if
(
!
m_strRcvBuf
.
empty
())
{
handle_rtmp
();
...
...
This diff is collapsed.
Click to expand it.
src/Rtmp/RtmpProtocol.h
查看文件 @
2d71e5d3
...
...
@@ -67,6 +67,14 @@ protected:
private
:
void
handle_S0S1S2
(
const
function
<
void
()
>
&
cb
);
void
handle_C0C1
();
void
handle_C1_simple
();
void
handle_C1_complex
();
string
get_C1_digest
(
const
uint8_t
*
ptr
,
char
**
digestPos
);
string
get_C1_key
(
const
uint8_t
*
ptr
);
void
check_C1_Digest
(
const
string
&
digest
,
const
string
&
data
);
void
send_complex_S0S1S2
(
int
schemeType
,
const
string
&
digest
);
void
handle_C2
();
void
handle_rtmp
();
void
handle_rtmpChunk
(
RtmpPacket
&
chunkData
);
...
...
This diff is collapsed.
Click to expand it.
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论