When parsing an URL, older Git versions did handle URLs like ssh://2001:db8::1/repo.git the same way as ssh://[2001:db8::1]/repo.git Commit 83b058 broke the parsing of IPV6 adresses without "[]": It was written in mind that the fist ':' in a URL was the beginning of a port number, while the old code was more clever: Literal IPV6 addresses have always at least two ':'. When the "hostandport" had a ':' and the rest of the hostandport string could be parsed into an integer between 0 and 65536, then it was used as a port number, like "host:22". Otherwise the first ':' was assumed to be part of a literal IPV6 address, like "ssh://[2001:db8::1]/repo.git" or "ssh://[::1]/repo.git". Re-introduce the old parsing in get_host_and_port(). Improve the parsing to handle URLs which have a user name and a literal IPV6 like "ssh://user@[2001:db8::1]/repo.git". (Thanks to Christian Taube <lists@xxxxxxxxxxxxxx> for reporting this long standing issue) Another regression was introduced in 83b058: A non-RFC conform URL like "[localhost:222]" could be used in older versions of Git to connect to run "ssh -p 222 localhost". The new stricter parsing did not allow this any more. See even 8d3d28f5dba why "[localhost:222]" should be declared a feature. Signed-off-by: Torsten Bögershausen <tboegi@xxxxxx> --- Unfortunatly my attemps to improve connect.c introduced some regressions: - "git clone ssh://::1/repo" did not work anymore (for some reason I assumed that literall IPV6 addresses always should have brackets, but that was wrong) - "ssh://host:2222/repo" written in the "unofficial short form" "[host:2222]:repo did not work anymore. I think that should be an "unoffical feature", because it worked in older Git versions On top of that, the combination of ssh://username@[host] had never be handled correctly, so fix that as well. Thanks to Christian Taube for reporting it. Comments and an extra pair of critical eyes are welcome. connect.c | 63 ++++++++++++++++++++++++++++++++++---------------------- t/t5601-clone.sh | 2 +- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/connect.c b/connect.c index d47d0ec..b608976 100644 --- a/connect.c +++ b/connect.c @@ -274,28 +274,44 @@ static enum protocol get_protocol(const char *name) die("I don't handle protocol '%s'", name); } +static char *host_end(char **hoststart, int removebrackets) +{ + char *host = *hoststart; + char *end; + char *start = strstr(host, "@["); + if (start) + start++; /* Jump over '@' */ + else + start = host; + if (start[0] == '[') { + end = strchr(start + 1, ']'); + if (end) { + if (removebrackets) { + *end = 0; + memmove(start, start + 1, end - start); + end++; + } + } else + end = host; + } else + end = host; + return end; +} + #define STR_(s) # s #define STR(s) STR_(s) static void get_host_and_port(char **host, const char **port) { char *colon, *end; - - if (*host[0] == '[') { - end = strchr(*host + 1, ']'); - if (end) { - *end = 0; - end++; - (*host)++; - } else - end = *host; - } else - end = *host; + end = host_end(host, 1); colon = strchr(end, ':'); - if (colon) { - *colon = 0; - *port = colon + 1; + long portnr = strtol(colon + 1, &end, 10); + if (end != colon + 1 && *end == '\0' && 0 <= portnr && portnr < 65536) { + *colon = 0; + *port = colon + 1; + } } } @@ -547,13 +563,16 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) return proxy; } -static const char *get_port_numeric(const char *p) +static char *get_port(char *host) { char *end; + char *p = strchr(host, ':'); + if (p) { long port = strtol(p + 1, &end, 10); if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) { - return p; + *p = '\0'; + return p+1; } } @@ -595,14 +614,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, * Don't do destructive transforms as protocol code does * '[]' unwrapping in get_host_and_port() */ - if (host[0] == '[') { - end = strchr(host + 1, ']'); - if (end) { - end++; - } else - end = host; - } else - end = host; + end = host_end(&host, 0); if (protocol == PROTO_LOCAL) path = end; @@ -705,7 +717,8 @@ struct child_process *git_connect(int fd[2], const char *url, char *ssh_host = hostandport; const char *port = NULL; get_host_and_port(&ssh_host, &port); - port = get_port_numeric(port); + if (!port) + port = get_port(ssh_host); if (!ssh) ssh = "ssh"; diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index e4f10c0..f901b8a 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -326,7 +326,7 @@ test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' ' test_expect_success 'bracketed hostnames are still ssh' ' git clone "[myhost:123]:src" ssh-bracket-clone && - expect_ssh myhost:123 src + expect_ssh myhost '-p 123' src ' counter=0 -- 2.2.0.rc1.26.g3e3a70d -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html