diff --git a/doc/APIchanges b/doc/APIchanges index 45ccf13358..6fa2ef1004 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -15,6 +15,9 @@ libavutil: 2015-08-28 API changes, most recent first: +2016-02-01 - xxxxxxx - lavf 57.24.100 + Add protocol_whitelist to AVFormatContext, AVIOContext + 2016-01-31 - xxxxxxx - lavu 55.17.100 Add AV_FRAME_DATA_GOP_TIMECODE for exporting MPEG1/2 GOP timecodes. diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 285bb16a2c..273a6ae863 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1827,6 +1827,13 @@ typedef struct AVFormatContext { * Demuxing: Set by user. */ int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options); + + /** + * ',' separated list of allowed protocols. + * - encoding: unused + * - decoding: set by user through AVOptions (NO direct access) + */ + char *protocol_whitelist; } AVFormatContext; int av_format_get_probe_score(const AVFormatContext *s); diff --git a/libavformat/avio.c b/libavformat/avio.c index 96b18fd88e..362099dd82 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -73,7 +73,13 @@ static const AVClass *urlcontext_child_class_next(const AVClass *prev) return NULL; } -static const AVOption options[] = { { NULL } }; +#define OFFSET(x) offsetof(URLContext,x) +#define E AV_OPT_FLAG_ENCODING_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, + { NULL } +}; const AVClass ffurl_context_class = { .class_name = "URLContext", .item_name = urlcontext_to_name, @@ -201,12 +207,43 @@ fail: int ffurl_connect(URLContext *uc, AVDictionary **options) { - int err = + int err; + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; + + if (!options) + options = &tmp_opts; + + // Check that URLContext was initialized correctly and lists are matching if set + av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || + (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value))); + + if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) { + av_log(uc, AV_LOG_ERROR, "Protocol not on whitelist \'%s\'!\n", uc->protocol_whitelist); + return AVERROR(EINVAL); + } + + if (!uc->protocol_whitelist && uc->prot->default_whitelist) { + av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist); + uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist); + if (!uc->protocol_whitelist) { + return AVERROR(ENOMEM); + } + } else if (!uc->protocol_whitelist) + av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist + + if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0) + return err; + + err = uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) : uc->prot->url_open(uc, uc->filename, uc->flags); + + av_dict_set(options, "protocol_whitelist", NULL, 0); + if (err) return err; uc->is_connected = 1; @@ -296,18 +333,33 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, return AVERROR_PROTOCOL_NOT_FOUND; } -int ffurl_open(URLContext **puc, const char *filename, int flags, - const AVIOInterruptCB *int_cb, AVDictionary **options) +int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist) { + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; int ret = ffurl_alloc(puc, filename, flags, int_cb); if (ret < 0) return ret; if (options && (*puc)->prot->priv_data_class && (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) goto fail; + + if (!options) + options = &tmp_opts; + + av_assert0(!whitelist || + !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || + !strcmp(whitelist, e->value)); + + if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0) + goto fail; + if ((ret = av_opt_set_dict(*puc, options)) < 0) goto fail; + ret = ffurl_connect(*puc, options); + if (!ret) return 0; fail: @@ -316,6 +368,13 @@ fail: return ret; } +int ffurl_open(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + return ffurl_open_whitelist(puc, filename, flags, + int_cb, options, NULL); +} + static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, int size, int size_min, int (*transfer_func)(URLContext *h, diff --git a/libavformat/avio.h b/libavformat/avio.h index c3c0b73f2c..7fbce32b6c 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -249,6 +249,11 @@ typedef struct AVIOContext { * This is current internal only, do not use from outside. */ int short_seek_threshold; + + /** + * ',' separated list of allowed protocols. + */ + const char *protocol_whitelist; } AVIOContext; /* unbuffered I/O */ diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index ad505673c4..f7c85882e7 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -149,6 +149,10 @@ int ffio_fdopen(AVIOContext **s, URLContext *h); */ int ffio_open_null_buf(AVIOContext **s); +int ffio_open_whitelist(AVIOContext **s, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist); + /** * Close a null buffer. * diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 9851981cee..213ee96f91 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -53,7 +53,11 @@ static const AVClass *ff_avio_child_class_next(const AVClass *prev) return prev ? NULL : &ffurl_context_class; } +#define OFFSET(x) offsetof(AVIOContext,x) +#define E AV_OPT_FLAG_ENCODING_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM static const AVOption ff_avio_options[] = { + {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, { NULL }, }; @@ -800,6 +804,11 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) av_free(buffer); return AVERROR(ENOMEM); } + (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist); + if (!(*s)->protocol_whitelist && h->protocol_whitelist) { + avio_closep(s); + return AVERROR(ENOMEM); + } (*s)->direct = h->flags & AVIO_FLAG_DIRECT; (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; (*s)->max_packet_size = max_packet_size; @@ -919,13 +928,15 @@ int avio_open(AVIOContext **s, const char *filename, int flags) return avio_open2(s, filename, flags, NULL, NULL); } -int avio_open2(AVIOContext **s, const char *filename, int flags, - const AVIOInterruptCB *int_cb, AVDictionary **options) +int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist + ) { URLContext *h; int err; - err = ffurl_open(&h, filename, flags, int_cb, options); + err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist); if (err < 0) return err; err = ffio_fdopen(s, h); @@ -936,10 +947,16 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, return 0; } +int avio_open2(AVIOContext **s, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL); +} + int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options) { - return avio_open2(pb, url, flags, int_cb, options); + return ffio_open_whitelist(pb, url, flags, int_cb, options, s->protocol_whitelist); } int avio_close(AVIOContext *s) diff --git a/libavformat/options_table.h b/libavformat/options_table.h index cc64beaead..8926fe5734 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -100,6 +100,7 @@ static const AVOption avformat_options[] = { {"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, CHAR_MIN, CHAR_MAX, D|E}, {"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, {"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, +{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, {NULL}, }; diff --git a/libavformat/url.h b/libavformat/url.h index 391e3bca2a..0b4f2ee376 100644 --- a/libavformat/url.h +++ b/libavformat/url.h @@ -47,6 +47,7 @@ typedef struct URLContext { int is_connected; AVIOInterruptCB interrupt_callback; int64_t rw_timeout; /**< maximum time to wait for (network) read/write operation completion, in mcs */ + const char *protocol_whitelist; } URLContext; typedef struct URLProtocol { @@ -94,6 +95,7 @@ typedef struct URLProtocol { int (*url_close_dir)(URLContext *h); int (*url_delete)(URLContext *h); int (*url_move)(URLContext *h_src, URLContext *h_dst); + const char *default_whitelist; } URLProtocol; /** @@ -138,6 +140,10 @@ int ffurl_connect(URLContext *uc, AVDictionary **options); * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ +int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist); + int ffurl_open(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options); diff --git a/libavformat/utils.c b/libavformat/utils.c index 6bf2fd17a9..f8846b7115 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -139,11 +139,15 @@ void av_format_inject_global_side_data(AVFormatContext *s) int ff_copy_whitelists(AVFormatContext *dst, AVFormatContext *src) { - av_assert0(!dst->codec_whitelist && !dst->format_whitelist); + av_assert0(!dst->codec_whitelist && + !dst->format_whitelist && + !dst->protocol_whitelist); dst-> codec_whitelist = av_strdup(src->codec_whitelist); dst->format_whitelist = av_strdup(src->format_whitelist); + dst->protocol_whitelist = av_strdup(src->protocol_whitelist); if ( (src-> codec_whitelist && !dst-> codec_whitelist) - || (src->format_whitelist && !dst->format_whitelist)) { + || (src-> format_whitelist && !dst-> format_whitelist) + || (src->protocol_whitelist && !dst->protocol_whitelist)) { av_log(dst, AV_LOG_ERROR, "Failed to duplicate whitelist\n"); return AVERROR(ENOMEM); } @@ -352,9 +356,11 @@ static int init_input(AVFormatContext *s, const char *filename, (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) return score; - if ((ret = avio_open2(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags, - &s->interrupt_callback, options)) < 0) + if ((ret = ffio_open_whitelist(&s->pb, filename, AVIO_FLAG_READ | s->avio_flags, + &s->interrupt_callback, options, + s->protocol_whitelist)) < 0) return ret; + if (s->iformat) return 0; return av_probe_input_buffer2(s->pb, &s->iformat, filename, @@ -441,6 +447,14 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, goto fail; s->probe_score = ret; + if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) { + s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist); + if (!s->protocol_whitelist) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) { av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist); ret = AVERROR(EINVAL); diff --git a/libavformat/version.h b/libavformat/version.h index 423317dac4..16df2b88aa 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -30,8 +30,8 @@ #include "libavutil/version.h" #define LIBAVFORMAT_VERSION_MAJOR 57 -#define LIBAVFORMAT_VERSION_MINOR 23 -#define LIBAVFORMAT_VERSION_MICRO 101 +#define LIBAVFORMAT_VERSION_MINOR 24 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \