The ssh:// syntax was added in 2386d65822c91, it accepted ssh://user@2001:db8::1/repo.git, which is now legacy. Over the years the parser was improved to support [] and port numbers, but the combination of ssh://user@[2001:db8::1]:222/repo.git did never work. The only only way to use a user name, a literall IPV6 address and a port number was ssh://[user@2001:db8::1]:222/repo.git (Thanks to Christian Taube <lists@xxxxxxxxxxxxxx> for reporting this long standing issue) New users would use ssh://user@[2001:db8::1]:222/repo.git, so change the parser to handle it correctly. Support the old legacy URL's as well, to be backwards compatible, and avoid regressions for users which upgrade an existing installation to a later Git version. Signed-off-by: Torsten Bögershausen <tboegi@xxxxxx> --- Thanks for the reviews I hope the intention of being backward compatible is a little bit clearer now, as well as the intention to accept URL's conforming to the RFC 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.790.ge19fcd2 -- 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