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

Revision 1125, 10.7 KB (checked in by shiretu, 3 weeks ago)

-- implemented pause/resume for VOD
-- fixed inbound stream cleanup for VOD
-- removed some logs

Line 
1/*
2 * Copyright (c) 2009, Gavriloaie Eugen-Andrei (shiretu@gmail.com)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *              *Redistributions of source code must retain the above copyright notice,
9 *               this list of conditions and the following disclaimer.
10 *              *Redistributions in binary form must reproduce the above copyright
11 *               notice, this list of conditions and the following disclaimer in the
12 *               documentation and/or other materials provided with the distribution.
13 *              *Neither the name of the DEVSS nor the names of its
14 *               contributors may be used to endorse or promote products derived from
15 *               this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "protocols/rtmp/inboundrtmpprotocol.h"
31#include "buffering/iobuffer.h"
32#include "protocols/rtmp/rtmpeprotocol.h"
33#include "protocols/rtmp/basertmpappprotocolhandler.h"
34#include "utils/crypto.h"
35
36InboundRTMPProtocol::InboundRTMPProtocol()
37: BaseRTMPProtocol(PT_INBOUNDRTMP) {
38    _pKeyIn = NULL;
39    _pKeyOut = NULL;
40    _pOutputBuffer = NULL;
41    _currentFPVersion = 0;
42    _validationScheme = 0;
43}
44
45InboundRTMPProtocol::~InboundRTMPProtocol() {
46    if (_pKeyIn != NULL) {
47        delete _pKeyIn;
48        _pKeyIn = NULL;
49    }
50
51    if (_pKeyOut != NULL) {
52        delete _pKeyOut;
53        _pKeyOut = NULL;
54    }
55
56    if (_pOutputBuffer != NULL) {
57        delete[] _pOutputBuffer;
58        _pOutputBuffer = NULL;
59    }
60}
61
62bool InboundRTMPProtocol::PerformHandshake(IOBuffer &buffer) {
63    switch (_rtmpState) {
64        case RTMP_STATE_NOT_INITIALIZED:
65        {
66            if (GETAVAILABLEBYTESCOUNT(buffer) < 1537) {
67                //FINEST("Not enough data");
68                return true;
69            }
70            uint8_t handshakeType = GETIBPOINTER(buffer)[0];
71            if (!buffer.Ignore(1)) {
72                FATAL("Unable to ignore one byte");
73                return false;
74            }
75
76            _currentFPVersion = ntohl(*((uint32_t *) (GETIBPOINTER(buffer) + 4))); //----MARKED-LONG---
77            //FINEST("Flash player: %s", STR(_currentFlashPlayerVersion));
78
79            switch (handshakeType) {
80                case 3: //plain
81                {
82                    return PerformHandshake(buffer, false);
83                }
84                case 6: //encrypted
85                {
86                    return PerformHandshake(buffer, true);
87                }
88                default:
89                {
90                    FATAL("Handshake type not implemented: %d", handshakeType);
91                    return false;
92                }
93            }
94        }
95        case RTMP_STATE_SERVER_RESPONSE_SENT:
96        {
97            //FINEST("I: Player request 2:\n%s", STR(*pInputBuffer));
98            if (GETAVAILABLEBYTESCOUNT(buffer) < 1536) {
99                return true;
100            } else {
101                //ignore the client's last handshake part
102                if (!buffer.Ignore(1536)) {
103                    FATAL("Unable to ignore inbound data");
104                    return false;
105                }
106                _handshakeCompleted = true;
107                _rtmpState = RTMP_STATE_DONE;
108
109                if (_pKeyIn != NULL && _pKeyOut != NULL) {
110                    //insert the RTMPE protocol in the current protocol stack
111                    BaseProtocol *pFarProtocol = GetFarProtocol();
112                    RTMPEProtocol *pRTMPE = new RTMPEProtocol(_pKeyIn, _pKeyOut);
113                    ResetFarProtocol();
114                    pFarProtocol->SetNearProtocol(pRTMPE);
115                    pRTMPE->SetNearProtocol(this);
116                    FINEST("New protocol chain: %s", STR(*pFarProtocol));
117
118                    //decrypt the leftovers
119                    RC4(_pKeyIn, GETAVAILABLEBYTESCOUNT(buffer),
120                            GETIBPOINTER(buffer),
121                            GETIBPOINTER(buffer));
122                }
123
124                return true;
125            }
126        }
127        default:
128        {
129            FATAL("Invalid RTMP state: %d", _rtmpState);
130            return false;
131        }
132    }
133}
134
135bool InboundRTMPProtocol::ValidateClient(IOBuffer &inputBuffer) {
136    if (_currentFPVersion == 0) {
137        WARN("This version of player doesn't support validation");
138        return true;
139    }
140    if (ValidateClientScheme(inputBuffer, 0)) {
141        _validationScheme = 0;
142        return true;
143    }
144    if (ValidateClientScheme(inputBuffer, 1)) {
145        _validationScheme = 1;
146        return true;
147    }
148    FATAL("Unable to validate client");
149    return false;
150}
151
152bool InboundRTMPProtocol::ValidateClientScheme(IOBuffer &inputBuffer, uint8_t scheme) {
153    uint8_t *pBuffer = GETIBPOINTER(inputBuffer);
154
155    uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
156
157    uint8_t *pTempBuffer = new uint8_t[1536 - 32];
158    memcpy(pTempBuffer, pBuffer, clientDigestOffset);
159    memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32,
160            1536 - clientDigestOffset - 32);
161
162    //uint8_t *pTempHash = new uint8_t[mhash_get_hash_pblock(MHASH_SHA256)];
163    uint8_t *pTempHash = new uint8_t[512];
164    HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
165
166    bool result = true;
167    for (uint32_t i = 0; i < 32; i++) {
168        if (pBuffer[clientDigestOffset + i] != pTempHash[i]) {
169            result = false;
170            break;
171        }
172    }
173
174    delete[] pTempBuffer;
175    delete[] pTempHash;
176
177    return result;
178}
179
180bool InboundRTMPProtocol::PerformHandshake(IOBuffer &buffer, bool encrypted) {
181    if (encrypted || _pProtocolHandler->ValidateHandshake()) {
182        if (!ValidateClient(buffer)) {
183            FATAL("Unable to validate client");
184            return false;
185        }
186    }
187
188    //get the buffers
189    uint8_t *pInputBuffer = GETIBPOINTER(buffer);
190    if (_pOutputBuffer == NULL) {
191        _pOutputBuffer = new uint8_t[3072];
192    } else {
193        delete[] _pOutputBuffer;
194        _pOutputBuffer = new uint8_t[3072];
195    }
196
197    //timestamp
198    *(uint32_t *) _pOutputBuffer = 0;
199
200    //version
201    *(uint32_t *) (_pOutputBuffer + 4) = htonl(0x01020304); //----MARKED-LONG---
202
203    //generate random data
204    srand((uint32_t) time(NULL));
205    for (uint32_t i = 8; i < 3072; i++) {
206        _pOutputBuffer[i] = rand() % 256;
207    }
208
209    //**** FIRST 1536 bytes from server response ****//
210    //compute DH key position
211    uint32_t serverDHOffset = GetDHOffset(_pOutputBuffer, _validationScheme);
212    uint32_t clientDHOffset = GetDHOffset(pInputBuffer, _validationScheme);
213    //FINEST("serverDHOffset: %u", serverDHOffset);
214
215    //generate DH key
216    DHWrapper dhWrapper(1024);
217
218    if (!dhWrapper.Initialize()) {
219        FATAL("Unable to initialize DH wrapper");
220        return false;
221    }
222
223    if (!dhWrapper.CreateSharedKey(pInputBuffer + clientDHOffset, 128)) {
224        FATAL("Unable to create shared key");
225        return false;
226    }
227
228    if (!dhWrapper.CopyPublicKey(_pOutputBuffer + serverDHOffset, 128)) {
229        FATAL("Couldn't write public key!");
230        return false;
231    }
232
233    if (encrypted) {
234        uint8_t secretKey[128];
235        if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) {
236            FATAL("Unable to copy shared key");
237            return false;
238        }
239
240        _pKeyIn = new RC4_KEY;
241        _pKeyOut = new RC4_KEY;
242        InitRC4Encryption(
243                secretKey,
244                (uint8_t*) & pInputBuffer[clientDHOffset],
245                (uint8_t*) & _pOutputBuffer[serverDHOffset],
246                _pKeyIn,
247                _pKeyOut);
248
249        //bring the keys to correct cursor
250        uint8_t data[1536];
251        RC4(_pKeyIn, 1536, data, data);
252        RC4(_pKeyOut, 1536, data, data);
253    }
254
255    //generate the digest
256    uint32_t serverDigestOffset = GetDigestOffset(_pOutputBuffer, _validationScheme);
257    //FINEST("serverDigestOffset: %u", serverDigestOffset);
258
259    uint8_t *pTempBuffer = new uint8_t[1536 - 32];
260    memcpy(pTempBuffer, _pOutputBuffer, serverDigestOffset);
261    memcpy(pTempBuffer + serverDigestOffset, _pOutputBuffer + serverDigestOffset + 32,
262            1536 - serverDigestOffset - 32);
263
264    uint8_t *pTempHash = new uint8_t[512];
265    HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash);
266
267    //put the digest in place
268    memcpy(_pOutputBuffer + serverDigestOffset, pTempHash, 32);
269
270    //cleanup
271    delete[] pTempBuffer;
272    delete[] pTempHash;
273
274
275    //**** SECOND 1536 bytes from server response ****//
276    //Compute the chalange index from the initial client request
277    uint32_t keyChallengeIndex = GetDigestOffset(pInputBuffer, _validationScheme);
278    //FINEST("keyChallengeIndex: %u", keyChallengeIndex);
279
280    //compute the key
281    pTempHash = new uint8_t[512];
282    HMACsha256(pInputBuffer + keyChallengeIndex, //pData
283            32, //dataLength
284            BaseRTMPProtocol::genuineFMSKey, //key
285            68, //keyLength
286            pTempHash //pResult
287            );
288
289    //generate the hash
290    uint8_t *pLastHash = new uint8_t[512];
291    HMACsha256(_pOutputBuffer + 1536, //pData
292            1536 - 32, //dataLength
293            pTempHash, //key
294            32, //keyLength
295            pLastHash //pResult
296            );
297
298    //put the hash where it belongs
299    memcpy(_pOutputBuffer + 1536 * 2 - 32, pLastHash, 32);
300
301
302    //cleanup
303    delete[] pTempHash;
304    delete[] pLastHash;
305    //***** DONE BUILDING THE RESPONSE ***//
306
307
308    //wire the response
309    if (encrypted)
310        _outputBuffer.PutByte(6);
311    else
312        _outputBuffer.PutByte(3);
313    _outputBuffer.PutBuffer(_pOutputBuffer, 3072);
314
315    //final cleanup
316    delete[] _pOutputBuffer;
317    _pOutputBuffer = NULL;
318    if (!buffer.IgnoreAll()) {
319        FATAL("Unable to ignore input buffer");
320        return false;
321    }
322
323    //signal outbound data
324    if (!_pFarProtocol->EnqueueForOutbound()) {
325        FATAL("Unable to signal outbound data");
326        return false;
327    }
328
329    //move to the next stage in the handshake
330    _rtmpState = RTMP_STATE_SERVER_RESPONSE_SENT;
331
332    return true;
333}
334
Note: See TracBrowser for help on using the browser.