Merge commit '7395784ba72742b6daa62d35db4028e09f3fdf06'
authorClément Bœsch <u@pkh.me>
Thu, 23 Mar 2017 15:34:19 +0000 (16:34 +0100)
committerClément Bœsch <u@pkh.me>
Thu, 23 Mar 2017 15:34:19 +0000 (16:34 +0100)
* commit '7395784ba72742b6daa62d35db4028e09f3fdf06':
  rtmpproto: Check the return from ff_amf_read_string

Merged-by: Clément Bœsch <u@pkh.me>
1  2 
libavformat/rtmpproto.c

diff --combined libavformat/rtmpproto.c
index 7b2bd81bd12beb8ec0a8f6c1f1732cc91b44fa1d,64024259e2ca50696c73895ff17458508d1fa8a6..c0e2df447ecd9230caded25715b76fe513159e2b
@@@ -2,20 -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,9 -48,9 +48,9 @@@
  #include <zlib.h>
  #endif
  
 -#define APP_MAX_LENGTH 128
 -#define PLAYPATH_MAX_LENGTH 256
 -#define TCURL_MAX_LENGTH 512
 +#define APP_MAX_LENGTH 1024
 +#define PLAYPATH_MAX_LENGTH 512
 +#define TCURL_MAX_LENGTH 1024
  #define FLASHVER_MAX_LENGTH 64
  #define RTMP_PKTDATA_DEFAULT_SIZE 4096
  #define RTMP_HEADER 11
@@@ -94,8 -94,8 +94,8 @@@ typedef struct RTMPContext 
      int           flv_nb_packets;             ///< number of flv packets published
      RTMPPacket    out_pkt;                    ///< rtmp packet, created from flv a/v or metadata (for output)
      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
 +    uint64_t      bytes_read;                 ///< number of bytes read from server
 +    uint64_t      last_bytes_read;            ///< number of bytes read last reported to server
      uint32_t      last_timestamp;             ///< last timestamp received in a packet
      int           skip_bytes;                 ///< number of bytes to skip from the input FLV stream in the next write call
      int           has_audio;                  ///< presence of audio data
@@@ -156,8 -156,6 +156,8 @@@ static const uint8_t rtmp_server_key[] 
  };
  
  static int handle_chunk_size(URLContext *s, RTMPPacket *pkt);
 +static int handle_server_bw(URLContext *s, RTMPPacket *pkt);
 +static int handle_client_bw(URLContext *s, RTMPPacket *pkt);
  
  static int add_tracked_method(RTMPContext *rt, const char *name, int id)
  {
@@@ -219,8 -217,9 +219,8 @@@ static void free_tracked_methods(RTMPCo
      int i;
  
      for (i = 0; i < rt->nb_tracked_methods; i ++)
 -        av_free(rt->tracked_methods[i].name);
 -    av_free(rt->tracked_methods);
 -    rt->tracked_methods      = NULL;
 +        av_freep(&rt->tracked_methods[i].name);
 +    av_freep(&rt->tracked_methods);
      rt->tracked_methods_size = 0;
      rt->nb_tracked_methods   = 0;
  }
@@@ -323,7 -322,7 +323,7 @@@ static int gen_connect(URLContext *s, R
      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;
      return rtmp_send_packet(rt, &pkt, 1);
  }
  
 +
 +#define RTMP_CTRL_ABORT_MESSAGE  (2)
 +
  static int read_connect(URLContext *s, RTMPContext *rt)
  {
      RTMPPacket pkt = { 0 };
      uint8_t tmpstr[256];
      GetByteContext gbc;
  
 -    if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
 -                                   &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
 -        return ret;
 -
 -    if (pkt.type == RTMP_PT_CHUNK_SIZE) {
 -        if ((ret = handle_chunk_size(s, &pkt)) < 0)
 -            return ret;
 -        ff_rtmp_packet_destroy(&pkt);
 +    // handle RTMP Protocol Control Messages
 +    for (;;) {
          if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
                                         &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
              return ret;
 +#ifdef DEBUG
 +        ff_rtmp_packet_dump(s, &pkt);
 +#endif
 +        if (pkt.type == RTMP_PT_CHUNK_SIZE) {
 +            if ((ret = handle_chunk_size(s, &pkt)) < 0) {
 +                ff_rtmp_packet_destroy(&pkt);
 +                return ret;
 +            }
 +        } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) {
 +            av_log(s, AV_LOG_ERROR, "received abort message\n");
 +            ff_rtmp_packet_destroy(&pkt);
 +            return AVERROR_UNKNOWN;
 +        } else if (pkt.type == RTMP_PT_BYTES_READ) {
 +            av_log(s, AV_LOG_TRACE, "received acknowledgement\n");
 +        } else if (pkt.type == RTMP_PT_SERVER_BW) {
 +            if ((ret = handle_server_bw(s, &pkt)) < 0) {
 +                ff_rtmp_packet_destroy(&pkt);
 +                return ret;
 +            }
 +        } else if (pkt.type == RTMP_PT_CLIENT_BW) {
 +            if ((ret = handle_client_bw(s, &pkt)) < 0) {
 +                ff_rtmp_packet_destroy(&pkt);
 +                return ret;
 +            }
 +        } else if (pkt.type == RTMP_PT_INVOKE) {
 +            // received RTMP Command Message
 +            break;
 +        } else {
 +            av_log(s, AV_LOG_ERROR, "Unknown control message type (%d)\n", pkt.type);
 +        }
 +        ff_rtmp_packet_destroy(&pkt);
      }
  
      cp = pkt.data;
@@@ -1147,9 -1118,8 +1147,9 @@@ static int rtmp_calc_swfhash(URLContex
      int ret = 0;
  
      /* Get the SWF player file. */
 -    if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
 -                          &s->interrupt_callback, NULL, s->protocols, s)) < 0) {
 +    if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
 +                                    &s->interrupt_callback, NULL,
 +                                    s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
          av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
          goto fail;
      }
