http: Handle proxy authentication
authorMartin Storsjö <martin@martin.st>
Fri, 11 Nov 2011 09:21:42 +0000 (11:21 +0200)
committerMartin Storsjö <martin@martin.st>
Fri, 11 Nov 2011 12:07:57 +0000 (14:07 +0200)
Tested with both Basic and Digest authentication, and tested with
both proxy authentication and authentication for the requested
resource at the same time.

Signed-off-by: Martin Storsjö <martin@martin.st>
libavformat/http.c
libavformat/httpauth.c

index 093bb82..a8a7688 100644 (file)
@@ -47,6 +47,7 @@ typedef struct {
     int64_t off, filesize;
     char location[MAX_URL_SIZE];
     HTTPAuthState auth_state;
+    HTTPAuthState proxy_auth_state;
     char *headers;
     int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
     int chunked_post;
@@ -71,25 +72,29 @@ static const AVClass flavor ## _context_class = {\
 HTTP_CLASS(http);
 HTTP_CLASS(https);
 
-static int http_connect(URLContext *h, const char *path, const char *hoststr,
-                        const char *auth, int *new_location);
+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);
 
 void ff_http_init_auth_state(URLContext *dest, const URLContext *src)
 {
     memcpy(&((HTTPContext*)dest->priv_data)->auth_state,
            &((HTTPContext*)src->priv_data)->auth_state, sizeof(HTTPAuthState));
+    memcpy(&((HTTPContext*)dest->priv_data)->proxy_auth_state,
+           &((HTTPContext*)src->priv_data)->proxy_auth_state,
+           sizeof(HTTPAuthState));
 }
 
 /* return non zero if error */
 static int http_open_cnx(URLContext *h)
 {
-    const char *path, *proxy_path, *lower_proto = "tcp";
+    const char *path, *proxy_path, *lower_proto = "tcp", *local_path;
     char hostname[1024], hoststr[1024], proto[10];
-    char auth[1024];
+    char auth[1024], proxyauth[1024];
     char path1[1024];
-    char buf[1024];
+    char buf[1024], urlbuf[1024];
     int port, use_proxy, err, location_changed = 0, redirects = 0;
-    HTTPAuthType cur_auth_type;
+    HTTPAuthType cur_auth_type, cur_proxy_auth_type;
     HTTPContext *s = h->priv_data;
     URLContext *hd = NULL;
 
@@ -105,15 +110,19 @@ static int http_open_cnx(URLContext *h)
                  path1, sizeof(path1), s->location);
     ff_url_join(hoststr, sizeof(hoststr), NULL, NULL, hostname, port, NULL);
 
