diff options
Diffstat (limited to 'third_party/git/connect.c')
-rw-r--r-- | third_party/git/connect.c | 1428 |
1 files changed, 0 insertions, 1428 deletions
diff --git a/third_party/git/connect.c b/third_party/git/connect.c deleted file mode 100644 index 8b8f56cf6d23..000000000000 --- a/third_party/git/connect.c +++ /dev/null @@ -1,1428 +0,0 @@ -#include "git-compat-util.h" -#include "cache.h" -#include "config.h" -#include "pkt-line.h" -#include "quote.h" -#include "refs.h" -#include "run-command.h" -#include "remote.h" -#include "connect.h" -#include "url.h" -#include "string-list.h" -#include "oid-array.h" -#include "transport.h" -#include "strbuf.h" -#include "version.h" -#include "protocol.h" -#include "alias.h" - -static char *server_capabilities_v1; -static struct strvec server_capabilities_v2 = STRVEC_INIT; -static const char *next_server_feature_value(const char *feature, int *len, int *offset); - -static int check_ref(const char *name, unsigned int flags) -{ - if (!flags) - return 1; - - if (!skip_prefix(name, "refs/", &name)) - return 0; - - /* REF_NORMAL means that we don't want the magic fake tag refs */ - if ((flags & REF_NORMAL) && check_refname_format(name, 0)) - return 0; - - /* REF_HEADS means that we want regular branch heads */ - if ((flags & REF_HEADS) && starts_with(name, "heads/")) - return 1; - - /* REF_TAGS means that we want tags */ - if ((flags & REF_TAGS) && starts_with(name, "tags/")) - return 1; - - /* All type bits clear means that we are ok with anything */ - return !(flags & ~REF_NORMAL); -} - -int check_ref_type(const struct ref *ref, int flags) -{ - return check_ref(ref->name, flags); -} - -static NORETURN void die_initial_contact(int unexpected) -{ - /* - * 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. - */ - 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.")); -} - -/* Checks if the server supports the capability 'c' */ -int server_supports_v2(const char *c, int die_on_error) -{ - int i; - - for (i = 0; i < server_capabilities_v2.nr; i++) { - const char *out; - if (skip_prefix(server_capabilities_v2.v[i], c, &out) && - (!*out || *out == '=')) - return 1; - } - - if (die_on_error) - die(_("server doesn't support '%s'"), c); - - return 0; -} - -int server_feature_v2(const char *c, const char **v) -{ - int i; - - for (i = 0; i < server_capabilities_v2.nr; i++) { - const char *out; - if (skip_prefix(server_capabilities_v2.v[i], c, &out) && - (*out == '=')) { - *v = out + 1; - return 1; - } - } - return 0; -} - -int server_supports_feature(const char *c, const char *feature, - int die_on_error) -{ - int i; - - for (i = 0; i < server_capabilities_v2.nr; i++) { - const char *out; - if (skip_prefix(server_capabilities_v2.v[i], c, &out) && - (!*out || *(out++) == '=')) { - if (parse_feature_request(out, feature)) - return 1; - else - break; - } - } - - if (die_on_error) - die(_("server doesn't support feature '%s'"), feature); - - return 0; -} - -static void process_capabilities_v2(struct packet_reader *reader) -{ - while (packet_reader_read(reader) == PACKET_READ_NORMAL) - strvec_push(&server_capabilities_v2, reader->line); - - if (reader->status != PACKET_READ_FLUSH) - die(_("expected flush after capabilities")); -} - -enum protocol_version discover_version(struct packet_reader *reader) -{ - enum protocol_version version = protocol_unknown_version; - - /* - * Peek the first line of the server's response to - * determine the protocol version the server is speaking. - */ - switch (packet_reader_peek(reader)) { - case PACKET_READ_EOF: - die_initial_contact(0); - case PACKET_READ_FLUSH: - case PACKET_READ_DELIM: - case PACKET_READ_RESPONSE_END: - version = protocol_v0; - break; - case PACKET_READ_NORMAL: - version = determine_protocol_version_client(reader->line); - break; - } - - switch (version) { - case protocol_v2: - process_capabilities_v2(reader); - break; - case protocol_v1: - /* Read the peeked version line */ - packet_reader_read(reader); - break; - case protocol_v0: - break; - case protocol_unknown_version: - BUG("unknown protocol version"); - } - - return version; -} - -static void parse_one_symref_info(struct string_list *symref, const char *val, int len) -{ - char *sym, *target; - struct string_list_item *item; - - if (!len) - return; /* just "symref" */ - /* e.g. "symref=HEAD:refs/heads/master" */ - sym = xmemdupz(val, len); - target = strchr(sym, ':'); - if (!target) - /* just "symref=something" */ - goto reject; - *(target++) = '\0'; - if (check_refname_format(sym, REFNAME_ALLOW_ONELEVEL) || - check_refname_format(target, REFNAME_ALLOW_ONELEVEL)) - /* "symref=bogus:pair */ - goto reject; - item = string_list_append_nodup(symref, sym); - item->util = target; - return; -reject: - free(sym); - return; -} - -static void annotate_refs_with_symref_info(struct ref *ref) -{ - struct string_list symref = STRING_LIST_INIT_DUP; - int offset = 0; - - while (1) { - int len; - const char *val; - - val = next_server_feature_value("symref", &len, &offset); - if (!val) - break; - parse_one_symref_info(&symref, val, len); - } - string_list_sort(&symref); - - for (; ref; ref = ref->next) { - struct string_list_item *item; - item = string_list_lookup(&symref, ref->name); - if (!item) - continue; - ref->symref = xstrdup((char *)item->util); - } - string_list_clear(&symref, 0); -} - -static void process_capabilities(struct packet_reader *reader, int *linelen) -{ - const char *feat_val; - int feat_len; - const char *line = reader->line; - int nul_location = strlen(line); - if (nul_location == *linelen) - return; - server_capabilities_v1 = xstrdup(line + nul_location + 1); - *linelen = nul_location; - - feat_val = server_feature_value("object-format", &feat_len); - if (feat_val) { - char *hash_name = xstrndup(feat_val, feat_len); - int hash_algo = hash_algo_by_name(hash_name); - if (hash_algo != GIT_HASH_UNKNOWN) - reader->hash_algo = &hash_algos[hash_algo]; - free(hash_name); - } else { - reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; - } -} - -static int process_dummy_ref(const struct packet_reader *reader) -{ - const char *line = reader->line; - struct object_id oid; - const char *name; - - if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo)) - return 0; - if (*name != ' ') - return 0; - name++; - - return oideq(&null_oid, &oid) && !strcmp(name, "capabilities^{}"); -} - -static void check_no_capabilities(const char *line, int len) -{ - if (strlen(line) != len) - warning(_("ignoring capabilities after first line '%s'"), - line + strlen(line)); -} - -static int process_ref(const struct packet_reader *reader, int len, - struct ref ***list, unsigned int flags, - struct oid_array *extra_have) -{ - const char *line = reader->line; - struct object_id old_oid; - const char *name; - - if (parse_oid_hex_algop(line, &old_oid, &name, reader->hash_algo)) - return 0; - if (*name != ' ') - return 0; - name++; - - if (extra_have && !strcmp(name, ".have")) { - oid_array_append(extra_have, &old_oid); - } else if (!strcmp(name, "capabilities^{}")) { - die(_("protocol error: unexpected capabilities^{}")); - } else if (check_ref(name, flags)) { - struct ref *ref = alloc_ref(name); - oidcpy(&ref->old_oid, &old_oid); - **list = ref; - *list = &ref->next; - } - check_no_capabilities(line, len); - return 1; -} - -static int process_shallow(const struct packet_reader *reader, int len, - struct oid_array *shallow_points) -{ - const char *line = reader->line; - const char *arg; - struct object_id old_oid; - - if (!skip_prefix(line, "shallow ", &arg)) - return 0; - - if (get_oid_hex_algop(arg, &old_oid, reader->hash_algo)) - die(_("protocol error: expected shallow sha-1, got '%s'"), arg); - if (!shallow_points) - die(_("repository on the other end cannot be shallow")); - oid_array_append(shallow_points, &old_oid); - check_no_capabilities(line, len); - return 1; -} - -enum get_remote_heads_state { - EXPECTING_FIRST_REF = 0, - EXPECTING_REF, - EXPECTING_SHALLOW, - EXPECTING_DONE, -}; - -/* - * Read all the refs from the other end - */ -struct ref **get_remote_heads(struct packet_reader *reader, - struct ref **list, unsigned int flags, - struct oid_array *extra_have, - struct oid_array *shallow_points) -{ - struct ref **orig_list = list; - int len = 0; - enum get_remote_heads_state state = EXPECTING_FIRST_REF; - - *list = NULL; - - while (state != EXPECTING_DONE) { - switch (packet_reader_read(reader)) { - case PACKET_READ_EOF: - die_initial_contact(1); - case PACKET_READ_NORMAL: - len = reader->pktlen; - break; - case PACKET_READ_FLUSH: - state = EXPECTING_DONE; - break; - case PACKET_READ_DELIM: - case PACKET_READ_RESPONSE_END: - die(_("invalid packet")); - } - - switch (state) { - case EXPECTING_FIRST_REF: - process_capabilities(reader, &len); - if (process_dummy_ref(reader)) { - state = EXPECTING_SHALLOW; - break; - } - state = EXPECTING_REF; - /* fallthrough */ - case EXPECTING_REF: - if (process_ref(reader, len, &list, flags, extra_have)) - break; - state = EXPECTING_SHALLOW; - /* fallthrough */ - case EXPECTING_SHALLOW: - if (process_shallow(reader, len, shallow_points)) - break; - die(_("protocol error: unexpected '%s'"), reader->line); - case EXPECTING_DONE: - break; - } - } - - annotate_refs_with_symref_info(*orig_list); - - return list; -} - -/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */ -static int process_ref_v2(struct packet_reader *reader, struct ref ***list) -{ - int ret = 1; - int i = 0; - struct object_id old_oid; - struct ref *ref; - struct string_list line_sections = STRING_LIST_INIT_DUP; - const char *end; - const char *line = reader->line; - - /* - * Ref lines have a number of fields which are space deliminated. The - * first field is the OID of the ref. The second field is the ref - * name. Subsequent fields (symref-target and peeled) are optional and - * don't have a particular order. - */ - if (string_list_split(&line_sections, line, ' ', -1) < 2) { - ret = 0; - goto out; - } - - if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) || - *end) { - ret = 0; - goto out; - } - - ref = alloc_ref(line_sections.items[i++].string); - - memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz); - **list = ref; - *list = &ref->next; - - for (; i < line_sections.nr; i++) { - const char *arg = line_sections.items[i].string; - if (skip_prefix(arg, "symref-target:", &arg)) - ref->symref = xstrdup(arg); - - if (skip_prefix(arg, "peeled:", &arg)) { - struct object_id peeled_oid; - char *peeled_name; - struct ref *peeled; - if (parse_oid_hex_algop(arg, &peeled_oid, &end, - reader->hash_algo) || *end) { - ret = 0; - goto out; - } - - peeled_name = xstrfmt("%s^{}", ref->name); - peeled = alloc_ref(peeled_name); - - memcpy(peeled->old_oid.hash, peeled_oid.hash, - reader->hash_algo->rawsz); - **list = peeled; - *list = &peeled->next; - - free(peeled_name); - } - } - -out: - string_list_clear(&line_sections, 0); - return ret; -} - -void check_stateless_delimiter(int stateless_rpc, - struct packet_reader *reader, - const char *error) -{ - if (!stateless_rpc) - return; /* not in stateless mode, no delimiter expected */ - if (packet_reader_read(reader) != PACKET_READ_RESPONSE_END) - die("%s", error); -} - -struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, - struct ref **list, int for_push, - const struct strvec *ref_prefixes, - const struct string_list *server_options, - int stateless_rpc) -{ - int i; - const char *hash_name; - *list = NULL; - - if (server_supports_v2("ls-refs", 1)) - packet_write_fmt(fd_out, "command=ls-refs\n"); - - if (server_supports_v2("agent", 0)) - packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized()); - - if (server_feature_v2("object-format", &hash_name)) { - int hash_algo = hash_algo_by_name(hash_name); - if (hash_algo == GIT_HASH_UNKNOWN) - die(_("unknown object format '%s' specified by server"), hash_name); - reader->hash_algo = &hash_algos[hash_algo]; - packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name); - } else { - reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; - } - - if (server_options && server_options->nr && - server_supports_v2("server-option", 1)) - for (i = 0; i < server_options->nr; i++) - packet_write_fmt(fd_out, "server-option=%s", - server_options->items[i].string); - - packet_delim(fd_out); - /* When pushing we don't want to request the peeled tags */ - if (!for_push) - packet_write_fmt(fd_out, "peel\n"); - packet_write_fmt(fd_out, "symrefs\n"); - for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) { - packet_write_fmt(fd_out, "ref-prefix %s\n", - ref_prefixes->v[i]); - } - packet_flush(fd_out); - - /* Process response from server */ - while (packet_reader_read(reader) == PACKET_READ_NORMAL) { - if (!process_ref_v2(reader, &list)) - die(_("invalid ls-refs response: %s"), reader->line); - } - - if (reader->status != PACKET_READ_FLUSH) - die(_("expected flush after ref listing")); - - check_stateless_delimiter(stateless_rpc, reader, - _("expected response end packet after ref listing")); - - return list; -} - -const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset) -{ - int len; - - if (!feature_list) - return NULL; - - len = strlen(feature); - if (offset) - feature_list += *offset; - while (*feature_list) { - const char *found = strstr(feature_list, feature); - if (!found) - return NULL; - if (feature_list == found || isspace(found[-1])) { - const char *value = found + len; - /* feature with no value (e.g., "thin-pack") */ - if (!*value || isspace(*value)) { - if (lenp) - *lenp = 0; - return value; - } - /* feature with a value (e.g., "agent=git/1.2.3") */ - else if (*value == '=') { - int end; - - value++; - end = strcspn(value, " \t\n"); - if (lenp) - *lenp = end; - if (offset) - *offset = value + end - feature_list; - return value; - } - /* - * otherwise we matched a substring of another feature; - * keep looking - */ - } - feature_list = found + 1; - } - return NULL; -} - -int server_supports_hash(const char *desired, int *feature_supported) -{ - int offset = 0; - int len; - const char *hash; - - hash = next_server_feature_value("object-format", &len, &offset); - if (feature_supported) - *feature_supported = !!hash; - if (!hash) { - hash = hash_algos[GIT_HASH_SHA1].name; - len = strlen(hash); - } - while (hash) { - if (!xstrncmpz(desired, hash, len)) - return 1; - - hash = next_server_feature_value("object-format", &len, &offset); - } - return 0; -} - -int parse_feature_request(const char *feature_list, const char *feature) -{ - return !!parse_feature_value(feature_list, feature, NULL, NULL); -} - -static const char *next_server_feature_value(const char *feature, int *len, int *offset) -{ - return parse_feature_value(server_capabilities_v1, feature, len, offset); -} - -const char *server_feature_value(const char *feature, int *len) -{ - return parse_feature_value(server_capabilities_v1, feature, len, NULL); -} - -int server_supports(const char *feature) -{ - return !!server_feature_value(feature, NULL); -} - -enum protocol { - PROTO_LOCAL = 1, - PROTO_FILE, - PROTO_SSH, - PROTO_GIT -}; - -int url_is_local_not_ssh(const char *url) -{ - const char *colon = strchr(url, ':'); - const char *slash = strchr(url, '/'); - return !colon || (slash && slash < colon) || - (has_dos_drive_prefix(url) && is_valid_path(url)); -} - -static const char *prot_name(enum protocol protocol) -{ - switch (protocol) { - case PROTO_LOCAL: - case PROTO_FILE: - return "file"; - case PROTO_SSH: - return "ssh"; - case PROTO_GIT: - return "git"; - default: - return "unknown protocol"; - } -} - -static enum protocol get_protocol(const char *name) -{ - if (!strcmp(name, "ssh")) - return PROTO_SSH; - if (!strcmp(name, "git")) - return PROTO_GIT; - if (!strcmp(name, "git+ssh")) /* deprecated - do not use */ - return PROTO_SSH; - if (!strcmp(name, "ssh+git")) /* deprecated - do not use */ - return PROTO_SSH; - if (!strcmp(name, "file")) - return PROTO_FILE; - die(_("protocol '%s' is not supported"), name); -} - -static char *host_end(char **hoststart, int removebrackets) -{ - char *host = *hoststart; - char *end; - char *start = strstr(host, "@["); - if (start) - start++; /* Jump over '@' */ - else - start = host; - if (start[0] == '[') { - end = strchr(start + 1, ']'); - if (end) { - if (removebrackets) { - *end = 0; - memmove(start, start + 1, end - start); - end++; - } - } else - end = host; - } else - end = host; - return end; -} - -#define STR_(s) # s -#define STR(s) STR_(s) - -static void get_host_and_port(char **host, const char **port) -{ - char *colon, *end; - end = host_end(host, 1); - colon = strchr(end, ':'); - if (colon) { - long portnr = strtol(colon + 1, &end, 10); - if (end != colon + 1 && *end == '\0' && 0 <= portnr && portnr < 65536) { - *colon = 0; - *port = colon + 1; - } else if (!colon[1]) { - *colon = 0; - } - } -} - -static void enable_keepalive(int sockfd) -{ - int ka = 1; - - if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) - error_errno(_("unable to set SO_KEEPALIVE on socket")); -} - -#ifndef NO_IPV6 - -static const char *ai_name(const struct addrinfo *ai) -{ - static char addr[NI_MAXHOST]; - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0, - NI_NUMERICHOST) != 0) - xsnprintf(addr, sizeof(addr), "(unknown)"); - - return addr; -} - -/* - * Returns a connected socket() fd, or else die()s. - */ -static int git_tcp_connect_sock(char *host, int flags) -{ - struct strbuf error_message = STRBUF_INIT; - int sockfd = -1; - const char *port = STR(DEFAULT_GIT_PORT); - struct addrinfo hints, *ai0, *ai; - int gai; - int cnt = 0; - - get_host_and_port(&host, &port); - if (!*port) - 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; - - if (flags & CONNECT_VERBOSE) - fprintf(stderr, _("Looking up %s ... "), host); - - gai = getaddrinfo(host, port, &hints, &ai); - if (gai) - die(_("unable to look up %s (port %s) (%s)"), host, port, gai_strerror(gai)); - - if (flags & CONNECT_VERBOSE) - /* TRANSLATORS: this is the end of "Looking up %s ... " */ - fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); - - for (ai0 = ai; ai; ai = ai->ai_next, cnt++) { - sockfd = socket(ai->ai_family, - ai->ai_socktype, ai->ai_protocol); - if ((sockfd < 0) || - (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) { - strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n", - host, cnt, ai_name(ai), strerror(errno)); - if (0 <= sockfd) - close(sockfd); - sockfd = -1; - continue; - } - if (flags & CONNECT_VERBOSE) - fprintf(stderr, "%s ", ai_name(ai)); - break; - } - - freeaddrinfo(ai0); - - if (sockfd < 0) - die(_("unable to connect to %s:\n%s"), host, error_message.buf); - - enable_keepalive(sockfd); - - if (flags & CONNECT_VERBOSE) - /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ - fprintf_ln(stderr, _("done.")); - - strbuf_release(&error_message); - - return sockfd; -} - -#else /* NO_IPV6 */ - -/* - * Returns a connected socket() fd, or else die()s. - */ -static int git_tcp_connect_sock(char *host, int flags) -{ - struct strbuf error_message = STRBUF_INIT; - int sockfd = -1; - const char *port = STR(DEFAULT_GIT_PORT); - char *ep; - struct hostent *he; - struct sockaddr_in sa; - char **ap; - unsigned int nport; - int cnt; - - get_host_and_port(&host, &port); - - if (flags & CONNECT_VERBOSE) - fprintf(stderr, _("Looking up %s ... "), host); - - he = gethostbyname(host); - if (!he) - die(_("unable to look up %s (%s)"), host, hstrerror(h_errno)); - nport = strtoul(port, &ep, 10); - if ( ep == port || *ep ) { - /* Not numeric */ - struct servent *se = getservbyname(port,"tcp"); - if ( !se ) - die(_("unknown port %s"), port); - nport = se->s_port; - } - - if (flags & CONNECT_VERBOSE) - /* TRANSLATORS: this is the end of "Looking up %s ... " */ - fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); - - for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) { - memset(&sa, 0, sizeof sa); - sa.sin_family = he->h_addrtype; - sa.sin_port = htons(nport); - memcpy(&sa.sin_addr, *ap, he->h_length); - - sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); - if ((sockfd < 0) || - connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { - strbuf_addf(&error_message, "%s[%d: %s]: errno=%s\n", - host, - cnt, - inet_ntoa(*(struct in_addr *)&sa.sin_addr), - strerror(errno)); - if (0 <= sockfd) - close(sockfd); - sockfd = -1; - continue; - } - if (flags & CONNECT_VERBOSE) - fprintf(stderr, "%s ", - inet_ntoa(*(struct in_addr *)&sa.sin_addr)); - break; - } - - if (sockfd < 0) - die(_("unable to connect to %s:\n%s"), host, error_message.buf); - - enable_keepalive(sockfd); - - if (flags & CONNECT_VERBOSE) - /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ - fprintf_ln(stderr, _("done.")); - - return sockfd; -} - -#endif /* NO_IPV6 */ - - -/* - * Dummy child_process returned by git_connect() if the transport protocol - * does not need fork(2). - */ -static struct child_process no_fork = CHILD_PROCESS_INIT; - -int git_connection_is_socket(struct child_process *conn) -{ - return conn == &no_fork; -} - -static struct child_process *git_tcp_connect(int fd[2], char *host, int flags) -{ - int sockfd = git_tcp_connect_sock(host, flags); - - fd[0] = sockfd; - fd[1] = dup(sockfd); - - return &no_fork; -} - - -static char *git_proxy_command; - -static int git_proxy_command_options(const char *var, const char *value, - void *cb) -{ - if (!strcmp(var, "core.gitproxy")) { - const char *for_pos; - int matchlen = -1; - int hostlen; - const char *rhost_name = cb; - int rhost_len = strlen(rhost_name); - - if (git_proxy_command) - return 0; - if (!value) - return config_error_nonbool(var); - /* [core] - * ;# matches www.kernel.org as well - * gitproxy = netcatter-1 for kernel.org - * gitproxy = netcatter-2 for sample.xz - * gitproxy = netcatter-default - */ - for_pos = strstr(value, " for "); - if (!for_pos) - /* matches everybody */ - matchlen = strlen(value); - else { - hostlen = strlen(for_pos + 5); - if (rhost_len < hostlen) - matchlen = -1; - else if (!strncmp(for_pos + 5, - rhost_name + rhost_len - hostlen, - hostlen) && - ((rhost_len == hostlen) || - rhost_name[rhost_len - hostlen -1] == '.')) - matchlen = for_pos - value; - else - matchlen = -1; - } - if (0 <= matchlen) { - /* core.gitproxy = none for kernel.org */ - if (matchlen == 4 && - !memcmp(value, "none", 4)) - matchlen = 0; - git_proxy_command = xmemdupz(value, matchlen); - } - return 0; - } - - return git_default_config(var, value, cb); -} - -static int git_use_proxy(const char *host) -{ - git_proxy_command = getenv("GIT_PROXY_COMMAND"); - git_config(git_proxy_command_options, (void*)host); - return (git_proxy_command && *git_proxy_command); -} - -static struct child_process *git_proxy_connect(int fd[2], char *host) -{ - const char *port = STR(DEFAULT_GIT_PORT); - struct child_process *proxy; - - 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); - strvec_push(&proxy->args, git_proxy_command); - strvec_push(&proxy->args, host); - strvec_push(&proxy->args, port); - proxy->in = -1; - proxy->out = -1; - if (start_command(proxy)) - die(_("cannot start proxy %s"), git_proxy_command); - fd[0] = proxy->out; /* read from proxy stdout */ - fd[1] = proxy->in; /* write to proxy stdin */ - return proxy; -} - -static char *get_port(char *host) -{ - char *end; - char *p = strchr(host, ':'); - - if (p) { - long port = strtol(p + 1, &end, 10); - if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) { - *p = '\0'; - return p+1; - } - } - - return NULL; -} - -/* - * Extract protocol and relevant parts from the specified connection URL. - * The caller must free() the returned strings. - */ -static enum protocol parse_connect_url(const char *url_orig, char **ret_host, - char **ret_path) -{ - char *url; - char *host, *path; - char *end; - int separator = '/'; - enum protocol protocol = PROTO_LOCAL; - - if (is_url(url_orig)) - url = url_decode(url_orig); - else - url = xstrdup(url_orig); - - host = strstr(url, "://"); - if (host) { - *host = '\0'; - protocol = get_protocol(url); - host += 3; - } else { - host = url; - if (!url_is_local_not_ssh(url)) { - protocol = PROTO_SSH; - separator = ':'; - } - } - - /* - * Don't do destructive transforms as protocol code does - * '[]' unwrapping in get_host_and_port() - */ - end = host_end(&host, 0); - - if (protocol == PROTO_LOCAL) - path = end; - else if (protocol == PROTO_FILE && *host != '/' && - !has_dos_drive_prefix(host) && - offset_1st_component(host - 2) > 1) - path = host - 2; /* include the leading "//" */ - else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) - path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ - else - path = strchr(end, separator); - - if (!path || !*path) - die(_("no path specified; see 'git help pull' for valid url syntax")); - - /* - * null-terminate hostname and point path to ~ for URL's like this: - * ssh://host.xz/~user/repo - */ - - end = path; /* Need to \0 terminate host here */ - if (separator == ':') - path++; /* path starts after ':' */ - if (protocol == PROTO_GIT || protocol == PROTO_SSH) { - if (path[1] == '~') - path++; - } - - path = xstrdup(path); - *end = '\0'; - - *ret_host = xstrdup(host); - *ret_path = path; - free(url); - return protocol; -} - -static const char *get_ssh_command(void) -{ - const char *ssh; - - if ((ssh = getenv("GIT_SSH_COMMAND"))) - return ssh; - - if (!git_config_get_string_tmp("core.sshcommand", &ssh)) - return ssh; - - return NULL; -} - -enum ssh_variant { - VARIANT_AUTO, - VARIANT_SIMPLE, - VARIANT_SSH, - VARIANT_PLINK, - VARIANT_PUTTY, - VARIANT_TORTOISEPLINK, -}; - -static void override_ssh_variant(enum ssh_variant *ssh_variant) -{ - const char *variant = getenv("GIT_SSH_VARIANT"); - - if (!variant && git_config_get_string_tmp("ssh.variant", &variant)) - return; - - if (!strcmp(variant, "auto")) - *ssh_variant = VARIANT_AUTO; - else if (!strcmp(variant, "plink")) - *ssh_variant = VARIANT_PLINK; - else if (!strcmp(variant, "putty")) - *ssh_variant = VARIANT_PUTTY; - else if (!strcmp(variant, "tortoiseplink")) - *ssh_variant = VARIANT_TORTOISEPLINK; - else if (!strcmp(variant, "simple")) - *ssh_variant = VARIANT_SIMPLE; - else - *ssh_variant = VARIANT_SSH; -} - -static enum ssh_variant determine_ssh_variant(const char *ssh_command, - int is_cmdline) -{ - enum ssh_variant ssh_variant = VARIANT_AUTO; - const char *variant; - char *p = NULL; - - override_ssh_variant(&ssh_variant); - - if (ssh_variant != VARIANT_AUTO) - return ssh_variant; - - if (!is_cmdline) { - p = xstrdup(ssh_command); - variant = basename(p); - } else { - const char **ssh_argv; - - p = xstrdup(ssh_command); - if (split_cmdline(p, &ssh_argv) > 0) { - variant = basename((char *)ssh_argv[0]); - /* - * At this point, variant points into the buffer - * referenced by p, hence we do not need ssh_argv - * any longer. - */ - free(ssh_argv); - } else { - free(p); - return ssh_variant; - } - } - - if (!strcasecmp(variant, "ssh") || - !strcasecmp(variant, "ssh.exe")) - ssh_variant = VARIANT_SSH; - else if (!strcasecmp(variant, "plink") || - !strcasecmp(variant, "plink.exe")) - ssh_variant = VARIANT_PLINK; - else if (!strcasecmp(variant, "tortoiseplink") || - !strcasecmp(variant, "tortoiseplink.exe")) - ssh_variant = VARIANT_TORTOISEPLINK; - - free(p); - return ssh_variant; -} - -/* - * Open a connection using Git's native protocol. - * - * The caller is responsible for freeing hostandport, but this function may - * modify it (for example, to truncate it to remove the port part). - */ -static struct child_process *git_connect_git(int fd[2], char *hostandport, - const char *path, const char *prog, - enum protocol_version version, - int flags) -{ - struct child_process *conn; - struct strbuf request = STRBUF_INIT; - /* - * Set up virtual host information based on where we will - * connect, unless the user has overridden us in - * the environment. - */ - char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); - if (target_host) - target_host = xstrdup(target_host); - else - target_host = xstrdup(hostandport); - - transport_check_allowed("git"); - - /* - * These underlying connection commands die() if they - * cannot connect. - */ - if (git_use_proxy(hostandport)) - conn = git_proxy_connect(fd, hostandport); - else - conn = git_tcp_connect(fd, hostandport, flags); - /* - * Separate original protocol components prog and path - * from extended host header with a NUL byte. - * - * Note: Do not add any other headers here! Doing so - * will cause older git-daemon servers to crash. - */ - 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 */ - if (version > 0) { - strbuf_addch(&request, '\0'); - strbuf_addf(&request, "version=%d%c", - version, '\0'); - } - - packet_write(fd[1], request.buf, request.len); - - free(target_host); - strbuf_release(&request); - return conn; -} - -/* - * Append the appropriate environment variables to `env` and options to - * `args` for running ssh in Git's SSH-tunneled transport. - */ -static void push_ssh_options(struct strvec *args, struct strvec *env, - enum ssh_variant variant, const char *port, - enum protocol_version version, int flags) -{ - if (variant == VARIANT_SSH && - version > 0) { - strvec_push(args, "-o"); - strvec_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT); - strvec_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d", - version); - } - - if (flags & CONNECT_IPV4) { - switch (variant) { - case VARIANT_AUTO: - BUG("VARIANT_AUTO passed to push_ssh_options"); - case VARIANT_SIMPLE: - die(_("ssh variant 'simple' does not support -4")); - case VARIANT_SSH: - case VARIANT_PLINK: - case VARIANT_PUTTY: - case VARIANT_TORTOISEPLINK: - strvec_push(args, "-4"); - } - } else if (flags & CONNECT_IPV6) { - switch (variant) { - case VARIANT_AUTO: - BUG("VARIANT_AUTO passed to push_ssh_options"); - case VARIANT_SIMPLE: - die(_("ssh variant 'simple' does not support -6")); - case VARIANT_SSH: - case VARIANT_PLINK: - case VARIANT_PUTTY: - case VARIANT_TORTOISEPLINK: - strvec_push(args, "-6"); - } - } - - if (variant == VARIANT_TORTOISEPLINK) - strvec_push(args, "-batch"); - - if (port) { - switch (variant) { - case VARIANT_AUTO: - BUG("VARIANT_AUTO passed to push_ssh_options"); - case VARIANT_SIMPLE: - die(_("ssh variant 'simple' does not support setting port")); - case VARIANT_SSH: - strvec_push(args, "-p"); - break; - case VARIANT_PLINK: - case VARIANT_PUTTY: - case VARIANT_TORTOISEPLINK: - strvec_push(args, "-P"); - } - - strvec_push(args, port); - } -} - -/* Prepare a child_process for use by Git's SSH-tunneled transport. */ -static void fill_ssh_args(struct child_process *conn, const char *ssh_host, - const char *port, enum protocol_version version, - int flags) -{ - const char *ssh; - enum ssh_variant variant; - - if (looks_like_command_line_option(ssh_host)) - die(_("strange hostname '%s' blocked"), ssh_host); - - ssh = get_ssh_command(); - if (ssh) { - variant = determine_ssh_variant(ssh, 1); - } else { - /* - * GIT_SSH is the no-shell version of - * GIT_SSH_COMMAND (and must remain so for - * historical compatibility). - */ - conn->use_shell = 0; - - ssh = getenv("GIT_SSH"); - if (!ssh) - ssh = "ssh"; - variant = determine_ssh_variant(ssh, 0); - } - - if (variant == VARIANT_AUTO) { - struct child_process detect = CHILD_PROCESS_INIT; - - detect.use_shell = conn->use_shell; - detect.no_stdin = detect.no_stdout = detect.no_stderr = 1; - - strvec_push(&detect.args, ssh); - strvec_push(&detect.args, "-G"); - push_ssh_options(&detect.args, &detect.env_array, - VARIANT_SSH, port, version, flags); - strvec_push(&detect.args, ssh_host); - - variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH; - } - - strvec_push(&conn->args, ssh); - push_ssh_options(&conn->args, &conn->env_array, variant, port, version, flags); - strvec_push(&conn->args, ssh_host); -} - -/* - * This returns the dummy child_process `no_fork` if the transport protocol - * does not need fork(2), or a struct child_process object if it does. Once - * done, finish the connection with finish_connect() with the value returned - * from this function (it is safe to call finish_connect() with NULL to - * support the former case). - * - * If it returns, the connect is successful; it just dies on errors (this - * will hopefully be changed in a libification effort, to return NULL when - * the connection failed). - */ -struct child_process *git_connect(int fd[2], const char *url, - const char *prog, int flags) -{ - char *hostandport, *path; - struct child_process *conn; - enum protocol protocol; - enum protocol_version version = get_protocol_version_config(); - - /* - * NEEDSWORK: If we are trying to use protocol v2 and we are planning - * to perform a push, then fallback to v0 since the client doesn't know - * how to push yet using v2. - */ - if (version == protocol_v2 && !strcmp("git-receive-pack", prog)) - version = protocol_v0; - - /* Without this we cannot rely on waitpid() to tell - * what happened to our children. - */ - signal(SIGCHLD, SIG_DFL); - - protocol = parse_connect_url(url, &hostandport, &path); - if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { - printf("Diag: url=%s\n", url ? url : "NULL"); - printf("Diag: protocol=%s\n", prot_name(protocol)); - printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); - printf("Diag: path=%s\n", path ? path : "NULL"); - conn = NULL; - } else if (protocol == PROTO_GIT) { - conn = git_connect_git(fd, hostandport, path, prog, version, flags); - conn->trace2_child_class = "transport/git"; - } else { - struct strbuf cmd = STRBUF_INIT; - const char *const *var; - - 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); - - /* remove repo-local variables from the environment */ - for (var = local_repo_env; *var; var++) - strvec_push(&conn->env_array, *var); - - conn->use_shell = 1; - conn->in = conn->out = -1; - if (protocol == PROTO_SSH) { - char *ssh_host = hostandport; - const char *port = NULL; - transport_check_allowed("ssh"); - get_host_and_port(&ssh_host, &port); - - if (!port) - port = get_port(ssh_host); - - if (flags & CONNECT_DIAG_URL) { - printf("Diag: url=%s\n", url ? url : "NULL"); - printf("Diag: protocol=%s\n", prot_name(protocol)); - printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); - printf("Diag: port=%s\n", port ? port : "NONE"); - printf("Diag: path=%s\n", path ? path : "NULL"); - - free(hostandport); - free(path); - free(conn); - strbuf_release(&cmd); - return NULL; - } - conn->trace2_child_class = "transport/ssh"; - fill_ssh_args(conn, ssh_host, port, version, flags); - } else { - transport_check_allowed("file"); - conn->trace2_child_class = "transport/file"; - if (version > 0) { - strvec_pushf(&conn->env_array, - GIT_PROTOCOL_ENVIRONMENT "=version=%d", - version); - } - } - strvec_push(&conn->args, cmd.buf); - - if (start_command(conn)) - die(_("unable to fork")); - - fd[0] = conn->out; /* read from child's stdout */ - fd[1] = conn->in; /* write to child's stdin */ - strbuf_release(&cmd); - } - free(hostandport); - free(path); - return conn; -} - -int finish_connect(struct child_process *conn) -{ - int code; - if (!conn || git_connection_is_socket(conn)) - return 0; - - code = finish_command(conn); - free(conn); - return code; -} |