SRS之RTMP handshake

1. SrsRtmpServer::handshake

位于 srs_rtmp_stack.cpp.

int SrsRtmpServer::handshake()
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(hs_bytes);
    
    /* 先尝试进行 complex handshake,若失败则再次尝试 simple handshake */
    SrsComplexHandshake complex_hs;
    if ((ret = complex_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) {
        if (ret == ERROR_RTMP_TRY_SIMPLE_HS) {
            SrsSimpleHandshake simple_hs;
            if ((ret = simple_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) {
                return ret;
            }
        }
        return ret;
    }
    
    srs_freep(hs_bytes);
    
    return ret;
}

recv: c0c1

send: s0s1s2

image

recv: c2

image

2. complex handshake

2.1 相关类定义

2.1.1 SrsComplexHandshake 类定义

/**
 * rtmp complex handshake,
 * @see also crtmp(crtmpserver) or librtmp,
 * @see also: http://blog.csdn.net/win_lin/article/details/13006803
 */
class SrsComplexHandshake
{
public:
    SrsComplexHandshake();
    virtual ~SrsComplexHandshake();
public:
    /**
     * complex handshake.
     * @return user must:
     *     continue connect app if success,
     *     try simple handshake if error is ERROR_RTMP_TRY_SIMPLE_HS,
     *     otherwise, disconnect
     */
    virtual int handshake_with_client(SrsHandshakeBytes* hs_bytes, 
            ISrsProtocolReaderWriter* io);
    virtual int handshake_with_server(SrsHandshakeBytes* hs_bytes, 
            ISrsProtocolReaderWriter* io);
};

该类提供了方法与客户端或服务器进行 handshake。

2.1.2 SrsHandshakeBytes 类定义

/**
 * store the handshake bytes,
 * for smart switch between complex and simple handshake.
 */
class SrsHandshakeBytes
{
public:
    // [1 + 1536]
    char* c0c1;
    // [1 + 1536 + 1536]
    char* s0s1s2;
    // [1536]
    char* c2;
public:
    SrsHandshakeBytes();
    virtual ~SrsHandshakeBytes();
public:
    virtual int read_c0c1(ISrsProtocolReaderWriter* io);
    virtual int read_s0s1s2(ISrsProtocolReaderWriter* io);
    virtual int read_c2(ISrsProtocolReaderWriter* io);
    virtual int create_c0c1();
    virtual int create_s0s1s2(cosnt char* c1 = NULL);
    virtual int create_c2();
};

该类提供了读取/生成 handshake 过程数据包的方法。

2.1.3 ISrsProtocolReaderWriter 类定义

/**
 * the reader and writer.
 */
class ISrsProtocolReaderWriter : 
    public virtual ISrsProtocolReader, 
    public virtual ISrsProtocolWriter
{
public:
    ISrsProtocolReaderWriter();
    virtual ~ISrsProtocolReaderWriter();
// for protocol
public:
    /**
     * whether the specified timeout_us is never timeout.
     */
    virtual bool is_never_timeout(int64_t timeout_us) = 0;
};
ISrsProtocolReaderWriter 与其父类还有子类关系图

2.2 SrsComplexHandshake::handshake_with_client

// srs_rtmp_handshake.hpp
namespace _srs_internal
{
    ...
    /**
     * the schema type.
     */
    enum srs_schema_type 
    {
        srs_schema_invalid = 2;
        
        /**
         * key-digest sequence
         */
        srs_schema0 = 0,
        
        /**
         * digest-key sequence
         * @remark, FMS requires the schema1(digest-key), or connect failed.
         */
        srs_schema1 = 1,
    };
}

int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, 
    ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nsize;
    
    /* 首先,接收 c0、c1 */
    if ((ret = hs_bytes->read_c0c1(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // decode c1
    c1s1 c1;
    // try schema0.
    // @remark, use schema0 to make flash player happy.
    if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)) != ERROR_SUCCESS) {
        srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret);
        return ret;
    }
    // try schema1
    bool is_valid = false;
    /* 验证解析出来的 digest 是否正确:根据 c1 的数据除了 digest-data 外,算出
     * digest,然后将该 digest 与提取出的 c1 的 digest-data 比较是否相同,相同
     * 则验证成功,否则失败 */
    if ((ret = cl.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
        srs_info("schema0 failed, try schema1.");
        /* 假若使用 srs_schema0(即 key-digest)解析失败,则再次使用 srs_schema1(即 digest-key)
         * 进行解析 */
        if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema1)) != ERROR_SUCCESS) {
            srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret);
            return ret;
        }
        
        if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
            /* 若使用两种模式都解析失败,则说明当前的 handshake 数据不是使用 complex 方式的 */
            ret = ERROR_RTMP_TRY_SIMPLE_HS;
            srs_info("all schema valid failed, try simple handshake. ret=%d", ret);
            return ret;
        }
    } else {
        srs_info("schema0 is ok.");
    }
    srs_verbose("decode c1 success.");
    
    // encode s1
    c1s1 s1;
    /* 解析 c1 成功后,开始根据 c1 数据构造 s1 数据*/
    if ((ret = s1.s1_create(&c1)) != ERROR_SUCCESS) {
        srs_error("create s1 from c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("create s1 from c1 success.");
    // verify s1
    if ((ret = s1.s1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
        ret = ERROR_RTMP_TRY_SIMPLE_HS;
        srs_info("verify s1 failed, try simple handshake. ret=%d", ret);
        return ret;
    }
    srs_verbose("verify s1 success.");
    
    /* 生成 s2 */
    s2s2 s2;
    if ((ret = s2.s2_create(&c1)) != ERROR_SUCCESS) {
        srs_error("create s2 from c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("create s2 from c1 success.");
    // verify s2
    if ((ret = s2.s2_validate(&c1, is_valid)) != ERROR_SUCCESS || !is_valid) {
        ret = ERROR_RTMP_TRY_SIMPLE_HS;
        srs_info("verify s2 failed, try simple handshake. ret=%d", ret);
        return ret;
    }
    srs_verbose("verify s2 success.");
    
    // sendout s0s1s2
    if ((ret = hs_bytes->create_s0s1s2()) != ERROR_SUCCESS) {
        return ret;
    }
    /* 将 s1 中的数据写入到 hs_bytes->s0s1s2 + 1 中 */
    if ((ret = s1.dump(hs_bytes->s0s1s2 + 1, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    /* 将 s2 中的数据写入到 hs_bytes->s0s1s2 + 1537 中 */
    if ((ret = s2.dump(hs_bytes->s0s1s2 + 1537, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    /* 调用子类 SrsStSocket 的 write 函数将 s0s1s2 发送给客户端 */
    if ((ret = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) {
        srs_warn("complex handshake send s0s1s2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("complex handshake send s0s1s2 success.");
    
    // recv c2
    if ((ret = hs_bytes->read_c2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    c2s2 c2;
    if ((ret = c2.parse(hs_bytes->c2, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_verbose("complex handshake read c2 success.");
    
    // verify c2
    // never verify c2, for ffmpeg will failed.
    // it's ok for flash.
    
    srs_trace("complex handshake success");
    
    return ret;
}

2.3 SrsHandshakeBytes::read_c0c1

int SrsHandshakeBytes::read_c0c1(ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    if (c0c1) {
        return ret;
    }
    
    ssize_t nsize;
    
    c0c1 = new char[1537];
    /* 调用子类 SrsStSocket 的 read_fully 函数 */
    if ((ret = io->read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) {
        srs_warn("read c0c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("read c0c1 success.");
    
    return ret;
}

2.3.1 SrsStSocket::read_fully

int SrsStSocket::read_fully(void* buf, size_t size, ssize_t* nread)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nb_read = st_read_fully(stfd, buf, size, recv_timeout);
    if (nread) {
        *nread = nb_read;
    }
    
    /* On success a non-negative integer indicating the number of bytes 
     * actually read is retuned (a value less than nbyte means the network
     * connection is closed or end of file is reached). Otherwise, a value 
     * of -1 is returned and errno is set to indicated the error.*/
    if (nb_read != (ssize_t)size) {
        // @see https://github.com/ossrs/srs/issues/200
        if (nb_read < 0 && errno == ETIME) {
            return ERROR_SOCKET_TIMEOUT;
        }
        
        if (nb_read >= 0) {
            errno = ECONNRESET;
        }
        
        return ERROR_SOCKET_READ_FULLY;
    }
    
    recv_bytes += nb_read;
    
    return ret;
}

2.3.2 st_read_fully

io.c:

ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
{
    size_t resid = nbyte;
    return st_read_resid(fd, buf, &resid, timeout) == 0 ? 
        (ssize_t) (nbyte - resid) : -1;
}

2.3.3 st_read_resid

int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout)
{
    struct iovec iov, *riov;
    int riov_size, rv;
    
    iov.iov_base = buf;
    iov.iov_len = *resid;
    riov = &iov;
    riov_size = 1;
    rv = st_readv_resid(fd, &riov, &riov_size, timeout);
    *resid = iov.iov_len;
    return rv;
}

2.3.4 st_readv_resid

int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout)
{
    ssize_t n;
    
    while (*iov_size > 0) {
        if (*iov_size == 1)
            n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
        else
            n = readv(fd->osfd, *iov, *iov_size);
        if (n < 0) {
            if (errno == EINTR)
                continue;
            if (!_IO_NOT_READY_ERROR)
                return -1;
        } else if (n == 0)
            break;
        else {
            while ((size_t) n >= (*iov)->iov_len) {
                n -= (*iov)->iov_len;
                (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
                (*iov)->iov_len = 0;
                (*iov)++;
                (*iov_size)--;
                if (n == 0)
                    break;
            }
            if (*iov_size == 0)
                break;
            (*iov)->iov_base = (char *) (*iov)->iov_base + n;
            (*iov)->iov_len -= n;
        }
        /* Wait until the socket becomes readable */
        /* 若当前读取的数据不足,则将线程添加到 io 队列中,监听该 fd 上是否有数据可读,
         * 有则再次调度该线程继续读,直到读够所需数据为止 */
        if (st_netfd_poll(fd, POLLIN, timeout) < 0)
            return -1;
    }
    
    return 0;
}

2.4 decode c1: try schem0(key-digest sequence)

2.4.1 c1s1 类

namespace _srs_internal
{
    ...
    /**
     * c1s1 schema0 
     *     time: 4 bytes
     *     version: 4 bytes
     *     key: 764 bytes
     *     digest: 764 bytes
     * c1s1 schema1
     *     time: 4 bytes
     *     version: 4 bytes
     *     digest: 764 bytes
     *     key: 764 bytes
     * @see also: http://blog.csdn.net/win_lin/article/details/13006803
     */
    class c1s1
    {
    public:
        // 4bytes
        int32_t time;
        // 4bytes
        int32_t version;
        // 764bytes+764bytes
        c1s1_strategy* payload;
    public:
        c1s1();
        virtual ~c1s1();
    public:
        /**
        * get the scema.
        */
        virtual srs_schema_type schema();
        /**
        * get the digest key.
        */
        virtual char* get_digest();
        /**
        * get the key.
        */
        virtual char* get_key();
    public:
        /**
        * copy to bytes.
        * @param size, must always be 1536.
        */
        virtual int dump(char* _c1s1, int size);
        /**
        * server: parse the c1s1, discovery the key and digest by schema.
        * @param size, must always be 1536.
        * use the c1_validate_digest() to valid the digest of c1.
        * use the s1_validate_digest() to valid the digest of s1.
        */
        virtual int parse(char* _c1s1, int size, srs_schema_type _schema);
    public:
        /**
        * client: create and sign c1 by schema.
        * sign the c1, generate the digest.
        *         calc_c1_digest(c1, schema) {
        *            get c1s1-joined from c1 by specified schema
        *            digest-data = HMACsha256(c1s1-joined, FPKey, 30)
        *            return digest-data;
        *        }
        *        random fill 1536bytes c1 // also fill the c1-128bytes-key
        *        time = time() // c1[0-3]
        *        version = [0x80, 0x00, 0x07, 0x02] // c1[4-7]
        *        schema = choose schema0 or schema1
        *        digest-data = calc_c1_digest(c1, schema)
        *        copy digest-data to c1
        */
        virtual int c1_create(srs_schema_type _schema);
        /**
        * server: validate the parsed c1 schema
        */
        virtual int c1_validate_digest(bool& is_valid);
    public:
        /**
        * server: create and sign the s1 from c1.
        *       // decode c1 try schema0 then schema1
        *       c1-digest-data = get-c1-digest-data(schema0)
        *       if c1-digest-data equals to calc_c1_digest(c1, schema0) {  
        *           c1-key-data = get-c1-key-data(schema0)  
        *           schema = schema0
        *       } else {  
        *           c1-digest-data = get-c1-digest-data(schema1)  
        *           if c1-digest-data not equals to calc_c1_digest(c1, schema1) {
        *               switch to simple handshake.  
        *               return  
        *           }
        *           c1-key-data = get-c1-key-data(schema1)  
        *           schema = schema1
        *       }
        * 
        *       // generate s1
        *       random fill 1536bytes s1
        *       time = time() // c1[0-3]
        *       version = [0x04, 0x05, 0x00, 0x01] // s1[4-7]
        *       s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data)
        *       get c1s1-joined by specified schema
        *       s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36)
        *       copy s1-digest-data and s1-key-data to s1.
        */
        virtual int s1_create(c1s1* c1);
        /**
        * server: validate the parsed s1 schema
        */
        virtual int s1_validate_digest(bool& is_valid);
    };
    ...
}

2.4.2 c1s1_strategy 类

namespace _srs_internal 
{
    ...
    
    /**
     * the c1s1 strategy, use schema0 or schema1.
     * the template method class to defines common behaviors,
     * while the concrete class to implements in schema0 or schema1.
     */
    class c1s1_strategy {
    protected:
        key_block key;
        digest_block digest;
    public:
        c1s1_strategy();
        virtual ~c1s1_strategy();
    public:
        /**
        * get the scema.
        */
        virtual srs_schema_type schema() = 0;
        /**
        * get the digest.
        */
        virtual char* get_digest();
        /**
        * get the key.
        */
        virtual char* get_key();
        /**
        * copy to bytes.
        * @param size must be 1536.
        */
        virtual int dump(c1s1* owner, char* _c1s1, int size);
        /**
        * server: parse the c1s1, discovery the key and digest by schema.
        * use the c1_validate_digest() to valid the digest of c1.
        */
        virtual int parse(char* _c1s1, int size) = 0;
    public:
        /**
        * client: create and sign c1 by schema.
        * sign the c1, generate the digest.
        *         calc_c1_digest(c1, schema) {
        *            get c1s1-joined from c1 by specified schema
        *            digest-data = HMACsha256(c1s1-joined, FPKey, 30)
        *            return digest-data;
        *        }
        *        random fill 1536bytes c1 // also fill the c1-128bytes-key
        *        time = time() // c1[0-3]
        *        version = [0x80, 0x00, 0x07, 0x02] // c1[4-7]
        *        schema = choose schema0 or schema1
        *        digest-data = calc_c1_digest(c1, schema)
        *        copy digest-data to c1
        */
        virtual int c1_create(c1s1* owner);
        /**
        * server: validate the parsed c1 schema
        */
        virtual int c1_validate_digest(c1s1* owner, bool& is_valid);
        /**
        * server: create and sign the s1 from c1.
        *       // decode c1 try schema0 then schema1
        *       c1-digest-data = get-c1-digest-data(schema0)
        *       if c1-digest-data equals to calc_c1_digest(c1, schema0) {  
        *           c1-key-data = get-c1-key-data(schema0)  
        *           schema = schema0
        *       } else {  
        *           c1-digest-data = get-c1-digest-data(schema1)  
        *           if c1-digest-data not equals to calc_c1_digest(c1, schema1) {
        *               switch to simple handshake.  
        *               return  
        *           }
        *           c1-key-data = get-c1-key-data(schema1)  
        *           schema = schema1
        *       }
        * 
        *       // generate s1
        *       random fill 1536bytes s1
        *       time = time() // c1[0-3]
        *       version = [0x04, 0x05, 0x00, 0x01] // s1[4-7]
        *       s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data)
        *       get c1s1-joined by specified schema
        *       s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36)
        *       copy s1-digest-data and s1-key-data to s1.
        * @param c1, to get the peer_pub_key of client.
        */
        virtual int s1_create(c1s1* owner, c1s1* c1);
        /**
        * server: validate the parsed s1 schema
        */
        virtual int s1_validate_digest(c1s1* owner, bool& is_valid);
    public:
        /**
        * calc the digest for c1
        */
        virtual int calc_c1_digest(c1s1* owner, char*& c1_digest);
        /**
        * calc the digest for s1
        */
        virtual int calc_s1_digest(c1s1* owner, char*& s1_digest);
        /**
        * copy whole c1s1 to bytes.
        * @param size must always be 1536 with digest, and 1504 without digest.
        */
        virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest) = 0;
        /**
        * copy time and version to stream.
        */
        virtual void copy_time_version(SrsStream* stream, c1s1* owner);
        /**
        * copy key to stream.
        */
        virtual void copy_key(SrsStream* stream);
        /**
        * copy digest to stream.
        */
        virtual void copy_digest(SrsStream* stream, bool with_digest);
    };
    
    ...
}

2.4.3 c1s1::parse

int c1s1::parse(char* _c1s1, int size, srs_schema_type schema)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(size == 1536);
    
    /* 当前 schema 要么是 schema0(key-digest),要么是 schema1(digest-key) */
    if (schema != srs_schema0 && schema != srs_schema1) {
        ret = ERROR_RTMP_CH_SCHEMA;
        srs_error("parse c1 failed. invalid schema=%d, ret=%d", schema, ret);
        return ret;
    }
    
    /* 构造一个 SrsStream 类,该类是 bytes utility,used to:
     * convert basic types to bytes, 
     * build basic bytes from bytes.*/
    SrsStream stream;
    
    /* 初始化一个字节流 */
    if ((ret = stream.initialize(_c1s1, size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    /* 读取 4 字节的数据 */
    time = stream.read_4bytes();
    version = stream.read_4bytes(); // client c1 version
    
    srs_freep(payload);
    if (schema == srs_schema0) {
        /* 构造 c1s1_strategy_schema0 类:
         * c1s1 schema0
         *     key: 764 bytes
         *     digest: 764 bytes */
        payload = new c1s1_strategy_schema0();
    } else {
        payload = new c1s1_strategy_schema1();
    }
    
    /* 调用子类 c1s1_strategy_schema1 的成员函数 parse,
     * 解析出 key 和 digest 数据 */
    return payload->parse(_c1s1, size);
}

2.4.5 SrsStream::initialize

/**
 * initialize the stream from bytes.
 * @b, the bytes to convert from/to basic types.
 * @nb, the size of bytes, total number of bytes for stream.
 * @remark, stream never free the bytes, user must free it.
 * @remark, return rerror when bytes NULL.
 * @remark, return error when size is not positive.
 */
int SrsStream::initialize(char* b, int nb)
{
    int ret = ERROR_SUCCESS;
    
    if (!b) {
        ret = ERROR_KERNEL_STREAM_INIT;
        srs_error("stream param bytes must not be NULL. ret=%d", ret);
        return ret;
    }
    
    if (nb <= 0) {
        ret = ERROR_KERNEL_STREAM_INIT;
        srs_error("stream param size must be positive. ret=%d", ret);
        return ret;
    }
    
    /* 总的字节数 */
    nb_bytes = nb;
    /* p 为当前所在字节的索引值
     * bytes 为数据流要读取或写入的字节数据 
     * 初始化两个指针都指向字节数据的起始 */
    p = bytes = b;
    srs_info("init stream ok, size=%d", size());
    
    return ret;
}

2.4.6 SrsStream::read_4bytes

int32_t SrsStream::read_4bytes()
{
    /* 确保必须要有 4 字节的数据 */
    srs_assert(require(4));
    
    int32_t value;
    char* pp = (char*)&value;
    /* 从网络读取到的 RTMP 数据为大端,而本地一般为小端 */
    pp[3] = *p++;
    pp[2] = *p++;
    pp[1] = *p++;
    pp[0] = *p++;
}

2.4.7 c1s1_strategy_schema0::parse

int c1s1_strategy_schema0::parse(char* _c1s1, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(size == 1536);
    
    /* 构造一个字节流 */
    SrsStream stream;
    
    if ((ret = stream.initialize(_c1s1 + 8, 764)) != ERROR_SUCCESS) {
        return ret;
    }
    
    /* 将 key 结构的数据读取出来,保存到 key 中 */
    if ((ret = key.parse(&stream)) != ERROR_SUCCESS) {
        srs_error("parse the c1 key failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) {
        return ret;
    }

    /* 将 digest 结构中的数据读取出来,保存到 digest 中 */
    if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) {
        srs_error("parse the c1 digest failed. ret=%d", ret);
        return ret;
    }
    
    srs_verbose("parse c1 key-digest success");
    
    return ret;
}

2.4.8 key_block::parse

/**
 * 764 bytes key 结构:
 * - random-data: (offset) bytes
 * - key-data: 128 bytes
 * - random-data: (764 - offset - 128 - 4) bytes
 * - offset: 4 bytes
 */
int key_block::parse(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // the key must be 764 bytes
    srs_assert(stream->require(764));
    
    /* 读取 key 结构中的 4 字节 offset 值 */
    // read the last offset first, 760-763
    stream->skip(764 - sizeof(int32_t));
    offset = stream->read_4bytes();

    // reset stream to read others.
    stream->skip(-764);
    
    /* 计算 offset 值 */
    int valid_offset = calc_valid_offset();
    srs_assert(valid_offset >= 0);
    
    random0_size = valid_offset;
    if (random0_size > 0) {
        srs_freepa(random0);
        random0 = new char[random0_size];
        stream->read_bytes(random0, random0_size);
    }
    
    /* 读取 128 字节的 key */
    stream->read_bytes(key, 128);
    
    random1_size = 764 - valid_offset - 128 - 4;
    if (random1_size > 0) {
        srs_freepa(random1);
        random1 = new char[random1_size];
        stream->read_bytes(random1, random1_size);
    }
    
    return ret;
}

2.4.9 key_block::calc_valid_offset

int key_block::calc_valid_offset()
{
    int max_offset_size = 764 - 128 - 4;
    
    int valid_offset = 0;
    u_int8_t* pp = (u_int8_t*)&offset;
    valid_offset += *pp++;
    valid_offset += *pp++;
    valid_offset += *pp++;
    valid_offset += *pp++;
    
    return valid_offset % max_offset_size;
}

将 4 字节的 offset 数据按字节相加,返回最终值。

2.4.10 digest_block::parse

/**
 * 764 bytes digest结构:
 * - offset: 4 bytes
 * - random-data: (offset) bytes
 * - digest-data: 32 bytes
 * - random-data: (764 - 4 - offset - 32) bytes
 */
int digest_block::parse(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // the digest must be 764 bytes.
    srs_assert(stream->require(764));
    
    offset = stream->read_4bytes();
    
    int valid_offset = calc_valid_offset();
    srs_assert(valid_offset >= 0);
    
    random0_size = valid_offset;
    if (random0_size > 0) {
        srs_freepa(random0);
        random0 = new char[random0_size];
        stream->read_bytes(random0, random0_size);
    }
    
    /* 读取 32 字节的 digest */
    stream->read_bytes(digest, 32);
    
    random1_size = 764 - 4 - valid_offset - 32;
    if (random1_size > 0) {
        srs_freepa(random1);
        random1 = new char[random1_size];
        stream->read_bytes(random1, random1_size);
    }
    
    return ret;
}

2.5 c1s1::c1_validate_digest

int c1s1::c1_validate_digest(bool& is_valid)
{
    is_valid = false;
    srs_assert(payload);
    /* 由于子类 c1s1_strategy_schema0/c1s1_strategy_schema1 没有实现
     * c1_validate_digest,因此调用父类 c1s1_strategy 的 c1_validate_digest*/
    return payload->c1_validate_digest(this, is_valid);
}

2.5.1 c1s1_strategy::c1_validate_digest

int c1s1_strategy::c1_validate_digest(c1s1* owner, bool& is_valid)
{
    int ret = ERROR_SUCCESS;
    
    char* c1_digest = NULL;
    
    if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) {
        srs_error("validate c1 error, failed to calc digest. ret=%d", ret);
        return ret;
    }
    
    srs_assert(c1_digest != NULL);
    SrsAutoFreeA(char, c1_digest);
    
    /* 比较两个 digest 是否一致 */
    is_valie = srs_bytes_equals(digest.digest, c1_digest, 32);
    
    return ret;
}

2.5.2 c1s1_strategy::calc_c1_digest

// 62bytes FP key which is used to sign the client packet.
u_int8_t SrsGenuineFPKey[] = {
    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

int c1s1_strategy::calc_c1_digest(c1s1* owner, char*& c1_digest)
{
    int ret = ERROR_SUCCESS;
    
    /**
     * c1s1 is splited by digest:
     *     c1s1-part1: n bytes (time, version, key and digest-part1).
     *     digest-data: 32 bytes
     *     c1s1-part2: (1536 - n - 32) bytes (digest-part2)
     * @return a new allocated bytes, user must free it.
     */
    char* c1s1_joined_bytes = new char[1536 - 32];
    /* 构造自动释放内存的类,当该函数返回时,自动释放 c1s1_joined_bytes 的资源 */
    SrsAutoFreeA(char, c1s1_joined_bytes);
    /* 将 owner 中的 c1/s1 的数据中除了 digest-data 不拷贝外,其余都拷贝到 c1s1_joined_bytes 中 */
    if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) {
        return ret;
    }
    
    c1_digest = new char[SRS_OpensslHashSize];
    if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 
                                  1536 - 32, c1_digest)) 
        != ERROR_SUCCESS) {
        srs_freepa(c1_digest);
            srs_error("calc digest for c1 failed. ret=%d", ret);
            return ret;
    }
    srs_verbose("digest calculated for c1");
        
    return ret;
}

2.5.3 openssl_HMACsha256

/**
 * sha256 digest algorithm.
 * @param key the sha256 key, NULL to use EVP_Digest, for instance,
 *       hashlib.sha256(data).digest().
 */
int openssl_HMACsha256(const void* key, int key_size, const void* data, 
    int data_size, void* digest)
{
    int ret = ERROR_SUCCESS;
    
    unsigned int digest_size = 0;
    
    unsigned char* temp_key = (unsigned char*)key;
    unsigned char* temp_digest = (unsigned char*)digest;
    
    if (key == NULL) {
        // use data to digest.
        // @see ./crypto/sha/sha256t.c
        // @see ./crypto/evp/digest.c
        if (EVP_Digest(data, data_size, temp_digest, &digest_size, EVP_sha256(), NULL) < 0)
        {
            ret = ERROR_OpenSslSha256EvpDigest;
            return ret;
        }
    } else {
        // use key-data to digest.
        HMAC_CTX ctx;
        
        // @remark, if no key, use EVP_Digest to digest,
        // for instance, in python, hashlib.sha256(data).digest().
        /* 初始化 ctx,在计算 MAC 之前必须调用此函数 */
        HMAC_CTX_init(&ctx);
        /* 初始化 ctx 数据,在 ctx 中复制 EVP_MD() 方法,密钥数据,
         * 密钥数据长度 key_size,最后一个参数可以为 NULL */
        if (HMAC_Init_ex(&ctx, temp_key, key_size, EVP_sha256(), NULL) < 0) {
            ret = ERROR_OpenSslSha256Init;
            return ret;
        }
        
        ret = do_openssl_HMACsha256(&ctx, data, data_size, temp_digest, &digest_size);
        /* 将 ctx 中密钥数据及其他相关数据清除,如果不在计算 ctx,需要调用此函数,将 ctx 中数据清除 */
        HMAC_CTX_cleanup(&ctx);
        
        if (ret != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    if (digest_size != 32) {
        ret = ERROR_OpenSslSha256DigestSize;
        return ret;
    }
    
    return ret;
}

2.5.4 do_openssl_HMACsha256

int do_openssl_HMACsha256(HMAC_CTX* ctx, const void* data, int data_size, 
    void* digest, unsigned int* digest_size) 
{
    int ret = ERROR_SUCCESS;
    
    /* 此函数可以被重复调用,将数据块加入到计算结果中,数据缓冲区 data,数据长度 data_size */
    if (HMAC_Update(ctx, (unsigned char *) data, data_size) < 0) {
        ret = ERROR_OpenSslSha256Update;
        return ret;
    }

    /* 计算出的 MAC 数据放置在 digest 中,用户需要保证 digest 数据有足够长的空间,
     * 长度为 digest_size */
    if (HMAC_Final(ctx, (unsigned char *) digest, digest_size) < 0) {
        ret = ERROR_OpenSslSha256Final;
        return ret;
    }
    
    return ret;
}

2.6 encode s1: c1s1::s1_create

int c1s1::s1_create(c1s1* c1)
{
    int ret = ERROR_SUCCESS;
        
    if (c1->schema() != srs_schema0 && c1->schema() != srs_schema1) {
        ret = ERROR_RTMP_CH_SCHEMA;
        srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema(), ret);
        return ret;
    }
    
    /* 将当前时间填入到 s1 结构中 time 字段 */
    time = ::time(NULL);
    /* 将服务器使用的版本填入到 s1 结构中的 version 字段 */
    version = 0x01000504; // server s1 version
    
    srs_freep(payload);
    /* 假设当前使用 schema0 */
    if (c1->schema() == srs_schema0) {
        /* 根据当前 handshake 的数据使用的模式构造相应的 c1s1_strategy_schema 类 */
        payload = new c1s1_strategy_schema0();
    } else {
        payload = new c1s1_strategy_schema1();
    }
    
    /* 子类 c1s1_strategy_schema0 没有实现 s1_create,因此调用父类 c1s1_strategy 的
     * s1_create 函数 */
    return payload->s1_create(this, c1);
}

2.6.1 构造 c1s1_strategy

在构造 c1s1_strategy 类中,主要构造 c1s1_strategy 类中 key_block 类的成员变量 key 的构造函数和 digest_block 类的成员变量 digest 的构造函数。

key_block

/**
 * 764bytes key structure
 *     random-data: (offset)bytes
 *     key-data: 128bytes
 *     random-data: (764-offset-128-4)bytes
 *     offset: 4bytes
 * @see also: http://blog.csdn.net/win_lin/article/details/13006803
 */
class key_block
{
public:
    // (offset)bytes
    char* random0;
    int random0_size;
    
    // 128bytes
    char key[128];
    
    // (764-offset-128-4)bytes
    char* random1;
    int random1_size;
    
    // 4bytes
    int32_t offset;
public:
    key_block();
    virtual ~key_block();
public:
    // parse key block from c1s1.
    // if created, user must free it by srs_key_block_free
    // @stream contains c1s1_key_bytes the key start bytes
    int parse(SrsStream* stream);
private:
    // calc the offset of key,
    // the key->offset cannot be used as the offset of key.
    int calc_valid_offset();
};

key_block::key_block()
{
    /* 生成一个随机数的偏移值 */
    offset = (int32_t)rand();
    random0 = NULL;
    random1 = NULL;
    
    /* 将该随机数的 4 字节数据依次相加 */
    int valid_offset = calc_valid_offset();
    srs_assert(valid_offset >= 0);
    
    random0_size = valid_offset;
    if (random0_size > 0) {
        random0 = new char[random0_size];
        srs_random_generate(random0, random0_size);
        snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
    }
    
    srs_random_generate(key, sizeof(key));
    
    random1_size = 764 - valid_offset - 128 - 4;
    if (random1_size > 0) {
        random1 = new char[random1_size];
        srs_random_generate(random1, random1_size);
        snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
    }
}

将 key 结构中的每个字段填充随机值。

digest_block

/**
 * 764bytes digest structure
 *     offset: 4bytes
 *     random-data: (offset)bytes
 *     digest-data: 32bytes
 *     random-data: (764-4-offset-32)bytes
 * @see also: http://blog.csdn.net/win_lin/article/details/13006803
 */
class digest_block
{
public:
    // 4bytes
    int32_t offset;
    
    // (offset)bytes
    char* random0;
    int random0_size;
    
    // 32bytes
    char digest[32];
    
    // (764-4-offset-32)bytes
    char* random1;
    int random1_size;
public:
    digest_block();
    virtual ~digest_block();
public:
    // parse digest block from c1s1.
    // if created, user must free it by srs_digest_block_free
    // @stream contains c1s1_digest_bytes the digest start bytes
    int parse(SrsStream* stream);
private:
    // calc the offset of digest,
    // the key->offset cannot be used as the offset of digest.
    int calc_valid_offset();
};

digest_block::digest_block()
{
    offset = (int32_t)rand();
    random0 = NULL;
    random1 = NULL;
    
    int valid_offset = calc_valid_offset();
    srs_assert(valid_offset >= 0);
    
    random0_size = valid_offset;
    if (random0_size > 0) {
        random0 = new char[random0_size];
        srs_random_generate(random0, random0_size);
        snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
    }
    
    srs_random_generate(digest, sizeof(digest));
    
    random1_size = 764 - 4 - valid_offset - 32;
    if (random1_size > 0) {
        random1 = new char[random1_size];
        srs_random_generate(random1, random1_size);
        snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
    }
}

将 digest 结构中的每个字段填充随机值。

2.6.2 c1s1_strategy::s1_create

/**
 * 764 bytes key 结构:
 * - random-data: (offset) bytes
 * - key-data: 128 bytes
 * - random-data: (764 - offset - 128 - 4) bytes
 * - offset: 4 bytes
 */
int c1s1_strategy::s1_create(c1s1* owner, c1s1* c1)
{
    int ret = ERROR_SUCCESS;
    
    /* 构造 SrsDH 类,该类封装了 openssl 的 DH 方法 */
    SrsDH dh;
    
    /* 初始化并生成 128 字节的公钥 */
    // ensure generate 128bytes public key.
    if ((ret = dh.initialize(true)) != ERROR_SUCCESS) {
        return ret;
    }
    
    /* 首先生成 s1 的 key */
    // directly generate the public key.
    // @see: https://github.com/ossrs/srs/issues/148
    int pkey_size = 128;
    /* 先生成己方公私钥,然后根据 c1 中的 key 计算对方公钥,再根据这两者生成共享密钥,
     * 保存在 key.key 中,大小为 pkey_size */
    if ((ret = dh.copy_shared_key(c1->get_key(), 128, key.key, pkey_size)) 
        != ERROR_SUCCESS) {
        srs_error("calc s1 key failed. ret=%d", ret);
        return ret;
    }
    
    // altough the public key is always 128bytes, but the share key maybe not.
    // we just ignore the actual key size, but if need to use the key, must 
    // use the actual size.
    // TODO: FIXME: use the actual key size.
    //srs_assert(pkey_size == 128);
    srs_verbose("calc s1 key success.");
    
    /* 接着生成 s1 的 digest */
    char* s1_digest = NULL;
    if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) {
        srs_error("calc sl digest failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("calc s1 digest success.");
    
    srs_assert(s1_digest != NULL);
    SrsAutoFreeA(char, s1_digest);
    
    memcpy(digest.digest, s1_digest, 32);
    srs_verbose("copy s1 key success.");
        
    return ret;
}

2.6.3 SrsDH::initialize

openssl 库中 BIGNUM 的用法可以参考:

openssl 库中 DH 的用法可以参考:

int SrsDH::initialize(bool ensure_128bytes_public_key)
{
    int ret = ERROR_SUCCESS;
    
    for ( ;; ) {
        /* 进行初始化并生成公私钥 */
        if ((ret = do_initialize()) != ERROR_SUCCESS) {
            return ret;
        }
        
        /* 若必须要确保生成的公钥字节数是否是 128 字节,
         * 则进行以下验证 */
        if (ensure_128bytes_public_key) {
            /* 返回 pdh->pub_key(即公钥) 的字节数 */
            int32_t key_size = BN_num_bytes(pdh->pub_key);
            if (key_size != 128) {
                srs_warn("regenerate 128B key, current=%dB", key_size);
                continue;
            }
        }
        
        break;
    }
    
    return ret;
}

2.6.4 SrsDH::do_initialize

int SrsDH::do_initialize()
{
    int ret = ERROR_SUCCESS;
    
    int32_t bits_count = 1024;
    
    close();
    
    /* DH: 公钥算法 */
    //1. Create the DH
    /* 生成 DH 数据结构,其 DH_METHOD 采用 openssl 默认提供的 */
    if ((pdh = DH_new()) == NULL) {
        ret = ERROR_OpenSslCreateDH; 
        return ret;
    }

    /* BN:openssl 中有关大数运算函数 */
    //2. Create his internal p and g
    /* 创建一个 BIGNUM 的结构,返回新的 BIGNUM 结构的指针 */
    if ((pdh->p = BN_new()) == NULL) {
        ret = ERROR_OpenSslCreateP; 
        return ret;
    }
    if ((pdh->g = BN_new()) == NULL) {
        ret = ERROR_OpenSslCreateG; 
        return ret;
    }

    //3. initialize p and g, @see ./test/ectest.c:260
    /* 赋 16 进制值 RFC2409_PRIME_1024 到 pdh->p 中,返回成功与否 */
    if (!BN_hex2bn(&pdh->p, RFC2409_PRIME_1024)) {
        ret = ERROR_OpenSslParseP1024; 
        return ret;
    }
    // @see ./test/bntest.c:1764
    /* 设置 pdh->g 为 2 */
    if (!BN_set_word(pdh->g, 2)) {
        ret = ERROR_OpenSslSetG;
        return ret;
    }

    // 4. Set the key length
    pdh->length = bits_count;

    /* 生成己方的 DH 公私钥 */
    // 5. Generate private and public key
    // @see ./test/dhtest.c:152
    if (!DH_generate_key(pdh)) {
        ret = ERROR_OpenSslGenerateDHKeys;
        return ret;
    }
    
    return ret;
}

2.6.5 SrsDH::copy_shared_key

int SrsDH::copy_shared_key(const char* ppkey, int32_t ppkey_size, 
    char* skey, int32_t& skey_size)
{
    int ret = ERROR_SUCCESS;
    
    BIGNUM* ppk = NULL;
    /* 赋二进制值 ppkey 到第三个参数 BIGNUM 类型的变量中,同时返回该 BIGNUM 类指针
     * 有前面调用知,传入的 ppkey 为解析 c1 后的 128 字节的 key 数据,因此这里是
     * 生成对方的公钥 */
    if ((ppk = BN_bin2bn((const unsigned char*)ppkey, ppkey_size, 0)) == NULL) {
        ret = ERROR_OpenSslGetPeerPublicKey;
        return ret;
    }
    
    // if failed, donot return, do cleanup, @see ./test/dhtest.c:168
    // maybe the key_size is 127, but dh will write all 128bytes skey,
    // so, donot need to set/initialize the skey.
    // @see https://github.com/ossrs/srs/issues/165: public key有时候不是128字节
    /* 计算共享密钥,用于数据交换
     * 也就是根据对方公钥 ppk 和己方 pdh 密钥来生成共享密钥 skey,返回生成的共享密钥大小 */
    int32_t key_size = DH_compute_key((unsigned char*)skey, ppk, pdh);
    
    /* 若生成的共享密钥大小 < 对方的公钥大小 */
    if (key_size < ppkey_size) {
        srs_warn("shared key size=%d, ppk_size=%d", key_size, ppkey_size);
    }
    
    if (key_size < 0 || key_size > skey_size) {
        ret = ERROR_OpenSslComputeSharedKey;
    } else {
        /* 通过 skey_size 返回生成的共享密钥的大小 */
        skey_size = key_size;
    }
    
    /* 释放生成的对方公钥 BIGNUM 类数据 */
    if (ppk) {
        BN_free(ppk);
    }
    
    return ret;
}

2.6.6 c1s1::s1_validate_digest

int c1s1::s1_validate_digest(bool& is_valid)
{
    is_valid = false;
    srs_assert(payload);
    return payload->s1_validate_digest(this, is_valid);
}

2.6.7 c1s1_strategy::s1_validate_digest

int c1s1_strategy::s1_validate_digest(c1s1* owner, bool& is_valid)
{
    int ret = ERROR_SUCCESS;
    
    char* s1_digest = NULL;
    
    /* 计算 s1 的 digest */
    if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) {
        srs_error("validate s1 error, failed to calc digest. ret=%d", ret);
        return ret;
    }
    
    srs_assert(s1_digest != NULL);
    SrsAutoFreeA(char, s1_digest);
    
    /* 比较两个 digest 是否一致 */
    is_valid = srs_bytes_equals(digest.digest, s1_digest, 32);
    
    return ret;
}

2.7 encode s2: c2s2::s2_create

/*
 * RTMP complex handshake: C2/S2
 * S2: 前 1504 bytes数据随机生成(前 8 bytes需要注意),然后对 1504 bytes 数据进行HMACsha256
 * 得到digest,将digest放到最后的32bytes。
 *
 * 1536 bytes C2/S2 结构  
 * random-data: 1504 bytes  
 * digest-data: 32 bytes
 */
int c2s2::s2_create(c1s1* c1)
{
    int ret = ERROR_SUCCESS;
    
    /* 根据 SrsGenuineFMSKey 和 c1->get_digest() 生成一个临时的 key  */
    char temp_key[SRS_OpensslHashSize];
    if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) 
        != ERROR_SUCCESS) {
        srs_error("create s2 temp key failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("generate s2 temp key success.");
    
    /* 接着根据 temp_key 和 random 生成 digest */
    char _digest[SRS_OpensslHashSize];
    if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) {
        srs_error("create s2 digest failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("generate s2 digest success.");
    
    memcpy(digest, _digest, 32);
    
    return ret;
}

2.8 SrsHandshakeBytes::create_s0s1s2

int SrsHandshakeBytes::create_s0s1s2(const char* c1)
{
    int ret = ERROR_SUCCESS;
    
    if (s0s1s2) {
        return ret;
    }
    
    s0s1s2 = new char[3073];
    /* 将 s0s1s2 先全部填充随机数 */
    srs_random_generate(s0s1s2, 3073);
    
    // plain text required.
    SrsStream stream;
    /* 初始化一个字节流,起始地址为 s0s1s2 首地址,大小 9 字节 */
    if ((ret = stream.initialize(s0s1s2, 9)) != ERROR_SUCCESS) {
        return ret;
    }
    /* 首先写入 s0,即版本号,通常为 0x03 */
    stream.write_1bytes(0x03);
    /* 接着写入 4 字节的当前时间 */
    stream.write_4bytes((int32_t)::time(NULL));
    // s1 time2 copy from c1
    if (c0c1) {
        /* 将 c0c1 中的 time 写入到 s0s1s2 中 */
        stream.write_bytes(c0c1 + 1, 4);
    }
    
    // if c1 specified, copy c1 to s2.
    // @see: https://github.com/ossrs/srs/issues/46:RTMPDUMP/FFMPEG总说客户端的signature不匹配
    if (c1) {
        memcpy(s0s1s2 + 1537, c1, 1536);
    }
}

2.9 c1s1::dump

int c1s1::dump(char* _c1s1, int size)
{
    srs_assert(size == 1536);
    srs_assert(payload != NULL);
    return payload->dump(this, _c1s1, size);
}

2.9.1 c1s1_strategy::dump

int c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size)
{
    srs_assert(size == 1536);
    return copy_to(owner, _c1s1, size, true);
}

2.9.2 c1s1_strategy_schema0::copy_to

int c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, int size, bool with_digest)
{
    int ret = ERROR_SUCCESS;
    
    if (with_digest) {
        srs_assert(size == 1536);
    } else {
        srs_assert(size == 1504);
    }
    
    SrsStream stream;
    
    if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    copy_time_version(&stream, owner);
    copy_key(&stream);
    copy_digest(&stream, with_digest);
    
    srs_assert(stream.empty());
    
    return ret;
}

2.10 SrsStSocket::write

位于 srs_app_st.cpp.

int SrsStSocket::write(void* buf, size_t size, ssize_t* nwrite)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nb_write = st_write(stfd, buf, size, send_timeout);
    if (nwrite) {
        *nwrite = nb_write;
    }
    
    // On success a non-negative integer equal to nbyte is returned.
    // Otherwise, a value of -1 is returned and errno is set to indicate the error.
    if (nb_write <= 0) {
        // @see https://github.com/ossrs/srs/issues/200: 
        // when timeout, the read always timeout when fd closed.
        if (nb_write < 0 && errno == ETIME) {
            return ERROR_SOCKET_TIMEOUT;
        }
        
        return ERROR_SOCKET_WRITE;
    }
    
    send_bytes += nb_write;
    
    return ret;
}

2.10.1 st_write

ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout)
{
    size_t resid = nbyte;
    return st_write_resid(fd, buf, &resid, timeout) == 0 ?  
        (ssize_t) (nbyte - resid) : -1;
}

2.10.2 st_write_resid

int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout)
{
    struct iovec iov, *riov;
    int riov_size, rv;
    
    iov.iov_base = (void *) buf;    /* we promise not to modify buf */
    iov.iov_len = *resid;
    riov = &iov;
    riov_size = 1;
    rv = st_writev_resid(fd, &riov, &riov_size, timeout);
    *resid = iov.iov_len;
    return rv;
}

2.10.3 st_writev_resid

int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, 
    st_utime_t timeout)
{
    ssize_t n;
    
    while (*iov_size > 0) {
        if (*iov_size == 1) 
            n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
        else 
            n = writev(fd->osfd, *iov, *iov_size);
        if (n < 0) {
            if (errno == EINTR)
                continue;
            if (!_IO_NOT_READY_ERROR)
                return -1;
        } else {
            while ((size_t) n >= (*iov)->iov_len) {
                n -= (*iov)->iov_len;
                (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
                (*iov)->iov_len = 0;
                (*iov)++;
                (*iov_size)--;
                if (n == 0)
                    break;
            }
            if (*iov_size == 0)
                break;
            (*iov)->iov_base = (char *) (*iov)->iov_base + n;
            (*iov)->iov_len -= n;
        }
        /* wait until the socket becomes writable */
        if (st_netfd_poll(fd, POLLOUT, timeout) < 0)
            return -1;
    }
    
    return 0;
}

2.11 SrsHandshakeBytes::read_c2

int SrsHandshakeBytes::read_c2(ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    if (c1) {
        return ret;
    }
    
    ssize_t nsize;
    
    c2 = new char[1536];
    if ((ret = io->read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) {
        srs_warn("read c2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("read c2 success.");
    
    return ret;
}

2.12 decode c2: c2s2::parse

/*
 * RTMP complex handshake: C2/S2
 *
 * 1536 bytes C2/S2 结构  
 * random-data: 1504 bytes  
 * digest-data: 32 bytes
 */
int c2s2::parse(char* _c2s2, int size)
{
    srs_assert(size == 1536);
    
    memcpy(random, _c2s2, 1504);
    memcpy(digest, _c2s2 + 1504, 32);
    
    return ERROR_SUCCESS;
}

3. simple handshake

3.1 SrsSimpleHandshake 类定义

/**
 * simple handshake.
 * user can try complex handshake first,
 * rollback to simple handshake if error ERROR_RTMP_TRY_SIMPLE_HS
 */
class SrsSimpleHandshake
{
public:
    SrsSimpleHandshake();
    virtual ~SrsSimpleHandshake();
public:
    /**
     * simple handhshake.
     */
    virtual int handshake_with_client(SrsHandshakeBytes* hs_bytes, 
            ISrsProtocolReaderWriter* io);
    virtual int handshake_with_server(SrsHandshakeBytes* hs_bytes, 
            ISrsProtocolReaderWriter* io);
};

3.2 SrsSimpleHandshake::handshake_with_client

/**
 * @hs_bytes: SrsHandshakeBytes 类指针,提供读取/生成 handshake 过程数据包的方法
 * @io: 父类(ISrsProtocolReaderWriter)指针指向子类(SrsStSocket)指针对象
 */
int SrsSimpleHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, 
    ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nsize;
    
    if ((ret = hs_bytes->read_c0c1(io)) != ERROR_SUCCESS) {
        return ret;
    }

    // plain text required.
    if (hs_bytes->c0c1[0] != 0x03) {
        ret = ERROR_RTMP_PLAIN_REQUIRED;
        srs_warn("only support rtmp plain text. ret=%d", ret);
        return ret;
    }
    srs_verbose("check c0 success, required plain text.");
    
    if ((ret = hs_bytes->create_s0s1s2(hs_bytes->c0c1 + 1)) != ERROR_SUCCESS) {
        return ret;
    }
    
    /* 发送 s0s1s2 */
    if ((ret = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) {
        srs_warn("simple handshake send s0s1s2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("simple handshake send s0s1s2 success.");
    
    if ((ret = hs_bytes->read_c2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    srs_trace("simple handshake success.");
    
    return ret;
}

从这里分析,s0、s1、s2 格式:

  • s0: 1 byte,version,为 0x03
  • s1:
    • time:4 bytes,当前时间
    • time2:4 bytes,拷贝自接收到的 c1 的开始 4 字节 time
    • 余下随机数
  • s2: 完全拷贝自 c1 数据

3.3 SrsHandshakeBytes::create_s0s1s2

int SrsHandshakeBytes::create_s0s1s2(const char* c1)
{
    int ret = ERROR_SUCCESS;
    
    if (s0s1s2) {
        return ret;
    }
    
    s0s1s2 = new char[3073];
    srs_random_generate(s0s1s2, 3073);
    
    // plain text required.
    SrsStream stream;
    if ((ret = stream.initialize(s0s1s2, 9)) != ERROR_SUCCESS) {
        return ret;
    }
    /* s0:1 byte,版本号,为 0x03 */
    stream.write_1bytes(0x03);
    /* s1: time, 4 bytes */
    stream.write_4bytes((int32_t)::time(NULL));
    // s1 time2 copy from c1
    if (c0c1) {
        /* 若是在 simple handshake 情况下,将接收到的 c1 的开始 4 字节 time 
         * 拷贝到 c1 的 4~7 字节中 */
        stream.write_bytes(c0c1 + 1, 4);
    }
    
    // if c1 specified, copy c1 to s2.
    // @see: https://github.com/ossrs/srs/issues/46
    if (c1) {
        /* 在 simple handshake,直接拷贝 c1 数据作为 s2 */
        memcpy(s0s1s2 + 1537, c1, 1536);
    }
    
    return ret;
}
posted @ 2018-05-20 22:19  季末的天堂  阅读(2684)  评论(0编辑  收藏  举报