Skip to content

Commit 419b1b5

Browse files
committed
Merge branch 'mh/connect' into pu
Rewrite Git-URL parsing routine (hopefully) without changing any behaviour. It has been two months without any support. We may want to discard this. * mh/connect: connect: [host:port] is legacy for ssh connect: move ssh command line preparation to a separate function connect: actively reject git:// urls with a user part connect: change the --diag-url output to separate user and host connect: make parse_connect_url() return the user part of the url as a separate value connect: group CONNECT_DIAG_URL handling code connect: make parse_connect_url() return separated host and port connect: re-derive a host:port string from the separate host and port variables connect: call get_host_and_port() earlier connect: document why we sometimes call get_port after get_host_and_port
2 parents cac2cb8 + 052b49d commit 419b1b5

File tree

2 files changed

+190
-113
lines changed

2 files changed

+190
-113
lines changed

connect.c

Lines changed: 142 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -363,18 +363,16 @@ static const char *ai_name(const struct addrinfo *ai)
363363
/*
364364
* Returns a connected socket() fd, or else die()s.
365365
*/
366-
static int git_tcp_connect_sock(char *host, int flags)
366+
static int git_tcp_connect_sock(const char *host, const char *port, int flags)
367367
{
368368
struct strbuf error_message = STRBUF_INIT;
369369
int sockfd = -1;
370-
const char *port = STR(DEFAULT_GIT_PORT);
371370
struct addrinfo hints, *ai0, *ai;
372371
int gai;
373372
int cnt = 0;
374373

375-
get_host_and_port(&host, &port);
376-
if (!*port)
377-
port = "<none>";
374+
if (!port)
375+
port = STR(DEFAULT_GIT_PORT);
378376

379377
memset(&hints, 0, sizeof(hints));
380378
if (flags & CONNECT_IPV4)
@@ -431,19 +429,19 @@ static int git_tcp_connect_sock(char *host, int flags)
431429
/*
432430
* Returns a connected socket() fd, or else die()s.
433431
*/
434-
static int git_tcp_connect_sock(char *host, int flags)
432+
static int git_tcp_connect_sock(const char *host, const char *port, int flags)
435433
{
436434
struct strbuf error_message = STRBUF_INIT;
437435
int sockfd = -1;
438-
const char *port = STR(DEFAULT_GIT_PORT);
439436
char *ep;
440437
struct hostent *he;
441438
struct sockaddr_in sa;
442439
char **ap;
443440
unsigned int nport;
444441
int cnt;
445442

446-
get_host_and_port(&host, &port);
443+
if (!port)
444+
port = STR(DEFAULT_GIT_PORT);
447445

448446
if (flags & CONNECT_VERBOSE)
449447
fprintf(stderr, "Looking up %s ... ", host);
@@ -502,9 +500,10 @@ static int git_tcp_connect_sock(char *host, int flags)
502500
#endif /* NO_IPV6 */
503501

504502

505-
static void git_tcp_connect(int fd[2], char *host, int flags)
503+
static void git_tcp_connect(int fd[2], const char *host, const char *port,
504+
int flags)
506505
{
507-
int sockfd = git_tcp_connect_sock(host, flags);
506+
int sockfd = git_tcp_connect_sock(host, port, flags);
508507

509508
fd[0] = sockfd;
510509
fd[1] = dup(sockfd);
@@ -570,18 +569,16 @@ static int git_use_proxy(const char *host)
570569
return (git_proxy_command && *git_proxy_command);
571570
}
572571

573-
static struct child_process *git_proxy_connect(int fd[2], char *host)
572+
static struct child_process *git_proxy_connect(int fd[2], const char *host,
573+
const char *port)
574574
{
575-
const char *port = STR(DEFAULT_GIT_PORT);
576575
struct child_process *proxy;
577576

578-
get_host_and_port(&host, &port);
579-
580577
proxy = xmalloc(sizeof(*proxy));
581578
child_process_init(proxy);
582579
argv_array_push(&proxy->args, git_proxy_command);
583580
argv_array_push(&proxy->args, host);
584-
argv_array_push(&proxy->args, port);
581+
argv_array_push(&proxy->args, port ? port : STR(DEFAULT_GIT_PORT));
585582
proxy->in = -1;
586583
proxy->out = -1;
587584
if (start_command(proxy))
@@ -611,11 +608,14 @@ static char *get_port(char *host)
611608
* Extract protocol and relevant parts from the specified connection URL.
612609
* The caller must free() the returned strings.
613610
*/
614-
static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
611+
static enum protocol parse_connect_url(const char *url_orig, char **ret_user,
612+
char **ret_host, char **ret_port,
615613
char **ret_path)
616614
{
617615
char *url;
618616
char *host, *path;
617+
const char *user = NULL;
618+
const char *port = NULL;
619619
char *end;
620620
int separator = '/';
621621
enum protocol protocol = PROTO_LOCAL;
@@ -638,10 +638,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
638638
}
639639
}
640640

641-
/*
642-
* Don't do destructive transforms as protocol code does
643-
* '[]' unwrapping in get_host_and_port()
644-
*/
641+
/* '[]' unwrapping is done in get_host_and_port() */
645642
end = host_end(&host, 0);
646643

647644
if (protocol == PROTO_LOCAL)
@@ -670,14 +667,40 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
670667
path = xstrdup(path);
671668
*end = '\0';
672669

670+
get_host_and_port(&host, &port);
671+
672+
if (*host) {
673+
/*
674+
* At this point, the host part may look like user@host:port.
675+
* As the `user` portion tends to be less strict than
676+
* `host:port`, we first put it out of the equation: since a
677+
* hostname cannot contain a '@', we start from the last '@'
678+
* in the string.
679+
*/
680+
char *end_user = strrchr(host, '@');
681+
if (end_user) {
682+
*end_user = '\0';
683+
user = host;
684+
host = end_user + 1;
685+
}
686+
}
687+
/*
688+
* get_host_and_port does not return a port in the [host:port]:path
689+
* case. In that case, it is called with "[host:port]" and returns
690+
* "host:port" and NULL. To support this undocumented legacy
691+
* (for ssh only) we still need to split the port.
692+
*/
693+
if (!port && protocol == PROTO_SSH)
694+
port = get_port(host);
695+
696+
*ret_user = user ? xstrdup(user) : NULL;
673697
*ret_host = xstrdup(host);
698+
*ret_port = port ? xstrdup(port) : NULL;
674699
*ret_path = path;
675700
free(url);
676701
return protocol;
677702
}
678703

679-
static struct child_process no_fork = CHILD_PROCESS_INIT;
680-
681704
static const char *get_ssh_command(void)
682705
{
683706
const char *ssh;
@@ -691,6 +714,63 @@ static const char *get_ssh_command(void)
691714
return NULL;
692715
}
693716

717+
static int prepare_ssh_command(struct argv_array *cmd, const char *user,
718+
const char *host, const char *port, int flags)
719+
{
720+
const char *ssh;
721+
int putty = 0, tortoiseplink = 0, use_shell = 1;
722+
transport_check_allowed("ssh");
723+
724+
ssh = get_ssh_command();
725+
if (!ssh) {
726+
const char *base;
727+
char *ssh_dup;
728+
729+
/*
730+
* GIT_SSH is the no-shell version of
731+
* GIT_SSH_COMMAND (and must remain so for
732+
* historical compatibility).
733+
*/
734+
use_shell = 0;
735+
736+
ssh = getenv("GIT_SSH");
737+
if (!ssh)
738+
ssh = "ssh";
739+
740+
ssh_dup = xstrdup(ssh);
741+
base = basename(ssh_dup);
742+
743+
tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
744+
!strcasecmp(base, "tortoiseplink.exe");
745+
putty = tortoiseplink ||
746+
!strcasecmp(base, "plink") ||
747+
!strcasecmp(base, "plink.exe");
748+
749+
free(ssh_dup);
750+
}
751+
752+
argv_array_push(cmd, ssh);
753+
if (flags & CONNECT_IPV4)
754+
argv_array_push(cmd, "-4");
755+
else if (flags & CONNECT_IPV6)
756+
argv_array_push(cmd, "-6");
757+
if (tortoiseplink)
758+
argv_array_push(cmd, "-batch");
759+
if (port) {
760+
/* P is for PuTTY, p is for OpenSSH */
761+
argv_array_push(cmd, putty ? "-P" : "-p");
762+
argv_array_push(cmd, port);
763+
}
764+
if (user)
765+
argv_array_pushf(cmd, "%s@%s", user, host);
766+
else
767+
argv_array_push(cmd, host);
768+
769+
return use_shell;
770+
}
771+
772+
static struct child_process no_fork = CHILD_PROCESS_INIT;
773+
694774
/*
695775
* This returns a dummy child_process if the transport protocol does not
696776
* need fork(2), or a struct child_process object if it does. Once done,
@@ -705,7 +785,7 @@ static const char *get_ssh_command(void)
705785
struct child_process *git_connect(int fd[2], const char *url,
706786
const char *prog, int flags)
707787
{
708-
char *hostandport, *path;
788+
char *user, *host, *port, *path;
709789
struct child_process *conn = &no_fork;
710790
enum protocol protocol;
711791
struct strbuf cmd = STRBUF_INIT;
@@ -715,11 +795,13 @@ struct child_process *git_connect(int fd[2], const char *url,
715795
*/
716796
signal(SIGCHLD, SIG_DFL);
717797

718-
protocol = parse_connect_url(url, &hostandport, &path);
719-
if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) {
798+
protocol = parse_connect_url(url, &user, &host, &port, &path);
799+
if (flags & CONNECT_DIAG_URL) {
720800
printf("Diag: url=%s\n", url ? url : "NULL");
721801
printf("Diag: protocol=%s\n", prot_name(protocol));
722-
printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
802+
printf("Diag: user=%s\n", user ? user : "NULL");
803+
printf("Diag: host=%s\n", host ? host : "NULL");
804+
printf("Diag: port=%s\n", port ? port : "NONE");
723805
printf("Diag: path=%s\n", path ? path : "NULL");
724806
conn = NULL;
725807
} else if (protocol == PROTO_GIT) {
@@ -728,21 +810,38 @@ struct child_process *git_connect(int fd[2], const char *url,
728810
* connect, unless the user has overridden us in
729811
* the environment.
730812
*/
731-
char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
732-
if (target_host)
733-
target_host = xstrdup(target_host);
734-
else
735-
target_host = xstrdup(hostandport);
813+
struct strbuf target_host = STRBUF_INIT;
814+
struct strbuf virtual_host = STRBUF_INIT;
815+
const char *colon = strchr(host, ':');
816+
char *override_vhost = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
817+
818+
if (user)
819+
die("user@host is not allowed in git:// urls");
820+
821+
/* If the host contains a colon (ipv6 address), it needs to
822+
* be enclosed with square brackets. */
823+
if (colon)
824+
strbuf_addch(&target_host, '[');
825+
strbuf_addstr(&target_host, host);
826+
if (colon)
827+
strbuf_addch(&target_host, ']');
828+
if (port) {
829+
strbuf_addch(&target_host, ':');
830+
strbuf_addstr(&target_host, port);
831+
}
832+
833+
strbuf_addstr(&virtual_host, override_vhost ? override_vhost
834+
: target_host.buf);
736835

737836
transport_check_allowed("git");
738837

739838
/* These underlying connection commands die() if they
740839
* cannot connect.
741840
*/
742-
if (git_use_proxy(hostandport))
743-
conn = git_proxy_connect(fd, hostandport);
841+
if (git_use_proxy(target_host.buf))
842+
conn = git_proxy_connect(fd, host, port);
744843
else
745-
git_tcp_connect(fd, hostandport, flags);
844+
git_tcp_connect(fd, host, port, flags);
746845
/*
747846
* Separate original protocol components prog and path
748847
* from extended host header with a NUL byte.
@@ -753,8 +852,9 @@ struct child_process *git_connect(int fd[2], const char *url,
753852
packet_write_fmt(fd[1],
754853
"%s %s%chost=%s%c",
755854
prog, path, 0,
756-
target_host, 0);
757-
free(target_host);
855+
virtual_host.buf, 0);
856+
strbuf_release(&virtual_host);
857+
strbuf_release(&target_host);
758858
} else {
759859
conn = xmalloc(sizeof(*conn));
760860
child_process_init(conn);
@@ -765,74 +865,12 @@ struct child_process *git_connect(int fd[2], const char *url,
765865

766866
/* remove repo-local variables from the environment */
767867
conn->env = local_repo_env;
768-
conn->use_shell = 1;
769868
conn->in = conn->out = -1;
770869
if (protocol == PROTO_SSH) {
771-
const char *ssh;
772-
int putty = 0, tortoiseplink = 0;
773-
char *ssh_host = hostandport;
774-
const char *port = NULL;
775-
transport_check_allowed("ssh");
776-
get_host_and_port(&ssh_host, &port);
777-
778-
if (!port)
779-
port = get_port(ssh_host);
780-
781-
if (flags & CONNECT_DIAG_URL) {
782-
printf("Diag: url=%s\n", url ? url : "NULL");
783-
printf("Diag: protocol=%s\n", prot_name(protocol));
784-
printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL");
785-
printf("Diag: port=%s\n", port ? port : "NONE");
786-
printf("Diag: path=%s\n", path ? path : "NULL");
787-
788-
free(hostandport);
789-
free(path);
790-
free(conn);
791-
return NULL;
792-
}
793-
794-
ssh = get_ssh_command();
795-
if (!ssh) {
796-
const char *base;
797-
char *ssh_dup;
798-
799-
/*
800-
* GIT_SSH is the no-shell version of
801-
* GIT_SSH_COMMAND (and must remain so for
802-
* historical compatibility).
803-
*/
804-
conn->use_shell = 0;
805-
806-
ssh = getenv("GIT_SSH");
807-
if (!ssh)
808-
ssh = "ssh";
809-
810-
ssh_dup = xstrdup(ssh);
811-
base = basename(ssh_dup);
812-
813-
tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
814-
!strcasecmp(base, "tortoiseplink.exe");
815-
putty = tortoiseplink ||
816-
!strcasecmp(base, "plink") ||
817-
!strcasecmp(base, "plink.exe");
818-
819-
free(ssh_dup);
820-
}
821-
822-
argv_array_push(&conn->args, ssh);
823-
if (flags & CONNECT_IPV4)
824-
argv_array_push(&conn->args, "-4");
825-
else if (flags & CONNECT_IPV6)
826-
argv_array_push(&conn->args, "-6");
827-
if (tortoiseplink)
828-
argv_array_push(&conn->args, "-batch");
829-
if (port) {
830-
/* P is for PuTTY, p is for OpenSSH */
831-
argv_array_push(&conn->args, putty ? "-P" : "-p");
832-
argv_array_push(&conn->args, port);
833-
}
834-
argv_array_push(&conn->args, ssh_host);
870+
conn->use_shell = prepare_ssh_command(
871+
&conn->args, user, host, port, flags);
835872
} else {
873+
conn->use_shell = 1;
836874
transport_check_allowed("file");
837875
}
838876
argv_array_push(&conn->args, cmd.buf);
@@ -844,7 +882,9 @@ struct child_process *git_connect(int fd[2], const char *url,
844882
fd[1] = conn->in; /* write to child's stdin */
845883
strbuf_release(&cmd);
846884
}
847-
free(hostandport);
885+
free(user);
886+
free(host);
887+
free(port);
848888
free(path);
849889
return conn;
850890
}

0 commit comments

Comments
 (0)