source: trunk/sources/thelib/src/protocols/rtmp/outboundrtmpprotocol.cpp @ 759

Revision 759, 12.4 KB checked in by shiretu, 8 weeks ago (diff)

-- fixed various bugs related to the new way in which the new xcode compiler is reporting errors

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
22#include "protocols/rtmp/outboundrtmpprotocol.h"
23#include "protocols/protocolfactorymanager.h"
24#include "netio/netio.h"
25#include "protocols/rtmp/rtmpeprotocol.h"
26#include "protocols/rtmp/basertmpappprotocolhandler.h"
27#include "application/clientapplicationmanager.h"
28
29//#define DEBUG_HANDSHAKE(...) FINEST(__VA_ARGS__)
30#define DEBUG_HANDSHAKE(...)
31
32OutboundRTMPProtocol::OutboundRTMPProtocol()
33: BaseRTMPProtocol(PT_OUTBOUND_RTMP) {
34        _pClientPublicKey = NULL;
35        _pOutputBuffer = NULL;
36        _pClientDigest = NULL;
37        _pKeyIn = NULL;
38        _pKeyOut = NULL;
39        _pDHWrapper = NULL;
40        _usedScheme = 0;
41}
42
43OutboundRTMPProtocol::~OutboundRTMPProtocol() {
44        if (_pKeyIn != NULL) {
45                delete _pKeyIn;
46                _pKeyIn = NULL;
47        }
48
49        if (_pKeyOut != NULL) {
50                delete _pKeyOut;
51                _pKeyOut = NULL;
52        }
53
54        if (_pDHWrapper != NULL) {
55                delete _pDHWrapper;
56                _pDHWrapper = NULL;
57        }
58
59        if (_pClientPublicKey != NULL) {
60                delete[] _pClientPublicKey;
61                _pClientPublicKey = NULL;
62        }
63        if (_pOutputBuffer != NULL) {
64                delete[] _pOutputBuffer;
65                _pOutputBuffer = NULL;
66        }
67        if (_pClientDigest != NULL) {
68                delete[] _pClientDigest;
69                _pClientDigest = NULL;
70        }
71}
72
73bool OutboundRTMPProtocol::PerformHandshake(IOBuffer &buffer) {
74        switch (_rtmpState) {
75                case RTMP_STATE_NOT_INITIALIZED:
76                {
77                        if ((VariantType) _customParameters[CONF_PROTOCOL] == V_STRING &&
78                                        _customParameters[CONF_PROTOCOL] == CONF_PROTOCOL_OUTBOUND_RTMPE) {
79                                return PerformHandshakeStage1(true);
80                        } else {
81                                return PerformHandshakeStage1(false);
82                        }
83                }
84                case RTMP_STATE_CLIENT_REQUEST_SENT:
85                {
86                        if (GETAVAILABLEBYTESCOUNT(buffer) < 3073)
87                                return true;
88
89                        bool encrypted = (VariantType) _customParameters[CONF_PROTOCOL] == V_STRING &&
90                                        _customParameters[CONF_PROTOCOL] == CONF_PROTOCOL_OUTBOUND_RTMPE;
91                        _usedScheme = encrypted ? 1 : 0;
92
93                        if (!PerformHandshakeStage2(buffer, encrypted)) {
94                                FATAL("Unable to handshake");
95                                return false;
96                        }
97
98                        if (_pFarProtocol != NULL) {
99                                if (!_pFarProtocol->EnqueueForOutbound()) {
100                                        FATAL("Unable to signal output data");
101                                        return false;
102                                }
103                        }
104
105                        if (_pKeyIn != NULL && _pKeyOut != NULL) {
106                                //insert the RTMPE protocol in the current protocol stack
107                                BaseProtocol *pFarProtocol = GetFarProtocol();
108                                RTMPEProtocol *pRTMPE = new RTMPEProtocol(_pKeyIn, _pKeyOut,
109                                                GETAVAILABLEBYTESCOUNT(_outputBuffer));
110                                ResetFarProtocol();
111                                pFarProtocol->SetNearProtocol(pRTMPE);
112                                pRTMPE->SetNearProtocol(this);
113                                FINEST("New protocol chain: %s", STR(*pFarProtocol));
114                        }
115
116                        if (!buffer.Ignore(3073)) {
117                                FATAL("Unable to ignore 3073 bytes");
118                                return false;
119                        }
120                        _handshakeCompleted = true;
121                        return true;
122                }
123                default:
124                {
125                        FATAL("Invalid RTMP state: %d", _rtmpState);
126                        return false;
127                }
128        }
129}
130
131bool OutboundRTMPProtocol::Connect(string ip, uint16_t port,
132                Variant customParameters) {
133
134        vector<uint64_t> chain = ProtocolFactoryManager::ResolveProtocolChain(
135                        CONF_PROTOCOL_OUTBOUND_RTMP);
136        if (chain.size() == 0) {
137                FATAL("Unable to obtain protocol chain from settings: %s",
138                                CONF_PROTOCOL_OUTBOUND_RTMP);
139                return false;
140        }
141        if (!TCPConnector<OutboundRTMPProtocol>::Connect(ip, port, chain,
142                        customParameters)) {
143                FATAL("Unable to connect to %s:%hu", STR(ip), port);
144                return false;
145        }
146        return true;
147}
148
149bool OutboundRTMPProtocol::SignalProtocolCreated(BaseProtocol *pProtocol,
150                Variant customParameters) {
151        //1. Get the application  designated for the newly created connection
152        if (customParameters[CONF_APPLICATION_NAME] != V_STRING) {
153                FATAL("connect parameters must have an application name");
154                return false;
155        }
156        BaseClientApplication *pApplication = ClientApplicationManager::FindAppByName(
157                        customParameters[CONF_APPLICATION_NAME]);
158        if (pApplication == NULL) {
159                FATAL("Application %s not found", STR(customParameters[CONF_APPLICATION_NAME]));
160                return false;
161        }
162
163        if (pProtocol == NULL) {
164                FATAL("Connection failed:\n%s", STR(customParameters.ToString()));
165                return pApplication->OutboundConnectionFailed(customParameters);
166        }
167
168        //2. Set the application
169        pProtocol->SetApplication(pApplication);
170
171
172        //3. Trigger processing, including handshake
173        OutboundRTMPProtocol *pOutboundRTMPProtocol = (OutboundRTMPProtocol *) pProtocol;
174        pOutboundRTMPProtocol->SetOutboundConnectParameters(customParameters);
175        IOBuffer dummy;
176        return pOutboundRTMPProtocol->SignalInputData(dummy);
177}
178
179bool OutboundRTMPProtocol::PerformHandshakeStage1(bool encrypted) {
180        DEBUG_HANDSHAKE("PHS1:  1. Put the protocol type. Connection is%sencrypted",
181                        encrypted ? " " : " NOT ");
182        _outputBuffer.ReadFromByte(encrypted ? 6 : 3);
183
184        DEBUG_HANDSHAKE("PHS1:  2. Prepare the buffer");
185        if (_pOutputBuffer == NULL) {
186                _pOutputBuffer = new uint8_t[1536];
187        } else {
188                delete[] _pOutputBuffer;
189                _pOutputBuffer = new uint8_t[1536];
190        }
191
192        DEBUG_HANDSHAKE("PHS1:  3. Randomize the buffer");
193        for (uint32_t i = 0; i < 1536; i++) {
194                _pOutputBuffer[i] = rand() % 256;
195        }
196
197        DEBUG_HANDSHAKE("PHS1:  4. Put the uptime. In our case, 0.");
198        EHTONLP(_pOutputBuffer, 0);
199
200        DEBUG_HANDSHAKE("PHS1:  5. Put the flash version. We impersonate with 9.0.124.2");
201        _pOutputBuffer[4] = 9;
202        _pOutputBuffer[5] = 0;
203        _pOutputBuffer[6] = 124;
204        _pOutputBuffer[7] = 2;
205
206        uint32_t clientDHOffset = GetDHOffset(_pOutputBuffer, _usedScheme);
207        DEBUG_HANDSHAKE("PHS1:  6. Get the DH public key position: %u", clientDHOffset);
208
209        DEBUG_HANDSHAKE("PHS1:  7. Generate the DH public/private key");
210        _pDHWrapper = new DHWrapper(1024);
211        if (!_pDHWrapper->Initialize()) {
212                FATAL("Unable to initialize DH wrapper");
213                return false;
214        }
215
216        DEBUG_HANDSHAKE("PHS1:  8. Get the public key and store it in the buffer at %u and _pClientPublicKey for later use", clientDHOffset);
217        if (!_pDHWrapper->CopyPublicKey(_pOutputBuffer + clientDHOffset, 128)) {
218                FATAL("Couldn't write public key!");
219                return false;
220        }
221        _pClientPublicKey = new uint8_t[128];
222        memcpy(_pClientPublicKey, _pOutputBuffer + clientDHOffset, 128);
223
224
225        uint32_t clientDigestOffset = GetDigestOffset(_pOutputBuffer, _usedScheme);
226        DEBUG_HANDSHAKE("PHS1:  9. Compute the final digest offset: %u", clientDigestOffset);
227
228        DEBUG_HANDSHAKE("PHS1: 10. Generate the digest from pBuffer EXCLUDING the digest portion.");
229        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
230        memcpy(pTempBuffer, _pOutputBuffer, clientDigestOffset);
231        memcpy(pTempBuffer + clientDigestOffset, _pOutputBuffer + clientDigestOffset + 32,
232                        1536 - clientDigestOffset - 32);
233
234        DEBUG_HANDSHAKE("PHS1: 11. Generate the hash");
235        uint8_t *pTempHash = new uint8_t[512];
236        HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
237
238        DEBUG_HANDSHAKE("PHS1: 12. put the bytes at %u offset. Also save them for later use", clientDigestOffset);
239        memcpy(_pOutputBuffer + clientDigestOffset, pTempHash, 32);
240        _pClientDigest = new uint8_t[32];
241        memcpy(_pClientDigest, pTempHash, 32);
242
243
244        DEBUG_HANDSHAKE("PHS1: 13. cleanup the temp buffers");
245        delete[] pTempBuffer;
246        delete[] pTempHash;
247
248        DEBUG_HANDSHAKE("PHS1: 14. Put the buffer on the outputBuffer");
249        _outputBuffer.ReadFromBuffer(_pOutputBuffer, 1536);
250        _outputBuffer222.ReadFromBuffer(_pOutputBuffer, 1536);
251
252        DEBUG_HANDSHAKE("PHS1: 15. delete the buffer");
253        delete[] _pOutputBuffer;
254        _pOutputBuffer = NULL;
255
256        DEBUG_HANDSHAKE("PHS1: 16. Signal the protocol stack that we have outbound data");
257        if (_pFarProtocol != NULL) {
258                if (!_pFarProtocol->EnqueueForOutbound()) {
259                        FATAL("Unable to signal output data");
260                        return false;
261                }
262        }
263
264        DEBUG_HANDSHAKE("PHS1: 17. Move to the next stage of the handshake");
265        _rtmpState = RTMP_STATE_CLIENT_REQUEST_SENT;
266
267        return true;
268}
269
270bool OutboundRTMPProtocol::VerifyServer(IOBuffer & inputBuffer) {
271        uint8_t *pBuffer = GETIBPOINTER(inputBuffer) + 1;
272
273        uint32_t serverDigestPos = GetDigestOffset(pBuffer, _usedScheme);
274        DEBUG_HANDSHAKE("VS:  1. Compute server digest offset: %u", serverDigestPos);
275
276        DEBUG_HANDSHAKE("VS:  2. Prepare the buffer");
277        uint8_t *pTempBuffer = new uint8_t[1536 - 32];
278        memcpy(pTempBuffer, pBuffer, serverDigestPos);
279        memcpy(pTempBuffer + serverDigestPos, pBuffer + serverDigestPos + 32,
280                        1536 - serverDigestPos - 32);
281
282        DEBUG_HANDSHAKE("VS:  3. Compute the digest");
283        uint8_t * pDigest = new uint8_t[512];
284        HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pDigest);
285
286        DEBUG_HANDSHAKE("VS:  3. Compare the results");
287        int result = memcmp(pDigest, pBuffer + serverDigestPos, 32);
288
289        DEBUG_HANDSHAKE("VS:  4. Cleanup");
290        delete[] pTempBuffer;
291        delete[] pDigest;
292
293        if (result != 0) {
294                FATAL("Server not verified");
295                return false;
296        }
297
298        DEBUG_HANDSHAKE("VS:  5. Advance the server response to the next chunk");
299        pBuffer = pBuffer + 1536;
300
301        DEBUG_HANDSHAKE("VS:  6. Compute the chalange");
302        uint8_t * pChallange = new uint8_t[512];
303        HMACsha256(_pClientDigest, 32, genuineFMSKey, 68, pChallange);
304
305        DEBUG_HANDSHAKE("VS:  7. Compute the second digest");
306        pDigest = new uint8_t[512];
307        HMACsha256(pBuffer, 1536 - 32, pChallange, 32, pDigest);
308
309        DEBUG_HANDSHAKE("VS:  8. Compare the results");
310        result = memcmp(pDigest, pBuffer + 1536 - 32, 32);
311
312        DEBUG_HANDSHAKE("VS:  9. Cleanup");
313        delete[] pChallange;
314        delete[] pDigest;
315
316        if (result != 0) {
317                FATAL("Server not verified");
318                return false;
319        }
320
321        DEBUG_HANDSHAKE("VS: 10. Server is verified");
322
323        return true;
324}
325
326bool OutboundRTMPProtocol::PerformHandshakeStage2(IOBuffer &inputBuffer,
327                bool encrypted) {
328        if (encrypted || _pProtocolHandler->ValidateHandshake()) {
329                if (!VerifyServer(inputBuffer)) {
330                        FATAL("Unable to verify server");
331                        return false;
332                }
333        }
334
335        uint8_t *pBuffer = GETIBPOINTER(inputBuffer) + 1;
336
337        uint32_t serverDHOffset = GetDHOffset(pBuffer, _usedScheme);
338        DEBUG_HANDSHAKE("PHS2:  1. get the serverDHOffset: %u", serverDHOffset);
339
340        DEBUG_HANDSHAKE("PHS2:  2. compute the secret key");
341        if (_pDHWrapper == NULL) {
342                FATAL("dh wrapper not initialized");
343                return false;
344        }
345
346        if (!_pDHWrapper->CreateSharedKey(pBuffer + serverDHOffset, 128)) {
347                FATAL("Unable to create shared key");
348                return false;
349        }
350
351        uint8_t secretKey[128];
352        if (!_pDHWrapper->CopySharedKey(secretKey, sizeof (secretKey))) {
353                FATAL("Unable to compute shared");
354                return false;
355        }
356
357        if (encrypted) {
358                _pKeyIn = new RC4_KEY;
359                _pKeyOut = new RC4_KEY;
360
361                InitRC4Encryption(
362                                secretKey,
363                                (uint8_t*) & pBuffer[serverDHOffset],
364                                _pClientPublicKey,
365                                _pKeyIn,
366                                _pKeyOut);
367
368                DEBUG_HANDSHAKE("PHS2:  3. bring the keys to correct cursor");
369                uint8_t data[1536];
370                RC4(_pKeyIn, 1536, data, data);
371                RC4(_pKeyOut, 1536, data, data);
372        } else {
373                DEBUG_HANDSHAKE("PHS2:  3. No encryptio, so no key exchange");
374        }
375
376        delete _pDHWrapper;
377        _pDHWrapper = NULL;
378
379        DEBUG_HANDSHAKE("PHS2:  4. Get server digest offset");
380        uint32_t serverDigestOffset = GetDigestOffset(pBuffer, _usedScheme);
381
382        DEBUG_HANDSHAKE("PHS2:  5. Prepare the response buffer");
383        if (_pOutputBuffer == NULL) {
384                _pOutputBuffer = new uint8_t[1536];
385        } else {
386                delete[] _pOutputBuffer;
387                _pOutputBuffer = new uint8_t[1536];
388        }
389
390        DEBUG_HANDSHAKE("PHS2:  6. Randomize the response");
391        for (uint32_t i = 0; i < 1536; i++) {
392                _pOutputBuffer[i] = rand() % 256;
393        }
394
395        DEBUG_HANDSHAKE("PHS2:  7. Compute the challange");
396        uint8_t * pChallangeKey = new uint8_t[512];
397        HMACsha256(pBuffer + serverDigestOffset, 32, genuineFPKey, 62, pChallangeKey);
398
399        DEBUG_HANDSHAKE("PHS2:  8. Compute the client digest");
400        uint8_t * pDigest = new uint8_t[512];
401        HMACsha256(_pOutputBuffer, 1536 - 32, pChallangeKey, 32, pDigest);
402
403        DEBUG_HANDSHAKE("PHS2:  9. Put the digest where it belongs");
404        memcpy(_pOutputBuffer + 1536 - 32, pDigest, 32);
405
406        DEBUG_HANDSHAKE("PHS2: 10. Cleanup");
407        delete[] pChallangeKey;
408        delete[] pDigest;
409
410        DEBUG_HANDSHAKE("PHS2: 11. Put the buffer on the outputBuffer");
411        _outputBuffer.ReadFromBuffer(_pOutputBuffer, 1536);
412
413        DEBUG_HANDSHAKE("PHS2: 12. delete the buffer");
414        delete[] _pOutputBuffer;
415        _pOutputBuffer = NULL;
416
417        DEBUG_HANDSHAKE("PHS2: 13. Go to the next stage in the handshake");
418        _rtmpState = RTMP_STATE_DONE;
419
420        return true;
421}
422#endif /* HAS_PROTOCOL_RTMP */
423
Note: See TracBrowser for help on using the repository browser.