From patchwork Thu Apr 27 01:48:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Micah Galizia X-Patchwork-Id: 3500 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.3.129 with SMTP id 123csp621961vsd; Wed, 26 Apr 2017 18:54:52 -0700 (PDT) X-Received: by 10.223.182.152 with SMTP id j24mr1551645wre.152.1493258092478; Wed, 26 Apr 2017 18:54:52 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id x82si1477970wma.75.2017.04.26.18.54.51; Wed, 26 Apr 2017 18:54:52 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A32816882CA; Thu, 27 Apr 2017 04:54:46 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-it0-f68.google.com (mail-it0-f68.google.com [209.85.214.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 329B0687EBA for ; Thu, 27 Apr 2017 04:54:40 +0300 (EEST) Received: by mail-it0-f68.google.com with SMTP id x188so719820itb.3 for ; Wed, 26 Apr 2017 18:54:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=rgZ9HOeF2yRrTeUlw17j4V5OMsLm6N5MLCzVsgRYVEk=; b=LIWBN7C5TjSU7y0Wg+4pjs8DuBGbBvP9+PjOfbHMSd5WgaUtkHe46l8YKP3tNufPFm pkq5dkMv+XoJH6XNWMJ5MehAwmujSxcfvk93l4eo82Ml+hxHp6J8u1mJYocjfGaPsIn3 I2GM/ltVTISSP7jTL7lYett5fK4lP9uZgDhAQGZy1YDXkvAulwU+DgBV9Ellq/VnBgmw RUQaIz9w+Xu+8QImKCnnhQ3DZ9URtq2auWQxnc8D/TC8/BbrDL5lzOgRnAqEj2V/5CDS yuJ1nsRN8zh1lJYwODxIOkZ2weWrdpPKvmhQqztLNMbmjV+q8Kdg7CRrGvnWTtSBnhnS YXLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=rgZ9HOeF2yRrTeUlw17j4V5OMsLm6N5MLCzVsgRYVEk=; b=obmBr8qCCKVX2mFU08vJgiYuDVNDYczK/0jAuy/6K5f1b9LEwhI9PFtcn4+fOZroWx UsNVZQcwBoKsW7KxL5JEJ1CWmzu82zW/iGpM2gZh8GIHBRQaEx0r1SPEn6EfKuD0Yp9R /v+KfjvVXxmXyT8zJ+sSk8EXOt01gqCtb9yZ6SyKWWGiHZVPRy/9kxsJ7ZkpSpIfIt3e rhEfCYpAVi/tfCsu7zW9g9tXoKfzaIU97Kw9+CmFldYtvCFfTlWYXeho6dZoGTCEGtqg cuzpnXyAatU4hruJpDb011G0M4nyylws5KA2HGlJPIfQu4G6Qb7w8BJKmreEbel4cMIX pLzw== X-Gm-Message-State: AN3rC/6mO5ihMBLozlsCHEte3TPK8FX/o7u8rGAj4R2Jd43xx4aaPQTW CMGoj8Eg9DzIzA== X-Received: by 10.36.93.4 with SMTP id w4mr1045983ita.32.1493257721446; Wed, 26 Apr 2017 18:48:41 -0700 (PDT) Received: from localhost.localdomain (ip-24-54-73-138.user.start.ca. [24.54.73.138]) by smtp.gmail.com with ESMTPSA id m83sm483310ioi.32.2017.04.26.18.48.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 26 Apr 2017 18:48:40 -0700 (PDT) From: Micah Galizia To: ffmpeg-devel@ffmpeg.org Date: Wed, 26 Apr 2017 21:48:09 -0400 Message-Id: <20170427014809.9038-2-micahgalizia@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170427014809.9038-1-micahgalizia@gmail.com> References: <20170425093840.GH4714@nb4> <20170427014809.9038-1-micahgalizia@gmail.com> Subject: [FFmpeg-devel] [PATCH] Ignore expired cookies 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 Cc: Micah Galizia MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Micah Galizia --- libavformat/http.c | 212 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 155 insertions(+), 57 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 293a8a7204..58fc3902ab 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -29,6 +29,7 @@ #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/time.h" +#include "libavutil/parseutils.h" #include "avformat.h" #include "http.h" @@ -48,6 +49,8 @@ #define MAX_REDIRECTS 8 #define HTTP_SINGLE 1 #define HTTP_MUTLI 2 +#define MAX_EXPIRY 19 +#define WHITESPACES " \n\t\r" typedef enum { LOWER_PROTO, READ_HEADERS, @@ -680,10 +683,110 @@ static int parse_icy(HTTPContext *s, const char *tag, const char *p) return 0; } +static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf) +{ + char exp_buf[MAX_EXPIRY]; + int i, j, exp_buf_len = MAX_EXPIRY-1; + char *expiry; + + // strip off any punctuation or whitespace + for (i = 0, j = 0; exp_str[i] != '\0' && j < exp_buf_len; i++) { + if ((exp_str[i] >= '0' && exp_str[i] <= '9') || + (exp_str[i] >= 'A' && exp_str[i] <= 'Z') || + (exp_str[i] >= 'a' && exp_str[i] <= 'z')) { + exp_buf[j] = exp_str[i]; + j++; + } + } + exp_buf[j] = '\0'; + expiry = exp_buf; + + // move the string beyond the day of week + while ((*expiry < '0' || *expiry > '9') && *expiry != '\0') + expiry++; + + return av_small_strptime(expiry, "%d%b%Y%H%M%S", buf) ? 0 : AVERROR(EINVAL); +} + +static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) +{ + char *param, *next_param, *cstr, *back; + + if (!(cstr = av_strdup(set_cookie))) + return AVERROR(EINVAL); + + // strip any trailing whitespace + back = &cstr[strlen(cstr)-1]; + while (strchr(WHITESPACES, *back)) { + *back='\0'; + back--; + } + + next_param = cstr; + while ((param = av_strtok(next_param, ";", &next_param))) { + char *name, *value; + param += strspn(param, WHITESPACES); + if ((name = av_strtok(param, "=", &value))) { + if (av_dict_set(dict, name, value, 0) < 0) + return -1; + } + } + + av_free(cstr); + return 0; +} + static int parse_cookie(HTTPContext *s, const char *p, AVDictionary **cookies) { + AVDictionary *new_params = NULL; + AVDictionaryEntry *e, *cookie_entry; char *eql, *name; + // ensure the cookie is parsable + if (parse_set_cookie(p, &new_params)) + return -1; + + // if there is no cookie value there is nothing to parse + cookie_entry = av_dict_get(new_params, "", NULL, AV_DICT_IGNORE_SUFFIX); + if (!cookie_entry || !cookie_entry->value) { + av_dict_free(&new_params); + return -1; + } + + // ensure the cookie is not expired or older than an existing value + if ((e = av_dict_get(new_params, "expires", NULL, 0)) && e->value) { + struct tm new_tm = {0}; + if (!parse_set_cookie_expiry_time(e->value, &new_tm)) { + AVDictionaryEntry *e2; + + // if the cookie has already expired ignore it + if (av_timegm(&new_tm) < av_gettime() / 1000000) { + av_dict_free(&new_params); + return -1; + } + + // only replace an older cookie with the same name + e2 = av_dict_get(*cookies, cookie_entry->key, NULL, 0); + if (e2 && e2->value) { + AVDictionary *old_params = NULL; + if (!parse_set_cookie(p, &old_params)) { + e2 = av_dict_get(old_params, "expires", NULL, 0); + if (e2 && e2->value) { + struct tm old_tm = {0}; + if (!parse_set_cookie_expiry_time(e->value, &old_tm)) { + if (av_timegm(&new_tm) < av_timegm(&old_tm)) { + av_dict_free(&new_params); + av_dict_free(&old_params); + return -1; + } + } + } + } + av_dict_free(&old_params); + } + } + } + // duplicate the cookie name (dict will dupe the value) if (!(eql = strchr(p, '='))) return AVERROR(EINVAL); if (!(name = av_strndup(p, eql - p))) return AVERROR(ENOMEM); @@ -868,7 +971,7 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, // cookie strings will look like Set-Cookie header field values. Multiple // Set-Cookie fields will result in multiple values delimited by a newline int ret = 0; - char *next, *cookie, *set_cookies = av_strdup(s->cookies), *cset_cookies = set_cookies; + char *cookie, *set_cookies = av_strdup(s->cookies), *next = set_cookies; if (!set_cookies) return AVERROR(EINVAL); @@ -876,87 +979,82 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, av_dict_free(&s->cookie_dict); *cookies = NULL; - while ((cookie = av_strtok(set_cookies, "\n", &next))) { - int domain_offset = 0; - char *param, *next_param, *cdomain = NULL, *cpath = NULL, *cvalue = NULL; - set_cookies = NULL; + while ((cookie = av_strtok(next, "\n", &next))) { + AVDictionary *cookie_params = NULL; + AVDictionaryEntry *cookie_entry, *e; // store the cookie in a dict in case it is updated in the response if (parse_cookie(s, cookie, &s->cookie_dict)) av_log(s, AV_LOG_WARNING, "Unable to parse '%s'\n", cookie); - while ((param = av_strtok(cookie, "; ", &next_param))) { - if (cookie) { - // first key-value pair is the actual cookie value - cvalue = av_strdup(param); - cookie = NULL; - } else if (!av_strncasecmp("path=", param, 5)) { - av_free(cpath); - cpath = av_strdup(¶m[5]); - } else if (!av_strncasecmp("domain=", param, 7)) { - // if the cookie specifies a sub-domain, skip the leading dot thereby - // supporting URLs that point to sub-domains and the master domain - int leading_dot = (param[7] == '.'); - av_free(cdomain); - cdomain = av_strdup(¶m[7+leading_dot]); - } else { - // ignore unknown attributes - } + // continue on to the next cookie if this one cannot be parsed + if (parse_set_cookie(cookie, &cookie_params)) + continue; + + // if the cookie has no value, skip it + cookie_entry = av_dict_get(cookie_params, "", NULL, AV_DICT_IGNORE_SUFFIX); + if (!cookie_entry || !cookie_entry->value) { + av_dict_free(&cookie_params); + continue; } - if (!cdomain) - cdomain = av_strdup(domain); - - // ensure all of the necessary values are valid - if (!cdomain || !cpath || !cvalue) { - av_log(s, AV_LOG_WARNING, - "Invalid cookie found, no value, path or domain specified\n"); - goto done_cookie; + + // if the cookie has expired, don't add it + if ((e = av_dict_get(cookie_params, "expires", NULL, 0)) && e->value) { + struct tm tm_buf = {0}; + if (!parse_set_cookie_expiry_time(e->value, &tm_buf)) { + if (av_timegm(&tm_buf) < av_gettime() / 1000000) { + av_dict_free(&cookie_params); + continue; + } + } } - // check if the request path matches the cookie path - if (av_strncasecmp(path, cpath, strlen(cpath))) - goto done_cookie; + // if no domain in the cookie assume it appied to this request + if ((e = av_dict_get(cookie_params, "domain", NULL, 0)) && e->value) { + // find the offset comparison is on the min domain (b.com, not a.b.com) + int domain_offset = strlen(domain) - strlen(e->value); + if (domain_offset < 0) { + av_dict_free(&cookie_params); + continue; + } - // the domain should be at least the size of our cookie domain - domain_offset = strlen(domain) - strlen(cdomain); - if (domain_offset < 0) - goto done_cookie; + // match the cookie domain + if (av_strcasecmp(&domain[domain_offset], e->value)) { + av_dict_free(&cookie_params); + continue; + } + } - // match the cookie domain - if (av_strcasecmp(&domain[domain_offset], cdomain)) - goto done_cookie; + // ensure this cookie matches the path + e = av_dict_get(cookie_params, "path", NULL, 0); + if (!e || av_strncasecmp(path, e->value, strlen(e->value))) { + av_dict_free(&cookie_params); + continue; + } // cookie parameters match, so copy the value if (!*cookies) { - if (!(*cookies = av_strdup(cvalue))) { + if (!(*cookies = av_asprintf("%s=%s", cookie_entry->key, cookie_entry->value))) { ret = AVERROR(ENOMEM); - goto done_cookie; + break; } } else { char *tmp = *cookies; - size_t str_size = strlen(cvalue) + strlen(*cookies) + 3; + size_t str_size = strlen(cookie_entry->key) + strlen(cookie_entry->value) + strlen(*cookies) + 4; if (!(*cookies = av_malloc(str_size))) { ret = AVERROR(ENOMEM); - goto done_cookie; + break; } - snprintf(*cookies, str_size, "%s; %s", tmp, cvalue); + snprintf(*cookies, str_size, "%s; %s=%s", tmp, cookie_entry->key, cookie_entry->value); av_free(tmp); } - - done_cookie: - av_freep(&cdomain); - av_freep(&cpath); - av_freep(&cvalue); - if (ret < 0) { - if (*cookies) av_freep(cookies); - av_free(cset_cookies); - return ret; - } } - av_free(cset_cookies); + av_free(set_cookies); + if (ret < 0) + av_freep(cookies); - return 0; + return ret; } static inline int has_header(const char *str, const char *header)