Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
ACL: Add connection limit type
  • Loading branch information
perexg committed Jun 5, 2015
1 parent 82214a8 commit 0cbfa37
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 24 deletions.
4 changes: 4 additions & 0 deletions docs/html/config_access.html
Expand Up @@ -78,6 +78,10 @@
<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.

<dt><b>Connection Limit Type</b>
<dd>
Allow to restrict the limit for connections to streaming or DVR only.

<dt><b>Limit Connections</b>
<dd>
If set, this will limit the number of concurrent streaming connections and
Expand Down
56 changes: 51 additions & 5 deletions src/access.c
Expand Up @@ -369,7 +369,7 @@ access_dump_a(access_t *a)
int first;

tvh_strlcatf(buf, sizeof(buf), l,
"%s:%s [%c%c%c%c%c%c%c%c%c%c], conn=%u%s",
"%s:%s [%c%c%c%c%c%c%c%c%c%c], conn=%u:s%u:r%u%s",
a->aa_representative ?: "<no-id>",
a->aa_username ?: "<no-user>",
a->aa_rights & ACCESS_STREAMING ? 'S' : ' ',
Expand All @@ -383,6 +383,8 @@ access_dump_a(access_t *a)
a->aa_rights & ACCESS_FAILED_RECORDER ? 'F' : ' ',
a->aa_rights & ACCESS_ADMIN ? '*' : ' ',
a->aa_conn_limit,
a->aa_conn_limit_streaming,
a->aa_conn_limit_dvr,
a->aa_match ? ", matched" : "");

if (a->aa_profiles) {
Expand Down Expand Up @@ -443,12 +445,37 @@ access_dump_a(access_t *a)
tvhtrace("access", "%s", buf);
}

/*
*
*/
static access_t *access_alloc(void)
{
return calloc(1, sizeof(access_t));
}

