connect.c: allow ssh://user@[2001:db8::1]/repo.git

The ssh:// syntax was added in 2386d658 (Add first cut at "git protocol" connect logic., 2005-07-13), 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@hcf.yourweb.de> 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 URLs 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@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Torsten Bögershausen committed Feb 21, 2015 at 16:52 UTC 86ceb337ec340c7db9b060b90bfab05a08b8251b
2 files changed +39 -26
connect.c
+38 -25
@@ -274,28 +274,44 @@ static enum protocol get_protocol(const char *name)
274 die("I don't handle protocol '%s'", name);
275 }
276
277 +static char *host_end(char **hoststart, int removebrackets)
278 +{
279 + char *host = *hoststart;
280 + char *end;
281 + char *start = strstr(host, "@[");
282 + if (start)
283 + start++; /* Jump over '@' */
284 + else
285 + start = host;
286 + if (start[0] == '[') {
287 + end = strchr(start + 1, ']');
288 + if (end) {
289 + if (removebrackets) {
290 + *end = 0;
291 + memmove(start, start + 1, end - start);
292 + end++;
293 + }
294 + } else
295 + end = host;
296 + } else
297 + end = host;
298 + return end;
299 +}
300 +
301 #define STR_(s) # s
302 #define STR(s) STR_(s)
303
304 static void get_host_and_port(char **host, const char **port)
305 {
306 char *colon, *end;
283 -
284 - if (*host[0] == '[') {
285 - end = strchr(*host + 1, ']');
286 - if (end) {
287 - *end = 0;
288 - end++;
289 - (*host)++;
290 - } else
291 - end = *host;
292 - } else
293 - end = *host;
307 + end = host_end(host, 1);
308 colon = strchr(end, ':');
295 -
309 if (colon) {
297 - *colon = 0;
298 - *port = colon + 1;
310 + long portnr = strtol(colon + 1, &end, 10);
311 + if (end != colon + 1 && *end == '\0' && 0 <= portnr && portnr < 65536) {
312 + *colon = 0;
313 + *port = colon + 1;
314 + }
315 }
316 }
317
@@ -547,13 +563,16 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
563 return proxy;
564 }
565
550 -static const char *get_port_numeric(const char *p)
566 +static char *get_port(char *host)
567 {
568 char *end;
569 + char *p = strchr(host, ':');
570 +
571 if (p) {
572 long port = strtol(p + 1, &end, 10);
573 if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
556 - return p;
574 + *p = '\0';
575 + return p+1;
576 }
577 }
578
@@ -595,14 +614,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
614 * Don't do destructive transforms as protocol code does
615 * '[]' unwrapping in get_host_and_port()
616 */
598 - if (host[0] == '[') {
599 - end = strchr(host + 1, ']');
600 - if (end) {
601 - end++;
602 - } else
603 - end = host;
604 - } else
605 - end = host;
617 + end = host_end(&host, 0);
618
619 if (protocol == PROTO_LOCAL)
620 path = end;
@@ -705,7 +717,8 @@ struct child_process *git_connect(int fd[2], const char *url,
717 char *ssh_host = hostandport;
718 const char *port = NULL;
719 get_host_and_port(&ssh_host, &port);
708 - port = get_port_numeric(port);
720 + if (!port)
721 + port = get_port(ssh_host);
722
723 if (!ssh) ssh = "ssh";
724
t/t5601-clone.sh
+1 -1
@@ -326,7 +326,7 @@ test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
326
327 test_expect_success 'bracketed hostnames are still ssh' '
328 git clone "[myhost:123]:src" ssh-bracket-clone &&
329 - expect_ssh myhost:123 src
329 + expect_ssh myhost '-p 123' src
330 '
331
332 counter=0