@@@ -1778,23 -1748,18 +1778,23 @@@ static int handle_connect_error(URLCont
          char *value = strchr(ptr, '=');
          if (next)
              *next++ = '\0';
 -        if (value)
 +        if (value) {
              *value++ = '\0';
 -        if (!strcmp(ptr, "user")) {
 -            user = value;
 -        } else if (!strcmp(ptr, "salt")) {
 -            salt = value;
 -        } else if (!strcmp(ptr, "opaque")) {
 -            opaque = value;
 -        } else if (!strcmp(ptr, "challenge")) {
 -            challenge = value;
 -        } else if (!strcmp(ptr, "nonce")) {
 -            nonce = value;
 +            if (!strcmp(ptr, "user")) {
 +                user = value;
 +            } else if (!strcmp(ptr, "salt")) {
 +                salt = value;
 +            } else if (!strcmp(ptr, "opaque")) {
 +                opaque = value;
 +            } else if (!strcmp(ptr, "challenge")) {
 +                challenge = value;
 +            } else if (!strcmp(ptr, "nonce")) {
 +                nonce = value;
 +            } else {
 +                av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr);
 +            }
 +        } else {
 +            av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr);
          }
          ptr = next;
      }
@@@ -1953,6 -1918,13 +1953,13 @@@ static int send_invoke_response(URLCont
          !strcmp(command, "publish")) {
          ret = ff_amf_read_string(&gbc, filename,
                                   sizeof(filename), &stringlen);
+         if (ret) {
+             if (ret == AVERROR(EINVAL))
+                 av_log(s, AV_LOG_ERROR, "Unable to parse stream name - name too long?\n");
+             else
+                 av_log(s, AV_LOG_ERROR, "Unable to parse stream name\n");
+             return ret;
+         }
          // check with url
          if (s->filename) {
              pchar = strrchr(s->filename, '/');
@@@ -2446,7 -2418,7 +2453,7 @@@ static int get_packet(URLContext *s, in
          rt->last_timestamp = rpkt.timestamp;
  
          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;
@@@ -2563,7 -2535,7 +2570,7 @@@ static int inject_fake_duration_metadat
      // Increase the size by the injected packet
      rt->flv_size += 55;
      // Delete the old FLV data
 -    av_free(old_flv_data);
 +    av_freep(&old_flv_data);
  
      p = rt->flv_data + 13;
      bytestream_put_byte(&p, FLV_TAG_TYPE_META);
   *             and 'playpath' is a file name (the rest of the path,
   *             may be prefixed with "mp4:")
   */
 -static int rtmp_open(URLContext *s, const char *uri, int flags)
 +static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts)
  {
      RTMPContext *rt = s->priv_data;
      char proto[8], hostname[256], path[1024], auth[100], *fname;
 -    char *old_app, *qmark, fname_buffer[1024];
 +    char *old_app, *qmark, *n, fname_buffer[1024];
      uint8_t buf[2048];
      int port;
 -    AVDictionary *opts = NULL;
      int ret;
  
      if (rt->listen_timeout > 0)
                   hostname, sizeof(hostname), &port,
                   path, sizeof(path), s->filename);
  
 -    if (strchr(path, ' ')) {
 +    n = strchr(path, ' ');
 +    if (n) {
          av_log(s, AV_LOG_WARNING,
                 "Detected librtmp style URL parameters, these aren't supported "
                 "by the libavformat internal RTMP handler currently enabled. "
                 "See the documentation for the correct way to pass parameters.\n");
 +        *n = '\0'; // Trim not supported part
      }
  
      if (auth[0]) {
      }
      if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
          if (!strcmp(proto, "rtmpts"))
 -            av_dict_set(&opts, "ffrtmphttp_tls", "1", 1);
 +            av_dict_set(opts, "ffrtmphttp_tls", "1", 1);
  
          /* open the http tunneling connection */
          ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
          ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
      } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
          if (!strcmp(proto, "rtmpte"))
 -            av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1);
 +            av_dict_set(opts, "ffrtmpcrypt_tunneling", "1", 1);
  
          /* open the encrypted connection */
          ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
      }
  
  reconnect:
 -    if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
 -                          &s->interrupt_callback, &opts, s->protocols, s)) < 0) {
 +    if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
 +                                    &s->interrupt_callback, opts,
 +                                    s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
          av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
          goto fail;
      }
          char *next = *path ? path + 1 : path;
          char *p = strchr(next, '/');
          if (!p) {
 -            fname = next;
 -            rt->app[0] = '\0';
 +            if (old_app) {
 +                // If name of application has been defined by the user, assume that
 +                // playpath is provided in the URL
 +                fname = next;
 +            } else {
 +                fname = NULL;
 +                av_strlcpy(rt->app, next, APP_MAX_LENGTH);
 +            }
          } else {
              // make sure we do not mismatch a playpath for an application instance
              char *c = strchr(p + 1, ':');
  
      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;
      }
  
      if (!rt->playpath) {
 -        int len = strlen(fname);
 -
          rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
          if (!rt->playpath) {
              ret = AVERROR(ENOMEM);
              goto fail;
          }
  
 -        if (!strchr(fname, ':') && len >= 4 &&
 -            (!strcmp(fname + len - 4, ".f4v") ||
 -             !strcmp(fname + len - 4, ".mp4"))) {
 -            memcpy(rt->playpath, "mp4:", 5);
 +        if (fname) {
 +            int len = strlen(fname);
 +            if (!strchr(fname, ':') && len >= 4 &&
 +                (!strcmp(fname + len - 4, ".f4v") ||
 +                 !strcmp(fname + len - 4, ".mp4"))) {
 +                memcpy(rt->playpath, "mp4:", 5);
 +            } else {
 +                if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
 +                    fname[len - 4] = '\0';
 +                rt->playpath[0] = 0;
 +            }
 +            av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
          } else {
 -            if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
 -                fname[len - 4] = '\0';
 -            rt->playpath[0] = 0;
 +            rt->playpath[0] = '\0';
          }
 -        av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
      }
  
      if (!rt->tcurl) {
      return 0;
  
  fail:
 -    av_dict_free(&opts);
 +    av_dict_free(opts);
      rtmp_close(s);
      return ret;
  }
@@@ -2983,7 -2940,6 +2990,7 @@@ static int rtmp_write(URLContext *s, co
          if (rt->flv_header_bytes < RTMP_HEADER) {
              const uint8_t *header = rt->flv_header;
              int channel = RTMP_AUDIO_CHANNEL;
 +
              copy = FFMIN(RTMP_HEADER - rt->flv_header_bytes, size_temp);
              bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
              rt->flv_header_bytes += copy;
@@@ -3140,7 -3096,7 +3147,7 @@@ static const AVClass flavor##_class = 
                                                   \
  const URLProtocol ff_##flavor##_protocol = {     \
      .name           = #flavor,                   \
 -    .url_open       = rtmp_open,                 \
 +    .url_open2      = rtmp_open,                 \
      .url_read       = rtmp_read,                 \
      .url_read_seek  = rtmp_seek,                 \
      .url_read_pause = rtmp_pause,                \