rtmpproto.c 53.4 KB
Newer Older
1 2 3 4
/*
 * RTMP network protocol
 * Copyright (c) 2009 Kostya Shishkov
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; if not, write to the Free Software
19 20 21 22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
23
 * @file
24 25 26 27 28
 * RTMP protocol
 */

#include "libavcodec/bytestream.h"
#include "libavutil/avstring.h"
29
#include "libavutil/intfloat.h"
30
#include "libavutil/lfg.h"
31
#include "libavutil/opt.h"
32 33
#include "libavutil/sha.h"
#include "avformat.h"
34
#include "internal.h"
35 36 37 38 39

#include "network.h"

#include "flv.h"
#include "rtmp.h"
Samuel Pitoiset's avatar
Samuel Pitoiset committed
40
#include "rtmpcrypt.h"
41
#include "rtmppkt.h"
42
#include "url.h"
43

44 45
//#define DEBUG

46
#define APP_MAX_LENGTH 128
47
#define PLAYPATH_MAX_LENGTH 256
48
#define TCURL_MAX_LENGTH 512
49
#define FLASHVER_MAX_LENGTH 64
50

51 52 53 54
/** RTMP protocol handler state */
typedef enum {
    STATE_START,      ///< client has not done anything yet
    STATE_HANDSHAKED, ///< client has performed handshake
55 56
    STATE_RELEASING,  ///< client releasing stream before publish it (for output)
    STATE_FCPUBLISH,  ///< client FCPublishing stream (for output)
57 58 59
    STATE_CONNECTING, ///< client connected to server successfully
    STATE_READY,      ///< client has sent all needed commands and waits for server reply
    STATE_PLAYING,    ///< client has started receiving multimedia data from server
60
    STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
61
    STATE_STOPPED,    ///< the broadcast has been stopped
62 63 64 65
} ClientState;

/** protocol handler context */
typedef struct RTMPContext {
66
    const AVClass *class;
67 68 69
    URLContext*   stream;                     ///< TCP stream used in interactions with RTMP server
    RTMPPacket    prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
    int           chunk_size;                 ///< size of the chunks RTMP packets are divided into
70
    int           is_input;                   ///< input/output flag
71
    char          *playpath;                  ///< stream identifier to play (with possible "mp4:" prefix)
72
    int           live;                       ///< 0: recorded, -1: live, -2: both
73
    char          *app;                       ///< name of application
74
    char          *conn;                      ///< append arbitrary AMF data to the Connect message
75 76 77 78 79
    ClientState   state;                      ///< current state
    int           main_channel_id;            ///< an additional channel ID which is used for some invocations
    uint8_t*      flv_data;                   ///< buffer with data for demuxer
    int           flv_size;                   ///< current buffer size
    int           flv_off;                    ///< number of bytes read from current buffer
80
    int           flv_nb_packets;             ///< number of flv packets published
81
    RTMPPacket    out_pkt;                    ///< rtmp packet, created from flv a/v or metadata (for output)
82 83 84
    uint32_t      client_report_size;         ///< number of bytes after which client should report to server
    uint32_t      bytes_read;                 ///< number of bytes read from server
    uint32_t      last_bytes_read;            ///< number of bytes read last reported to server
85
    int           skip_bytes;                 ///< number of bytes to skip from the input FLV stream in the next write call
86 87
    uint8_t       flv_header[11];             ///< partial incoming flv packet header
    int           flv_header_bytes;           ///< number of initialized bytes in flv_header
88
    int           nb_invokes;                 ///< keeps track of invoke messages
89
    int           create_stream_invoke;       ///< invoke id for the create stream command
90
    char*         tcurl;                      ///< url of the target stream
91
    char*         flashver;                   ///< version of the flash plugin
92
    char*         swfurl;                     ///< url of the swf player
93
    int           server_bw;                  ///< server bandwidth
94
    int           client_buffer_time;         ///< client buffer time in ms
95
    int           flush_interval;             ///< number of packets flushed in the same request (RTMPT only)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
96
    int           encrypted;                  ///< use an encrypted connection (RTMPE only)
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
} RTMPContext;

