rtmpproto.c 54.6 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
    STATE_FCPUBLISH,  ///< client FCPublishing stream (for output)
56
    STATE_PLAYING,    ///< client has started receiving multimedia data from server
57
    STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
58
    STATE_STOPPED,    ///< the broadcast has been stopped
59 60
} ClientState;

Samuel Pitoiset's avatar
Samuel Pitoiset committed
61 62 63 64 65
typedef struct TrackedMethod {
    char *name;
    int id;
} TrackedMethod;

66 67
/** protocol handler context */
typedef struct RTMPContext {
68
    const AVClass *class;
69 70 71
    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
72
    int           is_input;                   ///< input/output flag
73
    char          *playpath;                  ///< stream identifier to play (with possible "mp4:" prefix)
74
    int           live;                       ///< 0: recorded, -1: live, -2: both
75
    char          *app;                       ///< name of application
76
    char          *conn;                      ///< append arbitrary AMF data to the Connect message
77 78 79 80 81
    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
82
    int           flv_nb_packets;             ///< number of flv packets published
83
    RTMPPacket    out_pkt;                    ///< rtmp packet, created from flv a/v or metadata (for output)
84 85 86
    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
87
    int           skip_bytes;                 ///< number of bytes to skip from the input FLV stream in the next write call
88 89
    uint8_t       flv_header[11];             ///< partial incoming flv packet header
    int           flv_header_bytes;           ///< number of initialized bytes in flv_header
90
    int           nb_invokes;                 ///< keeps track of invoke messages
91
    char*         tcurl;                      ///< url of the target stream
92
    char*         flashver;                   ///< version of the flash plugin
93
    char*         swfurl;                     ///< url of the swf player
94
    char*         pageurl;                    ///< url of the web page
95
    char*         subscribe;                  ///< name of live stream to subscribe
96
    int           server_bw;                  ///< server bandwidth
97
    int           client_buffer_time;         ///< client buffer time in ms
98
    int           flush_interval;             ///< number of packets flushed in the same request (RTMPT only)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
99
    int           encrypted;                  ///< use an encrypted connection (RTMPE only)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
100 101 102
    TrackedMethod*tracked_methods;            ///< tracked methods buffer
    int           nb_tracked_methods;         ///< number of tracked methods
    int           tracked_methods_size;       ///< size of the tracked methods buffer
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
} 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
};

Samuel Pitoiset's avatar
Samuel Pitoiset committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 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 185 186 187 188 189 190 191 192 193
static int add_tracked_method(RTMPContext *rt, const char *name, int id)
{
    void *ptr;

    if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) {
        rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2;
        ptr = av_realloc(rt->tracked_methods,
                         rt->tracked_methods_size * sizeof(*rt->tracked_methods));
        if (!ptr)
            return AVERROR(ENOMEM);
        rt->tracked_methods = ptr;
    }

    rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
    if (!rt->tracked_methods[rt->nb_tracked_methods].name)
        return AVERROR(ENOMEM);
    rt->tracked_methods[rt->nb_tracked_methods].id = id;
    rt->nb_tracked_methods++;

    return 0;
}

static void del_tracked_method(RTMPContext *rt, int index)
{
    memmove(&rt->tracked_methods[index], &rt->tracked_methods[index + 1],
            sizeof(*rt->tracked_methods) * (rt->nb_tracked_methods - index - 1));
    rt->nb_tracked_methods--;
}

static void free_tracked_methods(RTMPContext *rt)
{
    int i;

    for (i = 0; i < rt->nb_tracked_methods; i ++)
        av_free(rt->tracked_methods[i].name);
    av_free(rt->tracked_methods);
}

static int rtmp_send_packet(RTMPContext *rt, RTMPPacket *pkt, int track)
{
    int ret;

    if (pkt->type == RTMP_PT_INVOKE && track) {
        GetByteContext gbc;
        char name[128];
        double pkt_id;
        int len;

        bytestream2_init(&gbc, pkt->data, pkt->data_size);
        if ((ret = ff_amf_read_string(&gbc, name, sizeof(name), &len)) < 0)
            goto fail;

        if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
            goto fail;

        if ((ret = add_tracked_method(rt, name, pkt_id)) < 0)
            goto fail;
    }

    ret = ff_rtmp_packet_write(rt->stream, pkt, rt->chunk_size,
                               rt->prev_pkt[1]);
fail:
    ff_rtmp_packet_destroy(pkt);
    return ret;
}

194 195
static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
{
196
    char *field, *value;
197 198 199 200 201 202 203 204 205 206 207 208 209 210
    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];
211 212 213 214 215 216
        field = param + 3;
        value = strchr(field, ':');
        if (!value)
            goto fail;
        *value = '\0';
        value++;
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

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

257
/**
258
 * Generate 'connect' call and send it to the server.
259
 */
260
static int gen_connect(URLContext *s, RTMPContext *rt)
261 262
{
    RTMPPacket pkt;
263
    uint8_t *p;
264 265 266 267 268
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 4096)) < 0)
        return ret;
269 270 271 272

    p = pkt.data;

    ff_amf_write_string(&p, "connect");
273
    ff_amf_write_number(&p, ++rt->nb_invokes);
274 275
    ff_amf_write_object_start(&p);
    ff_amf_write_field_name(&p, "app");
276
    ff_amf_write_string(&p, rt->app);
277

278
    if (!rt->is_input) {
279 280 281
        ff_amf_write_field_name(&p, "type");
        ff_amf_write_string(&p, "nonprivate");
    }
282
    ff_amf_write_field_name(&p, "flashVer");
283
    ff_amf_write_string(&p, rt->flashver);
284 285 286 287 288 289

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

290
    ff_amf_write_field_name(&p, "tcUrl");
291
    ff_amf_write_string(&p, rt->tcurl);
292
    if (rt->is_input) {
293 294 295 296
        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);
297 298 299 300

        /* 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. */
301
        ff_amf_write_field_name(&p, "audioCodecs");
302
        ff_amf_write_number(&p, 4071.0);
303 304 305 306
        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);
307 308 309 310 311

        if (rt->pageurl) {
            ff_amf_write_field_name(&p, "pageUrl");
            ff_amf_write_string(&p, rt->pageurl);
        }
312
    }
313 314
    ff_amf_write_object_end(&p);

315
    if (rt->conn) {
316
        char *param = rt->conn;
317 318 319

        // Write arbitrary AMF data to the Connect message.
        while (param != NULL) {
320 321 322 323 324 325 326
            char *sep;
            param += strspn(param, " ");
            if (!*param)
                break;
            sep = strchr(param, ' ');
            if (sep)
                *sep = '\0';
327 328 329 330 331 332
            if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
                // Invalid AMF parameter.
                ff_rtmp_packet_destroy(&pkt);
                return ret;
            }

333 334 335 336
            if (sep)
                param = sep + 1;
            else
                break;
337 338 339
        }
    }

340 341
    pkt.data_size = p - pkt.data;

Samuel Pitoiset's avatar
Samuel Pitoiset committed
342
    return rtmp_send_packet(rt, &pkt, 1);
343 344
}

345
/**
346
 * Generate 'releaseStream' call and send it to the server. It should make
347 348
 * the server release some channel for media streams.
 */
349
static int gen_release_stream(URLContext *s, RTMPContext *rt)
350 351 352
{
    RTMPPacket pkt;
    uint8_t *p;
353
    int ret;
354

355 356 357
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 29 + strlen(rt->playpath))) < 0)
        return ret;
358

359
    av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
360 361
    p = pkt.data;
    ff_amf_write_string(&p, "releaseStream");
362
    ff_amf_write_number(&p, ++rt->nb_invokes);
363 364 365
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
366
    return rtmp_send_packet(rt, &pkt, 0);
367 368 369
}

