On 07/28/2012 09:12 PM, Jordi Ortiz wrote:
> ---
>  doc/protocols.texi      |    5 +
>  libavformat/rtmp.h      |    1 +
>  libavformat/rtmpproto.c |  532 
> +++++++++++++++++++++++++++++++++++++++++++++--
>  3 files changed, 517 insertions(+), 21 deletions(-)
> 
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index ff872fc..cf15b36 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -188,6 +188,11 @@ application specified in @var{app}, may be prefixed by 
> "mp4:". You
>  can override the value parsed from the URI through the @code{rtmp_playpath}
>  option, too.
>  
> +@item listen
> +Act as a server, listening for an incoming connection.
> +
> +@item timeout
> +Maximum time to wait for the incoming connection. Implies listen.
>  @end table
>  
>  Additionally, the following parameters can be set via command line options
> diff --git a/libavformat/rtmp.h b/libavformat/rtmp.h
> index b9c5f1e..638f926 100644
> --- a/libavformat/rtmp.h
> +++ b/libavformat/rtmp.h
> @@ -28,6 +28,7 @@
>  #define RTMPS_DEFAULT_PORT 443
>  
>  #define RTMP_HANDSHAKE_PACKET_SIZE 1536
> +#define RTMP_HANDSHAKE_PACKET_ARRAY_SIZE 1528
>  
>  #define HMAC_IPAD_VAL 0x36
>  #define HMAC_OPAD_VAL 0x5C
> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
> index 501e0ed..8bb7e9c 100644
> --- a/libavformat/rtmpproto.c
> +++ b/libavformat/rtmpproto.c
> @@ -29,6 +29,7 @@
>  #include "libavutil/intfloat.h"
>  #include "libavutil/lfg.h"
>  #include "libavutil/opt.h"
> +#include "libavutil/random_seed.h"
>  #include "libavutil/sha.h"
>  #include "avformat.h"
>  #include "internal.h"
> @@ -47,6 +48,7 @@
>  #define PLAYPATH_MAX_LENGTH 256
>  #define TCURL_MAX_LENGTH 512
>  #define FLASHVER_MAX_LENGTH 64
> +#define RTMP_PKTDATA_DEFAULT_SIZE 4096
>  
>  /** RTMP protocol handler state */
>  typedef enum {
> @@ -95,6 +97,8 @@ typedef struct RTMPContext {
>      int           client_buffer_time;         ///< client buffer time in ms
>      int           flush_interval;             ///< number of packets flushed 
> in the same request (RTMPT only)
>      int           encrypted;                  ///< use an encrypted 
> connection (RTMPE only)
> +    int           listen;                     ///< listen mode flag
> +    int           listen_timeout;             ///< listen timeout to wait 
> for new connections
>  } RTMPContext;
>  
>  #define PLAYER_KEY_OPEN_PART_LEN 30   ///< length of partial key used for 
> first client digest signing
> @@ -275,6 +279,135 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
>      return ret;
>  }
>  
> +static int read_connect(URLContext *s, RTMPContext *rt)
> +{
> +    RTMPPacket pkt = { 0 };
> +    uint8_t *p;
> +    const uint8_t *cp;
> +    int ret;
> +    char command[64];
> +    int stringlen;
> +    double seqnum;
> +    GetByteContext gbc;
> +
> +    if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->chunk_size,
> +                                   rt->prev_pkt[1])) < 0)
> +        return ret;
> +    cp = pkt.data;

Why not using directly data?

> +    bytestream2_init(&gbc, cp, pkt.data_size);
> +    if (ff_amf_read_string(&gbc, command, sizeof(command), &stringlen)) {
> +        av_log(s, AV_LOG_ERROR, "Unable to read command string\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    if (strcmp(command, "connect")) {
> +        av_log(s, AV_LOG_ERROR, "Expecting connect\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    ret = ff_amf_read_number(&gbc, &seqnum);
> +    if (ret)
> +        av_log(s, AV_LOG_WARNING, "SeqNum not found\n");
> +    /* Here one could parse an AMF Object with data as flashVers and others.
> +     *  Not needed by now */
> +    ff_rtmp_packet_destroy(&pkt);
> +
> +    // Send Window Acknowledgement Size (as defined in speficication)
> +    pkt.data = NULL;

ff_rtmp_packet_destroy should reset pkt fields already (if it does not
it is a bug)

> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
> +                                     RTMP_PT_SERVER_BW, 0, 4)) < 0)
> +        return ret;
> +    p = pkt.data;
> +    bytestream_put_be32(&p, rt->server_bw);
> +    pkt.data_size = p - pkt.data;
> +    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                               rt->prev_pkt[1]);
> +    ff_rtmp_packet_destroy(&pkt);
> +
> +    // Send Peer Bandwidth
> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
> +                                     RTMP_PT_CLIENT_BW, 0, 5)) < 0)
> +        return ret;
> +    p = pkt.data;
> +    bytestream_put_be32(&p, rt->server_bw);
> +    bytestream_put_byte(&p, 2); // dynamic
> +    pkt.data_size = p - pkt.data;


