connect & http: support -4 and -6 switches for remote operations

Sometimes it is necessary to force IPv4-only or IPv6-only operation on networks where name lookups may return a non-routable address and stall remote operations. The ssh(1) command has an equivalent switches which we may pass when we run them. There may be old ssh(1) implementations out there which do not support these switches; they should report the appropriate error in that case. rsync support is untouched for now since it is deprecated and scheduled to be removed. Signed-off-by: Eric Wong <normalperson@yhbt.net> Reviewed-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Wong committed Feb 3, 2016 at 04:09 UTC c915f11eb4922e154e29cf62d3b549d65c06a170
13 files changed +95
Documentation/fetch-options.txt
+8
@@ -150,3 +150,11 @@ endif::git-pull[]
150 by default when it is attached to a terminal, unless -q
151 is specified. This flag forces progress status even if the
152 standard error stream is not directed to a terminal.
153 +
154 +-4::
155 +--ipv4::
156 + Use IPv4 addresses only, ignoring IPv6 addresses.
157 +
158 +-6::
159 +--ipv6::
160 + Use IPv6 addresses only, ignoring IPv4 addresses.
Documentation/git-push.txt
+7
@@ -277,6 +277,13 @@ origin +master` to force a push to the `master` branch). See the
277 default is --verify, giving the hook a chance to prevent the
278 push. With --no-verify, the hook is bypassed completely.
279
280 +-4::
281 +--ipv4::
282 + Use IPv4 addresses only, ignoring IPv6 addresses.
283 +
284 +-6::
285 +--ipv6::
286 + Use IPv6 addresses only, ignoring IPv4 addresses.
287
288 include::urls-remotes.txt[]
289
builtin/clone.c
+6
@@ -47,6 +47,7 @@ static const char *real_git_dir;
47 static char *option_upload_pack = "git-upload-pack";
48 static int option_verbosity;
49 static int option_progress = -1;
50 +static enum transport_family family;
51 static struct string_list option_config;
52 static struct string_list option_reference;
53 static int option_dissociate;
@@ -92,6 +93,10 @@ static struct option builtin_clone_options[] = {
93 N_("separate git dir from working tree")),
94 OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
95 N_("set config inside the new repository")),
96 + OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
97 + TRANSPORT_FAMILY_IPV4),
98 + OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
99 + TRANSPORT_FAMILY_IPV6),
100 OPT_END()
101 };
102
@@ -967,6 +972,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
972 remote = remote_get(option_origin);
973 transport = transport_get(remote, remote->url[0]);
974 transport_set_verbosity(transport, option_verbosity, option_progress);
975 + transport->family = family;
976
977 path = get_repo_path(remote->url[0], &is_bundle);
978 is_local = option_local != 0 && path && !is_bundle;
builtin/fetch.c
+6
@@ -37,6 +37,7 @@ static int prune = -1; /* unspecified */
37 static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
38 static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
39 static int tags = TAGS_DEFAULT, unshallow, update_shallow;
40 +static enum transport_family family;
41 static const char *depth;
42 static const char *upload_pack;
43 static struct strbuf default_rla = STRBUF_INIT;
@@ -124,6 +125,10 @@ static struct option builtin_fetch_options[] = {
125 N_("accept refs that update .git/shallow")),
126 { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
127 N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
128 + OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
129 + TRANSPORT_FAMILY_IPV4),
130 + OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
131 + TRANSPORT_FAMILY_IPV6),
132 OPT_END()
133 };
134
@@ -861,6 +866,7 @@ static struct transport *prepare_transport(struct remote *remote)
866 struct transport *transport;
867 transport = transport_get(remote, NULL);
868 transport_set_verbosity(transport, verbosity, progress);
869 + transport->family = family;
870 if (upload_pack)
871 set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
872 if (keep)
builtin/push.c
+6
@@ -23,6 +23,7 @@ static const char *receivepack;
23 static int verbosity;
24 static int progress = -1;
25 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
26 +static enum transport_family family;
27
28 static struct push_cas_option cas;
29
@@ -346,6 +347,7 @@ static int push_with_options(struct transport *transport, int flags)
347 unsigned int reject_reasons;
348
349 transport_set_verbosity(transport, verbosity, progress);
350 + transport->family = family;
351
352 if (receivepack)
353 transport_set_option(transport,
@@ -565,6 +567,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
567 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
568 PARSE_OPT_OPTARG, option_parse_push_signed },
569 OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
570 + OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
571 + TRANSPORT_FAMILY_IPV4),
572 + OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
573 + TRANSPORT_FAMILY_IPV6),
574 OPT_END()
575 };
576
connect.c
+8
@@ -357,6 +357,10 @@ static int git_tcp_connect_sock(char *host, int flags)
357 port = "<none>";
358
359 memset(&hints, 0, sizeof(hints));
360 + if (flags & CONNECT_IPV4)
361 + hints.ai_family = AF_INET;
362 + else if (flags & CONNECT_IPV6)
363 + hints.ai_family = AF_INET6;
364 hints.ai_socktype = SOCK_STREAM;
365 hints.ai_protocol = IPPROTO_TCP;
366
@@ -783,6 +787,10 @@ struct child_process *git_connect(int fd[2], const char *url,
787 }
788
789 argv_array_push(&conn->args, ssh);
790 + if (flags & CONNECT_IPV4)
791 + argv_array_push(&conn->args, "-4");
792 + else if (flags & CONNECT_IPV6)
793 + argv_array_push(&conn->args, "-6");
794 if (tortoiseplink)
795 argv_array_push(&conn->args, "-batch");
796 if (port) {
connect.h
+2
@@ -3,6 +3,8 @@
3
4 #define CONNECT_VERBOSE (1u << 0)
5 #define CONNECT_DIAG_URL (1u << 1)
6 +#define CONNECT_IPV4 (1u << 2)
7 +#define CONNECT_IPV6 (1u << 3)
8 extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
9 extern int finish_connect(struct child_process *conn);
10 extern int git_connection_is_socket(struct child_process *conn);
http.c
+9
@@ -11,6 +11,11 @@
11 #include "gettext.h"
12 #include "transport.h"
13
14 +#if LIBCURL_VERSION_NUM >= 0x070a08
15 +long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
16 +#else
17 +long int git_curl_ipresolve;
18 +#endif
19 int active_requests;
20 int http_is_verbose;
21 size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
@@ -692,6 +697,10 @@ struct active_request_slot *get_active_slot(void)
697 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
698 curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
699 curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
700 +
701 +#if LIBCURL_VERSION_NUM >= 0x070a08
702 + curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
703 +#endif
704 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
705 curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
706 #endif
http.h
+1
@@ -106,6 +106,7 @@ extern void http_init(struct remote *remote, const char *url,
106 int proactive_auth);
107 extern void http_cleanup(void);
108
109 +extern long int git_curl_ipresolve;
110 extern int active_requests;
111 extern int http_is_verbose;
112 extern size_t http_post_buffer;
remote-curl.c
+13
@@ -119,6 +119,19 @@ static int set_option(const char *name, const char *value)
119 else
120 return -1;
121 return 0;
122 +
123 +#if LIBCURL_VERSION_NUM >= 0x070a08
124 + } else if (!strcmp(name, "family")) {
125 + if (!strcmp(value, "ipv4"))
126 + git_curl_ipresolve = CURL_IPRESOLVE_V4;
127 + else if (!strcmp(value, "ipv6"))
128 + git_curl_ipresolve = CURL_IPRESOLVE_V6;
129 + else if (!strcmp(value, "all"))
130 + git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
131 + else
132 + return -1;
133 + return 0;
134 +#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
135 } else {
136 return 1 /* unsupported */;
137 }
transport-helper.c
+15
@@ -320,6 +320,21 @@ static void standard_options(struct transport *t)
320 if (n >= sizeof(buf))
321 die("impossibly large verbosity value");
322 set_helper_option(t, "verbosity", buf);
323 +
324 + switch (t->family) {
325 + case TRANSPORT_FAMILY_ALL:
326 + /*
327 + * this is already the default,
328 + * do not break old remote helpers by setting "all" here
329 + */
330 + break;
331 + case TRANSPORT_FAMILY_IPV4:
332 + set_helper_option(t, "family", "ipv4");
333 + break;
334 + case TRANSPORT_FAMILY_IPV6:
335 + set_helper_option(t, "family", "ipv6");
336 + break;
337 + }
338 }
339
340 static int release_helper(struct transport *transport)
transport.c
+6
@@ -489,6 +489,12 @@ static int connect_setup(struct transport *transport, int for_push)
489 if (data->conn)
490 return 0;
491
492 + switch (transport->family) {
493 + case TRANSPORT_FAMILY_ALL: break;
494 + case TRANSPORT_FAMILY_IPV4: flags |= CONNECT_IPV4; break;
495 + case TRANSPORT_FAMILY_IPV6: flags |= CONNECT_IPV6; break;
496 + }
497 +
498 data->conn = git_connect(data->fd, transport->url,
499 for_push ? data->options.receivepack :
500 data->options.uploadpack,
transport.h
+8
@@ -18,6 +18,12 @@ struct git_transport_options {
18 struct push_cas_option *cas;
19 };
20
21 +enum transport_family {
22 + TRANSPORT_FAMILY_ALL = 0,
23 + TRANSPORT_FAMILY_IPV4,
24 + TRANSPORT_FAMILY_IPV6
25 +};
26 +
27 struct transport {
28 struct remote *remote;
29 const char *url;
@@ -110,6 +116,8 @@ struct transport {
116 * actually turns out to be smart.
117 */
118 struct git_transport_options *smart_options;
119 +
120 + enum transport_family family;
121 };
122
123 #define TRANSPORT_PUSH_ALL 1