首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >排查rtmp协议推流时握手bug

排查rtmp协议推流时握手bug

作者头像
职场亮哥
发布于 2020-10-10 07:33:49
发布于 2020-10-10 07:33:49
2K00
代码可运行
举报
文章被收录于专栏:职场亮哥职场亮哥
运行总次数:0
代码可运行

概况

转推流程序的过程:从一个观看地址拉流,然后推流到另一个推流地址。主要用于cdn之间转推,目前市面上大多数cdn厂商都愿意不支持动态转推,因此只能通过转推流程序进行转推。

bug现象:使用obs studio推流到微赞可以成功,但是使用Erlang版本的转推流程序推流到微赞却失败。

日志如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
14:12:35.926 [debug] payload [{amf,["onBWDone",0,null]}], msgtype[command_msg_4_amf0] 
14:12:35.949 [debug] play succ ======> url ["/live-sz/w1520993434573948"] 
14:12:35.949 [debug] {rtmp_msg,4,0,data_msg_4_amf0,1,{amf,["|RtmpSampleAccess",true,true]}}
14:12:35.949 [debug] {rtmp_msg,4,0,data_msg_4_amf0,1,{amf,["onStatus",{object,[{"code","NetStream.Data.Start"}]}]}}
14:12:36.038 [error] gen_server <0.122.0> terminated with reason: no match of right hand value <<0,0,0,0,0,0,0,0,113,142,194,240,185,25,41,180,242,33,5,112,128,97,178,8,79,179,28,53,152,242,82,43,234,104,113,246,170,189,182,146,122,36,155,3,152,180,226,122,36,97,52,67,53,158,107,170,178,119,209,132,40,233,102,182,142,233,218,71,55,8,121,67,117,58,130,91,107,224,202,5,1,132,37,245,143,231,20,198,121,204,57,80,102,165,104,245,79,71,254,169,15,3,166,12,148,45,24,62,253,66,93,139,84,139,54,236,47,5,98,95,51,231,222,144,8,153,232,166,227,151,57,98,214,63,238,167,212,49,51,160,83,248,246,199,...>> in rtmp_handshake:create_c2/2 line 61

很显然是rtmp_handshake:create_c2/2函数出现匹配错误,对应代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-spec create_c2(C0C1, S0S1S2) -> Result when
          C0C1 :: iodata(),
          S0S1S2 :: binary(),
          Result :: {ok, C2},
          C2 :: iolist().
create_c2(C0C1, S0S1S2) when is_list(C0C1) ->
    create_c2(iolist_to_binary(C0C1), S0S1S2);
