You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	libavformat/http: Ignore expired cookies
Signed-off-by: Micah Galizia <micahgalizia@gmail.com> Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
This commit is contained in:
		
				
					committed by
					
						 Michael Niedermayer
						Michael Niedermayer
					
				
			
			
				
	
			
			
			
						parent
						
							89812e423d
						
					
				
				
					commit
					28b2467074
				
			| @@ -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 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; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         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 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; | ||||
|             } | ||||
|  | ||||
|             // match the cookie domain | ||||
|             if (av_strcasecmp(&domain[domain_offset], e->value)) { | ||||
|                 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; | ||||
|  | ||||
|         // 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], 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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user