fetch-pack: test explicitly that --all can fetch tag references pointing to non-commits
[gitweb.git] / connect.c
index fd7ffe1840e64417cf94d208840cf97c91e962b7..ea316c485ab3d0329caf6711b1f422ee298fb0b0 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -43,14 +43,14 @@ int check_ref_type(const struct ref *ref, int flags)
        return check_ref(ref->name, flags);
 }
 
-static void die_initial_contact(int got_at_least_one_head)
+static void die_initial_contact(int unexpected)
 {
-       if (got_at_least_one_head)
-               die("The remote end hung up upon initial contact");
+       if (unexpected)
+               die(_("The remote end hung up upon initial contact"));
        else
-               die("Could not read from remote repository.\n\n"
-                   "Please make sure you have the correct access rights\n"
-                   "and the repository exists.");
+               die(_("Could not read from remote repository.\n\n"
+                     "Please make sure you have the correct access rights\n"
+                     "and the repository exists."));
 }
 
 static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
@@ -115,10 +115,18 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                              struct sha1_array *shallow_points)
 {
        struct ref **orig_list = list;
-       int got_at_least_one_head = 0;
+
+       /*
+        * A hang-up after seeing some response from the other end
+        * means that it is unexpected, as we know the other end is
+        * willing to talk to us.  A hang-up before seeing any
+        * response does not necessarily mean an ACL problem, though.
+        */
+       int saw_response;
+       int got_dummy_ref_with_capabilities_declaration = 0;
 
        *list = NULL;
-       for (;;) {
+       for (saw_response = 0; ; saw_response = 1) {
                struct ref *ref;
                struct object_id old_oid;
                char *name;
@@ -131,7 +139,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                  PACKET_READ_GENTLE_ON_EOF |
                                  PACKET_READ_CHOMP_NEWLINE);
                if (len < 0)
-                       die_initial_contact(got_at_least_one_head);
+                       die_initial_contact(saw_response);
 
                if (!len)
                        break;
@@ -165,13 +173,25 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                        continue;
                }
 
+               if (!strcmp(name, "capabilities^{}")) {
+                       if (saw_response)
+                               die("protocol error: unexpected capabilities^{}");
+                       if (got_dummy_ref_with_capabilities_declaration)
+                               die("protocol error: multiple capabilities^{}");
+                       got_dummy_ref_with_capabilities_declaration = 1;
+                       continue;
+               }
+
                if (!check_ref(name, flags))
                        continue;
+
+               if (got_dummy_ref_with_capabilities_declaration)
+                       die("protocol error: unexpected ref after capabilities^{}");
+
                ref = alloc_ref(buffer + GIT_SHA1_HEXSZ + 1);
                oidcpy(&ref->old_oid, &old_oid);
                *list = ref;
                list = &ref->next;
-               got_at_least_one_head = 1;
        }
 
        annotate_refs_with_symref_info(*orig_list);
@@ -267,9 +287,9 @@ static enum protocol get_protocol(const char *name)
                return PROTO_SSH;
        if (!strcmp(name, "git"))
                return PROTO_GIT;
-       if (!strcmp(name, "git+ssh"))
+       if (!strcmp(name, "git+ssh")) /* deprecated - do not use */
                return PROTO_SSH;
-       if (!strcmp(name, "ssh+git"))
+       if (!strcmp(name, "ssh+git")) /* deprecated - do not use */
                return PROTO_SSH;
        if (!strcmp(name, "file"))
                return PROTO_FILE;
@@ -357,6 +377,10 @@ static int git_tcp_connect_sock(char *host, int flags)
                port = "<none>";
 
        memset(&hints, 0, sizeof(hints));
+       if (flags & CONNECT_IPV4)
+               hints.ai_family = AF_INET;
+       else if (flags & CONNECT_IPV6)
+               hints.ai_family = AF_INET6;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
 
@@ -553,6 +577,11 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)
 
        get_host_and_port(&host, &port);
 
+       if (looks_like_command_line_option(host))
+               die("strange hostname '%s' blocked", host);
+       if (looks_like_command_line_option(port))
+               die("strange port '%s' blocked", port);
+
        proxy = xmalloc(sizeof(*proxy));
        child_process_init(proxy);
        argv_array_push(&proxy->args, git_proxy_command);
@@ -654,6 +683,19 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
 
 static struct child_process no_fork = CHILD_PROCESS_INIT;
 
+static const char *get_ssh_command(void)
+{
+       const char *ssh;
+
+       if ((ssh = getenv("GIT_SSH_COMMAND")))
+               return ssh;
+
+       if (!git_config_get_string_const("core.sshcommand", &ssh))
+               return ssh;
+
+       return NULL;
+}
+
 /*
  * This returns a dummy child_process if the transport protocol does not
  * need fork(2), or a struct child_process object if it does.  Once done,
@@ -713,7 +755,7 @@ 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(fd[1],
+               packet_write_fmt(fd[1],
                             "%s %s%chost=%s%c",
                             prog, path, 0,
                             target_host, 0);
@@ -722,6 +764,9 @@ struct child_process *git_connect(int fd[2], const char *url,
                conn = xmalloc(sizeof(*conn));
                child_process_init(conn);
 
+               if (looks_like_command_line_option(path))
+                       die("strange pathname '%s' blocked", path);
+
                strbuf_addstr(&cmd, prog);
                strbuf_addch(&cmd, ' ');
                sq_quote_buf(&cmd, path);
@@ -754,7 +799,10 @@ struct child_process *git_connect(int fd[2], const char *url,
                                return NULL;
                        }
 
-                       ssh = getenv("GIT_SSH_COMMAND");
+                       if (looks_like_command_line_option(ssh_host))
+                               die("strange hostname '%s' blocked", ssh_host);
+
+                       ssh = get_ssh_command();
                        if (!ssh) {
                                const char *base;
                                char *ssh_dup;
@@ -783,6 +831,10 @@ struct child_process *git_connect(int fd[2], const char *url,
                        }
 
                        argv_array_push(&conn->args, ssh);
+                       if (flags & CONNECT_IPV4)
+                               argv_array_push(&conn->args, "-4");
+                       else if (flags & CONNECT_IPV6)
+                               argv_array_push(&conn->args, "-6");
                        if (tortoiseplink)
                                argv_array_push(&conn->args, "-batch");
                        if (port) {