create_c2(<<_C0:1/binary, C1:16#600/binary>>, <<S0:1/binary, S1:16#600/binary, S2:16#600/binary>>) ->
    <<3>> = S0,
    case C1 of
        <<_:32, 0:32, _/binary>> ->
            S2 = C1,
            {ok, S1};
        _ ->
            {ok, S1DigestData} = verify_s1(S1),    
            DigestKey = crypto:hmac(sha256, ?C2_PUBLIC_KEY, S1DigestData),
            C2Len = 16#600,
            C2DigestDataLen = 32,
            RandomBin = random_binary(C2Len - 8 - C2DigestDataLen),
            {T1, T2, _} = now(),
            Epoch = <<(1000000 * T1 + T2):32/little>>,
            Data = [Epoch, binary:part(S1, 0, 4), RandomBin],
            S2DigestData = crypto:hmac(sha256, DigestKey, Data),
            {ok, [Data, S2DigestData]}
    end.

rtmp握手过程中C1数据包匹配<<_:32, 0:32, _/binary>>格式后和S2数据包匹配不成功,程序直接crash dump。因此需要弄清楚rtmp握手过程中是否有对S2和C1进行匹配验证。

Rtmp握手过程

此处重点关注rtmp握手过程中的C1和S2数据包。

先看官方文档中的握手过程,中文翻译版本可以参见:rtmp规范1.0。 官方文档中对于是否要保证C1和S2完全一致,并没有明确说法。因此可以先参见obs studio依赖的librtmp库,看握手过程是如何处理的。obs studio依赖的librtmp的代码如下连接:

  1. https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/librtmp/rtmp.c
  2. https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/librtmp/handshake.h

第一个链接rtmp.c中的代码是推流地址中没有加密串的情况下的握手过程代码,第二个链接handshake.h中的代码是推流地址中有加密串的情况下的握手过程代码。代码中使用条件编译CRYPTO宏来选择编译不同的代码。其中HandShake函数属于客户端的握手函数,SHandShake属于服务端的握手函数。非加密版本具体C语言代码如下(已添加对应的中文注释进行说明):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef CRYPTO
static int
HandShake(RTMP *r, int FP9HandShake)
{
    //C0,C1 -- S0, S1, S2 -- C2 消息握手协议
    int i;
    uint32_t uptime, suptime;
    int bMatch;
    char type;
    char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
    char serversig[RTMP_SIG_SIZE];

    clientbuf[0] = 0x03;  //C0, 一个字节。03代表协议版本号为3 		/* not encrypted */

    uptime = htonl(RTMP_GetTime());  //这是一个时间戳,放在C1消息头部  
    memcpy(clientsig, &uptime, 4);

    memset(&clientsig[4], 0, 4);  //后面放4个字节的空数据然后就是随机数据  

    //后面是随机数据,总共1536字节的C1消息 
#ifdef _DEBUG
    for (i = 8; i < RTMP_SIG_SIZE; i++)
        clientsig[i] = 0xff;
#else
    for (i = 8; i < RTMP_SIG_SIZE; i++)
        clientsig[i] = (char)(rand() % 256);
#endif

    //发送C0, C1消息  
    if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1))
        return FALSE;

    //下面读一个字节也就是S0消息,看协议是否一样  
    if (ReadN(r, &type, 1) != 1)	/* 0x03 or 0x06 */
        return FALSE;

    RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer   : %02X", __FUNCTION__, type);

    if (type != clientbuf[0])  //C/S版本不一致 
        RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d",
                 __FUNCTION__, clientbuf[0], type);

    //读取S1消息,里面有服务器运行时间  
    if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
        return FALSE;

    /* decode server response */

    memcpy(&suptime, serversig, 4);
    suptime = ntohl(suptime);

    RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime);
    RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__,
             serversig[4], serversig[5], serversig[6], serversig[7]);

    /* 2nd part of handshake */
    if (!WriteN(r, serversig, RTMP_SIG_SIZE))  //发送C2消息,内容就等于S1消息的内容。  
        return FALSE;

    //读取S2消息  
    if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
        return FALSE;

    //服务端返回的S2消息和C1消息进行了比对,但是即使没有match,也是返回TRUE,只是打印了log 
    bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
    if (!bMatch)
    {
        RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);
    }

    /* er, totally unused? */
    (void)FP9HandShake;
    return TRUE;
}

static int
SHandShake(RTMP *r)
{
    ...
    return TRUE;
}
#endif

rtmp握手过程中确实存在对S2和C1进行匹配验证的操作,但是这个操作并不影响握手是否是成功的,只是添加了一条warnning日志而已。因此obs studio还是能推流成功。相对应的在我们的转推流程序中,需要针对这个情况不进行强认证,删除掉匹配的操作即可。

抓包分析

以微赞和网宿为例

网宿推流没有走加密流程,S2和C1匹配,具体数据包截图如下:

微赞推流走加密流程,S2和C1不匹配,具体数据包截图如下:

到此,整个rtmp推流握手过程就比较清楚了。

因此只需要将Erlang代码的流程更改下即可(删除S2和C1的匹配过程),见下面的Erlang代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-spec create_c2(C0C1, S0S1S2) -> Result when
          C0C1 :: iodata(),
          S0S1S2 :: binary(),
          Result :: {ok, C2},
          C2 :: iolist().
create_c2(C0C1, S0S1S2) when is_list(C0C1) ->
    create_c2(iolist_to_binary(C0C1), S0S1S2);
