diff mbox

[FFmpeg-devel] fix rtmp handshake for some streams [v2]

Message ID 20190829225421.97898-1-unique.will.martin@gmail.com
State New
Headers show

Commit Message

William Martin Aug. 29, 2019, 10:54 p.m. UTC
From: Will Martin <will.martin@verizondigitalmedia.com>

Some rtmp streamers (i.e. AWS Elemental Encoder, Wirecast) send C0 and C1 together and expect S0 and S1 returned together. When sent in different packets, this results in a C2 handshake. This patch fixes that error.
Note that the patch is based off of a fix proposed by rubensanchez in https://trac.ffmpeg.org/ticket/6453.
The only difference between that propsed fix and this patch is that dummy_unit is declared as a uint32_t instead of unit8_8 (this addresses a crash in debug builds).
This patch being submitted in a [v2] so that these commit messages could be added for clarity.
---
 libavformat/rtmpproto.c | 103 +++++++++++++++++++++++-----------------
 1 file changed, 59 insertions(+), 44 deletions(-)

Comments

William Martin Sept. 20, 2019, 10:11 p.m. UTC | #1
hello. I didn't see any comments about this patch - does that mean it is
ready to be merged?

On Thu, Aug 29, 2019 at 3:54 PM William Martin <unique.will.martin@gmail.com>
wrote:

> From: Will Martin <will.martin@verizondigitalmedia.com>
>
> Some rtmp streamers (i.e. AWS Elemental Encoder, Wirecast) send C0 and C1
> together and expect S0 and S1 returned together. When sent in different
> packets, this results in a C2 handshake. This patch fixes that error.
> Note that the patch is based off of a fix proposed by rubensanchez in
> https://trac.ffmpeg.org/ticket/6453.
> The only difference between that propsed fix and this patch is that
> dummy_unit is declared as a uint32_t instead of unit8_8 (this addresses a
> crash in debug builds).
> This patch being submitted in a [v2] so that these commit messages could
> be added for clarity.
> ---
>  libavformat/rtmpproto.c | 103 +++++++++++++++++++++++-----------------
>  1 file changed, 59 insertions(+), 44 deletions(-)
>
> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
> index b741e421af..24070ba0f5 100644
> --- a/libavformat/rtmpproto.c
> +++ b/libavformat/rtmpproto.c
> @@ -1416,71 +1416,86 @@ static int rtmp_send_hs_packet(RTMPContext* rt,
> uint32_t first_int,
>   */
>  static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
>  {
> -    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint32_t hs_epoch;
> +    uint8_t hs_s0s1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_c0c1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_c2[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_s2[RTMP_HANDSHAKE_PACKET_SIZE];
> +    uint32_t dummy_uint;
>      uint32_t hs_my_epoch;
> -    uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint32_t zeroes;
>      uint32_t temp       = 0;
>      int randomidx       = 0;
>      int inoutsize       = 0;
>      int ret;
>
> -    inoutsize = ffurl_read_complete(rt->stream, buffer, 1);       //
> Receive C0
> -    if (inoutsize <= 0) {
> -        av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
> -        return AVERROR(EIO);
> +    /****************
> +     * Receive C0+C1
> +     ***************/
> +    ret = rtmp_receive_hs_packet(rt, &dummy_uint, &dummy_uint, hs_c0c1,
> +                                 RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +    if (ret) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error %d\n", ret);
> +        return ret;
>      }
>      // Check Version
> -    if (buffer[0] != 3) {
> -        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
> +    if (hs_c0c1[0] != 3) {
> +        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch. Expected
> 0x03 received %02x\n", hs_c0c1[0]);
>          return AVERROR(EIO);
>      }
> -    if (ffurl_write(rt->stream, buffer, 1) <= 0) {                 //
> Send S0
> -        av_log(s, AV_LOG_ERROR,
> -               "Unable to write answer - RTMP S0\n");
> +    // Get client epoch and set our with the same value
> +    hs_my_epoch = AV_RB32(hs_c0c1 + 1);
> +
> +    /*************
> +     * Send S0+S1
> +     ************/
> +    // Generate random data to send it on S0+S1
> +    for (randomidx = 9; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +         randomidx += 4)
> +        AV_WB32(hs_s0s1 + randomidx, av_get_random_seed());
> +    // Set the RTMP protocol code on S0+S1 (First byte)
> +    hs_s0s1[0] = 0x03;
> +    // Copy the random data from C1 to S1
> +    memcpy(hs_s0s1 + 1, hs_c0c1 + 1, RTMP_HANDSHAKE_PACKET_SIZE);
> +    AV_WB32(hs_s0s1 + 1, hs_my_epoch);
> +    AV_WB32(hs_s0s1 + 5, 0);
> +    inoutsize = ffurl_write(rt->stream, hs_s0s1,
> +                            RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error %d\n", ret);
>          return AVERROR(EIO);
>      }
> -    /* Receive C1 */
> -    ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
> -                                 RTMP_HANDSHAKE_PACKET_SIZE);
> -    if (ret) {
> -        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
> -        return ret;
> -    }
> -    /* Send S1 */
> -    /* By now same epoch will be sent */
> -    hs_my_epoch = hs_epoch;
> -    /* Generate random */
> -    for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
> -         randomidx += 4)
> -        AV_WB32(hs_s1 + randomidx, av_get_random_seed());
>
> -    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
> -                              RTMP_HANDSHAKE_PACKET_SIZE);
> -    if (ret) {
> -        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
> -        return ret;
> -    }
> -    /* Send S2 */
> -    ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
> +    /***********
> +     * Send S2
> +     **********/
> +    // Get the S2 random data from C0+C1
> +    memcpy(hs_s2, hs_c0c1, RTMP_HANDSHAKE_PACKET_SIZE);
> +    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s2,
>                                RTMP_HANDSHAKE_PACKET_SIZE);
>      if (ret) {
>          av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
>          return ret;
>      }
> -    /* Receive C2 */
> -    ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
> -                                 RTMP_HANDSHAKE_PACKET_SIZE);
> -    if (ret) {
> -        av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
> -        return ret;
> +
> +    /*************
> +     * Receive C2
> +     ************/
> +
> +    ret = ffurl_read_complete(rt->stream, hs_c2,
> +                              RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +    if (ret <= 0)
> +        return AVERROR(EIO);
> +    if (ret != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
> +        av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
> +               " not following standard\n", (int)inoutsize);
> +        return AVERROR(EINVAL);
>      }
> +
> +    // Check timestamp and random data from C2
> +    temp  = AV_RB32(hs_c2 + 1);
>      if (temp != hs_my_epoch)
>          av_log(s, AV_LOG_WARNING,
> -               "Erroneous C2 Message epoch does not match up with C1
> epoch\n");
> -    if (memcmp(buffer + 8, hs_s1 + 8,
> +               "Erroneous C2 Message epoch does not match up with C1
> epoch");
> +    if (memcmp(hs_c2 + 9, hs_c0c1 + 9,
>                 RTMP_HANDSHAKE_PACKET_SIZE - 8))
>          av_log(s, AV_LOG_WARNING,
>                 "Erroneous C2 Message random does not match up\n");
> --
> 2.20.1 (Apple Git-117)
>
>
Michael Niedermayer Sept. 21, 2019, 9:01 p.m. UTC | #2
On Thu, Aug 29, 2019 at 03:54:21PM -0700, William Martin wrote:
> From: Will Martin <will.martin@verizondigitalmedia.com>
> 
> Some rtmp streamers (i.e. AWS Elemental Encoder, Wirecast) send C0 and C1 together and expect S0 and S1 returned together. When sent in different packets, this results in a C2 handshake. This patch fixes that error.
> Note that the patch is based off of a fix proposed by rubensanchez in https://trac.ffmpeg.org/ticket/6453.
> The only difference between that propsed fix and this patch is that dummy_unit is declared as a uint32_t instead of unit8_8 (this addresses a crash in debug builds).
> This patch being submitted in a [v2] so that these commit messages could be added for clarity.
> ---
>  libavformat/rtmpproto.c | 103 +++++++++++++++++++++++-----------------
>  1 file changed, 59 insertions(+), 44 deletions(-)
> 
> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
> index b741e421af..24070ba0f5 100644
> --- a/libavformat/rtmpproto.c
> +++ b/libavformat/rtmpproto.c
> @@ -1416,71 +1416,86 @@ static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
>   */
>  static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
>  {
> -    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint32_t hs_epoch;
> +    uint8_t hs_s0s1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_c0c1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_c2[RTMP_HANDSHAKE_PACKET_SIZE + 1];
> +    uint8_t hs_s2[RTMP_HANDSHAKE_PACKET_SIZE];
> +    uint32_t dummy_uint;
>      uint32_t hs_my_epoch;
> -    uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
> -    uint32_t zeroes;
>      uint32_t temp       = 0;
>      int randomidx       = 0;
>      int inoutsize       = 0;
>      int ret;
>  
> -    inoutsize = ffurl_read_complete(rt->stream, buffer, 1);       // Receive C0
> -    if (inoutsize <= 0) {
> -        av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
> -        return AVERROR(EIO);
> +    /****************
> +     * Receive C0+C1
> +     ***************/
> +    ret = rtmp_receive_hs_packet(rt, &dummy_uint, &dummy_uint, hs_c0c1,
> +                                 RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +    if (ret) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error %d\n", ret);
> +        return ret;
>      }
>      // Check Version
> -    if (buffer[0] != 3) {
> -        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
> +    if (hs_c0c1[0] != 3) {
> +        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch. Expected 0x03 received %02x\n", hs_c0c1[0]);
>          return AVERROR(EIO);
>      }
> -    if (ffurl_write(rt->stream, buffer, 1) <= 0) {                 // Send S0
> -        av_log(s, AV_LOG_ERROR,
> -               "Unable to write answer - RTMP S0\n");
> +    // Get client epoch and set our with the same value
> +    hs_my_epoch = AV_RB32(hs_c0c1 + 1);
> +
> +    /*************
> +     * Send S0+S1
> +     ************/
> +    // Generate random data to send it on S0+S1
> +    for (randomidx = 9; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +         randomidx += 4)
> +        AV_WB32(hs_s0s1 + randomidx, av_get_random_seed());
> +    // Set the RTMP protocol code on S0+S1 (First byte)
> +    hs_s0s1[0] = 0x03;
> +    // Copy the random data from C1 to S1
> +    memcpy(hs_s0s1 + 1, hs_c0c1 + 1, RTMP_HANDSHAKE_PACKET_SIZE);
> +    AV_WB32(hs_s0s1 + 1, hs_my_epoch);
> +    AV_WB32(hs_s0s1 + 5, 0);
> +    inoutsize = ffurl_write(rt->stream, hs_s0s1,
> +                            RTMP_HANDSHAKE_PACKET_SIZE + 1);
> +    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error %d\n", ret);
>          return AVERROR(EIO);
>      }
> -    /* Receive C1 */
> -    ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
> -                                 RTMP_HANDSHAKE_PACKET_SIZE);
> -    if (ret) {
> -        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
> -        return ret;
> -    }
> -    /* Send S1 */
> -    /* By now same epoch will be sent */
> -    hs_my_epoch = hs_epoch;
> -    /* Generate random */
> -    for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
> -         randomidx += 4)
> -        AV_WB32(hs_s1 + randomidx, av_get_random_seed());
>  
> -    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
> -                              RTMP_HANDSHAKE_PACKET_SIZE);
> -    if (ret) {
> -        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
> -        return ret;
> -    }

> -    /* Send S2 */
> +    /***********
> +     * Send S2
> +     **********/
> +    // Get the S2 random data from C0+C1
> +    memcpy(hs_s2, hs_c0c1, RTMP_HANDSHAKE_PACKET_SIZE);

> -    ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
> +    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s2,

>                                RTMP_HANDSHAKE_PACKET_SIZE);
>      if (ret) {
>          av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
>          return ret;
>      }

> -    /* Receive C2 */
> +
> +    /*************
> +     * Receive C2
> +     ************/

this patch is mixing cosmetics and functional changes and is quite
messy, its unlikely that this will be applied
and its not easy to review because of this as well

If you could try to make it look more tidy overall so its easy
to review then its more likely to receice usefull review comments

generally, if you need to rename, move or reformat something do it in 
a seperate patch. This should make this more readable

Thanks

[...]
diff mbox

Patch

diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
index b741e421af..24070ba0f5 100644
--- a/libavformat/rtmpproto.c
+++ b/libavformat/rtmpproto.c
@@ -1416,71 +1416,86 @@  static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
  */
 static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
 {
-    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
-    uint32_t hs_epoch;
+    uint8_t hs_s0s1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
+    uint8_t hs_c0c1[RTMP_HANDSHAKE_PACKET_SIZE + 1];
+    uint8_t hs_c2[RTMP_HANDSHAKE_PACKET_SIZE + 1];
+    uint8_t hs_s2[RTMP_HANDSHAKE_PACKET_SIZE];
+    uint32_t dummy_uint;
     uint32_t hs_my_epoch;
-    uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
-    uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
-    uint32_t zeroes;
     uint32_t temp       = 0;
     int randomidx       = 0;
     int inoutsize       = 0;
     int ret;
 
-    inoutsize = ffurl_read_complete(rt->stream, buffer, 1);       // Receive C0
-    if (inoutsize <= 0) {
-        av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
-        return AVERROR(EIO);
+    /****************
+     * Receive C0+C1
+     ***************/
+    ret = rtmp_receive_hs_packet(rt, &dummy_uint, &dummy_uint, hs_c0c1,
+                                 RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    if (ret) {
+        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error %d\n", ret);
+        return ret;
     }
     // Check Version
-    if (buffer[0] != 3) {
-        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
+    if (hs_c0c1[0] != 3) {
+        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch. Expected 0x03 received %02x\n", hs_c0c1[0]);
         return AVERROR(EIO);
     }
-    if (ffurl_write(rt->stream, buffer, 1) <= 0) {                 // Send S0
-        av_log(s, AV_LOG_ERROR,
-               "Unable to write answer - RTMP S0\n");
+    // Get client epoch and set our with the same value
+    hs_my_epoch = AV_RB32(hs_c0c1 + 1);
+
+    /*************
+     * Send S0+S1
+     ************/
+    // Generate random data to send it on S0+S1
+    for (randomidx = 9; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE + 1);
+         randomidx += 4)
+        AV_WB32(hs_s0s1 + randomidx, av_get_random_seed());
+    // Set the RTMP protocol code on S0+S1 (First byte)
+    hs_s0s1[0] = 0x03;
+    // Copy the random data from C1 to S1
+    memcpy(hs_s0s1 + 1, hs_c0c1 + 1, RTMP_HANDSHAKE_PACKET_SIZE);
+    AV_WB32(hs_s0s1 + 1, hs_my_epoch);
+    AV_WB32(hs_s0s1 + 5, 0);
+    inoutsize = ffurl_write(rt->stream, hs_s0s1,
+                            RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
+        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error %d\n", ret);
         return AVERROR(EIO);
     }
-    /* Receive C1 */
-    ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
-                                 RTMP_HANDSHAKE_PACKET_SIZE);
-    if (ret) {
-        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
-        return ret;
-    }
-    /* Send S1 */
-    /* By now same epoch will be sent */
-    hs_my_epoch = hs_epoch;
-    /* Generate random */
-    for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
-         randomidx += 4)
-        AV_WB32(hs_s1 + randomidx, av_get_random_seed());
 
-    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
-                              RTMP_HANDSHAKE_PACKET_SIZE);
-    if (ret) {
-        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
-        return ret;
-    }
-    /* Send S2 */
-    ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
+    /***********
+     * Send S2
+     **********/
+    // Get the S2 random data from C0+C1
+    memcpy(hs_s2, hs_c0c1, RTMP_HANDSHAKE_PACKET_SIZE);
+    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s2,
                               RTMP_HANDSHAKE_PACKET_SIZE);
     if (ret) {
         av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
         return ret;
     }
-    /* Receive C2 */
-    ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
-                                 RTMP_HANDSHAKE_PACKET_SIZE);
-    if (ret) {
-        av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
-        return ret;
+
+    /*************
+     * Receive C2
+     ************/
+
+    ret = ffurl_read_complete(rt->stream, hs_c2,
+                              RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    if (ret <= 0)
+        return AVERROR(EIO);
+    if (ret != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
+        av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
+               " not following standard\n", (int)inoutsize);
+        return AVERROR(EINVAL);
     }
+
+    // Check timestamp and random data from C2
+    temp  = AV_RB32(hs_c2 + 1);
     if (temp != hs_my_epoch)
         av_log(s, AV_LOG_WARNING,
-               "Erroneous C2 Message epoch does not match up with C1 epoch\n");
-    if (memcmp(buffer + 8, hs_s1 + 8,
+               "Erroneous C2 Message epoch does not match up with C1 epoch");
+    if (memcmp(hs_c2 + 9, hs_c0c1 + 9,
                RTMP_HANDSHAKE_PACKET_SIZE - 8))
         av_log(s, AV_LOG_WARNING,
                "Erroneous C2 Message random does not match up\n");