rtmpproto.c 55.3 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
    char*         pageurl;                    ///< url of the web page
94
    int           server_bw;                  ///< server bandwidth
95
    int           client_buffer_time;         ///< client buffer time in ms
96
    int           flush_interval;             ///< number of packets flushed in the same request (RTMPT only)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
97
    int           encrypted;                  ///< use an encrypted connection (RTMPE only)
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
} 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
};

123 124
static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
{
125
    char *field, *value;
126 127 128 129 130 131 132 133 134 135 136 137 138 139
    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];
140 141 142 143 144 145
        field = param + 3;
        value = strchr(field, ':');
        if (!value)
            goto fail;
        *value = '\0';
        value++;
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 185

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

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

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

    p = pkt.data;

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

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

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

219
    ff_amf_write_field_name(&p, "tcUrl");
220
    ff_amf_write_string(&p, rt->tcurl);
221
    if (rt->is_input) {
222 223 224 225
        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);
226 227 228 229

        /* 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. */
230
        ff_amf_write_field_name(&p, "audioCodecs");
231
        ff_amf_write_number(&p, 4071.0);
232 233 234 235
        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);
236 237 238 239 240

        if (rt->pageurl) {
            ff_amf_write_field_name(&p, "pageUrl");
            ff_amf_write_string(&p, rt->pageurl);
        }
241
    }
242 243
    ff_amf_write_object_end(&p);

244
    if (rt->conn) {
245
        char *param = rt->conn;
246 247 248

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

262 263 264 265
            if (sep)
                param = sep + 1;
            else
                break;
266 267 268
        }
    }

269 270
    pkt.data_size = p - pkt.data;

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

275
    return ret;
276 277
}

278
/**
279
 * Generate 'releaseStream' call and send it to the server. It should make
280 281
 * the server release some channel for media streams.
 */
282
static int gen_release_stream(URLContext *s, RTMPContext *rt)
283 284 285
{
    RTMPPacket pkt;
    uint8_t *p;
286
    int ret;
287

288 289 290
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 29 + strlen(rt->playpath))) < 0)
        return ret;
291

292
    av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
293 294
    p = pkt.data;
    ff_amf_write_string(&p, "releaseStream");
295
    ff_amf_write_number(&p, ++rt->nb_invokes);
296 297 298
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

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

303
    return ret;
304 305 306
}

/**
307
 * Generate 'FCPublish' call and send it to the server. It should make
308 309
 * the server preapare for receiving media streams.
 */
310
static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
311 312 313
{
    RTMPPacket pkt;
    uint8_t *p;
314
    int ret;
315

316 317 318
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25 + strlen(rt->playpath))) < 0)
        return ret;
319

320
    av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
321 322
    p = pkt.data;
    ff_amf_write_string(&p, "FCPublish");
323
    ff_amf_write_number(&p, ++rt->nb_invokes);
324 325 326
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

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

331
    return ret;
332 333 334
}

/**
335
 * Generate 'FCUnpublish' call and send it to the server. It should make
336 337
 * the server destroy stream.
 */
338
static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
339 340 341
{
    RTMPPacket pkt;
    uint8_t *p;
342
    int ret;
343

344 345 346
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 27 + strlen(rt->playpath))) < 0)
        return ret;
347

348
    av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
349 350
    p = pkt.data;
    ff_amf_write_string(&p, "FCUnpublish");
351
    ff_amf_write_number(&p, ++rt->nb_invokes);
352 353 354
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

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

359
    return ret;
360 361
}

362
/**
363
 * Generate 'createStream' call and send it to the server. It should make
364 365
 * the server allocate some channel for media streams.
 */
366
static int gen_create_stream(URLContext *s, RTMPContext *rt)
367 368 369
{
    RTMPPacket pkt;
    uint8_t *p;
370
    int ret;
371

372
    av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
373 374 375 376

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25)) < 0)
        return ret;
377 378 379

    p = pkt.data;
    ff_amf_write_string(&p, "createStream");