create_c2(<<_C0:1/binary, C1:16#600/binary>>, <<S0:1/binary, S1:16#600/binary, _S2:16#600/binary>>) ->
    <<3>> = S0,
    case C1 of
        <<_:32, 0:32, _/binary>> ->
          {ok, S1};
        _ ->
            {ok, S1DigestData} = verify_s1(S1),    
            DigestKey = crypto:hmac(sha256, ?C2_PUBLIC_KEY, S1DigestData),
            C2Len = 16#600,
            C2DigestDataLen = 32,
            RandomBin = random_binary(C2Len - 8 - C2DigestDataLen),
            {T1, T2, _} = now(),
            Epoch = <<(1000000 * T1 + T2):32/little>>,
            Data = [Epoch, binary:part(S1, 0, 4), RandomBin],
            S2DigestData = crypto:hmac(sha256, DigestKey, Data),
            {ok, [Data, S2DigestData]}
    end.

至此,转推流成功,示例图如下:

结论

虽然Adobe公司自己出的rtmp协议不是iso标准的,但是你们这些公司好歹也尽量按照规定来啊,贼坑。

参考:

  • obs-studio: https://github.com/obsproject/obs-studio
  • obs studio握手:https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/librtmp/handshake.h
  • RTMPdump(libRTMP)握手源代码:https://blog.csdn.net/leixiaohua1020/article/details/12954329
  • rtmp协议过程分析:https://www.cnblogs.com/lidabo/p/7355262.html
  • librtmp使用示例: https://github.com/leixiaohua1020/simplest_librtmp_example
  • rtmplib rtmp协议过程分析:https://www.cnblogs.com/lidabo/p/7355262.html
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
玩转直播系列之RTMP协议和源码解析(2)
实时消息传输协议(Real-Time Messaging Protocol)是目前直播的主要协议,是Adobe公司为Flash播放器和服务器之间提供音视频数据传输服务而设计的应用层私有协议。RTMP协议是目前各大云厂商直线直播业务所公用的基本直播推拉流协议,随着国内直播行业的发展和5G时代的到来,对RTMP协议有基本的了解,也是我们程序员必须要掌握的基本技能。
2020labs小助手
2021/05/17
1.9K0
RTMP播放流媒体过程
服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。他们的关系如图所示:
音视频_李超
2020/04/02
1.7K0
视频直播源码开发中的流媒体协议:rtmp协议
视频直播源码的RTMP协议从属于应用层,被设计用来在适合的传输协议(如TCP)上复用和打包多媒体传输流(如音频、视频和互动内容)。RTMP提供了一套全双工的可靠的多路复用消息服务,类似于TCP协议[RFC0793],用来在一对结点之间并行传输带时间戳的音频流,视频流,数据流。通常情况下,不同类型的消息会被分配不同的优先级,当网络传输能力受限时,优先级用来控制消息在网络底层的排队顺序。
云豹kj的晨曦
2020/09/03
7650
实时消息传输协议(RTMP)详解
概述 概念:RTMP协议从属于应用层,被设计用来在适合的传输协议(如TCP)上复用和打包多媒体传输流(如音频、视频和互动内容)。RTMP提供了一套全双工的可靠的多路复用消息服务,类似于TCP协议[RFC0793],用来在一对结点之间并行传输带时间戳的音频流,视频流,数据流。通常情况下,不同类型的消息会被分配不同的优先级,当网络传输能力受限时,优先级用来控制消息在网络底层的排队顺序。 RTMP块流 实时消息传递协议块流(RTMP块流)。RTMP块流作为一款高级多媒体流协议提供了流的多路复用和打包服务。RTMP
xiangzhihong
2018/02/06
13.1K0
实时消息传输协议(RTMP)详解
视频协议学习:推流拉流都擅长的 RTMP
腾讯云开发者社区
2017/05/03
10.3K0
视频协议学习:推流拉流都擅长的 RTMP
手撕Rtmp协议细节(1)——握手
之前文章,我们介绍过基于rtmp的直播环境的搭建,接下来,我们一起来学习一下Rtmp协议的细节,由于协议本身比较琐碎,小编会将rtmp协议拆解为一个个的小的模块,通过本公号推送rtmp协议的系列文章,欢迎诸位关注,本篇首先来看看rtmp协议握手的部分。
视界音你而不同
2020/05/20
3.8K2
RTMP 协议:为什么直播推流协议都爱用它?丨音视频基础
(本文基本逻辑:RTMP 协议的数据传输流程和协议设计思想 → 消息概念具体细节 → 块概念具体细节)
关键帧
2022/06/13
3.1K0
RTMP 协议:为什么直播推流协议都爱用它?丨音视频基础
RTMP的工作原理
 点击上方“LiveVideoStack”关注我们 翻译:Alex 技术审校:章琦 本文来自OTTVerse,作者为Krishna Rao Vijayanagar。 ▲扫描图中二维码或点击阅读原文▲ 了解音视频技术大会更多信息 RTMP Easy-Tech #028# 什么是RTMP? RTMP(Real-Time Messaging Protocol,实时消息传输协议)是一种用于低延迟、实时音视频和数据传输的双向互联网通信协议,由Macromedia(后被Adobe收购)开发。RTMP的工作原理
LiveVideoStack
2022/05/30
1.5K0
RTMP的工作原理
rtmp协议详解_rtmp服务器
最近在学习rtmp协议,在看官方文档的时候总是懵懵懂懂,硬生生看了两天,现在基本上了解rtmp协议了,想用自己觉得比较清晰的方式来讲解rtmp协议,希望能够对向我一样的初学者有所帮助。
全栈程序员站长
2022/11/01
3.2K0
RTMP协议
RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。
233333
2022/05/10
1.3K0
RTMP协议
视频互联网直播/点播流媒体服务器RTMP协议分析及推流过程
RTMP(实时消息传输协议)是Adobe 公司开发的一个基于TCP的应用层协议。RTMP协议中基本的数据单元称为消息(Message)。当RTMP协议在互联网中传输数据的时候,消息会被拆分成更小的单元,称为消息块(Chunk)。我们视频直播点播流媒体服务器支持RTMP协议流的输出。
EasyNVR
2020/05/19
5450
RTMP协议详解及Wiresahrk抓包分析
本文主要讲解 RTMP 协议,并通过 wireshark 对 RTMP 进行抓包并分析。
Gnep@97
2023/11/08
6K0
RTMP协议详解及Wiresahrk抓包分析
rtmp规范1.0
本文为Adobe rtmp规范1.0的中文介绍,其中内容大部分都是翻译自rtmp官方文档rtmp_specification_1.0.pdf
职场亮哥
2020/10/10
1.7K0
rtmp规范1.0
一篇文章搞清楚直播协议RTMP
说起RTMP协议,相信很多人都比较陌生,这个协议相对HTTP、HTTPS、TCP等我们常见的协议而言,我们在工作中确实较少接触它,但是对现在如火如荼的直播行业,RTMP是一个重要的协议,它在实时音视频场景中使用非常广泛,而且目前市占率很高。
马上就说
2020/12/11
1.4K0
一篇文章搞清楚直播协议RTMP
数据抓包工具:看看竞品的协议都做了哪些优化丨音视频工具
数据抓包是我们做业务测试、竞品分析的常用方法,在直播、短视频等常见的音视频业务场景能有好的数据抓包工具帮助,很多时候也能事半功倍。这里我们就介绍两款常见的数据抓包工具:
关键帧
2022/06/13
9360
数据抓包工具:看看竞品的协议都做了哪些优化丨音视频工具
FFmpeg开发笔记(二十三)使用OBS Studio开启RTMP直播推流
​OBS是一个开源的直播录制软件,英文全称叫做Open Broadcaster Software,广泛用于视频录制、实时直播等领域。OBS不但开源,而且跨平台,兼容Windows、Mac OS、Linux等操作系统。
aqi00
2024/05/26
1.3K0
FFmpeg开发笔记(二十三)使用OBS Studio开启RTMP直播推流
实时消息传输协议 RTMP(Real Time Messaging Protocol)
http://blog.csdn.net/defonds/article/details/17403225
bear_fish
2018/09/20
2.8K0
实时消息传输协议 RTMP(Real Time Messaging Protocol)
Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)
代码地址 :https://github.com/deepsadness/MediaProjectionDemo
deep_sadness
2018/12/10
3K0
Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)
视频直播软件开发,直播软件开发中的常见协议有哪些
1、RTMP(Real RTMP(real time messaging protocol)实时消息传输协议 RTMP 给予TCP协议 是一个协议族 包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种 RTMP 内部使用的格式为 FLV
布谷鸟小刘
2020/12/16
2.3K0
视频直播软件开发,直播软件开发中的常见协议有哪些
三款RTMP推流模块比较:OBS VS SmartPublisher VS Flash Media Live Encoder
功能强大,几乎所有你想要的场景它都有,用起来很顺手。可以将桌面、摄像头、程序窗口通过rtmp推送到流媒体服务器上。
音视频牛哥
2021/03/25
2.2K0
推荐阅读
相关推荐
玩转直播系列之RTMP协议和源码解析(2)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档