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
May 16, 2017
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
,
...
...
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
();
...
...
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
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论