/**
370
 * Generate 'FCPublish' call and send it to the server. It should make
371 372
 * the server preapare for receiving media streams.
 */
373
static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
374 375 376
{
    RTMPPacket pkt;
    uint8_t *p;
377
    int ret;
378

379 380 381
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25 + strlen(rt->playpath))) < 0)
        return ret;
382

383
    av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
384 385
    p = pkt.data;
    ff_amf_write_string(&p, "FCPublish");
386
    ff_amf_write_number(&p, ++rt->nb_invokes);
387 388 389
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
390
    return rtmp_send_packet(rt, &pkt, 0);
391 392 393
}

/**
394
 * Generate 'FCUnpublish' call and send it to the server. It should make
395 396
 * the server destroy stream.
 */
397
static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
398 399 400
{
    RTMPPacket pkt;
    uint8_t *p;
401
    int ret;
402

403 404 405
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 27 + strlen(rt->playpath))) < 0)
        return ret;
406

407
    av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
408 409
    p = pkt.data;
    ff_amf_write_string(&p, "FCUnpublish");
410
    ff_amf_write_number(&p, ++rt->nb_invokes);
411 412 413
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
414
    return rtmp_send_packet(rt, &pkt, 0);
415 416
}

417
/**
418
 * Generate 'createStream' call and send it to the server. It should make
419 420
 * the server allocate some channel for media streams.
 */
421
static int gen_create_stream(URLContext *s, RTMPContext *rt)
422 423 424
{
    RTMPPacket pkt;
    uint8_t *p;
425
    int ret;
426

427
    av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
428 429 430 431

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 25)) < 0)
        return ret;
432 433 434

    p = pkt.data;
    ff_amf_write_string(&p, "createStream");
435
    ff_amf_write_number(&p, ++rt->nb_invokes);
436 437
    ff_amf_write_null(&p);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
438
    return rtmp_send_packet(rt, &pkt, 1);
439 440 441 442
}


/**
443
 * Generate 'deleteStream' call and send it to the server. It should make
444 445
 * the server remove some channel for media streams.
 */
446
static int gen_delete_stream(URLContext *s, RTMPContext *rt)
447 448 449
{
    RTMPPacket pkt;
    uint8_t *p;
450
    int ret;
451

452
    av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
453 454 455 456

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 34)) < 0)
        return ret;
457 458 459

    p = pkt.data;
    ff_amf_write_string(&p, "deleteStream");
460
    ff_amf_write_number(&p, ++rt->nb_invokes);
461
    ff_amf_write_null(&p);
462
    ff_amf_write_number(&p, rt->main_channel_id);
463

Samuel Pitoiset's avatar
Samuel Pitoiset committed
464
    return rtmp_send_packet(rt, &pkt, 0);
465 466
}

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
/**
 * 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);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
485
    return rtmp_send_packet(rt, &pkt, 0);
486 487
}

488
/**
489
 * Generate 'play' call and send it to the server, then ping the server
490 491
 * to start actual playing.
 */
492
static int gen_play(URLContext *s, RTMPContext *rt)
493 494 495
{
    RTMPPacket pkt;
    uint8_t *p;
496
    int ret;
497

498
    av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
499 500 501 502 503

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

504 505 506 507
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "play");
508
    ff_amf_write_number(&p, ++rt->nb_invokes);
509 510
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
511
    ff_amf_write_number(&p, rt->live);
512

Samuel Pitoiset's avatar
Samuel Pitoiset committed
513
    return rtmp_send_packet(rt, &pkt, 1);
514 515
}

516
/**
517
 * Generate 'publish' call and send it to the server.
518
 */