#define PLAYER_KEY_OPEN_PART_LEN 30   ///< length of partial key used for first client digest signing
/** Client key used for digest signing */
static const uint8_t rtmp_player_key[] = {
    'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
    'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',

    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
};

#define SERVER_KEY_OPEN_PART_LEN 36   ///< length of partial key used for first server digest signing
/** Key used for RTMP server digest signing */
static const uint8_t rtmp_server_key[] = {
    'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
    'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
    'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',

    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
};

122 123
static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
{
124
    char *field, *value;
125 126 127 128 129 130 131 132 133 134 135 136 137 138
    char type;

    /* The type must be B for Boolean, N for number, S for string, O for
     * object, or Z for null. For Booleans the data must be either 0 or 1 for
     * FALSE or TRUE, respectively. Likewise for Objects the data must be
     * 0 or 1 to end or begin an object, respectively. Data items in subobjects
     * may be named, by prefixing the type with 'N' and specifying the name
     * before the value (ie. NB:myFlag:1). This option may be used multiple times
     * to construct arbitrary AMF sequences. */
    if (param[0] && param[1] == ':') {
        type = param[0];
        value = param + 2;
    } else if (param[0] == 'N' && param[1] && param[2] == ':') {
        type = param[1];
139 140 141 142 143 144
        field = param + 3;
        value = strchr(field, ':');
        if (!value)
            goto fail;
        *value = '\0';
        value++;
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

        if (!field || !value)
            goto fail;

        ff_amf_write_field_name(p, field);
    } else {
        goto fail;
    }

    switch (type) {
    case 'B':
        ff_amf_write_bool(p, value[0] != '0');
        break;
    case 'S':
        ff_amf_write_string(p, value);
        break;
    case 'N':
        ff_amf_write_number(p, strtod(value, NULL));
        break;
    case 'Z':
        ff_amf_write_null(p);
        break;
    case 'O':
        if (value[0] != '0')
            ff_amf_write_object_start(p);
        else
            ff_amf_write_object_end(p);
        break;
    default:
        goto fail;
        break;
    }

    return 0;

fail:
    av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
    return AVERROR(EINVAL);
}

185
/**
186
 * Generate 'connect' call and send it to the server.
187
 */
188
static int gen_connect(URLContext *s, RTMPContext *rt)
189 190
{
    RTMPPacket pkt;
191
    uint8_t *p;
192 193 194 195 196
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 4096)) < 0)
        return ret;
197 198 199 200

    p = pkt.data;

    ff_amf_write_string(&p, "connect");
201
    ff_amf_write_number(&p, ++rt->nb_invokes);
202 203
    ff_amf_write_object_start(&p);
    ff_amf_write_field_name(&p, "app");
204
    ff_amf_write_string(&p, rt->app);
205

206
    if (!rt->is_input) {
207 208 209
        ff_amf_write_field_name(&p, "type");
        ff_amf_write_string(&p, "nonprivate");
    }
210
    ff_amf_write_field_name(&p, "flashVer");
211
    ff_amf_write_string(&p, rt->flashver);
212 213 214 215 216 217

    if (rt->swfurl) {
        ff_amf_write_field_name(&p, "swfUrl");
        ff_amf_write_string(&p, rt->swfurl);
    }

218
    ff_amf_write_field_name(&p, "tcUrl");
219
    ff_amf_write_string(&p, rt->tcurl);
220
    if (rt->is_input) {
221 222 223 224
        ff_amf_write_field_name(&p, "fpad");
        ff_amf_write_bool(&p, 0);
        ff_amf_write_field_name(&p, "capabilities");
        ff_amf_write_number(&p, 15.0);
225 226 227 228

        /* Tell the server we support all the audio codecs except
         * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
         * which are unused in the RTMP protocol implementation. */
229
        ff_amf_write_field_name(&p, "audioCodecs");
230
        ff_amf_write_number(&p, 4071.0);
231 232 233 234
        ff_amf_write_field_name(&p, "videoCodecs");
        ff_amf_write_number(&p, 252.0);
        ff_amf_write_field_name(&p, "videoFunction");
        ff_amf_write_number(&p, 1.0);
235
    }
