diff options
author | Ben Straub <bs@github.com> | 2013-09-25 20:41:56 -0700 |
---|---|---|
committer | Ben Straub <bs@github.com> | 2013-09-25 20:41:56 -0700 |
commit | 8988688c479c6e511432187c7e7e746aefb23c08 (patch) | |
tree | 1d86d7baa31181681e8a401f855e6eafa1d77e6c /src | |
parent | ac316e743878908df762cc0ea07a71cbee5c5802 (diff) | |
download | libgit2-8988688c479c6e511432187c7e7e746aefb23c08.tar.gz |
Migrate redirect URL handling to common utility
Diffstat (limited to 'src')
-rw-r--r-- | src/netops.c | 75 | ||||
-rw-r--r-- | src/netops.h | 25 | ||||
-rw-r--r-- | src/transports/http.c | 102 |
3 files changed, 130 insertions, 72 deletions
diff --git a/src/netops.c b/src/netops.c index c1e74546f..bda064cfb 100644 --- a/src/netops.c +++ b/src/netops.c @@ -573,6 +573,81 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv); } +static const char *prefix_http = "http://"; +static const char *prefix_https = "https://"; + +int gitno_connection_data_from_url( + gitno_connection_data *data, + const char *url, + const char *service_suffix, + const char *original_host, + bool original_use_ssl) +{ + int error = 0; + const char *default_port = NULL; + + /* service_suffix is optional */ + assert(data && url); + + if (!git__prefixcmp(url, prefix_http)) { + url = url + strlen(prefix_http); + default_port = "80"; + + if (data->use_ssl) { + giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); + return -1; + } + } + + if (!git__prefixcmp(url, prefix_https)) { + url += strlen(prefix_https); + default_port = "443"; + data->use_ssl = true; + } + + if (!default_port) { + giterr_set(GITERR_NET, "Unrecognized URL prefix"); + return -1; + } + + error = gitno_extract_url_parts( + &data->host, &data->port, &data->user, &data->pass, + 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)) + data->path = git__strndup(path, pathlen - suffixlen); + else + data->path = git__strdup(path); + + /* Check for errors in the resulting data */ + if (original_use_ssl && !data->use_ssl) { + giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed"); + error = -1; + } + if (original_host && url[0] != '/' && strcmp(original_host, data->host)) { + giterr_set(GITERR_NET, "Cross host redirect not allowed"); + error = -1; + } + } + + return error; +} + +void gitno_connection_data_free_ptrs(gitno_connection_data *d) +{ + git__free(d->host); d->host = NULL; + git__free(d->port); d->port = NULL; + git__free(d->path); d->path = NULL; + git__free(d->user); d->user = NULL; + git__free(d->pass); d->pass = NULL; +} + int gitno_extract_url_parts( char **host, char **port, diff --git a/src/netops.h b/src/netops.h index d352bf3b6..0c6e571d9 100644 --- a/src/netops.h +++ b/src/netops.h @@ -66,6 +66,31 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags); int gitno_close(gitno_socket *s); int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); +typedef struct gitno_connection_data { + char *host; + char *port; + char *path; + char *user; + char *pass; + bool use_ssl; +} gitno_connection_data; + +/* + * This replaces all the pointers in `data` with freshly-allocated strings, + * that the caller is responsible for freeing. + * `gitno_connection_data_free_ptrs` is good for this. + */ + +int gitno_connection_data_from_url( + gitno_connection_data *data, + const char *url, + const char *service_suffix, + const char *original_host, + bool original_use_ssl); + +/* This frees all the pointers IN the struct, but not the struct itself. */ +void gitno_connection_data_free_ptrs(gitno_connection_data *data); + int gitno_extract_url_parts( char **host, char **port, diff --git a/src/transports/http.c b/src/transports/http.c index ab2f9a47f..8d28d5b47 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -67,8 +67,8 @@ typedef struct { git_cred *cred; git_cred *url_cred; http_authmechanism_t auth_mechanism; - unsigned connected : 1, - use_ssl : 1; + bool connected, + use_ssl; /* Parser structures */ http_parser parser; @@ -277,70 +277,6 @@ static void free_connection_data(http_subtransport *t) } } -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) { parser_context *ctx = (parser_context *) parser->data; @@ -384,18 +320,30 @@ static int on_headers_complete(http_parser *parser) /* Check for a redirect. * Right now we only permit a redirect to the same hostname. */ if ((parser->status_code == 301 || - parser->status_code == 302 || - (parser->status_code == 303 && get_verb == s->verb) || - parser->status_code == 307) && - t->location) { + parser->status_code == 302 || + (parser->status_code == 303 && get_verb == s->verb) || + parser->status_code == 307) && + t->location) { + gitno_connection_data connection_data = {0}; if (s->redirect_count >= 7) { giterr_set(GITERR_NET, "Too many redirects"); return t->parse_error = PARSE_ERROR_GENERIC; } - if (set_connection_data_from_url(t, t->location, s->service_url) < 0) + if (gitno_connection_data_from_url(&connection_data, t->location, + s->service_url, t->host, t->use_ssl) < 0) { + gitno_connection_data_free_ptrs(&connection_data); return t->parse_error = PARSE_ERROR_GENERIC; + } + + free_connection_data(t); + t->host = connection_data.host; + t->port = connection_data.port; + t->path = connection_data.path; + t->user_from_url = connection_data.user; + t->pass_from_url = connection_data.pass; + t->use_ssl = connection_data.use_ssl; /* Set the redirect URL on the stream. This is a transfer of * ownership of the memory. */ @@ -912,8 +860,18 @@ static int http_action( return -1; if (!t->host || !t->port || !t->path) { - if ((ret = set_connection_data_from_url(t, url, NULL)) < 0) + gitno_connection_data data = {0}; + if ((ret = gitno_connection_data_from_url(&data, + url, NULL, NULL, false)) < 0) { + gitno_connection_data_free_ptrs(&data); return ret; + } + t->host = data.host; + t->port = data.port; + t->path = data.path; + t->user_from_url = data.user; + t->pass_from_url = data.pass; + t->use_ssl = data.use_ssl; } if (http_connect(t) < 0) |