519
static int gen_publish(URLContext *s, RTMPContext *rt)
520 521 522
{
    RTMPPacket pkt;
    uint8_t *p;
523
    int ret;
524

525
    av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
526 527 528 529 530

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

531 532 533 534
    pkt.extra = rt->main_channel_id;

    p = pkt.data;
    ff_amf_write_string(&p, "publish");
535
    ff_amf_write_number(&p, ++rt->nb_invokes);
536 537 538 539
    ff_amf_write_null(&p);
    ff_amf_write_string(&p, rt->playpath);
    ff_amf_write_string(&p, "live");

Samuel Pitoiset's avatar
Samuel Pitoiset committed
540
    return rtmp_send_packet(rt, &pkt, 1);
541 542
}

543
/**
544
 * Generate ping reply and send it to the server.
545
 */
546
static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
547 548 549
{
    RTMPPacket pkt;
    uint8_t *p;
550 551
    int ret;

552 553 554 555 556 557
    if (ppkt->data_size < 6) {
        av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
               ppkt->data_size);
        return AVERROR_INVALIDDATA;
    }

558 559 560
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
                                     ppkt->timestamp + 1, 6)) < 0)
        return ret;
561 562 563

    p = pkt.data;
    bytestream_put_be16(&p, 7);
564
    bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
565

Samuel Pitoiset's avatar
Samuel Pitoiset committed
566
    return rtmp_send_packet(rt, &pkt, 0);
567 568
}

569 570 571
/**
 * Generate server bandwidth message and send it to the server.
 */
572
static int gen_server_bw(URLContext *s, RTMPContext *rt)
573 574 575
{
    RTMPPacket pkt;
    uint8_t *p;
576 577 578 579 580
    int ret;

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

    p = pkt.data;
583
    bytestream_put_be32(&p, rt->server_bw);
584

Samuel Pitoiset's avatar
Samuel Pitoiset committed
585
    return rtmp_send_packet(rt, &pkt, 0);
586 587
}

588 589 590
/**
 * Generate check bandwidth message and send it to the server.
 */
591
static int gen_check_bw(URLContext *s, RTMPContext *rt)
592 593 594
{
    RTMPPacket pkt;
    uint8_t *p;
595
    int ret;
596

597 598 599
    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
                                     0, 21)) < 0)
        return ret;
600 601 602

    p = pkt.data;
    ff_amf_write_string(&p, "_checkbw");
603
    ff_amf_write_number(&p, RTMP_NOTIFICATION);
604 605
    ff_amf_write_null(&p);

Samuel Pitoiset's avatar
Samuel Pitoiset committed
606
    return rtmp_send_packet(rt, &pkt, 0);
607 608
}

609
/**
610
 * Generate report on bytes read so far and send it to the server.
611
 */
612
static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
613 614 615
{
    RTMPPacket pkt;
    uint8_t *p;
616 617 618 619 620
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
                                     ts, 4)) < 0)
        return ret;
621 622 623

    p = pkt.data;
    bytestream_put_be32(&p, rt->bytes_read);
624

Samuel Pitoiset's avatar
Samuel Pitoiset committed
625
    return rtmp_send_packet(rt, &pkt, 0);
626 627
}

628 629
static int gen_fcsubscribe_stream(URLContext *s, RTMPContext *rt,
                                  const char *subscribe)
630 631 632 633 634 635
{
    RTMPPacket pkt;
    uint8_t *p;
    int ret;

    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
636
                                     0, 27 + strlen(subscribe))) < 0)
637 638 639 640 641 642
        return ret;

    p = pkt.data;
    ff_amf_write_string(&p, "FCSubscribe");
    ff_amf_write_number(&p, ++rt->nb_invokes);
    ff_amf_write_null(&p);
643
    ff_amf_write_string(&p, subscribe);
644

Samuel Pitoiset's avatar
Samuel Pitoiset committed
645
    return rtmp_send_packet(rt, &pkt, 1);
646 647
}

648 649
int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
                        const uint8_t *key, int keylen, uint8_t *dst)
650 651 652 653 654 655
{
    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
656 657
    if (!sha)
        return AVERROR(ENOMEM);
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685

    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
686 687

    return 0;
688 689
}

690 691 692 693 694 695 696 697 698 699 700 701
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;
}