236 237
    ff_amf_write_object_end(&p);

238
    if (rt->conn) {
239
        char *param = rt->conn;
240 241 242

        // Write arbitrary AMF data to the Connect message.
        while (param != NULL) {
243 244 245 246 247 248 249
            char *sep;
            param += strspn(param, " ");
            if (!*param)
                break;
            sep = strchr(param, ' ');
            if (sep)
                *sep = '\0';
250 251 252 253 254 255
            if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
                // Invalid AMF parameter.
                ff_rtmp_packet_destroy(&pkt);
                return ret;
            }

256 257 258 259
            if (sep)
                param = sep + 1;
            else
                break;
260 261 262
        }
    }

263 264
    pkt.data_size = p - pkt.data;

265 266
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
267
    ff_rtmp_packet_destroy(&pkt);
268

269
    return ret;
270 271
}

272
/**
273
 * Generate 'releaseStream' call and send it to the server. It should make
274 275
 * the server release some channel for media streams.
 */
276
static int gen_release_stream(URLContext *s, RTMPContext *rt)
277 278 279
{
    RTMPPacket pkt;
    uint8_t *p;
280
    int ret;
281

282 283 284
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 29 + strlen(rt->playpath))) < 0)
        return ret;
285

286
    av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
287 288
    p = pkt.data;
    ff_amf_write_string(&p, "releaseStream");
289
    ff_amf_write_number(&p, ++rt->nb_invokes);
290 291 292
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

293 294
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
295
    ff_rtmp_packet_destroy(&pkt);
296

297
    return ret;
298 299 300
}

/**
301
 * Generate 'FCPublish' call and send it to the server. It should make
302 303
 * the server preapare for receiving media streams.
 */
304
static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
305 306 307
{
    RTMPPacket pkt;
    uint8_t *p;
308
    int ret;
309

310 311 312
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25 + strlen(rt->playpath))) < 0)
        return ret;
313

314
    av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
315 316
    p = pkt.data;
    ff_amf_write_string(&p, "FCPublish");
317
    ff_amf_write_number(&p, ++rt->nb_invokes);
318 319 320
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

321 322
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
323
    ff_rtmp_packet_destroy(&pkt);
324

325
    return ret;
326 327 328
}

/**
329
 * Generate 'FCUnpublish' call and send it to the server. It should make
330 331
 * the server destroy stream.
 */
332
static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
333 334 335
{
    RTMPPacket pkt;
    uint8_t *p;
336
    int ret;
337

338 339 340
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 27 + strlen(rt->playpath))) < 0)
        return ret;
341

342
    av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
343 344
    p = pkt.data;
    ff_amf_write_string(&p, "FCUnpublish");
345
    ff_amf_write_number(&p, ++rt->nb_invokes);
346 347 348
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

349 350
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
351
    ff_rtmp_packet_destroy(&pkt);
352

353
    return ret;
354 355
}

356
/**
357
 * Generate 'createStream' call and send it to the server. It should make
358 359
 * the server allocate some channel for media streams.
 */
360
static int gen_create_stream(URLContext *s, RTMPContext *rt)
361 362 363
{
    RTMPPacket pkt;
    uint8_t *p;
364
    int ret;
365

366
    av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
367 368 369 370

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25)) < 0)
        return ret;
371 372 373

    p = pkt.data;
    ff_amf_write_string(&p, "createStream");
374
    ff_amf_write_number(&p, ++rt->nb_invokes);
375
    ff_amf_write_null(&p);
376
    rt->create_stream_invoke = rt->nb_invokes;
377

378 379
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
380
    ff_rtmp_packet_destroy(&pkt);
381

382
    return ret;
383 384 385 386
}


/**
387
 * Generate 'deleteStream' call and send it to the server. It should make
388 389
 * the server remove some channel for media streams.
 */
390
static int gen_delete_stream(URLContext *s, RTMPContext *rt)
391 392 393
{
    RTMPPacket pkt;
    uint8_t *p;
394
    int ret;
395

396
    av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
397 398 399 400

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 34)) < 0)
        return ret;
401 402 403

    p = pkt.data;
    ff_amf_write_string(&p, "deleteStream");
404
    ff_amf_write_number(&p, ++rt->nb_invokes);
405
    ff_amf_write_null(&p);
406
    ff_amf_write_number(&p, rt->main_channel_id);
407

408 409
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
410
    ff_rtmp_packet_destroy(&pkt);
411

412
    return ret;
413 414
}

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
/**
 * Generate client buffer time and send it to the server.
 */
static int gen_buffer_time(URLContext *s, RTMPContext *rt)
{
    RTMPPacket pkt;
    uint8_t *p;
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
                                     1, 10)) < 0)
        return ret;

    p = pkt.data;
    bytestream_put_be16(&p, 3);
    bytestream_put_be32(&p, rt->main_channel_id);
    bytestream_put_be32(&p, rt->client_buffer_time);

    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
    ff_rtmp_packet_destroy(&pkt);

    return ret;
}

440
/**
441
 * Generate 'play' call and send it to the server, then ping the server
442 443
 * to start actual playing.
 */
444
static int gen_play(URLContext *s, RTMPContext *rt)
445 446 447
{
    RTMPPacket pkt;
    uint8_t *p;
448
    int ret;
449

450
    av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
451 452 453 454 455

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE,
                                     0, 29 + strlen(rt->playpath))) < 0)
        return ret;

456 457 458 459
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "play");
460
    ff_amf_write_number(&p, ++rt->nb_invokes);
461 462
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
463
    ff_amf_write_number(&p, rt->live);
464

465 466
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
467 468
    ff_rtmp_packet_destroy(&pkt);

469
    return ret;
470 471
}

472
/**
473
 * Generate 'publish' call and send it to the server.
474
 */
475
static int gen_publish(URLContext *s, RTMPContext *rt)
476 477 478
{
    RTMPPacket pkt;
    uint8_t *p;
479
    int ret;
480

481
    av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
482 483 484 485 486

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
                                     0, 30 + strlen(rt->playpath))) < 0)
        return ret;

487 488 489 490
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "publish");
491
    ff_amf_write_number(&p, ++rt->nb_invokes);
492 493 494 495
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
    ff_amf_write_string(&p, "live");

496 497
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
498
    ff_rtmp_packet_destroy(&pkt);
499 500

    return ret;
501 502
}

503
/**
504
 * Generate ping reply and send it to the server.
505
 */
506
static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
507 508 509
{
    RTMPPacket pkt;
    uint8_t *p;
510 511 512 513 514
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
                                     ppkt->timestamp + 1, 6)) < 0)
        return ret;
515 516 517

    p = pkt.data;
    bytestream_put_be16(&p, 7);
518
    bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
519 520
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
521
    ff_rtmp_packet_destroy(&pkt);
522

523
    return ret;
524 525
}

526 527 528
/**
 * Generate server bandwidth message and send it to the server.
 */
529
static int gen_server_bw(URLContext *s, RTMPContext *rt)
530 531 532
{
    RTMPPacket pkt;
    uint8_t *p;
533 534 535 536 537
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_SERVER_BW,
                                     0, 4)) < 0)
        return ret;
538 539

    p = pkt.data;
540
    bytestream_put_be32(&p, rt->server_bw);
541 542
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
543
    ff_rtmp_packet_destroy(&pkt);
544

545
    return ret;
546 547
}

548 549 550
/**
 * Generate check bandwidth message and send it to the server.
 */
551
static int gen_check_bw(URLContext *s, RTMPContext *rt)
552 553 554
{
    RTMPPacket pkt;
    uint8_t *p;
555
    int ret;
556

557 558 559
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 21)) < 0)
        return ret;
560 561 562 563 564 565

    p = pkt.data;
    ff_amf_write_string(&p, "_checkbw");
    ff_amf_write_number(&p, ++rt->nb_invokes);
    ff_amf_write_null(&p);

566 567
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
568
    ff_rtmp_packet_destroy(&pkt);
569 570

    return ret;
571 572
}

573
/**
574
 * Generate report on bytes read so far and send it to the server.
575
 */
