From 7e0359084ef0faba8f458f9cccad2dc8f5baf800 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Fri, 1 Nov 2013 15:29:25 -0700 Subject: [PATCH 1/9] Streamline url-parsing logic. --- src/netops.c | 50 +++++++++++++++++------------------ tests-clar/network/urlparse.c | 10 +++++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/netops.c b/src/netops.c index 7e13f12e7..0bf3a8f13 100644 --- a/src/netops.c +++ b/src/netops.c @@ -668,47 +668,47 @@ int gitno_extract_url_parts( const char *url, const char *default_port) { - char *colon, *slash, *at, *end; - const char *start; + char *colon, *slash, *at; /* * ==> [user[:pass]@]hostname.tld[:port]/resource */ - colon = strchr(url, ':'); - slash = strchr(url, '/'); + /* Check for user and maybe password */ at = strchr(url, '@'); + if (at) { + colon = strchr(url, ':'); + if (colon && colon < at) { + /* user:pass */ + *username = git__substrdup(url, colon-url); + *password = git__substrdup(colon+1, at-colon-1); + GITERR_CHECK_ALLOC(*password); + } else { + *username = git__substrdup(url, at-url); + } + GITERR_CHECK_ALLOC(*username); + url = at + 1; + } + /* Validate URL format. Colons shouldn't be in the path part. */ + slash = strchr(url, '/'); + colon = strchr(url, ':'); if (!slash || (colon && (slash < colon))) { giterr_set(GITERR_NET, "Malformed URL"); return GIT_EINVALIDSPEC; } - start = url; - if (at && at < slash) { - start = at+1; - *username = git__substrdup(url, at - url); - } - - 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) { - *port = git__strdup(default_port); + /* Check for hostname and maybe port */ + if (colon) { + *host = git__substrdup(url, colon-url); + *port = git__substrdup(colon+1, slash-colon-1); } else { - *port = git__substrdup(colon + 1, slash - colon - 1); + *host = git__substrdup(url, slash-url); + *port = git__strdup(default_port); } - GITERR_CHECK_ALLOC(*port); - - end = colon == NULL ? slash : colon; - - *host = git__substrdup(start, end - start); GITERR_CHECK_ALLOC(*host); + GITERR_CHECK_ALLOC(*port); return 0; } diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 15e841b35..9fc304a07 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -38,6 +38,16 @@ void test_network_urlparse__bad_url(void) GIT_EINVALIDSPEC); } +void test_network_urlparse__weird_url(void) +{ + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "arrbee:my/bad:password@github.com:1111/strange/words.git", "1")); + cl_assert_equal_s(host, "github.com"); + cl_assert_equal_s(port, "1111"); + cl_assert_equal_s(user, "arrbee"); + cl_assert_equal_s(pass, "my/bad:password"); +} + void test_network_urlparse__user(void) { cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, From 56c1cda28a4b33fb305d99a2c7985a37efd3839d Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Fri, 1 Nov 2013 19:22:43 -0700 Subject: [PATCH 2/9] Clarify parsing issues and errors --- src/netops.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/netops.c b/src/netops.c index 0bf3a8f13..d2acdb240 100644 --- a/src/netops.c +++ b/src/netops.c @@ -674,7 +674,8 @@ int gitno_extract_url_parts( * ==> [user[:pass]@]hostname.tld[:port]/resource */ - /* Check for user and maybe password */ + /* Check for user and maybe password. Note that this deviates from RFC-1738 + * in that it allows non-encoded colons in the password field. */ at = strchr(url, '@'); if (at) { colon = strchr(url, ':'); @@ -695,7 +696,7 @@ int gitno_extract_url_parts( colon = strchr(url, ':'); if (!slash || (colon && (slash < colon))) { - giterr_set(GITERR_NET, "Malformed URL"); + giterr_set(GITERR_NET, "Malformed URL: %s", url); return GIT_EINVALIDSPEC; } From c227c173b84c8107a8933aeed947f16d82224377 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Mon, 4 Nov 2013 11:42:14 -0800 Subject: [PATCH 3/9] Use http_parser_parse_url to parse urls --- src/netops.c | 91 +++++++++++++++++------------------ src/netops.h | 1 + src/transports/git.c | 72 ++++++++++++--------------- src/transports/ssh.c | 1 - tests-clar/network/urlparse.c | 55 ++++++++++----------- 5 files changed, 103 insertions(+), 117 deletions(-) diff --git a/src/netops.c b/src/netops.c index d2acdb240..9653344fe 100644 --- a/src/netops.c +++ b/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; @@ -663,53 +661,52 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) 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; + struct http_parser_url u = {0}; + const char *_host, *_port, *_path, *_userinfo; - /* - * ==> [user[:pass]@]hostname.tld[:port]/resource - */ - - /* Check for user and maybe password. Note that this deviates from RFC-1738 - * in that it allows non-encoded colons in the password field. */ - at = strchr(url, '@'); - if (at) { - colon = strchr(url, ':'); - if (colon && colon < at) { - /* user:pass */ - *username = git__substrdup(url, colon-url); - *password = git__substrdup(colon+1, at-colon-1); - GITERR_CHECK_ALLOC(*password); - } else { - *username = git__substrdup(url, at-url); - } - GITERR_CHECK_ALLOC(*username); - url = at + 1; - } - - /* Validate URL format. Colons shouldn't be in the path part. */ - slash = strchr(url, '/'); - colon = strchr(url, ':'); - if (!slash || - (colon && (slash < colon))) { - giterr_set(GITERR_NET, "Malformed URL: %s", url); + if (http_parser_parse_url(url, strlen(url), false, &u)) { + giterr_set(GITERR_NET, "Malformed URL '%s'", url); return GIT_EINVALIDSPEC; } - /* Check for hostname and maybe port */ - if (colon) { - *host = git__substrdup(url, colon-url); - *port = git__substrdup(colon+1, slash-colon-1); - } else { - *host = git__substrdup(url, slash-url); - *port = git__strdup(default_port); + _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_data[UF_HOST].len) { + *host = git__substrdup(_host, u.field_data[UF_HOST].len); + GITERR_CHECK_ALLOC(*host); } - GITERR_CHECK_ALLOC(*host); + + if (u.field_data[UF_PORT].len) + *port = git__substrdup(_port, u.field_data[UF_PORT].len); + else + *port = git__strdup(default_port); GITERR_CHECK_ALLOC(*port); + if (u.field_data[UF_PATH].len) { + *path = git__substrdup(_path, u.field_data[UF_PATH].len); + GITERR_CHECK_ALLOC(*path); + } + + if (u.field_data[UF_USERINFO].len) { + const char *colon = strchr(_userinfo, ':'); + if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) { + *username = git__substrdup(_userinfo, colon - _userinfo); + *password = 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; } diff --git a/src/netops.h b/src/netops.h index 5c105d6e3..666d66b12 100644 --- a/src/netops.h +++ b/src/netops.h @@ -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, diff --git a/src/transports/git.c b/src/transports/git.c index 79a9e7dd4..5dcd4eff7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -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( diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 4e2834b49..db950e53d 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -330,7 +330,6 @@ 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) goto on_error; } else { diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 9fc304a07..4babb0fa7 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -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,37 +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) -{ - cl_git_fail_with(gitno_extract_url_parts(&host, &port, &user, &pass, - "github.com/git://github.com/foo/bar.git.git", "443"), - GIT_EINVALIDSPEC); -} - void test_network_urlparse__weird_url(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "arrbee:my/bad:password@github.com:1111/strange/words.git", "1")); - cl_assert_equal_s(host, "github.com"); - cl_assert_equal_s(port, "1111"); - cl_assert_equal_s(user, "arrbee"); - cl_assert_equal_s(pass, "my/bad:password"); + 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%2fis%40bad"); } 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); } @@ -61,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"); } @@ -72,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); } @@ -83,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); } @@ -94,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"); } From 16bffd1c26d8d22a61b5d99fd4063c2f71751317 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Mon, 4 Nov 2013 12:04:17 -0800 Subject: [PATCH 4/9] Unescape url-encoded usernames and passwords --- src/netops.c | 28 ++++++++++++++++++++++++++-- tests-clar/network/urlparse.c | 16 ++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/netops.c b/src/netops.c index 9653344fe..72172f39b 100644 --- a/src/netops.c +++ b/src/netops.c @@ -658,6 +658,29 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) git__free(d->pass); d->pass = NULL; } +static char unescape_hex(char *x) +{ + char digit; + digit = ((x[0] >= 'A') ? ((x[0] & 0xdf) - 'A')+10 : (x[0] - '0')); + digit *= 16; + digit += ((x[1] >= 'A') ? ((x[1] & 0xdf) - 'A')+10 : (x[1] - '0')); + return digit; +} + +static char* unescape(char *str) +{ + int x, y; + + for (x=y=0; str[x]; ++x, ++y) { + if ((str[x] = str[y]) == '%') { + str[x] = unescape_hex(str+y+1); + y += 2; + } + } + str[x] = '\0'; + return str; +} + int gitno_extract_url_parts( char **host, char **port, @@ -699,13 +722,14 @@ int gitno_extract_url_parts( if (u.field_data[UF_USERINFO].len) { const char *colon = strchr(_userinfo, ':'); if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) { - *username = git__substrdup(_userinfo, colon - _userinfo); - *password = git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)); + *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; diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 4babb0fa7..3ec3a51a3 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -33,7 +33,7 @@ void test_network_urlparse__trivial(void) cl_assert_equal_p(pass, NULL); } -void test_network_urlparse__weird_url(void) +void test_network_urlparse__encoded_password(void) { cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, "https://user:pass%2fis%40bad@hostname.com:1234/", "1")); @@ -41,7 +41,7 @@ void test_network_urlparse__weird_url(void) cl_assert_equal_s(port, "1234"); cl_assert_equal_s(path, "/"); cl_assert_equal_s(user, "user"); - cl_assert_equal_s(pass, "pass%2fis%40bad"); + cl_assert_equal_s(pass, "pass/is@bad"); } void test_network_urlparse__user(void) @@ -127,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@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"); + cl_assert_equal_i(conndata.use_ssl, true); +} + void test_network_urlparse__connection_data_cross_host_redirect(void) { conndata.host = git__strdup("bar.com"); From 2d1feaa2c7aef2e0d230e5535b214289c2c4a905 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Mon, 4 Nov 2013 15:03:44 -0800 Subject: [PATCH 5/9] Compile HTTP parser on win32 (for url parsing) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a14abae0a..1d8e00cd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,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) From ac72051afa619446818adf286cf3cdbd13991b38 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Mon, 4 Nov 2013 19:09:30 -0800 Subject: [PATCH 6/9] Fix ssh.c compile --- src/transports/ssh.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/transports/ssh.c b/src/transports/ssh.c index db950e53d..4a905e3c9 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -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,7 +330,7 @@ static int _git_ssh_setup_conn( s = (ssh_stream *)*stream; if (!git__prefixcmp(url, 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) @@ -389,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); From fe294b95d1f04ceb4d279b312d3f88375dbe0dc9 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 5 Nov 2013 10:37:50 -0800 Subject: [PATCH 7/9] Incorporate feedback --- src/netops.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/netops.c b/src/netops.c index 72172f39b..1a0d102d7 100644 --- a/src/netops.c +++ b/src/netops.c @@ -703,24 +703,24 @@ int gitno_extract_url_parts( _path = url+u.field_data[UF_PATH].off; _userinfo = url+u.field_data[UF_USERINFO].off; - if (u.field_data[UF_HOST].len) { + if (u.field_set & (1 << UF_HOST)) { *host = git__substrdup(_host, u.field_data[UF_HOST].len); GITERR_CHECK_ALLOC(*host); } - if (u.field_data[UF_PORT].len) + if (u.field_set & (1 << UF_PORT)) *port = git__substrdup(_port, u.field_data[UF_PORT].len); else *port = git__strdup(default_port); GITERR_CHECK_ALLOC(*port); - if (u.field_data[UF_PATH].len) { + if (u.field_set & (1 << UF_PATH)) { *path = git__substrdup(_path, u.field_data[UF_PATH].len); GITERR_CHECK_ALLOC(*path); } - if (u.field_data[UF_USERINFO].len) { - const char *colon = strchr(_userinfo, ':'); + if (u.field_set & (1 << UF_USERINFO)) { + const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len); if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) { *username = unescape(git__substrdup(_userinfo, colon - _userinfo)); *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo))); From d6eb3f9ce7f3b794d8e9ec2bb52bf995dd32cde9 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 5 Nov 2013 10:54:44 -0800 Subject: [PATCH 8/9] Remove unnecessary check --- src/netops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/netops.c b/src/netops.c index 1a0d102d7..ce49116e3 100644 --- a/src/netops.c +++ b/src/netops.c @@ -721,7 +721,7 @@ int gitno_extract_url_parts( if (u.field_set & (1 << UF_USERINFO)) { const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len); - if (colon && (colon - _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); From 79c443425b1b3d67e8180663c6e80793b587c888 Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 5 Nov 2013 11:35:57 -0800 Subject: [PATCH 9/9] Make url decoding more bulletproof --- src/netops.c | 19 +++++++------------ tests-clar/network/urlparse.c | 4 ++-- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/netops.c b/src/netops.c index ce49116e3..d7f17b1fc 100644 --- a/src/netops.c +++ b/src/netops.c @@ -658,23 +658,18 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) git__free(d->pass); d->pass = NULL; } -static char unescape_hex(char *x) -{ - char digit; - digit = ((x[0] >= 'A') ? ((x[0] & 0xdf) - 'A')+10 : (x[0] - '0')); - digit *= 16; - digit += ((x[1] >= 'A') ? ((x[1] & 0xdf) - 'A')+10 : (x[1] - '0')); - return digit; -} - +#define hex2c(c) ((c | 32) % 39 - 9) static char* unescape(char *str) { int x, y; + int len = strlen(str); - for (x=y=0; str[x]; ++x, ++y) { + for (x=y=0; str[y]; ++x, ++y) { if ((str[x] = str[y]) == '%') { - str[x] = unescape_hex(str+y+1); - y += 2; + 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'; diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 3ec3a51a3..31616275a 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -130,12 +130,12 @@ void test_network_urlparse__connection_data_ssl(void) void test_network_urlparse__encoded_username_password(void) { cl_git_pass(gitno_connection_data_from_url(&conndata, - "https://user%2fname:pass%40word@example.com/foo/bar/baz", "bar/baz")); + "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"); + cl_assert_equal_s(conndata.pass, "pass@word%zyx%v"); cl_assert_equal_i(conndata.use_ssl, true); }