mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-08 14:02:48 +00:00
Merge pull request #1936 from libgit2/better-url-parsing
Streamline url-parsing logic.
This commit is contained in:
commit
0df96f2b05
@ -128,6 +128,8 @@ INCLUDE_DIRECTORIES(src include)
|
||||
|
||||
IF (WIN32 AND WINHTTP AND NOT MINGW)
|
||||
ADD_DEFINITIONS(-DGIT_WINHTTP)
|
||||
INCLUDE_DIRECTORIES(deps/http-parser)
|
||||
FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
|
||||
ELSE ()
|
||||
IF (NOT AMIGA)
|
||||
FIND_PACKAGE(OpenSSL)
|
||||
|
101
src/netops.c
101
src/netops.c
@ -32,6 +32,7 @@
|
||||
#include "netops.h"
|
||||
#include "posix.h"
|
||||
#include "buffer.h"
|
||||
#include "http_parser.h"
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
static void net_set_error(const char *str)
|
||||
@ -582,7 +583,7 @@ int gitno_connection_data_from_url(
|
||||
const char *service_suffix)
|
||||
{
|
||||
int error = -1;
|
||||
const char *default_port = NULL;
|
||||
const char *default_port = NULL, *path_search_start = NULL;
|
||||
char *original_host = NULL;
|
||||
|
||||
/* service_suffix is optional */
|
||||
@ -594,22 +595,18 @@ int gitno_connection_data_from_url(
|
||||
gitno_connection_data_free_ptrs(data);
|
||||
|
||||
if (!git__prefixcmp(url, prefix_http)) {
|
||||
url = url + strlen(prefix_http);
|
||||
path_search_start = url + strlen(prefix_http);
|
||||
default_port = "80";
|
||||
|
||||
if (data->use_ssl) {
|
||||
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!git__prefixcmp(url, prefix_https)) {
|
||||
url += strlen(prefix_https);
|
||||
} else if (!git__prefixcmp(url, prefix_https)) {
|
||||
path_search_start = url + strlen(prefix_https);
|
||||
default_port = "443";
|
||||
data->use_ssl = true;
|
||||
}
|
||||
|
||||
if (url[0] == '/')
|
||||
} else if (url[0] == '/')
|
||||
default_port = data->use_ssl ? "443" : "80";
|
||||
|
||||
if (!default_port) {
|
||||
@ -618,18 +615,19 @@ int gitno_connection_data_from_url(
|
||||
}
|
||||
|
||||
error = gitno_extract_url_parts(
|
||||
&data->host, &data->port, &data->user, &data->pass,
|
||||
&data->host, &data->port, &data->path, &data->user, &data->pass,
|
||||
url, default_port);
|
||||
|
||||
if (url[0] == '/') {
|
||||
/* Relative redirect; reuse original host name and port */
|
||||
path_search_start = url;
|
||||
git__free(data->host);
|
||||
data->host = original_host;
|
||||
original_host = NULL;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
const char *path = strchr(url, '/');
|
||||
const char *path = strchr(path_search_start, '/');
|
||||
size_t pathlen = strlen(path);
|
||||
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
|
||||
|
||||
@ -660,55 +658,74 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d)
|
||||
git__free(d->pass); d->pass = NULL;
|
||||
}
|
||||
|
||||
#define hex2c(c) ((c | 32) % 39 - 9)
|
||||
static char* unescape(char *str)
|
||||
{
|
||||
int x, y;
|
||||
int len = strlen(str);
|
||||
|
||||
for (x=y=0; str[y]; ++x, ++y) {
|
||||
if ((str[x] = str[y]) == '%') {
|
||||
if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
|
||||
str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
|
||||
y += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
str[x] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
int gitno_extract_url_parts(
|
||||
char **host,
|
||||
char **port,
|
||||
char **path,
|
||||
char **username,
|
||||
char **password,
|
||||
const char *url,
|
||||
const char *default_port)
|
||||
{
|
||||
char *colon, *slash, *at, *end;
|
||||
const char *start;
|
||||
struct http_parser_url u = {0};
|
||||
const char *_host, *_port, *_path, *_userinfo;
|
||||
|
||||
/*
|
||||
* ==> [user[:pass]@]hostname.tld[:port]/resource
|
||||
*/
|
||||
|
||||
colon = strchr(url, ':');
|
||||
slash = strchr(url, '/');
|
||||
at = strchr(url, '@');
|
||||
|
||||
if (!slash ||
|
||||
(colon && (slash < colon))) {
|
||||
giterr_set(GITERR_NET, "Malformed URL");
|
||||
if (http_parser_parse_url(url, strlen(url), false, &u)) {
|
||||
giterr_set(GITERR_NET, "Malformed URL '%s'", url);
|
||||
return GIT_EINVALIDSPEC;
|
||||
}
|
||||
|
||||
start = url;
|
||||
if (at && at < slash) {
|
||||
start = at+1;
|
||||
*username = git__substrdup(url, at - url);
|
||||
_host = url+u.field_data[UF_HOST].off;
|
||||
_port = url+u.field_data[UF_PORT].off;
|
||||
_path = url+u.field_data[UF_PATH].off;
|
||||
_userinfo = url+u.field_data[UF_USERINFO].off;
|
||||
|
||||
if (u.field_set & (1 << UF_HOST)) {
|
||||
*host = git__substrdup(_host, u.field_data[UF_HOST].len);
|
||||
GITERR_CHECK_ALLOC(*host);
|
||||
}
|
||||
|
||||
if (colon && colon < at) {
|
||||
git__free(*username);
|
||||
*username = git__substrdup(url, colon-url);
|
||||
*password = git__substrdup(colon+1, at-colon-1);
|
||||
colon = strchr(at, ':');
|
||||
}
|
||||
|
||||
if (colon == NULL) {
|
||||
if (u.field_set & (1 << UF_PORT))
|
||||
*port = git__substrdup(_port, u.field_data[UF_PORT].len);
|
||||
else
|
||||
*port = git__strdup(default_port);
|
||||
} else {
|
||||
*port = git__substrdup(colon + 1, slash - colon - 1);
|
||||
}
|
||||
GITERR_CHECK_ALLOC(*port);
|
||||
|
||||
end = colon == NULL ? slash : colon;
|
||||
if (u.field_set & (1 << UF_PATH)) {
|
||||
*path = git__substrdup(_path, u.field_data[UF_PATH].len);
|
||||
GITERR_CHECK_ALLOC(*path);
|
||||
}
|
||||
|
||||
*host = git__substrdup(start, end - start);
|
||||
GITERR_CHECK_ALLOC(*host);
|
||||
if (u.field_set & (1 << UF_USERINFO)) {
|
||||
const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
|
||||
if (colon) {
|
||||
*username = unescape(git__substrdup(_userinfo, colon - _userinfo));
|
||||
*password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
|
||||
GITERR_CHECK_ALLOC(*password);
|
||||
} else {
|
||||
*username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
|
||||
}
|
||||
GITERR_CHECK_ALLOC(*username);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data);
|
||||
int gitno_extract_url_parts(
|
||||
char **host,
|
||||
char **port,
|
||||
char **path,
|
||||
char **username,
|
||||
char **password,
|
||||
const char *url,
|
||||
|
@ -179,39 +179,33 @@ static int _git_uploadpack_ls(
|
||||
const char *url,
|
||||
git_smart_subtransport_stream **stream)
|
||||
{
|
||||
char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
|
||||
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
|
||||
const char *stream_url = url;
|
||||
git_stream *s;
|
||||
int error = -1;
|
||||
|
||||
*stream = NULL;
|
||||
|
||||
if (!git__prefixcmp(url, prefix_git))
|
||||
url += strlen(prefix_git);
|
||||
stream_url += strlen(prefix_git);
|
||||
|
||||
if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
|
||||
if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0)
|
||||
return -1;
|
||||
|
||||
s = (git_stream *)*stream;
|
||||
|
||||
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
|
||||
goto on_error;
|
||||
if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
|
||||
if (!(error = gitno_connect(&s->socket, host, port, 0)))
|
||||
t->current_stream = s;
|
||||
|
||||
if (gitno_connect(&s->socket, host, port, 0) < 0)
|
||||
goto on_error;
|
||||
|
||||
t->current_stream = s;
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(user);
|
||||
git__free(pass);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
if (*stream)
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(path);
|
||||
git__free(user);
|
||||
git__free(pass);
|
||||
} else if (*stream)
|
||||
git_stream_free(*stream);
|
||||
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int _git_uploadpack(
|
||||
@ -235,39 +229,33 @@ static int _git_receivepack_ls(
|
||||
const char *url,
|
||||
git_smart_subtransport_stream **stream)
|
||||
{
|
||||
char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
|
||||
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
|
||||
const char *stream_url = url;
|
||||
git_stream *s;
|
||||
int error;
|
||||
|
||||
*stream = NULL;
|
||||
|
||||
if (!git__prefixcmp(url, prefix_git))
|
||||
url += strlen(prefix_git);
|
||||
stream_url += strlen(prefix_git);
|
||||
|
||||
if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0)
|
||||
if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0)
|
||||
return -1;
|
||||
|
||||
s = (git_stream *)*stream;
|
||||
|
||||
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
|
||||
goto on_error;
|
||||
if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
|
||||
if (!(error = gitno_connect(&s->socket, host, port, 0)))
|
||||
t->current_stream = s;
|
||||
|
||||
if (gitno_connect(&s->socket, host, port, 0) < 0)
|
||||
goto on_error;
|
||||
|
||||
t->current_stream = s;
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(user);
|
||||
git__free(pass);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
if (*stream)
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(path);
|
||||
git__free(user);
|
||||
git__free(pass);
|
||||
} else if (*stream)
|
||||
git_stream_free(*stream);
|
||||
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int _git_receivepack(
|
||||
|
@ -317,7 +317,7 @@ static int _git_ssh_setup_conn(
|
||||
const char *cmd,
|
||||
git_smart_subtransport_stream **stream)
|
||||
{
|
||||
char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
|
||||
char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
|
||||
const char *default_port="22";
|
||||
ssh_stream *s;
|
||||
LIBSSH2_SESSION* session=NULL;
|
||||
@ -330,8 +330,7 @@ static int _git_ssh_setup_conn(
|
||||
s = (ssh_stream *)*stream;
|
||||
|
||||
if (!git__prefixcmp(url, prefix_ssh)) {
|
||||
url = url + strlen(prefix_ssh);
|
||||
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
|
||||
if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
|
||||
goto on_error;
|
||||
} else {
|
||||
if (git_ssh_extract_url_parts(&host, &user, url) < 0)
|
||||
@ -390,6 +389,7 @@ static int _git_ssh_setup_conn(
|
||||
t->current_stream = s;
|
||||
git__free(host);
|
||||
git__free(port);
|
||||
git__free(path);
|
||||
git__free(user);
|
||||
git__free(pass);
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "netops.h"
|
||||
|
||||
char *host, *port, *user, *pass;
|
||||
char *host, *port, *path, *user, *pass;
|
||||
gitno_connection_data conndata;
|
||||
|
||||
void test_network_urlparse__initialize(void)
|
||||
{
|
||||
host = port = user = pass = NULL;
|
||||
host = port = path = user = pass = NULL;
|
||||
memset(&conndata, 0, sizeof(conndata));
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void)
|
||||
#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
|
||||
FREE_AND_NULL(host);
|
||||
FREE_AND_NULL(port);
|
||||
FREE_AND_NULL(path);
|
||||
FREE_AND_NULL(user);
|
||||
FREE_AND_NULL(pass);
|
||||
|
||||
@ -23,27 +24,33 @@ void test_network_urlparse__cleanup(void)
|
||||
|
||||
void test_network_urlparse__trivial(void)
|
||||
{
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"example.com/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"http://example.com/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "8080");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_p(user, NULL);
|
||||
cl_assert_equal_p(pass, NULL);
|
||||
}
|
||||
|
||||
void test_network_urlparse__bad_url(void)
|
||||
void test_network_urlparse__encoded_password(void)
|
||||
{
|
||||
cl_git_fail_with(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"github.com/git://github.com/foo/bar.git.git", "443"),
|
||||
GIT_EINVALIDSPEC);
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
|
||||
cl_assert_equal_s(host, "hostname.com");
|
||||
cl_assert_equal_s(port, "1234");
|
||||
cl_assert_equal_s(path, "/");
|
||||
cl_assert_equal_s(user, "user");
|
||||
cl_assert_equal_s(pass, "pass/is@bad");
|
||||
}
|
||||
|
||||
void test_network_urlparse__user(void)
|
||||
{
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"user@example.com/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://user@example.com/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "8080");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_s(user, "user");
|
||||
cl_assert_equal_p(pass, NULL);
|
||||
}
|
||||
@ -51,10 +58,11 @@ void test_network_urlparse__user(void)
|
||||
void test_network_urlparse__user_pass(void)
|
||||
{
|
||||
/* user:pass@hostname.tld/resource */
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"user:pass@example.com/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://user:pass@example.com/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "8080");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_s(user, "user");
|
||||
cl_assert_equal_s(pass, "pass");
|
||||
}
|
||||
@ -62,10 +70,11 @@ void test_network_urlparse__user_pass(void)
|
||||
void test_network_urlparse__port(void)
|
||||
{
|
||||
/* hostname.tld:port/resource */
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"example.com:9191/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://example.com:9191/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "9191");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_p(user, NULL);
|
||||
cl_assert_equal_p(pass, NULL);
|
||||
}
|
||||
@ -73,10 +82,11 @@ void test_network_urlparse__port(void)
|
||||
void test_network_urlparse__user_port(void)
|
||||
{
|
||||
/* user@hostname.tld:port/resource */
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"user@example.com:9191/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://user@example.com:9191/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "9191");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_s(user, "user");
|
||||
cl_assert_equal_p(pass, NULL);
|
||||
}
|
||||
@ -84,10 +94,11 @@ void test_network_urlparse__user_port(void)
|
||||
void test_network_urlparse__user_pass_port(void)
|
||||
{
|
||||
/* user:pass@hostname.tld:port/resource */
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
|
||||
"user:pass@example.com:9191/resource", "8080"));
|
||||
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
|
||||
"https://user:pass@example.com:9191/resource", "8080"));
|
||||
cl_assert_equal_s(host, "example.com");
|
||||
cl_assert_equal_s(port, "9191");
|
||||
cl_assert_equal_s(path, "/resource");
|
||||
cl_assert_equal_s(user, "user");
|
||||
cl_assert_equal_s(pass, "pass");
|
||||
}
|
||||
@ -116,6 +127,18 @@ void test_network_urlparse__connection_data_ssl(void)
|
||||
cl_assert_equal_i(conndata.use_ssl, true);
|
||||
}
|
||||
|
||||
void test_network_urlparse__encoded_username_password(void)
|
||||
{
|
||||
cl_git_pass(gitno_connection_data_from_url(&conndata,
|
||||
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
|
||||
cl_assert_equal_s(conndata.host, "example.com");
|
||||
cl_assert_equal_s(conndata.port, "443");
|
||||
cl_assert_equal_s(conndata.path, "/foo/");
|
||||
cl_assert_equal_s(conndata.user, "user/name");
|
||||
cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
|
||||
cl_assert_equal_i(conndata.use_ssl, true);
|
||||
}
|
||||
|
||||
void test_network_urlparse__connection_data_cross_host_redirect(void)
|
||||
{
|
||||
conndata.host = git__strdup("bar.com");
|
||||
|
Loading…
Reference in New Issue
Block a user