Merge commit 'b97b1adb3f807e1acd00d56319ee6cb41cc727e4'
[ffmpeg.git] / libavformat / rtmpproto.c
index e9814a3..fca29b6 100644 (file)
@@ -2,20 +2,20 @@
  * RTMP network protocol
  * Copyright (c) 2009 Konstantin Shishkov
  *
- * This file is part of Libav.
+ * This file is part of FFmpeg.
  *
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
  * 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.
  *
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
  * 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
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
@@ -48,7 +48,7 @@
 #include <zlib.h>
 #endif
 
-#define APP_MAX_LENGTH 128
+#define APP_MAX_LENGTH 1024
 #define PLAYPATH_MAX_LENGTH 256
 #define TCURL_MAX_LENGTH 512
 #define FLASHVER_MAX_LENGTH 64
@@ -76,7 +76,7 @@ typedef struct TrackedMethod {
 typedef struct RTMPContext {
     const AVClass *class;
     URLContext*   stream;                     ///< TCP stream used in interactions with RTMP server
-    RTMPPacket    prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
+    RTMPPacket    prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets ([0] for reading, [1] for writing)
     int           in_chunk_size;              ///< size of the chunks incoming RTMP packets are divided into
     int           out_chunk_size;             ///< size of the chunks outgoing RTMP packets are divided into
     int           is_input;                   ///< input/output flag
@@ -150,15 +150,13 @@ static const uint8_t rtmp_server_key[] = {
 
 static int add_tracked_method(RTMPContext *rt, const char *name, int id)
 {
-    void *ptr;
+    int err;
 
     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;
+        if ((err = av_reallocp(&rt->tracked_methods, rt->tracked_methods_size *
+                               sizeof(*rt->tracked_methods))) < 0)
+            return err;
     }
 
     rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
@@ -266,9 +264,6 @@ static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
         *value = '\0';
         value++;
 
-        if (!field || !value)
-            goto fail;
-
         ff_amf_write_field_name(p, field);
     } else {
         goto fail;
@@ -315,7 +310,7 @@ static int gen_connect(URLContext *s, RTMPContext *rt)
     int ret;
 
     if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
-                                     0, 4096)) < 0)
+                                     0, 4096 + APP_MAX_LENGTH)) < 0)
         return ret;
 
     p = pkt.data;
@@ -406,7 +401,7 @@ static int read_connect(URLContext *s, RTMPContext *rt)
     GetByteContext gbc;
 
     if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
-                                   rt->prev_pkt[1])) < 0)
+                                   rt->prev_pkt[0])) < 0)
         return ret;
     cp = pkt.data;
     bytestream2_init(&gbc, cp, pkt.size);
@@ -1168,7 +1163,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
     for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
         tosend[i] = av_lfg_get(&rnd) >> 24;
 
-    if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+    if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
         /* 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. */
@@ -1246,7 +1241,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
         if (ret < 0)
             return ret;
 
-        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
             /* 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,
@@ -1276,7 +1271,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
         if (ret < 0)
             return ret;
 
-        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
             /* Encrypt the signature to be send to the server. */
             ff_rtmpe_encrypt_sig(rt->stream, tosend +
                                  RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
@@ -1288,13 +1283,13 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
                                RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
             return ret;
 
-        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
             /* Set RC4 keys for encryption and update the keystreams. */
             if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                 return ret;
         }
     } else {
-        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
             /* 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,
@@ -1312,7 +1307,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
                                RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
             return ret;
 
-        if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
             /* Set RC4 keys for encryption and update the keystreams. */
             if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
                 return ret;
@@ -2066,14 +2061,13 @@ static int handle_invoke(URLContext *s, RTMPPacket *pkt)
 static int handle_notify(URLContext *s, RTMPPacket *pkt) {
     RTMPContext *rt  = s->priv_data;
     const uint8_t *p = NULL;
-    uint8_t *cp      = NULL;
     uint8_t commandbuffer[64];
     char statusmsg[128];
     int stringlen;
     GetByteContext gbc;
     PutByteContext pbc;
     uint32_t ts;
-    int old_flv_size;
+    int old_flv_size, err;
     const uint8_t *datatowrite;
     unsigned datatowritelength;
 
@@ -2098,19 +2092,19 @@ static int handle_notify(URLContext *s, RTMPPacket *pkt) {
 
     // generate packet header and put data into buffer for FLV demuxer
     if (rt->flv_off < rt->flv_size) {
+        // There is old unread data in the buffer, thus append at the end
         old_flv_size  = rt->flv_size;
         rt->flv_size += datatowritelength + 15;
     } else {
+        // All data has been read, write the new data at the start of the buffer
         old_flv_size = 0;
         rt->flv_size = datatowritelength + 15;
         rt->flv_off  = 0;
     }
 
-    cp = av_realloc(rt->flv_data, rt->flv_size);
-    if (!cp)
-        return AVERROR(ENOMEM);
-    rt->flv_data = cp;
-    bytestream2_init_writer(&pbc, cp, rt->flv_size);
+    if ((err = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+        return err;
+    bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
     bytestream2_skip_p(&pbc, old_flv_size);
     bytestream2_put_byte(&pbc, pkt->type);
     bytestream2_put_be24(&pbc, datatowritelength);
@@ -2189,7 +2183,6 @@ static int get_packet(URLContext *s, int for_header)
 {
     RTMPContext *rt = s->priv_data;
     int ret;
-    uint8_t *p;
     const uint8_t *next;
     uint32_t size;
     uint32_t ts, cts, pts=0;
@@ -2208,7 +2201,7 @@ static int get_packet(URLContext *s, int for_header)
             }
         }
         rt->bytes_read += ret;
-        if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
+        if (rt->bytes_read - rt->last_bytes_read > rt->client_report_size) {
             av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
             if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0)
                 return ret;
@@ -2253,19 +2246,21 @@ static int get_packet(URLContext *s, int for_header)
         if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO ||
            (rpkt.type == RTMP_PT_NOTIFY &&
             ff_amf_match_string(rpkt.data, rpkt.size, "onMetaData"))) {
+            int err;
             ts = rpkt.timestamp;
 
             // generate packet header and put data into buffer for FLV demuxer
             rt->flv_off  = 0;
             rt->flv_size = rpkt.size + 15;
-            rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size);
-            bytestream_put_byte(&p, rpkt.type);
-            bytestream_put_be24(&p, rpkt.size);
-            bytestream_put_be24(&p, ts);
-            bytestream_put_byte(&p, ts >> 24);
-            bytestream_put_be24(&p, 0);
-            bytestream_put_buffer(&p, rpkt.data, rpkt.size);
-            bytestream_put_be32(&p, 0);
+            if ((err = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+                return err;
+            bytestream_put_byte(&rt->flv_data, rpkt.type);
+            bytestream_put_be24(&rt->flv_data, rpkt.size);
+            bytestream_put_be24(&rt->flv_data, ts);
+            bytestream_put_byte(&rt->flv_data, ts >> 24);
+            bytestream_put_be24(&rt->flv_data, 0);
+            bytestream_put_buffer(&rt->flv_data, rpkt.data, rpkt.size);
+            bytestream_put_be32(&rt->flv_data, 0);
             ff_rtmp_packet_destroy(&rpkt);
             return 0;
         } else if (rpkt.type == RTMP_PT_NOTIFY) {
@@ -2277,10 +2272,13 @@ static int get_packet(URLContext *s, int for_header)
             }
             return 0;
         } else if (rpkt.type == RTMP_PT_METADATA) {
+            int err;
+            uint8_t *p;
             // we got raw FLV data, make it available for FLV demuxer
             rt->flv_off  = 0;
             rt->flv_size = rpkt.size;
-            rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
+            if ((err = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+                return err;
             /* rewrite timestamps */
             next = rpkt.data;
             ts = rpkt.timestamp;
@@ -2309,7 +2307,7 @@ static int get_packet(URLContext *s, int for_header)
 static int rtmp_close(URLContext *h)
 {
     RTMPContext *rt = h->priv_data;
-    int ret = 0;
+    int ret = 0, i, j;
 
     if (!rt->is_input) {
         rt->flv_data = NULL;
@@ -2320,6 +2318,9 @@ static int rtmp_close(URLContext *h)
     }
     if (rt->state > STATE_HANDSHAKED)
         ret = gen_delete_stream(h, rt);
+    for (i = 0; i < 2; i++)
+        for (j = 0; j < RTMP_CHANNELS; j++)
+            ff_rtmp_packet_destroy(&rt->prev_pkt[i][j]);
 
     free_tracked_methods(rt);
     av_freep(&rt->flv_data);
@@ -2453,16 +2454,20 @@ reconnect:
             fname = strchr(p + 1, '/');
             if (!fname || (c && c < fname)) {
                 fname = p + 1;
-                av_strlcpy(rt->app, path + 1, p - path);
+                av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH));
             } else {
                 fname++;
-                av_strlcpy(rt->app, path + 1, fname - path - 1);
+                av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH));
             }
         }
     }
 
     if (old_app) {
         // The name of application has been defined by the user, override it.
+        if (strlen(old_app) >= APP_MAX_LENGTH) {
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
         av_free(rt->app);
         rt->app = old_app;
     }
@@ -2546,9 +2551,11 @@ reconnect:
     }
 
     if (rt->is_input) {
+        int err;
         // generate FLV header for demuxer
         rt->flv_size = 13;
-        rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
+        if ((err = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+            return err;
         rt->flv_off  = 0;
         memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
     } else {