Commit 4dbdbf64 by Vicent Martí

Merge pull request #1871 from libgit2/cross-protocol-redirects-alt

Alternative fix for cross protocol redirects
parents 95bff9d4 46fbc88e
...@@ -585,7 +585,6 @@ int gitno_extract_url_parts( ...@@ -585,7 +585,6 @@ int gitno_extract_url_parts(
const char *start; const char *start;
/* /*
*
* ==> [user[:pass]@]hostname.tld[:port]/resource * ==> [user[:pass]@]hostname.tld[:port]/resource
*/ */
......
...@@ -59,7 +59,7 @@ typedef struct { ...@@ -59,7 +59,7 @@ typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_socket socket; gitno_socket socket;
const char *path; char *path;
char *host; char *host;
char *port; char *port;
char *user_from_url; char *user_from_url;
...@@ -125,15 +125,9 @@ static int gen_request( ...@@ -125,15 +125,9 @@ static int gen_request(
size_t content_length) size_t content_length)
{ {
http_subtransport *t = OWNING_SUBTRANSPORT(s); http_subtransport *t = OWNING_SUBTRANSPORT(s);
const char *path = t->path ? t->path : "/";
if (!t->path) git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
t->path = "/";
/* If we were redirected, make sure to respect that here */
if (s->redirect_url)
git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
else
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n"); git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->host); git_buf_printf(buf, "Host: %s\r\n", t->host);
...@@ -209,7 +203,7 @@ static int on_header_ready(http_subtransport *t) ...@@ -209,7 +203,7 @@ static int on_header_ready(http_subtransport *t)
} }
else if (!strcasecmp("Location", git_buf_cstr(name))) { else if (!strcasecmp("Location", git_buf_cstr(name))) {
if (!t->location) { if (!t->location) {
t->location= git__strdup(git_buf_cstr(value)); t->location = git__strdup(git_buf_cstr(value));
GITERR_CHECK_ALLOC(t->location); GITERR_CHECK_ALLOC(t->location);
} }
} }
...@@ -255,6 +249,98 @@ static int on_header_value(http_parser *parser, const char *str, size_t len) ...@@ -255,6 +249,98 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0; return 0;
} }
static void free_connection_data(http_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
http_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && t->location[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int on_headers_complete(http_parser *parser) static int on_headers_complete(http_parser *parser)
{ {
parser_context *ctx = (parser_context *) parser->data; parser_context *ctx = (parser_context *) parser->data;
...@@ -308,10 +394,8 @@ static int on_headers_complete(http_parser *parser) ...@@ -308,10 +394,8 @@ static int on_headers_complete(http_parser *parser)
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
} }
if (t->location[0] != '/') { if (set_connection_data_from_url(t, t->location, s->service_url) < 0)
giterr_set(GITERR_NET, "Only relative redirects are supported");
return t->parse_error = PARSE_ERROR_GENERIC; return t->parse_error = PARSE_ERROR_GENERIC;
}
/* Set the redirect URL on the stream. This is a transfer of /* Set the redirect URL on the stream. This is a transfer of
* ownership of the memory. */ * ownership of the memory. */
...@@ -822,50 +906,31 @@ static int http_action( ...@@ -822,50 +906,31 @@ static int http_action(
git_smart_service_t action) git_smart_service_t action)
{ {
http_subtransport *t = (http_subtransport *)subtransport; http_subtransport *t = (http_subtransport *)subtransport;
const char *default_port = NULL;
int ret; int ret;
if (!stream) if (!stream)
return -1; return -1;
if (!t->host || !t->port || !t->path) { if (!t->host || !t->port || !t->path) {
if (!git__prefixcmp(url, prefix_http)) { if ((ret = set_connection_data_from_url(t, url, NULL)) < 0)
url = url + strlen(prefix_http);
default_port = "80";
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port)
return -1;
if ((ret = gitno_extract_url_parts(&t->host, &t->port,
&t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
return ret; return ret;
t->path = strchr(url, '/');
} }
if (http_connect(t) < 0) if (http_connect(t) < 0)
return -1; return -1;
switch (action) switch (action) {
{ case GIT_SERVICE_UPLOADPACK_LS:
case GIT_SERVICE_UPLOADPACK_LS: return http_uploadpack_ls(t, stream);
return http_uploadpack_ls(t, stream);
case GIT_SERVICE_UPLOADPACK: case GIT_SERVICE_UPLOADPACK:
return http_uploadpack(t, stream); return http_uploadpack(t, stream);
case GIT_SERVICE_RECEIVEPACK_LS: case GIT_SERVICE_RECEIVEPACK_LS:
return http_receivepack_ls(t, stream); return http_receivepack_ls(t, stream);
case GIT_SERVICE_RECEIVEPACK: case GIT_SERVICE_RECEIVEPACK:
return http_receivepack(t, stream); return http_receivepack(t, stream);
} }
*stream = NULL; *stream = NULL;
...@@ -893,25 +958,7 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -893,25 +958,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL; t->url_cred = NULL;
} }
if (t->host) { free_connection_data(t);
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
return 0; return 0;
} }
......
...@@ -64,6 +64,11 @@ void test_online_fetch__default_http(void) ...@@ -64,6 +64,11 @@ void test_online_fetch__default_http(void)
do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6); do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
} }
void test_online_fetch__default_https(void)
{
do_fetch("https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
}
void test_online_fetch__no_tags_git(void) void test_online_fetch__no_tags_git(void)
{ {
do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3); do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment