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
recv: c2
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;
}