From 8b6a5a7923dc4596091acaa9d8219ade39dd2d61 Mon Sep 17 00:00:00 2001 From: Samuel Pitoiset Date: Sat, 11 Aug 2012 12:41:33 +0200 Subject: rtmp: Do not send _checkbw calls as notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The _checkbw calls were changed to use transactionId 0 in commit 82613564 so that servers would not return _result/_error about it. While this is the strict interpretation of the spec, there are servers that return _error about it, even if transactionId was 0. The latest version of EvoStream Media Server (the commercial version of crtmpserver) behaves properly as described, i.e. returning an _error normally but not returning anything when using transactionId 0. The latest version of crtmpserver (right now at least) doesn't behave like this though, it returns an error even if transactionId was 0. There are also other servers that return errors even if transactionId is set to 0. Therefore set a proper transaction id so that the invoke can be tracked and the error properly ignored instead. Signed-off-by: Martin Storsjö --- libavformat/rtmpproto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libavformat/rtmpproto.c') diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 1db3152e8d..150e45a363 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -626,7 +626,7 @@ static int gen_check_bw(URLContext *s, RTMPContext *rt) p = pkt.data; ff_amf_write_string(&p, "_checkbw"); - ff_amf_write_number(&p, RTMP_NOTIFICATION); + ff_amf_write_number(&p, ++rt->nb_invokes); ff_amf_write_null(&p); return rtmp_send_packet(rt, &pkt, 0); -- cgit v1.2.3 From fb7e78089bf04aa73ba53972e8261bf0bbbb121a Mon Sep 17 00:00:00 2001 From: Samuel Pitoiset Date: Sat, 11 Aug 2012 12:42:17 +0200 Subject: rtmp: Gracefully ignore _checkbw errors by tracking them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Storsjö --- libavformat/rtmpproto.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'libavformat/rtmpproto.c') diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 150e45a363..a90d9dadb3 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -629,7 +629,7 @@ static int gen_check_bw(URLContext *s, RTMPContext *rt) ff_amf_write_number(&p, ++rt->nb_invokes); ff_amf_write_null(&p); - return rtmp_send_packet(rt, &pkt, 0); + return rtmp_send_packet(rt, &pkt, 1); } /** @@ -1055,15 +1055,27 @@ static int handle_server_bw(URLContext *s, RTMPPacket *pkt) static int handle_invoke_error(URLContext *s, RTMPPacket *pkt) { const uint8_t *data_end = pkt->data + pkt->data_size; + char *tracked_method = NULL; + int level = AV_LOG_ERROR; uint8_t tmpstr[256]; + int ret; + + if ((ret = find_tracked_method(s, pkt, 9, &tracked_method)) < 0) + return ret; if (!ff_amf_get_field_value(pkt->data + 9, data_end, "description", tmpstr, sizeof(tmpstr))) { - av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr); - return -1; + if (tracked_method && !strcmp(tracked_method, "_checkbw")) { + /* Ignore _checkbw errors. */ + level = AV_LOG_WARNING; + ret = 0; + } else + ret = -1; + av_log(s, level, "Server error: %s\n", tmpstr); } - return 0; + av_free(tracked_method); + return ret; } static int handle_invoke_result(URLContext *s, RTMPPacket *pkt) -- cgit v1.2.3 From 635ac8e1be91e941908f85642e4bbb609e48193f Mon Sep 17 00:00:00 2001 From: Samuel Pitoiset Date: Mon, 13 Aug 2012 17:05:00 +0200 Subject: rtmp: Add support for SWFVerification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specifies how the server verifies client SWF files before allowing the files to connect to an application. Verifying SWF files is a security measure that prevents someone from creating their own SWF files that can attempt to stream your resources. Signed-off-by: Martin Storsjö --- doc/protocols.texi | 6 +++++ libavformat/rtmpproto.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 2 +- 3 files changed, 73 insertions(+), 1 deletion(-) (limited to 'libavformat/rtmpproto.c') diff --git a/doc/protocols.texi b/doc/protocols.texi index bf67d89ad3..bdb3e8cae1 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -242,6 +242,12 @@ Name of live stream to subscribe to. By default no value will be sent. It is only sent if the option is specified or if rtmp_live is set to live. +@item rtmp_swfhash +SHA256 hash of the decompressed SWF file (32 bytes). + +@item rtmp_swfsize +Size of the decompressed SWF file, required for SWFVerification. + @item rtmp_swfurl URL of the SWF player for the media. By default no value will be sent. diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index a90d9dadb3..9b85e523c5 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -91,7 +91,11 @@ typedef struct RTMPContext { int nb_invokes; ///< keeps track of invoke messages char* tcurl; ///< url of the target stream char* flashver; ///< version of the flash plugin + char* swfhash; ///< SHA256 hash of the decompressed SWF file (32 bytes) + int swfhash_len; ///< length of the SHA256 hash + int swfsize; ///< size of the decompressed SWF file char* swfurl; ///< url of the swf player + char swfverification[42]; ///< hash of the SWF verification char* pageurl; ///< url of the web page char* subscribe; ///< name of live stream to subscribe int server_bw; ///< server bandwidth @@ -592,6 +596,27 @@ static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt) return rtmp_send_packet(rt, &pkt, 0); } +/** + * Generate SWF verification message and send it to the server. + */ +static int gen_swf_verification(URLContext *s, RTMPContext *rt) +{ + RTMPPacket pkt; + uint8_t *p; + int ret; + + av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n"); + if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, + 0, 44)) < 0) + return ret; + + p = pkt.data; + bytestream_put_be16(&p, 27); + memcpy(p, rt->swfverification, 42); + + return rtmp_send_packet(rt, &pkt, 0); +} + /** * Generate server bandwidth message and send it to the server. */ @@ -776,6 +801,30 @@ static int rtmp_validate_digest(uint8_t *buf, int off) return 0; } +static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt, + uint8_t *buf) +{ + uint8_t *p; + int ret; + + if (rt->swfhash_len != 32) { + av_log(s, AV_LOG_ERROR, + "Hash of the decompressed SWF file is not 32 bytes long.\n"); + return AVERROR(EINVAL); + } + + p = &rt->swfverification[0]; + bytestream_put_byte(&p, 1); + bytestream_put_byte(&p, 1); + bytestream_put_be32(&p, rt->swfsize); + bytestream_put_be32(&p, rt->swfsize); + + if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0) + return ret; + + return 0; +} + /** * Perform handshake with the server by means of exchanging pseudorandom data * signed with HMAC-SHA2 digest. @@ -866,6 +915,14 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt) } } + /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, + * key are the last 32 bytes of the server handshake. */ + if (rt->swfsize) { + if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 + + RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0) + return ret; + } + ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0, rtmp_server_key, sizeof(rtmp_server_key), digest); @@ -1001,6 +1058,13 @@ static int handle_ping(URLContext *s, RTMPPacket *pkt) if (t == 6) { if ((ret = gen_pong(s, rt, pkt)) < 0) return ret; + } else if (t == 26) { + if (rt->swfsize) { + if ((ret = gen_swf_verification(s, rt)) < 0) + return ret; + } else { + av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n"); + } } return 0; @@ -1717,6 +1781,8 @@ static const AVOption rtmp_options[] = { {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, + {"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC}, + {"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, DEC}, {"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, { NULL }, diff --git a/libavformat/version.h b/libavformat/version.h index e2cd0c7f05..54185fa5b7 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 54 #define LIBAVFORMAT_VERSION_MINOR 13 -#define LIBAVFORMAT_VERSION_MICRO 2 +#define LIBAVFORMAT_VERSION_MICRO 3 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ -- cgit v1.2.3