+    if (path1[0] == '\0')
+        path = "/";
+    else
+        path = path1;
+    local_path = path;
     if (use_proxy) {
-        av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
-                     NULL, 0, proxy_path);
-        path = s->location;
-    } else {
-        if (path1[0] == '\0')
-            path = "/";
-        else
-            path = path1;
+        /* Reassemble the request URL without auth string - we don't
+         * want to leak the auth to the proxy. */
+        ff_url_join(urlbuf, sizeof(urlbuf), proto, NULL, hostname, port, "%s",
+                    path1);
+        path = urlbuf;
+        av_url_split(NULL, 0, proxyauth, sizeof(proxyauth),
+                     hostname, sizeof(hostname), &port, NULL, 0, proxy_path);
     }
     if (!strcmp(proto, "https")) {
         lower_proto = "tls";
@@ -130,7 +139,8 @@ static int http_open_cnx(URLContext *h)
 
     s->hd = hd;
     cur_auth_type = s->auth_state.auth_type;
-    if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
+    cur_proxy_auth_type = s->auth_state.auth_type;
+    if (http_connect(h, path, local_path, hoststr, auth, proxyauth, &location_changed) < 0)
         goto fail;
     if (s->http_code == 401) {
         if (cur_auth_type == HTTP_AUTH_NONE && s->auth_state.auth_type != HTTP_AUTH_NONE) {
@@ -139,6 +149,14 @@ static int http_open_cnx(URLContext *h)
         } else
             goto fail;
     }
+    if (s->http_code == 407) {
+        if (cur_proxy_auth_type == HTTP_AUTH_NONE &&
+            s->proxy_auth_state.auth_type != HTTP_AUTH_NONE) {
+            ffurl_close(hd);
+            goto redo;
+        } else
+            goto fail;
+    }
     if ((s->http_code == 301 || s->http_code == 302 || s->http_code == 303 || s->http_code == 307)
         && location_changed == 1) {
         /* url moved, get next */
@@ -237,7 +255,8 @@ static int process_line(URLContext *h, char *line, int line_count,
         /* error codes are 4xx and 5xx, but regard 401 as a success, so we
          * don't abort until all headers have been parsed. */
         if (s->http_code >= 400 && s->http_code < 600 && (s->http_code != 401
-            || s->auth_state.auth_type != HTTP_AUTH_NONE)) {
+            || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
+            (s->http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) {
             end += strspn(end, SPACE_CHARS);
             av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n",
                    s->http_code, end);
@@ -278,6 +297,8 @@ static int process_line(URLContext *h, char *line, int line_count,
             ff_http_auth_handle_header(&s->auth_state, tag, p);
         } else if (!av_strcasecmp (tag, "Authentication-Info")) {
             ff_http_auth_handle_header(&s->auth_state, tag, p);
+        } else if (!av_strcasecmp (tag, "Proxy-Authenticate")) {
+            ff_http_auth_handle_header(&s->proxy_auth_state, tag, p);
         } else if (!av_strcasecmp (tag, "Connection")) {
             if (!strcmp(p, "close"))
                 s->willclose = 1;
@@ -294,22 +315,27 @@ static inline int has_header(const char *str, const char *header)
     return av_stristart(str, header + 2, NULL) || av_stristr(str, header);
 }
 
-static int http_connect(URLContext *h, const char *path, const char *hoststr,
-                        const char *auth, int *new_location)
+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)
 {
     HTTPContext *s = h->priv_data;
     int post, err;
     char line[1024];
     char headers[1024] = "";
-    char *authstr = NULL;
+    char *authstr = NULL, *proxyauthstr = NULL;
     int64_t off = s->off;
     int len = 0;
+    const char *method;
 
 
     /* send http header */
     post = h->flags & AVIO_FLAG_WRITE;
-    authstr = ff_http_auth_create_response(&s->auth_state, auth, path,
-                                        post ? "POST" : "GET");
+    method = post ? "POST" : "GET";
+    authstr = ff_http_auth_create_response(&s->auth_state, auth, local_path,
+                                           method);
+    proxyauthstr = ff_http_auth_create_response(&s->proxy_auth_state, proxyauth,
+                                                local_path, method);
 
     /* set default headers if needed */
     if (!has_header(s->headers, "\r\nUser-Agent: "))
@@ -337,14 +363,17 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
              "%s"
              "%s"
              "%s"
+             "%s%s"
              "\r\n",
-             post ? "POST" : "GET",
+             method,
              path,
              post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "",
              headers,
-             authstr ? authstr : "");
+             authstr ? authstr : "",
+             proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : "");
 
     av_freep(&authstr);
+    av_freep(&proxyauthstr);
     if (ffurl_write(s->hd, s->buffer, strlen(s->buffer)) < 0)
         return AVERROR(EIO);
 
index 1dda1ac..c8b8ace 100644 (file)
@@ -87,7 +87,7 @@ static void choose_qop(char *qop, int size)
 void ff_http_auth_handle_header(HTTPAuthState *state, const char *key,
                                 const char *value)
 {
-    if (!strcmp(key, "WWW-Authenticate")) {
+    if (!strcmp(key, "WWW-Authenticate") || !strcmp(key, "Proxy-Authenticate")) {
         const char *p;
         if (av_stristart(value, "Basic ", &p) &&
             state->auth_type <= HTTP_AUTH_BASIC) {