[FFmpeg-devel,1/1] Support new SRT streamid specification

Submitted by Aaron Boxer on Aug. 14, 2019, 12:31 p.m.

Details

Message ID CAK8GPyDFV1oyggGaNtvA3zCDExeWKYEh-Tnva16EW71Hr1+OiA@mail.gmail.com
State New
Headers show

Commit Message

Aaron Boxer Aug. 14, 2019, 12:31 p.m.
This patch supports the new SRT streamid spec allowing caller to send
key/value pairs
of parameters to the listener, including user name. A callback parses the
streamid
 - in this case, the parsers chooses the passphrase for the user based on
specified list
of passphrases.

Thanks,
Aaron

Comments

Matthias Hunstock Aug. 17, 2019, 8:59 p.m.
Am 14.08.19 um 14:31 schrieb Aaron Boxer:
> @@ -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 },


There is a typo at usrr1.

How can a password containing comma or equal sign be specified?


Matthias

Patch hide | download patch | download mbox

From a28ef3c7d7d02b273786a659f9e1d468fd29d0a5 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 | 131 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c
index b5568089fa..02fd840b37 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,6 +201,128 @@  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;
@@ -210,6 +337,7 @@  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);
+    (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 +623,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