380
    ff_amf_write_number(&p, ++rt->nb_invokes);
381
    ff_amf_write_null(&p);
382
    rt->create_stream_invoke = rt->nb_invokes;
383

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

388
    return ret;
389 390 391 392
}


/**
393
 * Generate 'deleteStream' call and send it to the server. It should make
394 395
 * the server remove some channel for media streams.
 */
396
static int gen_delete_stream(URLContext *s, RTMPContext *rt)
397 398 399
{
    RTMPPacket pkt;
    uint8_t *p;
400
    int ret;
401

402
    av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
403 404 405 406

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 34)) < 0)
        return ret;
407 408 409

    p = pkt.data;
    ff_amf_write_string(&p, "deleteStream");
410
    ff_amf_write_number(&p, ++rt->nb_invokes);
411
    ff_amf_write_null(&p);
412
    ff_amf_write_number(&p, rt->main_channel_id);
413

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

418
    return ret;
419 420
}

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
/**
 * 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;
}

446
/**
447
 * Generate 'play' call and send it to the server, then ping the server
448 449
 * to start actual playing.
 */
450
static int gen_play(URLContext *s, RTMPContext *rt)
451 452 453
{
    RTMPPacket pkt;
    uint8_t *p;
454
    int ret;
455

456
    av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
457 458 459 460 461

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

462 463 464 465
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "play");
466
    ff_amf_write_number(&p, ++rt->nb_invokes);
467 468
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
469
    ff_amf_write_number(&p, rt->live);
470

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

475
    return ret;
476 477
}

478
/**
479
 * Generate 'publish' call and send it to the server.
480
 */
481
static int gen_publish(URLContext *s, RTMPContext *rt)
482 483 484
{
    RTMPPacket pkt;
    uint8_t *p;
485
    int ret;
486

487
    av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
488 489 490 491 492

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

493 494 495 496
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "publish");
497
    ff_amf_write_number(&p, ++rt->nb_invokes);
498 499 500 501
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
    ff_amf_write_string(&p, "live");

502 503
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
504
    ff_rtmp_packet_destroy(&pkt);
505 506

    return ret;
507 508
}

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

518 519 520 521 522 523
    if (ppkt->data_size < 6) {
        av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
               ppkt->data_size);
        return AVERROR_INVALIDDATA;
    }

524 525 526
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
                                     ppkt->timestamp + 1, 6)) < 0)
        return ret;
527 528 529

    p = pkt.data;
    bytestream_put_be16(&p, 7);
530
    bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
531 532
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
533
    ff_rtmp_packet_destroy(&pkt);
534

535
    return ret;
536 537
}

538 539 540
/**
 * Generate server bandwidth message and send it to the server.
 */
541
static int gen_server_bw(URLContext *s, RTMPContext *rt)
542 543 544
{
    RTMPPacket pkt;
    uint8_t *p;
545 546 547 548 549
    int ret;

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

    p = pkt.data;
552
    bytestream_put_be32(&p, rt->server_bw);
553 554
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
555
    ff_rtmp_packet_destroy(&pkt);
556

557
    return ret;
558 559
}

560 561 562
/**
 * Generate check bandwidth message and send it to the server.
 */
563
static int gen_check_bw(URLContext *s, RTMPContext *rt)
564 565 566
{
    RTMPPacket pkt;
    uint8_t *p;
567
    int ret;
568

569 570 571
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 21)) < 0)
        return ret;
572 573 574 575 576 577

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

578 579
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
580
    ff_rtmp_packet_destroy(&pkt);
581 582

    return ret;
583 584
}

585
/**
586
 * Generate report on bytes read so far and send it to the server.
587
 */
588
static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
589 590 591
{
    RTMPPacket pkt;
    uint8_t *p;
592 593 594 595 596
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
                                     ts, 4)) < 0)
        return ret;
597 598 599

    p = pkt.data;
    bytestream_put_be32(&p, rt->bytes_read);
600 601
    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
602
    ff_rtmp_packet_destroy(&pkt);
603

604
    return ret;