702
/**
703
 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
704 705 706
 * will be stored) into that packet.
 *
 * @param buf handshake data (1536 bytes)
Samuel Pitoiset's avatar
Samuel Pitoiset committed
707
 * @param encrypted use an encrypted connection (RTMPE)
708 709
 * @return offset to the digest inside input data
 */
Samuel Pitoiset's avatar
Samuel Pitoiset committed
710
static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
711
{
712
    int ret, digest_pos;
713

Samuel Pitoiset's avatar
Samuel Pitoiset committed
714 715 716 717
    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);
718

719 720 721
    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
722 723 724
    if (ret < 0)
        return ret;

725 726 727 728
    return digest_pos;
}

/**
729
 * Verify that the received server response has the expected digest value.
730 731 732 733 734 735 736 737
 *
 * @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];
738
    int ret, digest_pos;
739

740
    digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
741

742 743 744
    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
745 746 747
    if (ret < 0)
        return ret;

748 749 750 751 752 753
    if (!memcmp(digest, buf + digest_pos, 32))
        return digest_pos;
    return 0;
}

/**
754
 * Perform handshake with the server by means of exchanging pseudorandom data
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
 * 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
774 775
    uint8_t digest[32], signature[32];
    int ret, type = 0;
776

777
    av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
778 779 780 781 782

    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
783

784
    if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
        /* 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;
    }

800
    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
801 802
    if (client_pos < 0)
        return client_pos;
803

804 805 806 807 808 809
    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;
    }

810 811
    if ((ret = ffurl_read_complete(rt->stream, serverdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
812
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
813
        return ret;
814
    }
815 816 817

    if ((ret = ffurl_read_complete(rt->stream, clientdata,
                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
818
        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
819
        return ret;
820 821
    }

Samuel Pitoiset's avatar
Samuel Pitoiset committed
822
    av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
823
    av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
824 825
           serverdata[5], serverdata[6], serverdata[7], serverdata[8]);

826
    if (rt->is_input && serverdata[5] >= 3) {
827
        server_pos = rtmp_validate_digest(serverdata + 1, 772);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
828 829 830
        if (server_pos < 0)
            return server_pos;

831
        if (!server_pos) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
832
            type = 1;
833
            server_pos = rtmp_validate_digest(serverdata + 1, 8);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
834 835 836
            if (server_pos < 0)
                return server_pos;

837
            if (!server_pos) {
838
                av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
839
                return AVERROR(EIO);
840
            }
841 842
        }

843 844 845
        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
846 847 848
        if (ret < 0)
            return ret;

849
        ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
Samuel Pitoiset's avatar
Samuel Pitoiset committed
850
                                  0, digest, 32, signature);
Samuel Pitoiset's avatar
Samuel Pitoiset committed
851 852 853
        if (ret < 0)
            return ret;

854
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
855 856 857 858 859 860 861 862 863 864 865
            /* 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)) {
866
            av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
867
            return AVERROR(EIO);
868
        }
869

870 871
        for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
            tosend[i] = av_lfg_get(&rnd) >> 24;
872 873 874
        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
875 876 877
        if (ret < 0)
            return ret;

878 879 880
        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
881 882
        if (ret < 0)
            return ret;
883

884
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
885 886 887 888 889 890
            /* Encrypt the signature to be send to the server. */
            ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
                                 serverdata[0]);
        }

891
        // write reply back to the server
892 893 894
        if ((ret = ffurl_write(rt->stream, tosend,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
895

896
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
897 898 899 900
            /* Set RC4 keys for encryption and update the keystreams. */
            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                return ret;
        }
901
    } else {
902
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
903 904 905 906 907 908 909 910 911 912 913 914 915
            /* 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]);
            }
        }

916 917 918
        if ((ret = ffurl_write(rt->stream, serverdata + 1,
                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
            return ret;
Samuel Pitoiset's avatar
Samuel Pitoiset committed
919

920
        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
Samuel Pitoiset's avatar
Samuel Pitoiset committed
921