From patchwork Wed Aug 14 12:31:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Boxer X-Patchwork-Id: 14506 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 26D364476BE for ; Wed, 14 Aug 2019 15:31:31 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 09AB9689D24; Wed, 14 Aug 2019 15:31:31 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f172.google.com (mail-lj1-f172.google.com [209.85.208.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6DB31689A5E for ; Wed, 14 Aug 2019 15:31:29 +0300 (EEST) Received: by mail-lj1-f172.google.com with SMTP id m24so2174796ljg.8 for ; Wed, 14 Aug 2019 05:31:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=xuNQWR64ayVPfz5g7DBB2TyBmVNRA9yFxVsl3G65Rro=; b=vDLdd4+MteQ1OmfiQl1MS42FKDKayndZE+eSxBiiS2ZWszs9ros221kmLn2pFUnbve Bx5qqQxud3dQitV0skpJpQ1JHEqwEvdUbYyaQJzTKqmDZ4biA+aK1wR9/7MZCLA/DrVN Vi2K/Z7jtAR6+ZdnZPvSXCTaLflj5KTE2v2Sv37VigjjTZHgLVqDVbEEPxGey9MJW+GR WditJ6p5ASpE7G5Bmr1I39lfndsIamqnx1iIk6P2lRvBvYAC6VUfFjVHnDfE5JND3RD/ ErZgApwa6oAtGoOL9pEt62HNTzgwzE+bwh6qbNS9m7uGJZK1w2MkNCK5Ha1nJ45QBl0G 2IIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=xuNQWR64ayVPfz5g7DBB2TyBmVNRA9yFxVsl3G65Rro=; b=TfhDS7GhcX+7qT9kNjNBswrXCvOKT9M2gYYMYWa3mf0pMYROWg2AK+Ze2Ukl4ooyNK y3etSSwxmx/9uLw33jjndQrjHUBcmy/i+3BJpWefKWV0FwvjNwfdJw3s28KEf56+mVBF lishxzYn8tHuQlkP/BhCuyK2MR8mgkT99kaWkvFuZNx+CeMVEJJBdoFMiw+hNMIBg77r vhy3f3LSQmEBJswJAL1A2v51+uUtqGbsi/eLGl6uW3yGvD9ihtbJscsX7PK3f4gpYH0b +HZuphCPb6YVsx+KjmYXGqgC/tIpCV1Ez08Gx90QEx9NEIU4uCgICScdRvFfP2+NxcVI 0sdg== X-Gm-Message-State: APjAAAUjNKDW/DaNhj93I4ey3y2tWAyC9gwkvrlsXm3FAKETaZ1IcZbb oMBxpzwkQn7kVT5MBiHVRW3ekUrNS1vKC6As/Z5p68Mt X-Google-Smtp-Source: APXvYqx56vVLhpXzYGrtVqMWxc/gXLPfkt1Syu3yFsuz4Eg/dku+IyP8fgfxTMwtyzBI67T0KUzxiYmG9vx25P2gtpY= X-Received: by 2002:a2e:8744:: with SMTP id q4mr24512325ljj.77.1565785888229; Wed, 14 Aug 2019 05:31:28 -0700 (PDT) MIME-Version: 1.0 From: Aaron Boxer Date: Wed, 14 Aug 2019 08:31:17 -0400 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] [PATCH 1/1] Support new SRT streamid specification X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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 From a28ef3c7d7d02b273786a659f9e1d468fd29d0a5 Mon Sep 17 00:00:00 2001 From: Aaron Boxer 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