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 <[email protected]>
---
 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

Reply via email to