diff options
author | Micah Galizia <micahgalizia@gmail.com> | 2017-04-30 21:25:29 +0300 |
---|---|---|
committer | Michael Niedermayer <michael@niedermayer.cc> | 2017-05-01 18:55:22 +0300 |
commit | 28b24670741e1de25bfc7b5ea7c1d6dbae1aef6f (patch) | |
tree | 2ea5c5ea882f4ff6d8def38be78c7b12b340ec32 /libavformat | |
parent | 89812e423de61ea77ea6deae8c8ed4c4a4413646 (diff) |
libavformat/http: Ignore expired cookies
Signed-off-by: Micah Galizia <micahgalizia@gmail.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Diffstat (limited to 'libavformat')
-rw-r--r-- | libavformat/http.c | 213 |
1 files changed, 156 insertions, 57 deletions
diff --git a/libavformat/http.c b/libavformat/http.c index 293a8a7204..d06103ab6d 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,112 @@ 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) { + av_free(cstr); + 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 +973,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 +981,81 @@ 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; + av_free(tmp); + 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); - return 0; + return ret; } static inline int has_header(const char *str, const char *header) |