Teach the connection logic to tell a serve that it understands protocol v2. This is done in 2 different ways for the built in protocols. 1. git:// A normal request is structured as "command path/to/repo\0host=..\0" and due to a bug in an old version of git-daemon 73bb33a94 (daemon: Strictly parse the "extra arg" part of the command, 2009-06-04) we aren't able to place any extra args (separated by NULs) besides the host. In order to get around this limitation put protocol version information after a second NUL byte so the request is structured like: "command path/to/repo\0host=..\0\0version=2\0". git-daemon can then parse out the version number and set GIT_PROTOCOL. 2. ssh://, file:// Set GIT_PROTOCOL envvar with the desired protocol version. The envvar can be sent across ssh by using '-o SendEnv=GIT_PROTOCOL' and having the server whitelist this envvar. Signed-off-by: Brandon Williams <bmwill@xxxxxxxxxx> --- connect.c | 31 ++++++++++++++++++++++++++----- daemon.c | 28 +++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/connect.c b/connect.c index 49b28b83b..d609267be 100644 --- a/connect.c +++ b/connect.c @@ -793,6 +793,7 @@ struct child_process *git_connect(int fd[2], const char *url, printf("Diag: path=%s\n", path ? path : "NULL"); conn = NULL; } else if (protocol == PROTO_GIT) { + struct strbuf request = STRBUF_INIT; /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in @@ -820,12 +821,23 @@ struct child_process *git_connect(int fd[2], const char *url, * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ - packet_write_fmt(fd[1], - "%s %s%chost=%s%c", - prog, path, 0, - target_host, 0); + strbuf_addf(&request, + "%s %s%chost=%s%c", + prog, path, 0, + target_host, 0); + + /* If using a new version put that stuff here after a second null byte */ + strbuf_addch(&request, '\0'); + strbuf_addf(&request, "version=%d%c", 2, '\0'); + /* subsequent supported versions can also be added */ + strbuf_addf(&request, "version=%d%c", 3, '\0'); + + packet_write(fd[1], request.buf, request.len); + free(target_host); + strbuf_release(&request); } else { + const char *const *var; conn = xmalloc(sizeof(*conn)); child_process_init(conn); @@ -837,7 +849,9 @@ struct child_process *git_connect(int fd[2], const char *url, sq_quote_buf(&cmd, path); /* remove repo-local variables from the environment */ - conn->env = local_repo_env; + for (var = local_repo_env; *var; var++) + argv_array_push(&conn->env_array, *var); + conn->use_shell = 1; conn->in = conn->out = -1; if (protocol == PROTO_SSH) { @@ -890,6 +904,12 @@ struct child_process *git_connect(int fd[2], const char *url, } argv_array_push(&conn->args, ssh); + + /* protocol v2! */ + argv_array_push(&conn->args, "-o"); + argv_array_push(&conn->args, "SendEnv=GIT_PROTOCOL"); + argv_array_push(&conn->env_array, "GIT_PROTOCOL=2"); + if (flags & CONNECT_IPV4) argv_array_push(&conn->args, "-4"); else if (flags & CONNECT_IPV6) @@ -904,6 +924,7 @@ struct child_process *git_connect(int fd[2], const char *url, argv_array_push(&conn->args, ssh_host); } else { transport_check_allowed("file"); + argv_array_push(&conn->env_array, "GIT_PROTOCOL=2"); } argv_array_push(&conn->args, cmd.buf); diff --git a/daemon.c b/daemon.c index 30747075f..76a7b2d64 100644 --- a/daemon.c +++ b/daemon.c @@ -574,7 +574,7 @@ static void canonicalize_client(struct strbuf *out, const char *in) /* * Read the host as supplied by the client connection. */ -static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen) +static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen) { char *val; int vallen; @@ -602,6 +602,22 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen) if (extra_args < end && *extra_args) die("Invalid request"); } + + return extra_args; +} + +static void parse_extra_args(const char *extra_args, int buflen) +{ + const char *end = extra_args + buflen; + + for (; extra_args < end; extra_args += strlen(extra_args) + 1) { + const char *arg = extra_args; + if (skip_prefix(arg, "version=", &arg)) + fprintf(stderr, "VERSION=%s\n", arg); + + fprintf(stderr, "%s\n", extra_args); + + } } /* @@ -716,8 +732,14 @@ static int execute(void) pktlen--; } - if (len != pktlen) - parse_host_arg(&hi, line + len + 1, pktlen - len - 1); + if (len != pktlen) { + const char *extra_args; + /* retreive host */ + extra_args = parse_host_arg(&hi, line + len + 1, pktlen - len - 1); + + /* determine version */ + parse_extra_args(extra_args + 1, pktlen - (extra_args - line) - 1); + } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); -- 2.14.1.342.g6490525c54-goog