source: trunk/sources/thelib/src/protocols/rtmp/inboundrtmpprotocol.cpp @ 460

Revision 460, 8.1 KB checked in by shiretu, 9 months ago (diff)

-- fixed a crash on ARM platform

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                                return true;
57                        }
58                        uint8_t handshakeType = GETIBPOINTER(buffer)[0];
59                        if (!buffer.Ignore(1)) {
60                                FATAL("Unable to ignore one byte");
61                                return false;
62                        }
63
64                        _currentFPVersion = ENTOHLP(GETIBPOINTER(buffer) + 4);
65
66                        switch (handshakeType) {
67                                case 3: //plain
68                                {
69                                        return PerformHandshake(buffer, false);
70                                }
71                                case 6: //encrypted
72                                {
73                                        return PerformHandshake(buffer, true);
74                                }
75                                default:
76                                {
77                                        FATAL("Handshake type not implemented: %hhu", handshakeType);
78                                        return false;
79                                }
80                        }
81                }
82                case RTMP_STATE_SERVER_RESPONSE_SENT:
83                {
84                        if (GETAVAILABLEBYTESCOUNT(buffer) < 1536) {
85                                return true;
86                        } else {
87                                //ignore the client's last handshake part
88                                if (!buffer.Ignore(1536)) {
89                                        FATAL("Unable to ignore inbound data");
90                                        return false;
91                                }
92                                _handshakeCompleted = true;
93                                _rtmpState = RTMP_STATE_DONE;
94
95                                if (_pKeyIn != NULL && _pKeyOut != NULL) {
96                                        //insert the RTMPE protocol in the current protocol stack
97                                        BaseProtocol *pFarProtocol = GetFarProtocol();
98                                        RTMPEProtocol *pRTMPE = new RTMPEProtocol(_pKeyIn, _pKeyOut);
99                                        ResetFarProtocol();
100                                        pFarProtocol->SetNearProtocol(pRTMPE);
101                                        pRTMPE->SetNearProtocol(this);
102                                        FINEST("New protocol chain: %s", STR(*pFarProtocol));
103
104                                        //decrypt the leftovers
105                                        RC4(_pKeyIn, GETAVAILABLEBYTESCOUNT(buffer),
106                                                        GETIBPOINTER(buffer),
107                                                        GETIBPOINTER(buffer));
108                                }
109
110                                return true;
111                        }
112                }
113                default:
114                {
115                        FATAL("Invalid RTMP state: %hhu", _rtmpState);
116                        return false;
117                }
118        }
119}
120
121bool InboundRTMPProtocol::ValidateClient(IOBuffer &inputBuffer) {
122        if (_currentFPVersion == 0) {
123                WARN("This version of player doesn't support validation");
124                return true;
125        }
126        if (ValidateClientScheme(inputBuffer, 0)) {
127                _validationScheme = 0;
128                return true;
129        }
130        if (ValidateClientScheme(inputBuffer, 1)) {
131                _validationScheme = 1;
132                return true;
133        }
134        FATAL("Unable to validate client");
135        return false;
136}
137
138bool InboundRTMPProtocol::ValidateClientScheme(IOBuffer &inputBuffer, uint8_t scheme) {
139        uint8_t *pBuffer = GETIBPOINTER(inputBuffer);
140
141        uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
142
143        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
144        memcpy(pTempBuffer, pBuffer, clientDigestOffset);
145        memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32,
146                        1536 - clientDigestOffset - 32);
147
148        uint8_t *pTempHash = new uint8_t[512];
149        HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
150
151        bool result = true;
152        for (uint32_t i = 0; i < 32; i++) {
153                if (pBuffer[clientDigestOffset + i] != pTempHash[i]) {
154                        result = false;
155                        break;
156                }
157        }
158
159        delete[] pTempBuffer;
160        delete[] pTempHash;
161
162        return result;
163}
164
165bool InboundRTMPProtocol::PerformHandshake(IOBuffer &buffer, bool encrypted) {
166        if (!ValidateClient(buffer)) {
167                if (encrypted || _pProtocolHandler->ValidateHandshake()) {
168                        FATAL("Unable to validate client");
169                        return false;
170                } else {
171                        WARN("Client not validated");
172                        _validationScheme = 0;
173                }
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        EHTONLP(_pOutputBuffer, (uint32_t) time(NULL));
188
189        //version
190        EHTONLP(_pOutputBuffer + 4, (uint32_t) 0x00000000);
191
192        //generate random data
193        for (uint32_t i = 8; i < 3072; i++) {
194                _pOutputBuffer[i] = rand() % 256;
195        }
196        for (uint32_t i = 0; i < 10; i++) {
197                uint32_t index = rand() % (3072 - HTTP_HEADERS_SERVER_US_LEN);
198                memcpy(_pOutputBuffer + index, HTTP_HEADERS_SERVER_US, HTTP_HEADERS_SERVER_US_LEN);
199        }
200
201        //**** FIRST 1536 bytes from server response ****//
202        //compute DH key position
203        uint32_t serverDHOffset = GetDHOffset(_pOutputBuffer, _validationScheme);
204        uint32_t clientDHOffset = GetDHOffset(pInputBuffer, _validationScheme);
205
206        //generate DH key
207        DHWrapper dhWrapper(1024);
208
209        if (!dhWrapper.Initialize()) {
210                FATAL("Unable to initialize DH wrapper");
211                return false;
212        }
213
214        if (!dhWrapper.CreateSharedKey(pInputBuffer + clientDHOffset, 128)) {
215                FATAL("Unable to create shared key");
216                return false;
217        }
218
219        if (!dhWrapper.CopyPublicKey(_pOutputBuffer + serverDHOffset, 128)) {
220                FATAL("Couldn't write public key!");
221                return false;
222        }
223
224        if (encrypted) {
225                uint8_t secretKey[128];
226                if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) {
227                        FATAL("Unable to copy shared key");
228                        return false;
229                }
230
231                _pKeyIn = new RC4_KEY;
232                _pKeyOut = new RC4_KEY;
233                InitRC4Encryption(
234                                secretKey,
235                                (uint8_t*) & pInputBuffer[clientDHOffset],
236                                (uint8_t*) & _pOutputBuffer[serverDHOffset],
237                                _pKeyIn,
238                                _pKeyOut);
239
240                //bring the keys to correct cursor
241                uint8_t data[1536];
242                RC4(_pKeyIn, 1536, data, data);
243                RC4(_pKeyOut, 1536, data, data);
244        }
245
246        //generate the digest
247        uint32_t serverDigestOffset = GetDigestOffset(_pOutputBuffer, _validationScheme);
248
249        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
250        memcpy(pTempBuffer, _pOutputBuffer, serverDigestOffset);
251        memcpy(pTempBuffer + serverDigestOffset, _pOutputBuffer + serverDigestOffset + 32,
252                        1536 - serverDigestOffset - 32);
253
254        uint8_t *pTempHash = new uint8_t[512];
255        HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash);
256
257        //put the digest in place
258        memcpy(_pOutputBuffer + serverDigestOffset, pTempHash, 32);
259
260        //cleanup
261        delete[] pTempBuffer;
262        delete[] pTempHash;
263
264
265        //**** SECOND 1536 bytes from server response ****//
266        //Compute the chalange index from the initial client request
267        uint32_t keyChallengeIndex = GetDigestOffset(pInputBuffer, _validationScheme);
268
269        //compute the key
270        pTempHash = new uint8_t[512];
271        HMACsha256(pInputBuffer + keyChallengeIndex, //pData
272                        32, //dataLength
273                        BaseRTMPProtocol::genuineFMSKey, //key
274                        68, //keyLength
275                        pTempHash //pResult
276                        );
277
278        //generate the hash
279        uint8_t *pLastHash = new uint8_t[512];
280        HMACsha256(_pOutputBuffer + 1536, //pData
281                        1536 - 32, //dataLength
282                        pTempHash, //key
283                        32, //keyLength
284                        pLastHash //pResult
285                        );
286
287        //put the hash where it belongs
288        memcpy(_pOutputBuffer + 1536 * 2 - 32, pLastHash, 32);
289
290
291        //cleanup
292        delete[] pTempHash;
293        delete[] pLastHash;
294        //***** DONE BUILDING THE RESPONSE ***//
295
296
297        //wire the response
298        if (encrypted)
299                _outputBuffer.ReadFromByte(6);
300        else
301                _outputBuffer.ReadFromByte(3);
302        _outputBuffer.ReadFromBuffer(_pOutputBuffer, 3072);
303
304        //final cleanup
305        delete[] _pOutputBuffer;
306        _pOutputBuffer = NULL;
307        if (!buffer.IgnoreAll()) {
308                FATAL("Unable to ignore input buffer");
309                return false;
310        }
311
312        //signal outbound data
313        if (!EnqueueForOutbound()) {
314                FATAL("Unable to signal outbound data");
315                return false;
316        }
317
318        //move to the next stage in the handshake
319        _rtmpState = RTMP_STATE_SERVER_RESPONSE_SENT;
320
321        return true;
322}
323
324#endif /* HAS_PROTOCOL_RTMP */
325
Note: See TracBrowser for help on using the repository browser.