]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/http.c
avformat/movenc: Fix segfault upon allocation error
[ffmpeg] / libavformat / http.c
index ee415667febe5757e35fc08fdc4a750cdae46310..3d25d652d3196a2300271928b894fbddb96b0baf 100644 (file)
@@ -46,7 +46,7 @@
 /* The IO buffer size is unrelated to the max URL size in itself, but needs
  * to be large enough to fit the full request headers (including long
  * path names). */
-#define BUFFER_SIZE   MAX_URL_SIZE
+#define BUFFER_SIZE   (MAX_URL_SIZE + HTTP_HEADERS_SIZE)
 #define MAX_REDIRECTS 8
 #define HTTP_SINGLE   1
 #define HTTP_MUTLI    2
@@ -577,7 +577,7 @@ static int http_open(URLContext *h, const char *uri, int flags,
                    "No trailing CRLF found in HTTP header. Adding it.\n");
             ret = av_reallocp(&s->headers, len + 3);
             if (ret < 0)
-                return ret;
+                goto bail_out;
             s->headers[len]     = '\r';
             s->headers[len + 1] = '\n';
             s->headers[len + 2] = '\0';
@@ -588,6 +588,7 @@ static int http_open(URLContext *h, const char *uri, int flags,
         return http_listen(h, uri, flags, options);
     }
     ret = http_open_cnx(h, options);
+bail_out:
     if (ret < 0)
         av_dict_free(&s->chained_options);
     return ret;
@@ -786,6 +787,7 @@ static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf)
 static int parse_set_cookie(const char *set_cookie, AVDictionary **dict)
 {
     char *param, *next_param, *cstr, *back;
+    char *saveptr = NULL;
 
     if (!set_cookie[0])
         return 0;
@@ -803,8 +805,9 @@ static int parse_set_cookie(const char *set_cookie, AVDictionary **dict)
     }
 
     next_param = cstr;
-    while ((param = av_strtok(next_param, ";", &next_param))) {
+    while ((param = av_strtok(next_param, ";", &saveptr))) {
         char *name, *value;
+        next_param = NULL;
         param += strspn(param, WHITESPACES);
         if ((name = av_strtok(param, "=", &value))) {
             if (av_dict_set(dict, name, value, 0) < 0) {
@@ -1064,6 +1067,7 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path,
     // Set-Cookie fields will result in multiple values delimited by a newline
     int ret = 0;
     char *cookie, *set_cookies, *next;
+    char *saveptr = NULL;
 
     // destroy any cookies in the dictionary.
     av_dict_free(&s->cookie_dict);
@@ -1076,10 +1080,11 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path,
         return AVERROR(ENOMEM);
 
     *cookies = NULL;
-    while ((cookie = av_strtok(next, "\n", &next)) && !ret) {
+    while ((cookie = av_strtok(next, "\n", &saveptr)) && !ret) {
         AVDictionary *cookie_params = NULL;
         AVDictionaryEntry *cookie_entry, *e;
 
+        next = NULL;
         // 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);
@@ -1179,6 +1184,37 @@ static int http_read_header(URLContext *h, int *new_location)
     return err;
 }
 
+/**
+ * Escape unsafe characters in path in order to pass them safely to the HTTP
+ * request. Insipred by the algorithm in GNU wget:
+ * - escape "%" characters not followed by two hex digits
+ * - escape all "unsafe" characters except which are also "reserved"
+ * - pass through everything else
+ */
+static void bprint_escaped_path(AVBPrint *bp, const char *path)
+{
+#define NEEDS_ESCAPE(ch) \
+    ((ch) <= ' ' || (ch) >= '\x7f' || \
+     (ch) == '"' || (ch) == '%' || (ch) == '<' || (ch) == '>' || (ch) == '\\' || \
+     (ch) == '^' || (ch) == '`' || (ch) == '{' || (ch) == '}' || (ch) == '|')
+    while (*path) {
+        char buf[1024];
+        char *q = buf;
+        while (*path && q - buf < sizeof(buf) - 4) {
+            if (path[0] == '%' && av_isxdigit(path[1]) && av_isxdigit(path[2])) {
+                *q++ = *path++;
+                *q++ = *path++;
+                *q++ = *path++;
+            } else if (NEEDS_ESCAPE(*path)) {
+                q += snprintf(q, 4, "%%%02X", (uint8_t)*path++);
+            } else {
+                *q++ = *path++;
+            }
+        }
+        av_bprint_append_data(bp, buf, q - buf);
+    }
+}
+
 static int http_connect(URLContext *h, const char *path, const char *local_path,
                         const char *hoststr, const char *auth,
                         const char *proxyauth, int *new_location)
@@ -1235,7 +1271,10 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
     }
 #endif
 
-    av_bprintf(&request, "%s %s HTTP/1.1\r\n", method, path);
+    av_bprintf(&request, "%s ", method);
+    bprint_escaped_path(&request, path);
+    av_bprintf(&request, " HTTP/1.1\r\n");
+
     if (post && s->chunked_post)
         av_bprintf(&request, "Transfer-Encoding: chunked\r\n");
     /* set default headers if needed */