Skip to content

Commit d30ad20

Browse files
committed
Merge branch 'mh/connect' into pu
Rewrite Git-URL parsing routine (hopefully) without changing any behaviour. Comments? * 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 f97556f + 052b49d commit d30ad20

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
@@ -343,18 +343,16 @@ static const char *ai_name(const struct addrinfo *ai)
343343
/*
344344
* Returns a connected socket() fd, or else die()s.
345345
*/
346-
static int git_tcp_connect_sock(char *host, int flags)
346+
static int git_tcp_connect_sock(const char *host, const char *port, int flags)
347347
{
348348
struct strbuf error_message = STRBUF_INIT;
349349
int sockfd = -1;
350-
const char *port = STR(DEFAULT_GIT_PORT);
351350
struct addrinfo hints, *ai0, *ai;
352351
int gai;
353352
int cnt = 0;
354353

355-
get_host_and_port(&host, &port);
356-
if (!*port)
357-
port = "<none>";
354+
if (!port)
355+
port = STR(DEFAULT_GIT_PORT);
358356

359357
memset(&hints, 0, sizeof(hints));
360358
if (flags & CONNECT_IPV4)
@@ -411,19 +409,19 @@ static int git_tcp_connect_sock(char *host, int flags)
411409
/*
412410
* Returns a connected socket() fd, or else die()s.
413411
*/
414-
static int git_tcp_connect_sock(char *host, int flags)
412+
static int git_tcp_connect_sock(const char *host, const char *port, int flags)
415413
{
416414
struct strbuf error_message = STRBUF_INIT;
417415
int sockfd = -1;
418-
const char *port = STR(DEFAULT_GIT_PORT);
419416
char *ep;
420417
struct hostent *he;
421418
struct sockaddr_in sa;
422419
char **ap;
423420
unsigned int nport;
424421
int cnt;
425422

426-
get_host_and_port(&host, &port);
423+
if (!port)
424+
port = STR(DEFAULT_GIT_PORT);
427425

428426
if (flags & CONNECT_VERBOSE)
429427
fprintf(stderr, "Looking up %s ... ", host);
@@ -482,9 +480,10 @@ static int git_tcp_connect_sock(char *host, int flags)
482480
#endif /* NO_IPV6 */
483481

484482

485-
static void git_tcp_connect(int fd[2], char *host, int flags)
483+
static void git_tcp_connect(int fd[2], const char *host, const char *port,
484+
int flags)
486485
{
487-
int sockfd = git_tcp_connect_sock(host, flags);
486+
int sockfd = git_tcp_connect_sock(host, port, flags);
488487

489488
fd[0] = sockfd;
490489
fd[1] = dup(sockfd);
@@ -550,18 +549,16 @@ static int git_use_proxy(const char *host)
550549
return (git_proxy_command && *git_proxy_command);
551550
}
552551

553-
static struct child_process *git_proxy_connect(int fd[2], char *host)
552+
static struct child_process *git_proxy_connect(int fd[2], const char *host,
553+
const char *port)
554554
{
555-
const char *port = STR(DEFAULT_GIT_PORT);
556555
struct child_process *proxy;
557556

558-
get_host_and_port(&host, &port);
559-
560557
proxy = xmalloc(sizeof(*proxy));
561558
child_process_init(proxy);
562559
argv_array_push(&proxy->args, git_proxy_command);
563560
argv_array_push(&proxy->args, host);
564-
argv_array_push(&proxy->args, port);
561+
argv_array_push(&proxy->args, port ? port : STR(DEFAULT_GIT_PORT));
565562
proxy->in = -1;
566563
proxy->out = -1;
567564
if (start_command(proxy))
@@ -591,11 +588,14 @@ static char *get_port(char *host)
591588
* Extract protocol and relevant parts from the specified connection URL.
592589
* The caller must free() the returned strings.
593590
*/
594-
static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
591+
static enum protocol parse_connect_url(const char *url_orig, char **ret_user,
592+
char **ret_host, char **ret_port,
595593
char **ret_path)
596594
{
597595
char *url;
598596
char *host, *path;
597+
const char *user = NULL;
598+
const char *port = NULL;
599599
char *end;
600600
int separator = '/';
601601
enum protocol protocol = PROTO_LOCAL;
@@ -618,10 +618,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
618618
}
619619
}
620620

621-
/*
622-
* Don't do destructive transforms as protocol code does
623-
* '[]' unwrapping in get_host_and_port()
624-
*/
621+
/* '[]' unwrapping is done in get_host_and_port() */
625622
end = host_end(&host, 0);
626623

627624
if (protocol == PROTO_LOCAL)
@@ -650,14 +647,40 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
650647
path = xstrdup(path);
651648
*end = '\0';
652649

650+
get_host_and_port(&host, &port);
651+
652+
if (*host) {
653+
/*
654+
* At this point, the host part may look like user@host:port.
655+
* As the `user` portion tends to be less strict than
656+
* `host:port`, we first put it out of the equation: since a
657+
* hostname cannot contain a '@', we start from the last '@'
658+
* in the string.
659+
*/
660+
char *end_user = strrchr(host, '@');
661+
if (end_user) {
662+
*end_user = '\0';
663+
user = host;
664+
host = end_user + 1;
665+
}
666+
}
667+
/*
668+
* get_host_and_port does not return a port in the [host:port]:path
669+
* case. In that case, it is called with "[host:port]" and returns
670+
* "host:port" and NULL. To support this undocumented legacy
671+
* (for ssh only) we still need to split the port.
672+
*/
673+
if (!port && protocol == PROTO_SSH)
674+
port = get_port(host);
675+
676+
*ret_user = user ? xstrdup(user) : NULL;
653677
*ret_host = xstrdup(host);
678+
*ret_port = port ? xstrdup(port) : NULL;
654679
*ret_path = path;
655680
free(url);
656681
return protocol;
657682
}
658683

659-
static struct child_process no_fork = CHILD_PROCESS_INIT;
660-
661684
static const char *get_ssh_command(void)
662685
{
663686
const char *ssh;
@@ -671,6 +694,63 @@ static const char *get_ssh_command(void)
671694
return NULL;
672695
}
673696

697+
static int prepare_ssh_command(struct argv_array *cmd, const char *user,
698+
const char *host, const char *port, int flags)
699+
{
700+
const char *ssh;
701+
int putty = 0, tortoiseplink = 0, use_shell = 1;
702+
transport_check_allowed("ssh");
703+
704+
ssh = get_ssh_command();
705+
if (!ssh) {
706+
const char *base;
707+
char *ssh_dup;
708+
709+
/*
710+
* GIT_SSH is the no-shell version of
711+
* GIT_SSH_COMMAND (and must remain so for
712+
* historical compatibility).
713+
*/
714+
use_shell = 0;
715+
716+
ssh = getenv("GIT_SSH");
717+
if (!ssh)
718+
ssh = "ssh";
719+
720+
ssh_dup = xstrdup(ssh);
721+
base = basename(ssh_dup);
722+
723+
tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
724+
!strcasecmp(base, "tortoiseplink.exe");
725+
putty = tortoiseplink ||
726+
!strcasecmp(base, "plink") ||
727+
!strcasecmp(base, "plink.exe");
728+
729+
free(ssh_dup);
730+
}
731+
732+
argv_array_push(cmd, ssh);
733+
if (flags & CONNECT_IPV4)
734+
argv_array_push(cmd, "-4");
735+
else if (flags & CONNECT_IPV6)
736+
argv_array_push(cmd, "-6");
737+
if (tortoiseplink)
738+
argv_array_push(cmd, "-batch");
739+
if (port) {
740+
/* P is for PuTTY, p is for OpenSSH */
741+
argv_array_push(cmd, putty ? "-P" : "-p");
742+
argv_array_push(cmd, port);
743+
}
744+
if (user)
745+
argv_array_pushf(cmd, "%s@%s", user, host);
746+
else
747+
argv_array_push(cmd, host);
748+
749+
return use_shell;
750+
}
751+
752+
static struct child_process no_fork = CHILD_PROCESS_INIT;
753+
674754
/*
675755
* This returns a dummy child_process if the transport protocol does not
676756
* need fork(2), or a struct child_process object if it does. Once done,
@@ -685,7 +765,7 @@ static const char *get_ssh_command(void)
685765
struct child_process *git_connect(int fd[2], const char *url,
686766
const char *prog, int flags)
687767
{
688-
char *hostandport, *path;
768+
char *user, *host, *port, *path;
689769
struct child_process *conn = &no_fork;
690770
enum protocol protocol;
691771
struct strbuf cmd = STRBUF_INIT;
@@ -695,11 +775,13 @@ struct child_process *git_connect(int fd[2], const char *url,
695775
*/
696776
signal(SIGCHLD, SIG_DFL);
697777

698-
protocol = parse_connect_url(url, &hostandport, &path);
699-
if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) {
778+
protocol = parse_connect_url(url, &user, &host, &port, &path);
779+
if (flags & CONNECT_DIAG_URL) {
700780
printf("Diag: url=%s\n", url ? url : "NULL");
701781
printf("Diag: protocol=%s\n", prot_name(protocol));
702-
printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL");
782+
printf("Diag: user=%s\n", user ? user : "NULL");
783+
printf("Diag: host=%s\n", host ? host : "NULL");
784+
printf("Diag: port=%s\n", port ? port : "NONE");
703785
printf("Diag: path=%s\n", path ? path : "NULL");
704786
conn = NULL;
705787
} else if (protocol == PROTO_GIT) {
@@ -708,21 +790,38 @@ struct child_process *git_connect(int fd[2], const char *url,
708790
* connect, unless the user has overridden us in
709791
* the environment.
710792
*/
711-
char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
712-
if (target_host)
713-
target_host = xstrdup(target_host);
714-
else
715-
target_host = xstrdup(hostandport);
793+
struct strbuf target_host = STRBUF_INIT;
794+
struct strbuf virtual_host = STRBUF_INIT;
795+
const char *colon = strchr(host, ':');
796+
char *override_vhost = getenv("GIT_OVERRIDE_VIRTUAL_HOST");
797+
798+
if (user)
799+
die("user@host is not allowed in git:// urls");
800+
801+
/* If the host contains a colon (ipv6 address), it needs to
802+
* be enclosed with square brackets. */
803+
if (colon)
804+
strbuf_addch(&target_host, '[');
805+
strbuf_addstr(&target_host, host);
806+
if (colon)
807+
strbuf_addch(&target_host, ']');
808+
if (port) {
809+
strbuf_addch(&target_host, ':');
810+
strbuf_addstr(&target_host, port);
811+
}
812+
813+
strbuf_addstr(&virtual_host, override_vhost ? override_vhost
814+
: target_host.buf);
716815

717816
transport_check_allowed("git");
718817

719818
/* These underlying connection commands die() if they
720819
* cannot connect.
721820
*/
722-
if (git_use_proxy(hostandport))
723-
conn = git_proxy_connect(fd, hostandport);
821+
if (git_use_proxy(target_host.buf))
822+
conn = git_proxy_connect(fd, host, port);
724823
else
725-
git_tcp_connect(fd, hostandport, flags);
824+
git_tcp_connect(fd, host, port, flags);
726825
/*
727826
* Separate original protocol components prog and path
728827
* from extended host header with a NUL byte.
@@ -733,8 +832,9 @@ struct child_process *git_connect(int fd[2], const char *url,
733832
packet_write(fd[1],
734833
"%s %s%chost=%s%c",
735834
prog, path, 0,
736-
target_host, 0);
737-
free(target_host);
835+
virtual_host.buf, 0);
836+
strbuf_release(&virtual_host);
837+
strbuf_release(&target_host);
738838
} else {
739839
conn = xmalloc(sizeof(*conn));
740840
child_process_init(conn);
@@ -745,74 +845,12 @@ struct child_process *git_connect(int fd[2], const char *url,
745845

746846
/* remove repo-local variables from the environment */
747847
conn->env = local_repo_env;
748-
conn->use_shell = 1;
749848
conn->in = conn->out = -1;
750849
if (protocol == PROTO_SSH) {
751-
const char *ssh;
752-
int putty = 0, tortoiseplink = 0;
753-
char *ssh_host = hostandport;
754-
const char *port = NULL;
755-
transport_check_allowed("ssh");
756-
get_host_and_port(&ssh_host, &port);
757-
758-
if (!port)
759-
port = get_port(ssh_host);
760-
761-
if (flags & CONNECT_DIAG_URL) {
762-
printf("Diag: url=%s\n", url ? url : "NULL");
763-
printf("Diag: protocol=%s\n", prot_name(protocol));
764-
printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL");
765-
printf("Diag: port=%s\n", port ? port : "NONE");
766-
printf("Diag: path=%s\n", path ? path : "NULL");
767-
768-
free(hostandport);
769-
free(path);
770-
free(conn);
771-
return NULL;
772-
}
773-
774-
ssh = get_ssh_command();
775-
if (!ssh) {
776-
const char *base;
777-
char *ssh_dup;
778-
779-
/*
780-
* GIT_SSH is the no-shell version of
781-
* GIT_SSH_COMMAND (and must remain so for
782-
* historical compatibility).
783-
*/
784-
conn->use_shell = 0;
785-
786-
ssh = getenv("GIT_SSH");
787-
if (!ssh)
788-
ssh = "ssh";
789-
790-
ssh_dup = xstrdup(ssh);
791-
base = basename(ssh_dup);
792-
793-
tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
794-
!strcasecmp(base, "tortoiseplink.exe");
795-
putty = tortoiseplink ||
796-
!strcasecmp(base, "plink") ||
797-
!strcasecmp(base, "plink.exe");
798-
799-
free(ssh_dup);
800-
}
801-
802-
argv_array_push(&conn->args, ssh);
803-
if (flags & CONNECT_IPV4)
804-
argv_array_push(&conn->args, "-4");
805-
else if (flags & CONNECT_IPV6)
806-
argv_array_push(&conn->args, "-6");
807-
if (tortoiseplink)
808-
argv_array_push(&conn->args, "-batch");
809-
if (port) {
810-
/* P is for PuTTY, p is for OpenSSH */
811-
argv_array_push(&conn->args, putty ? "-P" : "-p");
812-
argv_array_push(&conn->args, port);
813-
}
814-
argv_array_push(&conn->args, ssh_host);
850+
conn->use_shell = prepare_ssh_command(
851+
&conn->args, user, host, port, flags);
815852
} else {
853+
conn->use_shell = 1;
816854
transport_check_allowed("file");
817855
}
818856
argv_array_push(&conn->args, cmd.buf);
@@ -824,7 +862,9 @@ struct child_process *git_connect(int fd[2], const char *url,
824862
fd[1] = conn->in; /* write to child's stdin */
825863
strbuf_release(&cmd);
826864
}
827-
free(hostandport);
865+
free(user);
866+
free(host);
867+
free(port);
828868
free(path);
829869
return conn;
830870
}

0 commit comments

Comments
 (0)