當服務器和客戶端的握手是按照rtmp協議進行,是不支持h264/aac的,有數據,就是沒有視頻和聲音。
原因是adobe變更了握手的數據結構,標準rtmp協議的握手的包是隨機的1536字節(S1S2C1C2),變更後的是需要進行摘要和加密。
rtmp協議定義的爲simple handshake,變更後加密握手可稱爲complex handshake。
本文詳細分析了rtmpd(ccrtmpserver)中的處理邏輯,以及rtmpdump的處理邏輯,從一個全是魔法數字的世界找到他們的數據結構和算法。
complex handshake C1S1結構
complex handshake將C1S1分爲4個部分,它們的順序(schema)一種可能是:
- // c1s1 schema0
- time: 4bytes
- version: 4bytes
- key: 764bytes
- digest: 764bytes
其中,key和digest可能會交換位置,即schema可能是:
- // c1s1 schema1
- time: 4bytes
- version: 4bytes
- digest: 764bytes
- key: 764bytes
客戶端來決定使用哪種schema,服務器端則需要先嚐試按照schema0解析,失敗則用schema1解析。如下圖所示:
無論key和digest位置如何,它們的結構是不變的:
- // 764bytes key結構
- random-data: (offset)bytes
- key-data: 128bytes
- random-data: (764-offset-128-4)bytes
- offset: 4bytes
-
- // 764bytes digest結構
- offset: 4bytes
- random-data: (offset)bytes
- digest-data: 32bytes
- random-data: (764-4-offset-32)bytes
備註:發現FMS只認識digest-key結構。
如下圖所示:
crtmp中這些全是魔法數字。
complex handshake C2S2結構
C2S2主要是提供對C1S1的驗證。結構如下:
- // 1536bytes C2S2結構
- random-data: 1504bytes
- digest-data: 32bytes
C2S2的結構相對比較簡單。如下圖所示:
下面介紹C1S1C2S2的生成以及驗證算法。
complex handshake C1S1算法
C1S1中都是包含32字節的digest,而且digest將C1S1分成兩部分:
- // C1S1被digest分成兩部分
- c1s1-part1: n bytes
- digest-data: 32bytes
- c1s1-part2: (1536-n-32)bytes
如下圖所示:
在生成C1時,需要用到c1s1-part1和c1s1-part2這兩個部分的字節拼接起來的字節,定義爲:
- c1s1-joined = bytes_join(c1s1-part1, c1s1-part2)
也就是說,把1536字節的c1s1中的32字節的digest拿剪刀剪掉,剩下的頭和尾加在一起就是c1s1-joined。
用到的兩個常量FPKey和FMSKey:
- u_int8_t FMSKey[] = {
- 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
- 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
- 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
- 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 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
- }; // 68
-
- u_int8_t FPKey[] = {
- 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
生成C1的算法如下:
- 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
生成S1的算法如下:
- /*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.
C1S1的算法完畢。
complex handshake C2S2
C2S2的生成算法如下:
- random fill c2s2 1536 bytes
-
- // client generate C2, or server valid C2
- temp-key = HMACsha256(s1-digest, FPKey, 62)
- c2-digest-data = HMACsha256(c2-random-data, temp-key, 32)
-
- // server generate S2, or client valid S2
- temp-key = HMACsha256(c1-digest, FMSKey, 68)
- s2-digest-data = HMACsha256(s2-random-data, temp-key, 32)
驗證的算法是一樣的。
代碼實現
- /*
- The MIT License (MIT)
-
- Copyright (c) 2013-2014 winlin
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
- #ifndef SRS_RTMP_PROTOCOL_HANDSHKAE_HPP
- #define SRS_RTMP_PROTOCOL_HANDSHKAE_HPP
-
- /*
- #include
- */
-
- #include
-
- class ISrsProtocolReaderWriter;
- class SrsComplexHandshake;
- class SrsHandshakeBytes;
- class SrsStream;
-
- #ifdef SRS_AUTO_SSL
-
- // for openssl.
- #include
-
- namespace _srs_internal
- {
- // the digest key generate size.
- #define __SRS_OpensslHashSize 512
- extern u_int8_t SrsGenuineFMSKey[];
- extern u_int8_t SrsGenuineFPKey[];
- int openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest);
- int openssl_generate_key(char* public_key, int32_t size);
-
- /**
- * the DH wrapper.
- */
- class SrsDH
- {
- private:
- DH* pdh;
- public:
- SrsDH();
- virtual ~SrsDH();
- public:
- /**
- * initialize dh, generate the public and private key.
- * @param ensure_128bytes_public_key whether ensure public key is 128bytes,
- * sometimes openssl generate 127bytes public key.
- * default to false to donot ensure.
- */
- virtual int initialize(bool ensure_128bytes_public_key = false);
- /**
- * copy the public key.
- * @param pkey the bytes to copy the public key.
- * @param pkey_size the max public key size, output the actual public key size.
- * user should never ignore this size.
- * @remark, when ensure_128bytes_public_key, the size always 128.
- */
- virtual int copy_public_key(char* pkey, int32_t& pkey_size);
- /**
- * generate and copy the shared key.
- * generate the shared key with peer public key.
- * @param ppkey peer public key.
- * @param ppkey_size the size of ppkey.
- * @param skey the computed shared key.
- * @param skey_size the max shared key size, output the actual shared key size.
- * user should never ignore this size.
- */
- virtual int copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size);
- private:
- virtual int do_initialize();
- };
- /**
- * 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,
- };
-
- /**
- * 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();
- };
-
- /**
- * 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();
- };
-
- class c1s1;
-
- /**
- * 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);
- };
-
- /**
- * c1s1 schema0
- * key: 764bytes
- * digest: 764bytes
- */
- class c1s1_strategy_schema0 : public c1s1_strategy
- {
- public:
- c1s1_strategy_schema0();
- virtual ~c1s1_strategy_schema0();
- public:
- virtual srs_schema_type schema();
- virtual int parse(char* _c1s1, int size);
- public:
- virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest);
- };
-
- /**
- * c1s1 schema1
- * digest: 764bytes
- * key: 764bytes
- */
- class c1s1_strategy_schema1 : public c1s1_strategy
- {
- public:
- c1s1_strategy_schema1();
- virtual ~c1s1_strategy_schema1();
- public:
- virtual srs_schema_type schema();
- virtual int parse(char* _c1s1, int size);
- public:
- virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest);
- };
-
- /**
- * c1s1 schema0
- * time: 4bytes
- * version: 4bytes
- * key: 764bytes
- * digest: 764bytes
- * c1s1 schema1
- * time: 4bytes
- * version: 4bytes
- * digest: 764bytes
- * key: 764bytes
- * @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);
- };
-
- /**
- * the c2s2 complex handshake structure.
- * random-data: 1504bytes
- * digest-data: 32bytes
- * @see also: http://blog.csdn.net/win_lin/article/details/13006803
- */
- class c2s2
- {
- public:
- char random[1504];
- char digest[32];
- public:
- c2s2();
- virtual ~c2s2();
- public:
- /**
- * copy to bytes.
- * @param size, must always be 1536.
- */
- virtual int dump(char* _c2s2, int size);
- /**
- * parse the c2s2
- * @param size, must always be 1536.
- */
- virtual int parse(char* _c2s2, int size);
- public:
- /**
- * create c2.
- * random fill c2s2 1536 bytes
- *
- * // client generate C2, or server valid C2
- * temp-key = HMACsha256(s1-digest, FPKey, 62)
- * c2-digest-data = HMACsha256(c2-random-data, temp-key, 32)
- */
- virtual int c2_create(c1s1* s1);
-
- /**
- * validate the c2 from client.
- */
- virtual int c2_validate(c1s1* s1, bool& is_valid);
- public:
- /**
- * create s2.
- * random fill c2s2 1536 bytes
- *
- * // server generate S2, or client valid S2
- * temp-key = HMACsha256(c1-digest, FMSKey, 68)
- * s2-digest-data = HMACsha256(s2-random-data, temp-key, 32)
- */
- virtual int s2_create(c1s1* c1);
-
- /**
- * validate the s2 from server.
- */
- virtual int s2_validate(c1s1* c1, bool& is_valid);
- };
- }
-
- #endif
-
- /**
- * 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 handshake.
- */
- virtual int handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
- virtual int handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
- };
-
- /**
- * 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 hanshake.
- * @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);
- };
-
- #endif
- /*
- The MIT License (MIT)
-
- Copyright (c) 2013-2014 winlin
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
- #include
-
- #include
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #ifdef SRS_AUTO_SSL
-
- using namespace _srs_internal;
-
- // for openssl_HMACsha256
- #include
- #include
- // for __openssl_generate_key
- #include
-
- namespace _srs_internal
- {
- // 68bytes FMS key which is used to sign the sever packet.
- u_int8_t SrsGenuineFMSKey[] = {
- 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
- 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
- 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
- 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 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
- }; // 68
-
- // 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 __openssl_HMACsha256(HMAC_CTX* ctx, const void* data, int data_size, void* digest, unsigned int* digest_size)
- {
- int ret = ERROR_SUCCESS;
-
- if (HMAC_Update(ctx, (unsigned char *) data, data_size) < 0) {
- ret = ERROR_OpenSslSha256Update;
- return ret;
- }
-
- if (HMAC_Final(ctx, (unsigned char *) digest, digest_size) < 0) {
- ret = ERROR_OpenSslSha256Final;
- return ret;
- }
-
- return ret;
- }
- /**
- * 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* __key = (unsigned char*)key;
- unsigned char* __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, __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().
- HMAC_CTX_init(&ctx);
-
- if (HMAC_Init_ex(&ctx, __key, key_size, EVP_sha256(), NULL) < 0) {
- ret = ERROR_OpenSslSha256Init;
- return ret;
- }
-
- ret = __openssl_HMACsha256(&ctx, data, data_size, __digest, &digest_size);
- HMAC_CTX_cleanup(&ctx);
-
- if (ret != ERROR_SUCCESS) {
- return ret;
- }
- }
-
- if (digest_size != 32) {
- ret = ERROR_OpenSslSha256DigestSize;
- return ret;
- }
-
- return ret;
- }
-
- #define RFC2409_PRIME_1024 \
- "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
- "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
- "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
- "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
- "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
- "FFFFFFFFFFFFFFFF"
-
- SrsDH::SrsDH()
- {
- pdh = NULL;
- }
-
- SrsDH::~SrsDH()
- {
- if (pdh != NULL) {
- if (pdh->p != NULL) {
- BN_free(pdh->p);
- pdh->p = NULL;
- }
- if (pdh->g != NULL) {
- BN_free(pdh->g);
- pdh->g = NULL;
- }
- DH_free(pdh);
- pdh = NULL;
- }
- }
-
- int SrsDH::initialize(bool ensure_128bytes_public_key)
- {
- int ret = ERROR_SUCCESS;
-
- for (;;) {
- if ((ret = do_initialize()) != ERROR_SUCCESS) {
- return ret;
- }
-
- if (ensure_128bytes_public_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;
- }
-
- int SrsDH::copy_public_key(char* pkey, int32_t& pkey_size)
- {
- int ret = ERROR_SUCCESS;
-
- // copy public key to bytes.
- // sometimes, the key_size is 127, seems ok.
- int32_t key_size = BN_num_bytes(pdh->pub_key);
- srs_assert(key_size > 0);
-
- // maybe the key_size is 127, but dh will write all 128bytes pkey,
- // so, donot need to set/initialize the pkey.
- // @see https://github.com/winlinvip/simple-rtmp-server/issues/165
- key_size = BN_bn2bin(pdh->pub_key, (unsigned char*)pkey);
- srs_assert(key_size > 0);
-
- // output the size of public key.
- // @see https://github.com/winlinvip/simple-rtmp-server/issues/165
- srs_assert(key_size <= pkey_size);
- pkey_size = key_size;
-
- return ret;
- }
-
- 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;
- 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/winlinvip/simple-rtmp-server/issues/165
- 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 = key_size;
- }
-
- if (ppk) {
- BN_free(ppk);
- }
-
- return ret;
- }
-
- int SrsDH::do_initialize()
- {
- int ret = ERROR_SUCCESS;
-
- int32_t bits_count = 1024;
-
- //1. Create the DH
- if ((pdh = DH_new()) == NULL) {
- ret = ERROR_OpenSslCreateDH;
- return ret;
- }
-
- //2. Create his internal p and g
- 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
- if (!BN_hex2bn(&pdh->p, RFC2409_PRIME_1024)) {
- ret = ERROR_OpenSslParseP1024;
- return ret;
- }
- // @see ./test/bntest.c:1764
- if (!BN_set_word(pdh->g, 2)) {
- ret = ERROR_OpenSslSetG;
- return ret;
- }
-
- // 4. Set the key length
- pdh->length = bits_count;
-
- // 5. Generate private and public key
- // @see ./test/dhtest.c:152
- if (!DH_generate_key(pdh)) {
- ret = ERROR_OpenSslGenerateDHKeys;
- return ret;
- }
-
- return ret;
- }
-
- key_block::key_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(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_block::~key_block()
- {
- srs_freep(random0);
- srs_freep(random1);
- }
-
- int key_block::parse(SrsStream* stream)
- {
- int ret = ERROR_SUCCESS;
-
- // the key must be 764 bytes.
- srs_assert(stream->require(764));
-
- // 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);
-
- int valid_offset = calc_valid_offset();
- srs_assert(valid_offset >= 0);
-
- random0_size = valid_offset;
- if (random0_size > 0) {
- srs_freep(random0);
- random0 = new char[random0_size];
- stream->read_bytes(random0, random0_size);
- }
-
- stream->read_bytes(key, 128);
-
- random1_size = 764 - valid_offset - 128 - 4;
- if (random1_size > 0) {
- srs_freep(random1);
- random1 = new char[random1_size];
- stream->read_bytes(random1, random1_size);
- }
-
- return ret;
- }
-
- 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;
- }
-
- 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_block::~digest_block()
- {
- srs_freep(random0);
- srs_freep(random1);
- }
-
- 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_freep(random0);
- random0 = new char[random0_size];
- stream->read_bytes(random0, random0_size);
- }
-
- stream->read_bytes(digest, 32);
-
- random1_size = 764 - 4 - valid_offset - 32;
- if (random1_size > 0) {
- srs_freep(random1);
- random1 = new char[random1_size];
- stream->read_bytes(random1, random1_size);
- }
-
- return ret;
- }
-
- int digest_block::calc_valid_offset()
- {
- int max_offset_size = 764 - 32 - 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;
- }
-
- c1s1_strategy::c1s1_strategy()
- {
- }
-
- c1s1_strategy::~c1s1_strategy()
- {
- }
-
- char* c1s1_strategy::get_digest()
- {
- return digest.digest;
- }
-
- char* c1s1_strategy::get_key()
- {
- return key.key;
- }
-
- int c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size)
- {
- srs_assert(size == 1536);
- return copy_to(owner, _c1s1, size, true);
- }
-
- int c1s1_strategy::c1_create(c1s1* owner)
- {
- int ret = ERROR_SUCCESS;
-
- // generate digest
- char* c1_digest = NULL;
-
- if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) {
- srs_error("sign c1 error, failed to calc digest. ret=%d", ret);
- return ret;
- }
-
- srs_assert(c1_digest != NULL);
- SrsAutoFree(char, c1_digest);
-
- memcpy(digest.digest, c1_digest, 32);
-
- return ret;
- }
-
- 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);
- SrsAutoFree(char, c1_digest);
-
- is_valid = srs_bytes_equals(digest.digest, c1_digest, 32);
-
- return ret;
- }
-
- int c1s1_strategy::s1_create(c1s1* owner, c1s1* c1)
- {
- int ret = ERROR_SUCCESS;
-
- SrsDH dh;
-
- // ensure generate 128bytes public key.
- if ((ret = dh.initialize(true)) != ERROR_SUCCESS) {
- return ret;
- }
-
- // directly generate the public key.
- // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148
- int pkey_size = 128;
- 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;
- }
- srs_assert(pkey_size == 128);
- srs_verbose("calc s1 key success.");
-
- char* s1_digest = NULL;
- if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) {
- srs_error("calc s1 digest failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("calc s1 digest success.");
-
- srs_assert(s1_digest != NULL);
- SrsAutoFree(char, s1_digest);
-
- memcpy(digest.digest, s1_digest, 32);
- srs_verbose("copy s1 key success.");
-
- return ret;
- }
-
- int c1s1_strategy::s1_validate_digest(c1s1* owner, bool& is_valid)
- {
- int ret = ERROR_SUCCESS;
-
- char* s1_digest = NULL;
-
- 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);
- SrsAutoFree(char, s1_digest);
-
- is_valid = srs_bytes_equals(digest.digest, s1_digest, 32);
-
- return ret;
- }
-
- 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: 32bytes
- * 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];
- SrsAutoFree(char, 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_freep(c1_digest);
- srs_error("calc digest for c1 failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("digest calculated for c1");
-
- return ret;
- }
-
- int c1s1_strategy::calc_s1_digest(c1s1* owner, char*& s1_digest)
- {
- int ret = ERROR_SUCCESS;
-
- /**
- * c1s1 is splited by digest:
- * c1s1-part1: n bytes (time, version, key and digest-part1).
- * digest-data: 32bytes
- * 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];
- SrsAutoFree(char, c1s1_joined_bytes);
- if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) {
- return ret;
- }
-
- s1_digest = new char[__SRS_OpensslHashSize];
- if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) {
- srs_freep(s1_digest);
- srs_error("calc digest for s1 failed. ret=%d", ret);
- return ret;
- }
- srs_verbose("digest calculated for s1");
-
- return ret;
- }
-
- void c1s1_strategy::copy_time_version(SrsStream* stream, c1s1* owner)
- {
- srs_assert(stream->require(8));
-
- // 4bytes time
- stream->write_4bytes(owner->time);
-
- // 4bytes version
- stream->write_4bytes(owner->version);
- }
- void c1s1_strategy::copy_key(SrsStream* stream)
- {
- srs_assert(key.random0_size >= 0);
- srs_assert(key.random1_size >= 0);
-
- int total = key.random0_size + 128 + key.random1_size + 4;
- srs_assert(stream->require(total));
-
- // 764bytes key block
- if (key.random0_size > 0) {
- stream->write_bytes(key.random0, key.random0_size);
- }
-
- stream->write_bytes(key.key, 128);
-
- if (key.random1_size > 0) {
- stream->write_bytes(key.random1, key.random1_size);
- }
-
- stream->write_4bytes(key.offset);
- }
- void c1s1_strategy::copy_digest(SrsStream* stream, bool with_digest)
- {
- srs_assert(key.random0_size >= 0);
- srs_assert(key.random1_size >= 0);
-
- int total = 4 + digest.random0_size + digest.random1_size;
- if (with_digest) {
- total += 32;
- }
- srs_assert(stream->require(total));
-
- // 732bytes digest block without the 32bytes digest-data
- // nbytes digest block part1
- stream->write_4bytes(digest.offset);
-
- // digest random padding.
- if (digest.random0_size > 0) {
- stream->write_bytes(digest.random0, digest.random0_size);
- }
-
- // digest
- if (with_digest) {
- stream->write_bytes(digest.digest, 32);
- }
-
- // nbytes digest block part2
- if (digest.random1_size > 0) {
- stream->write_bytes(digest.random1, digest.random1_size);
- }
- }
-
- c1s1_strategy_schema0::c1s1_strategy_schema0()
- {
- }
-
- c1s1_strategy_schema0::~c1s1_strategy_schema0()
- {
- }
-
- srs_schema_type c1s1_strategy_schema0::schema()
- {
- return srs_schema0;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- c1s1_strategy_schema1::c1s1_strategy_schema1()
- {
- }
-
- c1s1_strategy_schema1::~c1s1_strategy_schema1()
- {
- }
-
- srs_schema_type c1s1_strategy_schema1::schema()
- {
- return srs_schema1;
- }
-
- int c1s1_strategy_schema1::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;
- }
-
- if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) {
- srs_error("parse the c1 digest failed. ret=%d", ret);
- return ret;
- }
-
- if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) {
- return ret;
- }
-
- if ((ret = key.parse(&stream)) != ERROR_SUCCESS) {
- srs_error("parse the c1 key failed. ret=%d", ret);
- return ret;
- }
-
- srs_verbose("parse c1 digest-key success");
-
- return ret;
- }
-
- int c1s1_strategy_schema1::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_digest(&stream, with_digest);
- copy_key(&stream);
-
- srs_assert(stream.empty());
-
- return ret;
- }
-
- c1s1::c1s1()
- {
- payload = NULL;
- }
- c1s1::~c1s1()
- {
- srs_freep(payload);
- }
-
- srs_schema_type c1s1::schema()
- {
- srs_assert(payload != NULL);
- return payload->schema();
- }
-
- char* c1s1::get_digest()
- {
- srs_assert(payload != NULL);
- return payload->get_digest();
- }
-
- char* c1s1::get_key()
- {
- srs_assert(payload != NULL);
- return payload->get_key();
- }
-
- int c1s1::dump(char* _c1s1, int size)
- {
- srs_assert(size == 1536);
- srs_assert(payload != NULL);
- return payload->dump(this, _c1s1, size);
- }
-
- int c1s1::parse(char* _c1s1, int size, srs_schema_type schema)
- {
- int ret = ERROR_SUCCESS;
-
- srs_assert(size == 1536);
-
- 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 stream;
-
- if ((ret = stream.initialize(_c1s1, size)) != ERROR_SUCCESS) {
- return ret;
- }
-
- time = stream.read_4bytes();
- version = stream.read_4bytes(); // client c1 version
-
- srs_freep(payload);
- if (schema == srs_schema0) {
- payload = new c1s1_strategy_schema0();
- } else {
- payload = new c1s1_strategy_schema1();
- }
-
- return payload->parse(_c1s1, size);
- }
-
- int c1s1::c1_create(srs_schema_type schema)
- {