Hello! SRT has added a new stream ID specification allowing extraction of various fields from the user password. This patch adds support for using an ACL to check user credentials - base on this Haivision sample code: https://github.com/Haivision/srt/blob/master/testing/srt-test-live.cpp
Thanks, Aaron
From 8a11e0d673872049b8260cb08f3c7cb40f9a1297 Mon Sep 17 00:00:00 2001 From: Aaron Boxer <aaron.bo...@collabora.com> Date: Wed, 14 Aug 2019 08:26:17 -0400 Subject: [PATCH] libsrt: support streamid spec --- libavformat/libsrt.c | 135 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c index b5568089fa..d597e5fce4 100644 --- a/libavformat/libsrt.c +++ b/libavformat/libsrt.c @@ -34,6 +34,9 @@ #include "os_support.h" #include "url.h" + +static srt_listen_callback_fn libsrt_listen_callback; + /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */ #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE #define SRT_LIVE_DEFAULT_PAYLOAD_SIZE 1316 @@ -62,6 +65,7 @@ typedef struct SRTContext { int64_t maxbw; int pbkeylen; char *passphrase; + char* user_passphrase_list; int mss; int ffs; int ipttl; @@ -101,6 +105,7 @@ static const AVOption libsrt_options[] = { { "maxbw", "Maximum bandwidth (bytes per second) that the connection can use", OFFSET(maxbw), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, { "pbkeylen", "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)", OFFSET(pbkeylen), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 32, .flags = D|E }, { "passphrase", "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto", OFFSET(passphrase), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, + { "user_passphrase_list", "Comma separated list users and passphrases, of form usrr1=pass1,usr2=pass2,...", OFFSET(user_passphrase_list), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, { "mss", "The Maximum Segment Size", OFFSET(mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1500, .flags = D|E }, { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "ipttl", "IP Time To Live", OFFSET(ipttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .flags = D|E }, @@ -196,10 +201,134 @@ static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int wr } } +typedef struct _libsrt_parsed_param { + char *key; + char *val; +} libsrt_parsed_param; + + +/** + * Parse a key-value string into an array of key/value structs. + * + * The key-value string should be a null terminated string of parameters separated + * by a delimiter. Each parameter are checked for the equal sign character. + * If the equal sign character appears in the parameter, it will be used as a null terminator + * and the part that comes after it will be the value of the parameter. + * + * + * param: keyvalue: the key-value string to parse. The string will be modified. + * param: delimiter: the character that separates the key/value pairs + * from each other. + * param: params: an array of parsed_param structs to hold the result. + * param: max_params: maximum number of parameters to parse. + * + * Return: the number of parsed items. -1 if there was an error. + */ +static int libsrt_parse_key_value(char *keyvalue, const char* delimiter, + libsrt_parsed_param *params, int max_params) +{ + int i = 0; + char *token = NULL; + + if (!keyvalue || *keyvalue == '\0') + return -1; + if (!params || max_params == 0) + return 0; + + token = strtok( keyvalue, delimiter ); + while (token != NULL && i < max_params) { + params[i].key = token; + params[i].val = NULL; + if ((params[i].val = strchr( params[i].key, '=' )) != NULL) { + size_t val_len = strlen( params[i].val ); + /* make key into a zero-delimited string */ + *(params[i].val) = '\0'; + /* make sure val is not empty */ + if (val_len > 1) { + params[i].val++; + /* make sure key is not empty */ + if (params[i].key[0]) + i++; + }; + } + token = strtok( NULL, delimiter ); + } + + return i; +} + +/* callback to parse streamid */ +static int libsrt_listen_callback(void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid) +{ + const char* username = NULL; + const char* expected_passphrase = NULL; + static const char stdhdr [] = "#!::"; + uint32_t* pattern = (uint32_t*)stdhdr; + uint8_t found = 0; + libsrt_parsed_param key_values[256]; + URLContext *h = (URLContext*)opaq; + int num_key_values; + int i; + + if (hsversion != 5) { + av_log(h, AV_LOG_ERROR,"ERROR: hsversion expected 5\n"); + return -1; + } + if (!peeraddr) { + av_log(h, AV_LOG_ERROR,"ERROR: null peeraddr\n"); + return -1; + } + + /* Try the "standard interpretation" with username at key 'u' */ + if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern){ + num_key_values = libsrt_parse_key_value((char*)(streamid-4), ",", key_values, sizeof(key_values)); + if (num_key_values == -1) { + av_log(h, AV_LOG_ERROR,"ERROR: null streamid \n"); + return -1; + } + for (int i = 0; i < num_key_values; ++i){ + if (key_values[i].key && strcmp(key_values[i].key,"u")== 0){ + username = key_values[i].val; + found = 1; + } + } + if (!found) { + av_log(h, AV_LOG_ERROR,"User not found, returning false.\n"); + return -1; + } + } + else { + /* By default the username is the whole streamid */ + username = streamid; + } + + num_key_values = libsrt_parse_key_value(( (SRTContext*)h->priv_data)->user_passphrase_list, ",", key_values, sizeof(key_values)); + for (i = 0; i < num_key_values; ++i){ + if (key_values[i].key && strcmp(key_values[i].key, username) == 0){ + expected_passphrase = key_values[i].val; + break; + } + } + if (!expected_passphrase) { + av_log(h, AV_LOG_ERROR,"Cannot find password for user '%s'\n", username ); + return -1; + } + + av_log(h, AV_LOG_INFO,"Setting password '%s' for user '%s'\n", expected_passphrase, username ); + if (srt_setsockflag(ns, SRTO_PASSPHRASE, expected_passphrase, strlen(expected_passphrase))){ + av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_PASSPHRASE) failed\n"); + } + + return 0; + +} + static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int timeout) { int ret; int reuse = 1; + SRTContext *c = h->priv_data; + if (srt_setsockopt(fd, SOL_SOCKET, SRTO_REUSEADDR, &reuse, sizeof(reuse))) { av_log(h, AV_LOG_WARNING, "setsockopt(SRTO_REUSEADDR) failed\n"); } @@ -210,6 +339,9 @@ static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t ret = srt_listen(fd, 1); if (ret) return libsrt_neterrno(h); + /* add callback if there is a user passphrase list */ + if (c->user_passphrase_list) + (void)srt_listen_callback(fd, &libsrt_listen_callback, h); while ((ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback))) { switch (ret) { @@ -495,6 +627,9 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) { s->passphrase = av_strndup(buf, strlen(buf)); } + if (av_find_info_tag(buf, sizeof(buf), "user_passphrase_list", p)) { + s->user_passphrase_list = av_strndup(buf, strlen(buf)); + } if (av_find_info_tag(buf, sizeof(buf), "mss", p)) { s->mss = strtol(buf, NULL, 10); } -- 2.20.1
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".