it is sort of wrong aligning this line.

> +    ret           = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                                         rt->prev_pkt[1]);

Check the return value.

> +    ff_rtmp_packet_destroy(&pkt);



> +    // Ping request
> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
> +                                     RTMP_PT_PING, 0, 6)) < 0)
> +        return ret;
> +
> +    p             = pkt.data;
> +    pkt.data_size = 6;

This kind of pattern puzzles me.

> +    bytestream_put_be16(&p, 0); // 0 -> Stream Begin
> +    bytestream_put_be32(&p, 0);

> +    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                               rt->prev_pkt[1]);

Again missing check.

> +    ff_rtmp_packet_destroy(&pkt);
> +
> +    // Chunk size
> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
> +                                     RTMP_PT_CHUNK_SIZE, 0, 4)) < 0)
> +        return ret;
> +
> +    p             = pkt.data;
> +    pkt.data_size = 4;

Again strange setting the size to 4 here and to p - pkt.data before.

> +    bytestream_put_be32(&p, rt->chunk_size);
> +    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                               rt->prev_pkt[1]);

again, please check the return value

> +    ff_rtmp_packet_destroy(&pkt);
> +


_result is the result from a message received, which message?

> +    // Send result_
> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
> +                                     RTMP_PT_INVOKE, 0,
> +                                     RTMP_PKTDATA_DEFAULT_SIZE)) < 0)
> +        return ret;
> +
> +    p = pkt.data;
> +    ff_amf_write_string(&p, "_result");
> +    ff_amf_write_number(&p, 1);
> +
> +    ff_amf_write_object_start(&p);
> +    ff_amf_write_field_name(&p, "fmsVer");
> +    ff_amf_write_string(&p, "FMS/3,0,1,123");
> +    ff_amf_write_field_name(&p, "capabilities");
> +    ff_amf_write_number(&p, 31);
> +    ff_amf_write_object_end(&p);
> +
> +    ff_amf_write_object_start(&p);
> +    ff_amf_write_field_name(&p, "level");
> +    ff_amf_write_string(&p, "status");
> +    ff_amf_write_field_name(&p, "code");
> +    ff_amf_write_string(&p, "NetConnection.Connect.Success");
> +    ff_amf_write_field_name(&p, "description");
> +    ff_amf_write_string(&p, "Connection succeeded.");
> +    ff_amf_write_field_name(&p, "objectEncoding");
> +    ff_amf_write_number(&p, 0);
> +    ff_amf_write_object_end(&p);
> +
> +    pkt.data_size = p - pkt.data;
> +    ret           = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                                         rt->prev_pkt[1]);
> +    ff_rtmp_packet_destroy(&pkt);
> +
> +    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
> +                                     RTMP_PT_INVOKE, 0, 28)) < 0)
> +        return ret;
> +    p = pkt.data;
> +    ff_amf_write_string(&p, "onBWDone");
> +    ff_amf_write_number(&p, 0);
> +    ff_amf_write_null(&p);
> +    ff_amf_write_number(&p, 8192);
> +    pkt.data_size = p - pkt.data;
> +    ret           = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
> +                                         rt->prev_pkt[1]);
> +    ff_rtmp_packet_destroy(&pkt);
> +
> +    return ret;
> +}
> +
>  /**
>   * Generate 'releaseStream' call and send it to the server. It should make
>   * the server release some channel for media streams.
> @@ -886,6 +1019,141 @@ static int rtmp_handshake(URLContext *s, RTMPContext 
> *rt)
>      return 0;
>  }
>  
> +static int rtmp_receive_hs_packet(RTMPContext* rt, uint32_t *first_int,
> +                                  uint32_t *second_int, char *arraydata,
> +                                  int size)
> +{
> +    ssize_t inoutsize;
> +    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
> +
> +    inoutsize = ffurl_read_complete(rt->stream, buffer, sizeof(buffer));
> +    if (!inoutsize)
> +        return AVERROR(EIO);
> +    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
> +        av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
> +               " not following standard\n", (int)inoutsize);
> +        return AVERROR_INVALIDDATA;

           AVERROR(EINVAL) while you are at it.

> +    }
> +
> +    *first_int  = AV_RB32(buffer);
> +    *second_int = AV_RB32(buffer + 4);
> +    memcpy(arraydata, buffer + 8,
> +           RTMP_HANDSHAKE_PACKET_ARRAY_SIZE);
> +    return 0;
> +}
> +
> +static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
> +                               uint32_t second_int, char *arraydata, int 
> size)
> +{
> +    ssize_t inoutsize;
> +    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
> +
> +    AV_WB32(buffer, first_int);
> +    AV_WB32(buffer + 4, first_int);
> +    memcpy(buffer + 8, arraydata, RTMP_HANDSHAKE_PACKET_ARRAY_SIZE);
> +    inoutsize = ffurl_write(rt->stream, buffer,
> +                            RTMP_HANDSHAKE_PACKET_SIZE);
> +    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
> +        av_log(rt, AV_LOG_ERROR, "Unable to write answer\n");
> +        return AVERROR(EIO);
> +    }
> +
> +    return 0;
> +}
> +
> +/**
> + * rtmp handshake server side
> + */
> +static int rtmp_shandshake(URLContext *s, RTMPContext *rt)
> +{
> +    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
> +    uint32_t epoch;    // to context?
> +    uint32_t my_epoch; // to context?
> +    uint32_t zeroes;
> +    uint32_t temp = 0;
> +    uint8_t peer_random[RTMP_HANDSHAKE_PACKET_ARRAY_SIZE]; // to context?
> +    uint8_t my_random[RTMP_HANDSHAKE_PACKET_ARRAY_SIZE];   // to context?

yes please.

> +    int randomidx     = 0;
> +    ssize_t 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);
> +    }
> +    if (buffer[0] != 3) { /* Check Version */
> +        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
> +        return AVERROR_PROTOCOL_NOT_FOUND;
> +    }
> +    if (!ffurl_write(rt->stream, buffer, 1)) {       // Send S0
> +        av_log(s, AV_LOG_ERROR,
> +               "Unable to write answer - RTMP S0\n");
> +        return AVERROR(EIO);
> +    }
> +    /* Receive C1 */
> +    ret = rtmp_receive_hs_packet(rt, &epoch, &zeroes, peer_random,
> +                                 sizeof(peer_random));
> +    if (ret) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
> +        return ret;
> +    }
> +    if (zeroes) {
> +        av_log(NULL, AV_LOG_WARNING,
> +               "Erroneous C1 Message zero != 0\n");
> +    }
> +    /* Send S1 */
> +    /* By now same epoch will be sent */
> +    my_epoch = epoch;
> +    /* Generate random */
> +    for (randomidx = 0;
> +         randomidx < (RTMP_HANDSHAKE_PACKET_ARRAY_SIZE);
> +         randomidx += 4) {
> +        temp = av_get_random_seed();
> +        memcpy(my_random + randomidx, &temp, 4);
> +    }
> +    ret = rtmp_send_hs_packet(rt, my_epoch, 0, my_random,
> +                              RTMP_HANDSHAKE_PACKET_ARRAY_SIZE);
> +    if (ret) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
> +        return ret;
> +    }
> +    /* Send S2 */
> +    ret = rtmp_send_hs_packet(rt, epoch, 0, peer_random,
> +                              RTMP_HANDSHAKE_PACKET_ARRAY_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, peer_random,
> +                                 sizeof(peer_random));
> +    if (ret) {
> +        av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
> +        return ret;
> +    }
> +
> +
> +    if (temp != my_epoch) {
> +        av_log(NULL, AV_LOG_ERROR, "Erroneous C2 Message epoch"
> +               " does not match up with C1 epoch\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (memcmp(peer_random, my_random,
> +               RTMP_HANDSHAKE_PACKET_ARRAY_SIZE)) {
> +        av_log(NULL, AV_LOG_ERROR, "Erroneous C2 Message random"
> +               " does not match up\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    /* Handshake successful */
> +    return 0;
> +
> +}
> +

The rest seems more or less ok.

Samuel will have to overhaul part of the same file so we should push
this change before he starts working on that. ^^

lu

-- 

Luca Barbato
Gentoo/linux
http://dev.gentoo.org/~lu_zero

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to