576
static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
577 578 579
{
    RTMPPacket pkt;
    uint8_t *p;
580 581 582 583 584
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
                                     ts, 4)) < 0)
        return ret;
585 586 587

    p = pkt.data;
    bytestream_put_be32(&p, rt->bytes_read);
588 589
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
590
    ff_rtmp_packet_destroy(&pkt);
591

592
    return ret;
593 594
}

595 596
int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
                        const uint8_t *key, int keylen, uint8_t *dst)
597 598 599 600 601 602
{
    struct AVSHA *sha;
    uint8_t hmac_buf[64+32] = {0};
    int i;

    sha = av_mallocz(av_sha_size);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
603 604
    if (!sha)
        return AVERROR(ENOMEM);
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632

    if (keylen < 64) {
        memcpy(hmac_buf, key, keylen);
    } else {
        av_sha_init(sha, 256);
        av_sha_update(sha,key, keylen);
        av_sha_final(sha, hmac_buf);
    }
    for (i = 0; i < 64; i++)
        hmac_buf[i] ^= HMAC_IPAD_VAL;

    av_sha_init(sha, 256);
    av_sha_update(sha, hmac_buf, 64);
    if (gap <= 0) {
        av_sha_update(sha, src, len);
    } else { //skip 32 bytes used for storing digest
        av_sha_update(sha, src, gap);
        av_sha_update(sha, src + gap + 32, len - gap - 32);
    }
    av_sha_final(sha, hmac_buf + 64);

    for (i = 0; i < 64; i++)
        hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
    av_sha_init(sha, 256);
    av_sha_update(sha, hmac_buf, 64+32);
    av_sha_final(sha, dst);

    av_free(sha);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
633 634

    return 0;
635 636
}

637 638 639 640 641 642 643 644 645 646 647 648
int ff_rtmp_calc_digest_pos(const uint8_t *buf, int off, int mod_val,
                            int add_val)
{
    int i, digest_pos = 0;

    for (i = 0; i < 4; i++)
        digest_pos += buf[i + off];
    digest_pos = digest_pos % mod_val + add_val;

    return digest_pos;
}

649
/**
650
 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
651 652 653
 * will be stored) into that packet.
 *
 * @param buf handshake data (1536 bytes)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
654
 * @param encrypted use an encrypted connection (RTMPE)
655 656
 * @return offset to the digest inside input data
 */
Samuel Pitoiset's avatar
Samuel Pitoiset committed
657
static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
658
{
659
    int ret, digest_pos;
660

Samuel Pitoiset's avatar
Samuel Pitoiset committed
661 662 663 664
    if (encrypted)
        digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
    else
        digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
665

666 667 668
    ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
                              rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
                              buf + digest_pos);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
669 670 671
    if (ret < 0)
        return ret;

672 673 674 675
    return digest_pos;
}

/**
676
 * Verify that the received server response has the expected digest value.
677 678 679 680 681 682 683 684
 *
 * @param buf handshake data received from the server (1536 bytes)
 * @param off position to search digest offset from
 * @return 0 if digest is valid, digest position otherwise
 */
static int rtmp_validate_digest(uint8_t *buf, int off)
{
    uint8_t digest[32];
685
    int ret, digest_pos;
686

687
    digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
688

689 690 691
    ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
                              rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
                              digest);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
692 693 694
    if (ret < 0)
        return ret;

695 696 697 698 699 700
    if (!memcmp(digest, buf + digest_pos, 32))
        return digest_pos;
    return 0;
}

/**
701
 * Perform handshake with the server by means of exchanging pseudorandom data
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
 * signed with HMAC-SHA2 digest.
 *
 * @return 0 if handshake succeeds, negative value otherwise
 */
static int rtmp_handshake(URLContext *s, RTMPContext *rt)
{
    AVLFG rnd;
    uint8_t tosend    [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
        3,                // unencrypted data
        0, 0, 0, 0,       // client uptime
        RTMP_CLIENT_VER1,
        RTMP_CLIENT_VER2,
        RTMP_CLIENT_VER3,
        RTMP_CLIENT_VER4,
    };
    uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
    uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
    int i;
    int server_pos, client_pos;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
721 722
    uint8_t digest[32], signature[32];
    int ret, type = 0;
723

724
    av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
725 726 727 728 729

    av_lfg_init(&rnd, 0xDEADC0DE);
    // generate handshake packet - 1536 bytes of pseudorandom data
    for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
        tosend[i] = av_lfg_get(&rnd) >> 24;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
730

731
    if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
        /* When the client wants to use RTMPE, we have to change the command
         * byte to 0x06 which means to use encrypted data and we have to set
         * the flash version to at least 9.0.115.0. */
        tosend[0] = 6;
        tosend[5] = 128;
        tosend[6] = 0;
        tosend[7] = 3;
        tosend[8] = 2;

        /* Initialize the Diffie-Hellmann context and generate the public key
         * to send to the server. */
        if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
            return ret;
    }

747
    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
748 749
    if (client_pos < 0)
        return client_pos;
750

751 752 753 754 755 756
    if ((ret = ffurl_write(rt->stream, tosend,
                           RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
        av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
        return ret;
    }

757 758
    if ((ret = ffurl_read_complete(rt->stream, serverdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
759
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
760
        return ret;
761
    }
762 763 764

    if ((ret = ffurl_read_complete(rt->stream, clientdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
765
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
766
        return ret;
767 768
    }

Samuel Pitoiset's avatar
Samuel Pitoiset committed
769
    av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
770
    av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
771 772
           serverdata[5], serverdata[6], serverdata[7], serverdata[8]);

773
    if (rt->is_input && serverdata[5] >= 3) {
774
        server_pos = rtmp_validate_digest(serverdata + 1, 772);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
775 776 777
        if (server_pos < 0)
            return server_pos;

778
        if (!server_pos) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
779
            type = 1;
780
            server_pos = rtmp_validate_digest(serverdata + 1, 8);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
781 782 783
            if (server_pos < 0)
                return server_pos;

784
            if (!server_pos) {
785
                av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
786
                return AVERROR(EIO);
787
            }
788 789
        }

790 791 792
        ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
                                  rtmp_server_key, sizeof(rtmp_server_key),
                                  digest);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
793 794 795
        if (ret < 0)
            return ret;

796
        ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
Samuel Pitoiset's avatar
Samuel Pitoiset committed
797
                                  0, digest, 32, signature);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
798 799 800
        if (ret < 0)
            return ret;

801
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
802 803 804 805 806 807 808 809 810 811 812
            /* Compute the shared secret key sent by the server and initialize
             * the RC4 encryption. */
            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
                                                   tosend + 1, type)) < 0)
                return ret;

            /* Encrypt the signature received by the server. */
            ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
        }

        if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
813
            av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
814
            return AVERROR(EIO);
815
        }
816

817 818
        for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
            tosend[i] = av_lfg_get(&rnd) >> 24;
819 820 821
        ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
                                  rtmp_player_key, sizeof(rtmp_player_key),
                                  digest);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
822 823 824
        if (ret < 0)
            return ret;

825 826 827
        ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
                                  digest, 32,
                                  tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
828 829
        if (ret < 0)
            return ret;
830

831
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
832 833 834 835 836 837
            /* Encrypt the signature to be send to the server. */
            ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
                                 serverdata[0]);
        }

838
        // write reply back to the server
839 840 841
        if ((ret = ffurl_write(rt->stream, tosend,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
842

843
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
844 845 846 847
            /* Set RC4 keys for encryption and update the keystreams. */
            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                return ret;
        }
848
    } else {
849
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
850 851 852 853 854 855 856 857 858 859 860 861 862
            /* Compute the shared secret key sent by the server and initialize
             * the RC4 encryption. */
            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
                            tosend + 1, 1)) < 0)
                return ret;

            if (serverdata[0] == 9) {
                /* Encrypt the signature received by the server. */
                ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
                                     serverdata[0]);
            }
        }

863 864 865
        if ((ret = ffurl_write(rt->stream, serverdata + 1,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
866

867
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
868 869 870 871
            /* Set RC4 keys for encryption and update the keystreams. */
            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                return ret;
        }
872 873
    }

874 875 876 877
    return 0;
}