/*
*
*/
static void
access_update(access_t *a, access_entry_t *ae)
{
switch (ae->ae_conn_limit_type) {
case ACCESS_CONN_LIMIT_TYPE_ALL:
if (a->aa_conn_limit < ae->ae_conn_limit)
a->aa_conn_limit = ae->ae_conn_limit;
break;
case ACCESS_CONN_LIMIT_TYPE_STREAMING:
if (ae->ae_conn_limit && a->aa_conn_limit_streaming < ae->ae_conn_limit) {
a->aa_conn_limit_streaming = ae->ae_conn_limit;
a->aa_conn_limit = 0;
}
break;
case ACCESS_CONN_LIMIT_TYPE_DVR:
if (a->aa_conn_limit_dvr < ae->ae_conn_limit)
a->aa_conn_limit_dvr = ae->ae_conn_limit;
break;
}

if(a->aa_conn_limit < ae->ae_conn_limit)
a->aa_conn_limit = ae->ae_conn_limit;

Expand Down Expand Up @@ -488,7 +515,7 @@ access_update(access_t *a, access_entry_t *ae)
access_t *
access_get(const char *username, const char *password, struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_t *a = access_alloc();
access_entry_t *ae;
int nouser = username == NULL || username[0] == '\0';

Expand Down Expand Up @@ -556,7 +583,7 @@ access_t *
access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_t *a = access_alloc();
access_entry_t *ae;
int nouser = username == NULL || username[0] == '\0';

Expand Down Expand Up @@ -622,7 +649,7 @@ access_get_hashed(const char *username, const uint8_t digest[20],
access_t *
access_get_by_username(const char *username)
{
access_t *a = calloc(1, sizeof(*a));
access_t *a = access_alloc();
access_entry_t *ae;

a->aa_username = strdup(username);
Expand Down Expand Up @@ -656,7 +683,7 @@ access_get_by_username(const char *username)
access_t *
access_get_by_addr(struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_t *a = access_alloc();
access_entry_t *ae;

a->aa_representative = malloc(50);
Expand Down Expand Up @@ -1201,6 +1228,18 @@ access_entry_profile_get(void *o)
return &ret;
}

static htsmsg_t *
access_entry_conn_limit_type_enum ( void *p )
{
static struct strtab
conn_limit_type_tab[] = {
{ "All (Streaming + DVR)", ACCESS_CONN_LIMIT_TYPE_ALL },
{ "Streaming", ACCESS_CONN_LIMIT_TYPE_STREAMING },
{ "DVR", ACCESS_CONN_LIMIT_TYPE_DVR },
};
return strtab2htsmsg(conn_limit_type_tab);
}

const idclass_t access_entry_class = {
.ic_class = "access",
.ic_caption = "Access",
Expand Down Expand Up @@ -1315,6 +1354,13 @@ const idclass_t access_entry_class = {
.name = "Admin",
.off = offsetof(access_entry_t, ae_admin),
},
{
.type = PT_INT,
.id = "conn_limit_type",
.name = "Connection Limit Type",
.off = offsetof(access_entry_t, ae_conn_limit_type),
.list = access_entry_conn_limit_type_enum,
},
{
.type = PT_U32,
.id = "conn_limit",
Expand Down
11 changes: 11 additions & 0 deletions src/access.h
Expand Up @@ -63,6 +63,12 @@ TAILQ_HEAD(access_entry_queue, access_entry);

extern struct access_entry_queue access_entries;

enum {
ACCESS_CONN_LIMIT_TYPE_ALL = 0,
ACCESS_CONN_LIMIT_TYPE_STREAMING,
ACCESS_CONN_LIMIT_TYPE_DVR,
};

typedef struct access_entry {
idnode_t ae_id;

Expand All @@ -80,6 +86,7 @@ typedef struct access_entry {
struct profile *ae_profile;
LIST_ENTRY(access_entry) ae_profile_link;

int ae_conn_limit_type;
uint32_t ae_conn_limit;

int ae_dvr;
Expand Down Expand Up @@ -117,6 +124,10 @@ typedef struct access {
htsmsg_t *aa_chtags;
int aa_match;
uint32_t aa_conn_limit;
uint32_t aa_conn_limit_streaming;
uint32_t aa_conn_limit_dvr;
uint32_t aa_conn_streaming;
uint32_t aa_conn_dvr;
} access_t;

TAILQ_HEAD(access_ticket_queue, access_ticket);
Expand Down
12 changes: 8 additions & 4 deletions src/dvr/dvr_rec.c
Expand Up @@ -70,6 +70,7 @@ dvr_rec_subscribe(dvr_entry_t *de)
struct sockaddr sa;
access_t *aa;
uint32_t rec_count, net_count;
int c1, c2;

assert(de->de_s == NULL);
assert(de->de_chain == NULL);
Expand All @@ -92,12 +93,15 @@ dvr_rec_subscribe(dvr_entry_t *de)
return -EPERM;
}

if (aa->aa_conn_limit) {
if (aa->aa_conn_limit || aa->aa_conn_limit_dvr) {
rec_count = dvr_usage_count(aa);
net_count = tcp_connection_count(aa);
if (rec_count + net_count >= aa->aa_conn_limit) {
net_count = aa->aa_conn_limit ? tcp_connection_count(aa) : 0;
/* the rule is: allow if one condition is OK */
c1 = aa->aa_conn_limit ? rec_count + net_count >= aa->aa_conn_limit : -1;
c2 = aa->aa_conn_limit_dvr ? rec_count >= aa->aa_conn_limit : -1;
if (c1 && c2) {
tvherror("dvr", "multiple connections are not allowed for user '%s' from '%s' "
"(limit %u, active streaming %u, active DVR %u)",
"(limit %u, streaming %u, active DVR %u)",
aa->aa_username ?: "", aa->aa_representative ?: "",
aa->aa_conn_limit, rec_count, net_count);
return -EOVERFLOW;
Expand Down
34 changes: 21 additions & 13 deletions src/tcp.c
Expand Up @@ -461,6 +461,7 @@ tcp_connection_launch
tcp_server_launch_t *tsl, *res;
uint32_t used = 0, used2;
time_t started = dispatch_clock;
int c1, c2;

lock_assert(&global_lock);

Expand All @@ -474,7 +475,7 @@ tcp_connection_launch
LIST_FOREACH(tsl, &tcp_server_active, alink) {
if (tsl->fd == fd) {
res = tsl;
if (!aa->aa_conn_limit)
if (!aa->aa_conn_limit && !aa->aa_conn_limit_streaming)
break;
continue;
}
Expand All @@ -484,20 +485,27 @@ tcp_connection_launch
if (res == NULL)
return NULL;

if (aa->aa_conn_limit && used + (used2 = dvr_usage_count(aa)) >= aa->aa_conn_limit) {
if (started + 3 < dispatch_clock) {
tvherror("tcp", "multiple connections are not allowed for user '%s' from '%s' "
"(limit %u, active streaming %u, active DVR %u)",
aa->aa_username ?: "", aa->aa_representative ?: "", aa->aa_conn_limit,
used, used2);
if (aa->aa_conn_limit || aa->aa_conn_limit_streaming) {
used2 = aa->aa_conn_limit ? dvr_usage_count(aa) : 0;
/* the rule is: allow if one condition is OK */
c1 = aa->aa_conn_limit ? used + used2 >= aa->aa_conn_limit : -1;
c2 = aa->aa_conn_limit_streaming ? used >= aa->aa_conn_limit_streaming : -1;

if (c1 && c2) {
if (started + 3 < dispatch_clock) {
tvherror("tcp", "multiple connections are not allowed for user '%s' from '%s' "
"(limit %u, active streaming %u, DVR %u)",
aa->aa_username ?: "", aa->aa_representative ?: "", aa->aa_conn_limit,
used, used2);
return NULL;
}
pthread_mutex_unlock(&global_lock);
usleep(250000);
pthread_mutex_lock(&global_lock);
if (tvheadend_running)
goto try_again;
return NULL;
}
pthread_mutex_unlock(&global_lock);
usleep(250000);
pthread_mutex_lock(&global_lock);
if (tvheadend_running)
goto try_again;
return NULL;
}

res->representative = aa->aa_representative ? strdup(aa->aa_representative) : NULL;
Expand Down
7 changes: 5 additions & 2 deletions src/webui/static/app/acleditor.js
Expand Up @@ -7,13 +7,15 @@ tvheadend.acleditor = function(panel, index)
var list = 'enabled,username,password,prefix,' +
'webui,admin,' +
'streaming,adv_streaming,htsp_streaming,' +
'profile,conn_limit,dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'profile,conn_limit_type,conn_limit,' +
'dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'dvr_config,channel_min,channel_max,channel_tag,comment';

var list2 = 'enabled,username,password,prefix,' +
'webui,admin,' +
'streaming,adv_streaming,htsp_streaming,' +
'profile,conn_limit,dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'profile,conn_limit_type,conn_limit,' +
'dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'failed_dvr,dvr_config,channel_min,channel_max,channel_tag,' +
'comment';

Expand All @@ -36,6 +38,7 @@ tvheadend.acleditor = function(panel, index)
all_rw_dvr: { width: 150 },
webui: { width: 140 },
admin: { width: 100 },
conn_limit_type:{ width: 160 },
conn_limit: { width: 160 },
channel_min: { width: 160 },
channel_max: { width: 160 }
Expand Down

0 comments on commit 0cbfa37

Please sign in to comment.