From 8a11e0d673872049b8260cb08f3c7cb40f9a1297 Mon Sep 17 00:00:00 2001
From: Aaron Boxer <aaron.boxer@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(+)
@@ -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