605 606
}

607 608
int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
                        const uint8_t *key, int keylen, uint8_t *dst)
609 610 611 612 613 614
{
    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
615 616
    if (!sha)
        return AVERROR(ENOMEM);
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

    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
645 646

    return 0;
647 648
}

649 650 651 652 653 654 655 656 657 658 659 660
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;
}

661
/**
662
 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
663 664 665
 * will be stored) into that packet.
 *
 * @param buf handshake data (1536 bytes)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
666
 * @param encrypted use an encrypted connection (RTMPE)
667 668
 * @return offset to the digest inside input data
 */
Samuel Pitoiset's avatar
Samuel Pitoiset committed
669
static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
670
{
671
    int ret, digest_pos;
672

Samuel Pitoiset's avatar
Samuel Pitoiset committed
673 674 675 676
    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);
677

678 679 680
    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
681 682 683
    if (ret < 0)
        return ret;

684 685 686 687
    return digest_pos;
}

/**
688
 * Verify that the received server response has the expected digest value.
689 690 691 692 693 694 695 696
 *
 * @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];
697
    int ret, digest_pos;
698

699
    digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
700

701 702 703
    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
704 705 706
    if (ret < 0)
        return ret;

707 708 709 710 711 712
    if (!memcmp(digest, buf + digest_pos, 32))
        return digest_pos;
    return 0;
}

/**
713
 * Perform handshake with the server by means of exchanging pseudorandom data
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
 * 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
733 734
    uint8_t digest[32], signature[32];
    int ret, type = 0;
735

736
    av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
737 738 739 740 741

    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
742

743
    if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
        /* 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;
    }

759
    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
760 761
    if (client_pos < 0)
        return client_pos;
762

763 764 765 766 767 768
    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;
    }

769 770
    if ((ret = ffurl_read_complete(rt->stream, serverdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
771
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
772
        return ret;
773
    }
774 775 776

    if ((ret = ffurl_read_complete(rt->stream, clientdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
777
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
778
        return ret;
779 780
    }

Samuel Pitoiset's avatar
Samuel Pitoiset committed
781
    av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
782
    av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
783 784
           serverdata[5], serverdata[6], serverdata[7], serverdata[8]);

785
    if (rt->is_input && serverdata[5] >= 3) {
786
        server_pos = rtmp_validate_digest(serverdata + 1, 772);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
787 788 789
        if (server_pos < 0)
            return server_pos;

790
        if (!server_pos) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
791
            type = 1;
792
            server_pos = rtmp_validate_digest(serverdata + 1, 8);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
793 794 795
            if (server_pos < 0)
                return server_pos;

796
            if (!server_pos) {
797
                av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
798
                return AVERROR(EIO);
799
            }
800 801
        }

802 803 804
        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
805 806 807
        if (ret < 0)
            return ret;

808
        ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
Samuel Pitoiset's avatar
Samuel Pitoiset committed
809
                                  0, digest, 32, signature);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
810 811 812
        if (ret < 0)
            return ret;

813
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
814 815 816 817 818 819 820 821 822 823 824
            /* 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)) {
825
            av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
826
            return AVERROR(EIO);
827
        }
828

829 830
        for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
            tosend[i] = av_lfg_get(&rnd) >> 24;
831 832 833
        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
834 835 836
        if (ret < 0)
            return ret;

837 838 839
        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
840 841
        if (ret < 0)
            return ret;
842

843
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
844 845 846 847 848 849
            /* Encrypt the signature to be send to the server. */
            ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
                                 serverdata[0]);
        }

850
        // write reply back to the server
851 852 853
        if ((ret = ffurl_write(rt->stream, tosend,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
854

855
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
856 857 858 859
            /* Set RC4 keys for encryption and update the keystreams. */
            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                return ret;
        }
860
    } else {
861
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
862 863 864 865 866 867 868 869 870 871 872 873 874
            /* 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]);
            }
        }

875 876 877
        if ((ret = ffurl_write(rt->stream, serverdata + 1,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;