root/trunk/sources/thelib/src/protocols/rtmp/inboundrtmpprotocol.cpp

Revision 56, 8.5 KB (checked in by shiretu, 3 weeks ago)

-- common library refactoring

Line 
1/*
2 *  Copyright (c) 2010,
3 *  Gavriloaie Eugen-Andrei (shiretu@gmail.com)
4 *
5 *  This file is part of crtmpserver.
6 *  crtmpserver is free software: you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation, either version 3 of the License, or
9 *  (at your option) any later version.
10 *
11 *  crtmpserver is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with crtmpserver.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAS_PROTOCOL_RTMP
21#include "protocols/rtmp/inboundrtmpprotocol.h"
22#include "protocols/rtmp/rtmpeprotocol.h"
23#include "protocols/rtmp/basertmpappprotocolhandler.h"
24
25InboundRTMPProtocol::InboundRTMPProtocol()
26: BaseRTMPProtocol(PT_INBOUND_RTMP) {
27        _pKeyIn = NULL;
28        _pKeyOut = NULL;
29        _pOutputBuffer = NULL;
30        _currentFPVersion = 0;
31        _validationScheme = 0;
32}
33
34InboundRTMPProtocol::~InboundRTMPProtocol() {
35        if (_pKeyIn != NULL) {
36                delete _pKeyIn;
37                _pKeyIn = NULL;
38        }
39
40        if (_pKeyOut != NULL) {
41                delete _pKeyOut;
42                _pKeyOut = NULL;
43        }
44
45        if (_pOutputBuffer != NULL) {
46                delete[] _pOutputBuffer;
47                _pOutputBuffer = NULL;
48        }
49}
50
51bool InboundRTMPProtocol::PerformHandshake(IOBuffer &buffer) {
52        switch (_rtmpState) {
53                case RTMP_STATE_NOT_INITIALIZED:
54                {
55                        if (GETAVAILABLEBYTESCOUNT(buffer) < 1537) {
56                                //FINEST("Not enough data");
57                                return true;
58                        }
59                        uint8_t handshakeType = GETIBPOINTER(buffer)[0];
60                        if (!buffer.Ignore(1)) {
61                                FATAL("Unable to ignore one byte");
62                                return false;
63                        }
64
65                        _currentFPVersion = ntohl(*((uint32_t *) (GETIBPOINTER(buffer) + 4))); //----MARKED-LONG---
66                        //FINEST("Flash player: %s", STR(_currentFlashPlayerVersion));
67
68                        switch (handshakeType) {
69                                case 3: //plain
70                                {
71                                        return PerformHandshake(buffer, false);
72                                }
73                                case 6: //encrypted
74                                {
75                                        return PerformHandshake(buffer, true);
76                                }
77                                default:
78                                {
79                                        FATAL("Handshake type not implemented: %d", handshakeType);
80                                        return false;
81                                }
82                        }
83                }
84                case RTMP_STATE_SERVER_RESPONSE_SENT:
85                {
86                        //FINEST("I: Player request 2:\n%s", STR(*pInputBuffer));
87                        if (GETAVAILABLEBYTESCOUNT(buffer) < 1536) {
88                                return true;
89                        } else {
90                                //ignore the client's last handshake part
91                                if (!buffer.Ignore(1536)) {
92                                        FATAL("Unable to ignore inbound data");
93                                        return false;
94                                }
95                                _handshakeCompleted = true;
96                                _rtmpState = RTMP_STATE_DONE;
97
98                                if (_pKeyIn != NULL && _pKeyOut != NULL) {
99                                        //insert the RTMPE protocol in the current protocol stack
100                                        BaseProtocol *pFarProtocol = GetFarProtocol();
101                                        RTMPEProtocol *pRTMPE = new RTMPEProtocol(_pKeyIn, _pKeyOut);
102                                        ResetFarProtocol();
103                                        pFarProtocol->SetNearProtocol(pRTMPE);
104                                        pRTMPE->SetNearProtocol(this);
105                                        FINEST("New protocol chain: %s", STR(*pFarProtocol));
106
107                                        //decrypt the leftovers
108                                        RC4(_pKeyIn, GETAVAILABLEBYTESCOUNT(buffer),
109                                                        GETIBPOINTER(buffer),
110                                                        GETIBPOINTER(buffer));
111                                }
112
113                                return true;
114                        }
115                }
116                default:
117                {
118                        FATAL("Invalid RTMP state: %d", _rtmpState);
119                        return false;
120                }
121        }
122}
123
124bool InboundRTMPProtocol::ValidateClient(IOBuffer &inputBuffer) {
125        if (_currentFPVersion == 0) {
126                WARN("This version of player doesn't support validation");
127                return true;
128        }
129        if (ValidateClientScheme(inputBuffer, 0)) {
130                _validationScheme = 0;
131                return true;
132        }
133        if (ValidateClientScheme(inputBuffer, 1)) {
134                _validationScheme = 1;
135                return true;
136        }
137        FATAL("Unable to validate client");
138        return false;
139}
140
141bool InboundRTMPProtocol::ValidateClientScheme(IOBuffer &inputBuffer, uint8_t scheme) {
142        uint8_t *pBuffer = GETIBPOINTER(inputBuffer);
143
144        uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
145
146        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
147        memcpy(pTempBuffer, pBuffer, clientDigestOffset);
148        memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32,
149                        1536 - clientDigestOffset - 32);
150
151        //uint8_t *pTempHash = new uint8_t[mhash_get_hash_pblock(MHASH_SHA256)];
152        uint8_t *pTempHash = new uint8_t[512];
153        HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
154
155        bool result = true;
156        for (uint32_t i = 0; i < 32; i++) {
157                if (pBuffer[clientDigestOffset + i] != pTempHash[i]) {
158                        result = false;
159                        break;
160                }
161        }
162
163        delete[] pTempBuffer;
164        delete[] pTempHash;
165
166        return result;
167}
168
169bool InboundRTMPProtocol::PerformHandshake(IOBuffer &buffer, bool encrypted) {
170        if (encrypted || _pProtocolHandler->ValidateHandshake()) {
171                if (!ValidateClient(buffer)) {
172                        FATAL("Unable to validate client");
173                        return false;
174                }
175        }
176
177        //get the buffers
178        uint8_t *pInputBuffer = GETIBPOINTER(buffer);
179        if (_pOutputBuffer == NULL) {
180                _pOutputBuffer = new uint8_t[3072];
181        } else {
182                delete[] _pOutputBuffer;
183                _pOutputBuffer = new uint8_t[3072];
184        }
185
186        //timestamp
187        *(uint32_t *) _pOutputBuffer = 0;
188
189        //version
190        *(uint32_t *) (_pOutputBuffer + 4) = htonl(0x01020304); //----MARKED-LONG---
191
192        //generate random data
193        srand((uint32_t) time(NULL));
194        for (uint32_t i = 8; i < 3072; i++) {
195                _pOutputBuffer[i] = rand() % 256;
196        }
197        for (uint32_t i = 0; i < 10; i++) {
198                uint32_t index = rand() % (3072 - HTTP_HEADERS_SERVER_US_LEN);
199                memcpy(_pOutputBuffer + index, HTTP_HEADERS_SERVER_US, HTTP_HEADERS_SERVER_US_LEN);
200        }
201
202        //**** FIRST 1536 bytes from server response ****//
203        //compute DH key position
204        uint32_t serverDHOffset = GetDHOffset(_pOutputBuffer, _validationScheme);
205        uint32_t clientDHOffset = GetDHOffset(pInputBuffer, _validationScheme);
206        //FINEST("serverDHOffset: %u", serverDHOffset);
207
208        //generate DH key
209        DHWrapper dhWrapper(1024);
210
211        if (!dhWrapper.Initialize()) {
212                FATAL("Unable to initialize DH wrapper");
213                return false;
214        }
215
216        if (!dhWrapper.CreateSharedKey(pInputBuffer + clientDHOffset, 128)) {
217                FATAL("Unable to create shared key");
218                return false;
219        }
220
221        if (!dhWrapper.CopyPublicKey(_pOutputBuffer + serverDHOffset, 128)) {
222                FATAL("Couldn't write public key!");
223                return false;
224        }
225
226        if (encrypted) {
227                uint8_t secretKey[128];
228                if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) {
229                        FATAL("Unable to copy shared key");
230                        return false;
231                }
232
233                _pKeyIn = new RC4_KEY;
234                _pKeyOut = new RC4_KEY;
235                InitRC4Encryption(
236                                secretKey,
237                                (uint8_t*) & pInputBuffer[clientDHOffset],
238                                (uint8_t*) & _pOutputBuffer[serverDHOffset],
239                                _pKeyIn,
240                                _pKeyOut);
241
242                //bring the keys to correct cursor
243                uint8_t data[1536];
244                RC4(_pKeyIn, 1536, data, data);
245                RC4(_pKeyOut, 1536, data, data);
246        }
247
248        //generate the digest
249        uint32_t serverDigestOffset = GetDigestOffset(_pOutputBuffer, _validationScheme);
250        //FINEST("serverDigestOffset: %u", serverDigestOffset);
251
252        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
253        memcpy(pTempBuffer, _pOutputBuffer, serverDigestOffset);
254        memcpy(pTempBuffer + serverDigestOffset, _pOutputBuffer + serverDigestOffset + 32,
255                        1536 - serverDigestOffset - 32);
256
257        uint8_t *pTempHash = new uint8_t[512];
258        HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash);
259
260        //put the digest in place
261        memcpy(_pOutputBuffer + serverDigestOffset, pTempHash, 32);
262
263        //cleanup
264        delete[] pTempBuffer;
265        delete[] pTempHash;
266
267
268        //**** SECOND 1536 bytes from server response ****//
269        //Compute the chalange index from the initial client request
270        uint32_t keyChallengeIndex = GetDigestOffset(pInputBuffer, _validationScheme);
271        //FINEST("keyChallengeIndex: %u", keyChallengeIndex);
272
273        //compute the key
274        pTempHash = new uint8_t[512];
275        HMACsha256(pInputBuffer + keyChallengeIndex, //pData
276                        32, //dataLength
277                        BaseRTMPProtocol::genuineFMSKey, //key
278                        68, //keyLength
279                        pTempHash //pResult
280                        );
281
282        //generate the hash
283        uint8_t *pLastHash = new uint8_t[512];
284        HMACsha256(_pOutputBuffer + 1536, //pData
285                        1536 - 32, //dataLength
286                        pTempHash, //key
287                        32, //keyLength
288                        pLastHash //pResult
289                        );
290
291        //put the hash where it belongs
292        memcpy(_pOutputBuffer + 1536 * 2 - 32, pLastHash, 32);
293
294
295        //cleanup
296        delete[] pTempHash;
297        delete[] pLastHash;
298        //***** DONE BUILDING THE RESPONSE ***//
299
300
301        //wire the response
302        if (encrypted)
303                _outputBuffer.ReadFromByte(6);
304        else
305                _outputBuffer.ReadFromByte(3);
306        _outputBuffer.ReadFromBuffer(_pOutputBuffer, 3072);
307
308        //final cleanup
309        delete[] _pOutputBuffer;
310        _pOutputBuffer = NULL;
311        if (!buffer.IgnoreAll()) {
312                FATAL("Unable to ignore input buffer");
313                return false;
314        }
315
316        //signal outbound data
317        if (!EnqueueForOutbound()) {
318                FATAL("Unable to signal outbound data");
319                return false;
320        }
321
322        //move to the next stage in the handshake
323        _rtmpState = RTMP_STATE_SERVER_RESPONSE_SENT;
324
325        return true;
326}
327
328#endif /* HAS_PROTOCOL_RTMP */
329
Note: See TracBrowser for help on using the browser.