From: Jaroslav Kysela Date: Wed, 20 May 2015 16:53:23 +0000 (+0200) Subject: Use the connection limit also for DVR, fixes #2485 X-Git-Tag: v4.2.1~2514 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=808fd4703f5314f279c498fb7c3b148fa4805a6d;p=thirdparty%2Ftvheadend.git Use the connection limit also for DVR, fixes #2485 --- diff --git a/docs/html/config_access.html b/docs/html/config_access.html index 46338ba9d..1f16125ef 100644 --- a/docs/html/config_access.html +++ b/docs/html/config_access.html @@ -84,7 +84,8 @@ The columns have the following functions:
Limit Connections
- If set, this will limit the number of concurrent streaming connections a user is permitted to have. 0=disabled + If set, this will limit the number of concurrent streaming connections and + DVR sessions a user is permitted to have. 0=disabled
Video Recorder
diff --git a/src/access.c b/src/access.c index c046f6d14..c4c65999e 100644 --- a/src/access.c +++ b/src/access.c @@ -486,7 +486,7 @@ access_get(const char *username, const char *password, struct sockaddr *src) a->aa_representative = strdup(username); } else { a->aa_representative = malloc(50); - tcp_get_ip_str((struct sockaddr*)src, a->aa_representative, 50); + tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50); } if (access_noacl) { @@ -555,7 +555,7 @@ access_get_hashed(const char *username, const uint8_t digest[20], a->aa_representative = strdup(username); } else { a->aa_representative = malloc(50); - tcp_get_ip_str((struct sockaddr*)src, a->aa_representative, 50); + tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50); } if(access_noacl) { @@ -617,7 +617,33 @@ access_get_hashed(const char *username, const uint8_t digest[20], return a; } +/** + * + */ +access_t * +access_get_by_username(const char *username) +{ + access_t *a = calloc(1, sizeof(*a)); + access_entry_t *ae; + + if(access_noacl) { + a->aa_rights = ACCESS_FULL; + return a; + } + TAILQ_FOREACH(ae, &access_entries, ae_link) { + + if(!ae->ae_enabled) + continue; + + if(ae->ae_username[0] == '*' || strcmp(ae->ae_username, username)) + continue; + + access_update(a, ae); + } + + return a; +} /** * diff --git a/src/access.h b/src/access.h index 5dd3fc367..964fe5c32 100644 --- a/src/access.h +++ b/src/access.h @@ -183,6 +183,12 @@ access_t * access_get_hashed(const char *username, const uint8_t digest[20], const uint8_t *challenge, struct sockaddr *src); +/** + * + */ +access_t * +access_get_by_username(const char *username); + /** * */ diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 75d19def5..35279b9e0 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -393,6 +393,8 @@ void dvr_config_destroy_by_profile(profile_t *pro, int delconf); void dvr_make_title(char *output, size_t outlen, dvr_entry_t *de); +uint32_t dvr_usage_count(access_t *aa); + static inline int dvr_entry_is_editable(dvr_entry_t *de) { return de->de_sched_state == DVR_SCHEDULED; } diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 20cb3c72c..39c931f2a 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -341,6 +341,27 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) } } +/** + * + */ +uint32_t +dvr_usage_count(access_t *aa) +{ + dvr_entry_t *de; + uint32_t used = 0; + + LIST_FOREACH(de, &dvrentries, de_global_link) { + if (de->de_owner && de->de_owner[0]) { + if (!strcmp(de->de_owner, aa->aa_username ?: "")) + used++; + } else if (!strcmp(de->de_creator ?: "", aa->aa_representative ?: "")) { + used++; + } + } + + return used; +} + static void dvr_entry_set_timer(dvr_entry_t *de) { @@ -498,7 +519,6 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf) if (de->de_channel) { LIST_FOREACH(de2, &de->de_channel->ch_dvrs, de_channel_link) if(de2 != de && - de2->de_channel == de->de_channel && de2->de_config == de->de_config && de2->de_start == de->de_start && de2->de_sched_state != DVR_COMPLETED && diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 4408beca4..af5f06940 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -27,6 +27,7 @@ #include "tvheadend.h" #include "streaming.h" +#include "tcp.h" #include "dvr.h" #include "spawn.h" #include "service.h" @@ -65,6 +66,9 @@ dvr_rec_subscribe(dvr_entry_t *de) int weight; profile_t *pro; profile_chain_t *prch; + struct sockaddr sa; + access_t *aa; + uint32_t rec_count, net_count; assert(de->de_s == NULL); assert(de->de_chain == NULL); @@ -76,6 +80,28 @@ dvr_rec_subscribe(dvr_entry_t *de) snprintf(buf, sizeof(buf), "DVR: %s", lang_str_get(de->de_title, NULL)); + if (de->de_owner && de->de_owner[0] != '\0') + aa = access_get_by_username(de->de_owner); + else if (de->de_creator && de->de_creator[0] != '\0' && + tcp_get_ip_from_str(de->de_creator, &sa) != NULL) + aa = access_get_by_addr(&sa); + else { + tvherror("dvr", "unable to find access"); + return -1; + } + + if (aa->aa_conn_limit) { + rec_count = dvr_usage_count(aa); + net_count = tcp_connection_count(aa); + if (rec_count + net_count >= aa->aa_conn_limit) { + tvherror("dvr", "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, rec_count, net_count); + return -1; + } + } + pro = de->de_config->dvr_profile; prch = malloc(sizeof(*prch)); profile_chain_init(prch, pro, de->de_channel); diff --git a/src/htsp_server.c b/src/htsp_server.c index 70ac5a7b2..ee7f04b01 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -577,7 +577,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) char buf[50]; addrlen = sizeof(addr); getsockname(htsp->htsp_fd, (struct sockaddr*)&addr, &addrlen); - tcp_get_ip_str((struct sockaddr*)&addr, buf, 50); + tcp_get_str_from_ip((struct sockaddr*)&addr, buf, 50); snprintf(url, sizeof(url), "http://%s%s%s:%d%s/%s", (addr.ss_family == AF_INET6)?"[":"", buf, @@ -2794,7 +2794,7 @@ htsp_serve(int fd, void **opaque, struct sockaddr_storage *source, // Note: global_lock held on entry - tcp_get_ip_str((struct sockaddr*)source, buf, 50); + tcp_get_str_from_ip((struct sockaddr*)source, buf, 50); memset(&htsp, 0, sizeof(htsp_connection_t)); *opaque = &htsp; diff --git a/src/http.c b/src/http.c index 31feb40a9..dc41751cf 100644 --- a/src/http.c +++ b/src/http.c @@ -721,7 +721,7 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill) char authbuf[150]; hc->hc_url_orig = tvh_strdupa(hc->hc_url); - tcp_get_ip_str((struct sockaddr*)hc->hc_peer, authbuf, sizeof(authbuf)); + tcp_get_str_from_ip((struct sockaddr*)hc->hc_peer, authbuf, sizeof(authbuf)); hc->hc_peer_ipstr = tvh_strdupa(authbuf); hc->hc_representative = hc->hc_peer_ipstr; hc->hc_username = NULL; diff --git a/src/satip/rtp.c b/src/satip/rtp.c index cfee3c041..f2b527f83 100644 --- a/src/satip/rtp.c +++ b/src/satip/rtp.c @@ -168,7 +168,7 @@ satip_rtp_thread(void *aux) char peername[50]; int alive = 1, fatal = 0, r; - tcp_get_ip_str((struct sockaddr *)&rtp->peer, peername, sizeof(peername)); + tcp_get_str_from_ip((struct sockaddr *)&rtp->peer, peername, sizeof(peername)); tvhdebug("satips", "RTP streaming to %s:%d open", peername, rtp->port); pthread_mutex_lock(&sq->sq_mutex); @@ -611,7 +611,7 @@ satip_rtcp_thread(void *aux) sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); if (r < 0) { err = errno; - tcp_get_ip_str((struct sockaddr*)&rtp->peer2, addrbuf, sizeof(addrbuf)); + tcp_get_str_from_ip((struct sockaddr*)&rtp->peer2, addrbuf, sizeof(addrbuf)); tvhwarn("satips", "RTCP send to error %s:%d : %s", addrbuf, IP_PORT(rtp->peer2), strerror(err)); } diff --git a/src/satip/rtsp.c b/src/satip/rtsp.c index cc3e5c225..570016fc8 100644 --- a/src/satip/rtsp.c +++ b/src/satip/rtsp.c @@ -1443,7 +1443,7 @@ rtsp_serve(int fd, void **opaque, struct sockaddr_storage *peer, memset(&aa, 0, sizeof(aa)); strcpy(buf, "SAT>IP Client "); - tcp_get_ip_str((struct sockaddr *)peer, buf + strlen(buf), sizeof(buf) - strlen(buf)); + tcp_get_str_from_ip((struct sockaddr *)peer, buf + strlen(buf), sizeof(buf) - strlen(buf)); aa.aa_representative = buf; tcp = tcp_connection_launch(fd, rtsp_stream_status, &aa); diff --git a/src/satip/server.c b/src/satip/server.c index 82567527c..2749c43a5 100644 --- a/src/satip/server.c +++ b/src/satip/server.c @@ -348,7 +348,7 @@ CONFIGID.UPNP.ORG: 0\r\n" return; #if ENABLE_TRACE - tcp_get_ip_str((struct sockaddr *)dst, buf, sizeof(buf)); + tcp_get_str_from_ip((struct sockaddr *)dst, buf, sizeof(buf)); tvhtrace("satips", "sending discover reply to %s:%d%s%s", buf, IP_PORT(*dst), deviceid ? " device: " : "", deviceid ?: ""); #endif @@ -453,7 +453,7 @@ satips_upnp_discovery_received return; #if ENABLE_TRACE - tcp_get_ip_str((struct sockaddr *)storage, buf2, sizeof(buf2)); + tcp_get_str_from_ip((struct sockaddr *)storage, buf2, sizeof(buf2)); tvhtrace("satips", "received %s M-SEARCH from %s:%d", conn->multicast ? "multicast" : "unicast", buf2, ntohs(IP_PORT(*storage))); @@ -465,7 +465,7 @@ satips_upnp_discovery_received satip_server_deviceid += 1; if (satip_server_deviceid >= 254) satip_server_deviceid = 1; - tcp_get_ip_str((struct sockaddr *)storage, buf2, sizeof(buf2)); + tcp_get_str_from_ip((struct sockaddr *)storage, buf2, sizeof(buf2)); tvhwarn("satips", "received duplicate SAT>IP DeviceID %s from %s:%d, using %d", deviceid, buf2, ntohs(IP_PORT(*storage)), satip_server_deviceid); satips_upnp_send_discover_reply(storage, deviceid); @@ -571,7 +571,7 @@ void satip_server_init(int rtsp_port) tvherror("satips", "Unable to determine the HTTP/RTSP address"); return; } - tcp_get_ip_str((const struct sockaddr *)&http, http_ip, sizeof(http_ip)); + tcp_get_str_from_ip((const struct sockaddr *)&http, http_ip, sizeof(http_ip)); http_server_ip = strdup(http_ip); http_server_port = ntohs(IP_PORT(http)); diff --git a/src/tcp.c b/src/tcp.c index 40ee54238..1e599fb79 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -39,6 +39,7 @@ #include "tvhpoll.h" #include "notify.h" #include "access.h" +#include "dvr/dvr.h" int tcp_preferred_address_family = AF_INET; int tcp_server_running; @@ -352,25 +353,49 @@ tcp_read_timeout(int fd, void *buf, size_t len, int timeout) * */ char * -tcp_get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen) +tcp_get_str_from_ip(const struct sockaddr *sa, char *dst, size_t maxlen) { - if(sa == NULL || s == NULL) + if (sa == NULL || dst == NULL) return NULL; switch(sa->sa_family) { case AF_INET: - inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), s, maxlen); + inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), dst, maxlen); break; case AF_INET6: - inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), s, maxlen); + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), dst, maxlen); break; default: - strncpy(s, "Unknown AF", maxlen); + strncpy(dst, "Unknown AF", maxlen); return NULL; } - return s; + return dst; +} + +/** + * + */ +struct sockaddr * +tcp_get_ip_from_str(const char *src, struct sockaddr *sa) +{ + if (sa == NULL || src == NULL) + return NULL; + + if (strstr(src, ":")) { + sa->sa_family = AF_INET6; + if (inet_pton(AF_INET6, src, &(((struct sockaddr_in6*)sa)->sin6_addr)) != 1) + return NULL; + } else if (strstr(src, ".")) { + sa->sa_family = AF_INET; + if (inet_pton(AF_INET, src, &(((struct sockaddr_in*)sa)->sin_addr)) != 1) + return NULL; + } else { + return NULL; + } + + return sa; } /** @@ -406,6 +431,26 @@ static LIST_HEAD(, tcp_server_launch) tcp_server_launches = { 0 }; static LIST_HEAD(, tcp_server_launch) tcp_server_active = { 0 }; static LIST_HEAD(, tcp_server_launch) tcp_server_join = { 0 }; +/** + * + */ +uint32_t +tcp_connection_count(access_t *aa) +{ + tcp_server_launch_t *tsl; + uint32_t used = 0; + + lock_assert(&global_lock); + + if (aa == NULL) + return 0; + + LIST_FOREACH(tsl, &tcp_server_active, alink) + if (!strcmp(aa->aa_representative ?: "", tsl->representative ?: "")) + used++; + return used; +} + /** * */ @@ -414,7 +459,7 @@ tcp_connection_launch (int fd, void (*status) (void *opaque, htsmsg_t *m), access_t *aa) { tcp_server_launch_t *tsl, *res; - uint32_t used = 0; + uint32_t used = 0, used2; time_t started = dispatch_clock; lock_assert(&global_lock); @@ -439,10 +484,12 @@ try_again: if (res == NULL) return NULL; - if (aa->aa_conn_limit && used >= aa->aa_conn_limit) { + 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)", - aa->aa_username ?: "", aa->aa_representative ?: "", aa->aa_conn_limit); + 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); return NULL; } pthread_mutex_unlock(&global_lock); @@ -854,7 +901,7 @@ tcp_server_connections ( void ) if (!tsl->status) continue; c++; e = htsmsg_create_map(); - tcp_get_ip_str((struct sockaddr*)&tsl->peer, buf, sizeof(buf)); + tcp_get_str_from_ip((struct sockaddr*)&tsl->peer, buf, sizeof(buf)); htsmsg_add_u32(e, "id", tsl->id); htsmsg_add_str(e, "peer", buf); htsmsg_add_s64(e, "started", tsl->started); diff --git a/src/tcp.h b/src/tcp.h index 7bf419049..090e5debc 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -85,10 +85,13 @@ int tcp_write_queue(int fd, htsbuf_queue_t *q); int tcp_read_timeout(int fd, void *buf, size_t len, int timeout); -char *tcp_get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen); +char *tcp_get_str_from_ip(const struct sockaddr *sa, char *dst, size_t maxlen); + +struct sockaddr *tcp_get_ip_from_str(const char *str, struct sockaddr *sa); struct access; +uint32_t tcp_connection_count(struct access *aa); void *tcp_connection_launch(int fd, void (*status) (void *opaque, htsmsg_t *m), struct access *aa); void tcp_connection_land(void *tcp_id); diff --git a/src/webui/comet.c b/src/webui/comet.c index 423af7261..1934a79ee 100644 --- a/src/webui/comet.c +++ b/src/webui/comet.c @@ -166,7 +166,7 @@ comet_serverIpPort(http_connection_t *hc, comet_mailbox_t *cmb) char buf[50]; uint32_t port; - tcp_get_ip_str((struct sockaddr*)hc->hc_self, buf, 50); + tcp_get_str_from_ip((struct sockaddr*)hc->hc_self, buf, 50); if(hc->hc_self->ss_family == AF_INET) port = ((struct sockaddr_in*)hc->hc_self)->sin_port;