From b911518d1c17223aa7fb83ace08b2bd41c203da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 11 Nov 2011 11:21:42 +0200 Subject: [PATCH] http: Handle proxy authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ö --- libavformat/http.c | 75 +++++++++++++++++++++++++++++------------- libavformat/httpauth.c | 2 +- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index 093bb822dc..a8a76882dd 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -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); diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index 1dda1ac8f0..c8b8ace3c2 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -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) {