{"thread":{"id":"29887","subject":"[PATCH 0/5] transport: unify ipv4 and ipv6 code paths","startedAt":"2012-03-08T12:48:57Z","lastAt":"2012-06-14T05:02:03Z","messageCount":20,"participants":["Jonathan Nieder","Erik Faye-Lund","Richard Hartmann","Johannes Sixt","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"186436","messageId":"20120308124857.GA7666@burratino","threadId":"29887","inReplyTo":null,"subject":"[PATCH 0/5] transport: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T12:48:57Z","receivedAt":"2012-03-08T12:48:57Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nThese patches eliminate some ifdef-ery concerning NO_IPV6.  I used\nthem when writing the SRV patch, which applies on top, but it's\nprobably best to think of it as an independent topic.\n\nPatch 4 is the heart of the series.  It provides an interface similar\nto getaddrinfo that can be implemented on top of either gethostbyname\nor getaddrinfo and puts each implementation in a separate file.  This\nway, callers can just use the common API and they do not need to have\ntwo copies of their code, one for each host resolution API.\n\nPatches 1-3 move code around until all the code that patch 4 touches\nis in one place.\n\nPatches 5 is a potential error handling improvement noticed while\nwriting patches 1-4.  It's probably not actually needed but it was a\ncomfort to me.\n\nThese patches have been in use in Debian since June of last year.  I'd\nlike to see this in mainline early in the 1.7.11 cycle to make coding\nthat touches this area during that cycle more pleasant.  Thoughts of\nall kinds welcome.\n\nJonathan Nieder (5):\n  transport: expose git_tcp_connect and friends in new tcp.h\n  daemon: make host resolution into a separate function\n  daemon: move locate_host() to tcp.c\n  tcp: unify ipv4 and ipv6 code paths\n  daemon: check for errors retrieving IP address\n\n Makefile   |    7 ++\n connect.c  |  277 +-----------------------------------------------------------\n daemon.c   |   58 ++-----------\n dns-ipv4.c |   33 ++++++++\n dns-ipv4.h |   68 +++++++++++++++\n dns-ipv6.c |   49 +++++++++++\n dns-ipv6.h |   31 +++++++\n tcp.c      |  217 +++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h      |   11 +++\n 9 files changed, 422 insertions(+), 329 deletions(-)\n create mode 100644 dns-ipv4.c\n create mode 100644 dns-ipv4.h\n create mode 100644 dns-ipv6.c\n create mode 100644 dns-ipv6.h\n create mode 100644 tcp.c\n create mode 100644 tcp.h\n"},{"id":"186438","messageId":"20120308130304.GA9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 1/5] transport: expose git_tcp_connect() and friends in new tcp.h","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:03:04Z","receivedAt":"2012-03-08T13:03:04Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Mon, 6 Jun 2011 04:37:14 -0500\n\nSplit off a new tcp.c with the functions git_tcp_connect() and\ngit_proxy_connect() that resolve and connect to a host.\n\nPart of a series to teach git to respect DNS SRV records when making\nnew connections.  This is a preliminary step to make the connection\nlibrary easier to understand before changing it.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nFrom http://thread.gmane.org/gmane.comp.version-control.git/175106/focus=175107\n\nReviewing mass code migration is a little tricky.  My current favorite\nstrategy is\n\n\tgit show connect.c Makefile tcp.h\n\tgit diff HEAD^:connect.c HEAD:tcp.c\n\nto make sure nothing important was added or dropped.\n\n Makefile  |    2 +\n connect.c |  277 +-----------------------------------------------------------\n tcp.c     |  278 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h     |    8 ++\n 4 files changed, 289 insertions(+), 276 deletions(-)\n create mode 100644 tcp.c\n create mode 100644 tcp.h\n\ndiff --git a/Makefile b/Makefile\nindex e4f8e0ef..0d0ac31d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -736,6 +736,7 @@ LIB_OBJS += string-list.o\n LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n+LIB_OBJS += tcp.o\n LIB_OBJS += trace.o\n LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n@@ -2148,6 +2149,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n+connect.o tcp.o: tcp.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/connect.c b/connect.c\nindex 912cddee..962dc030 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -5,6 +5,7 @@\n #include \"refs.h\"\n #include \"run-command.h\"\n #include \"remote.h\"\n+#include \"tcp.h\"\n #include \"url.h\"\n \n static char *server_capabilities;\n@@ -145,282 +146,6 @@ static enum protocol get_protocol(const char *name)\n \tdie(\"I don't handle protocol '%s'\", name);\n }\n \n-#define STR_(s)\t# s\n-#define STR(s)\tSTR_(s)\n-\n-static void get_host_and_port(char **host, const char **port)\n-{\n-\tchar *colon, *end;\n-\n-\tif (*host[0] == '[') {\n-\t\tend = strchr(*host + 1, ']');\n-\t\tif (end) {\n-\t\t\t*end = 0;\n-\t\t\tend++;\n-\t\t\t(*host)++;\n-\t\t} else\n-\t\t\tend = *host;\n-\t} else\n-\t\tend = *host;\n-\tcolon = strchr(end, ':');\n-\n-\tif (colon) {\n-\t\t*colon = 0;\n-\t\t*port = colon + 1;\n-\t}\n-}\n-\n-static void enable_keepalive(int sockfd)\n-{\n-\tint ka = 1;\n-\n-\tif (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)\n-\t\tfprintf(stderr, \"unable to set SO_KEEPALIVE on socket: %s\\n\",\n-\t\t\tstrerror(errno));\n-}\n-\n-#ifndef NO_IPV6\n-\n-static const char *ai_name(const struct addrinfo *ai)\n-{\n-\tstatic char addr[NI_MAXHOST];\n-\tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,\n-\t\t\tNI_NUMERICHOST) != 0)\n-\t\tstrcpy(addr, \"(unknown)\");\n-\n-\treturn addr;\n-}\n-\n-/*\n- * Returns a connected socket() fd, or else die()s.\n- */\n-static int git_tcp_connect_sock(char *host, int flags)\n-{\n-\tstruct strbuf error_message = STRBUF_INIT;\n-\tint sockfd = -1;\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tstruct addrinfo hints, *ai0, *ai;\n-\tint gai;\n-\tint cnt = 0;\n-\n-\tget_host_and_port(&host, &port);\n-\tif (!*port)\n-\t\tport = \"<none>\";\n-\n-\tmemset(&hints, 0, sizeof(hints));\n-\thints.ai_socktype = SOCK_STREAM;\n-\thints.ai_protocol = IPPROTO_TCP;\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"Looking up %s ... \", host);\n-\n-\tgai = getaddrinfo(host, port, &hints, &ai);\n-\tif (gai)\n-\t\tdie(\"Unable to look up %s (port %s) (%s)\", host, port, gai_strerror(gai));\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n-\n-\tfor (ai0 = ai; ai; ai = ai->ai_next, cnt++) {\n-\t\tsockfd = socket(ai->ai_family,\n-\t\t\t\tai->ai_socktype, ai->ai_protocol);\n-\t\tif ((sockfd < 0) ||\n-\t\t    (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) {\n-\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\t    host, cnt, ai_name(ai), strerror(errno));\n-\t\t\tif (0 <= sockfd)\n-\t\t\t\tclose(sockfd);\n-\t\t\tsockfd = -1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (flags & CONNECT_VERBOSE)\n-\t\t\tfprintf(stderr, \"%s \", ai_name(ai));\n-\t\tbreak;\n-\t}\n-\n-\tfreeaddrinfo(ai0);\n-\n-\tif (sockfd < 0)\n-\t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n-\n-\tenable_keepalive(sockfd);\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\n\");\n-\n-\tstrbuf_release(&error_message);\n-\n-\treturn sockfd;\n-}\n-\n-#else /* NO_IPV6 */\n-\n-/*\n- * Returns a connected socket() fd, or else die()s.\n- */\n-static int git_tcp_connect_sock(char *host, int flags)\n-{\n-\tstruct strbuf error_message = STRBUF_INIT;\n-\tint sockfd = -1;\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tchar *ep;\n-\tstruct hostent *he;\n-\tstruct sockaddr_in sa;\n-\tchar **ap;\n-\tunsigned int nport;\n-\tint cnt;\n-\n-\tget_host_and_port(&host, &port);\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"Looking up %s ... \", host);\n-\n-\the = gethostbyname(host);\n-\tif (!he)\n-\t\tdie(\"Unable to look up %s (%s)\", host, hstrerror(h_errno));\n-\tnport = strtoul(port, &ep, 10);\n-\tif ( ep == port || *ep ) {\n-\t\t/* Not numeric */\n-\t\tstruct servent *se = getservbyname(port,\"tcp\");\n-\t\tif ( !se )\n-\t\t\tdie(\"Unknown port %s\", port);\n-\t\tnport = se->s_port;\n-\t}\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n-\n-\tfor (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {\n-\t\tmemset(&sa, 0, sizeof sa);\n-\t\tsa.sin_family = he->h_addrtype;\n-\t\tsa.sin_port = htons(nport);\n-\t\tmemcpy(&sa.sin_addr, *ap, he->h_length);\n-\n-\t\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n-\t\tif ((sockfd < 0) ||\n-\t\t    connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n-\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\thost,\n-\t\t\t\tcnt,\n-\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr),\n-\t\t\t\tstrerror(errno));\n-\t\t\tif (0 <= sockfd)\n-\t\t\t\tclose(sockfd);\n-\t\t\tsockfd = -1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (flags & CONNECT_VERBOSE)\n-\t\t\tfprintf(stderr, \"%s \",\n-\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr));\n-\t\tbreak;\n-\t}\n-\n-\tif (sockfd < 0)\n-\t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n-\n-\tenable_keepalive(sockfd);\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\n\");\n-\n-\treturn sockfd;\n-}\n-\n-#endif /* NO_IPV6 */\n-\n-\n-static void git_tcp_connect(int fd[2], char *host, int flags)\n-{\n-\tint sockfd = git_tcp_connect_sock(host, flags);\n-\n-\tfd[0] = sockfd;\n-\tfd[1] = dup(sockfd);\n-}\n-\n-\n-static char *git_proxy_command;\n-\n-static int git_proxy_command_options(const char *var, const char *value,\n-\t\tvoid *cb)\n-{\n-\tif (!strcmp(var, \"core.gitproxy\")) {\n-\t\tconst char *for_pos;\n-\t\tint matchlen = -1;\n-\t\tint hostlen;\n-\t\tconst char *rhost_name = cb;\n-\t\tint rhost_len = strlen(rhost_name);\n-\n-\t\tif (git_proxy_command)\n-\t\t\treturn 0;\n-\t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\t/* [core]\n-\t\t * ;# matches www.kernel.org as well\n-\t\t * gitproxy = netcatter-1 for kernel.org\n-\t\t * gitproxy = netcatter-2 for sample.xz\n-\t\t * gitproxy = netcatter-default\n-\t\t */\n-\t\tfor_pos = strstr(value, \" for \");\n-\t\tif (!for_pos)\n-\t\t\t/* matches everybody */\n-\t\t\tmatchlen = strlen(value);\n-\t\telse {\n-\t\t\thostlen = strlen(for_pos + 5);\n-\t\t\tif (rhost_len < hostlen)\n-\t\t\t\tmatchlen = -1;\n-\t\t\telse if (!strncmp(for_pos + 5,\n-\t\t\t\t\t  rhost_name + rhost_len - hostlen,\n-\t\t\t\t\t  hostlen) &&\n-\t\t\t\t ((rhost_len == hostlen) ||\n-\t\t\t\t  rhost_name[rhost_len - hostlen -1] == '.'))\n-\t\t\t\tmatchlen = for_pos - value;\n-\t\t\telse\n-\t\t\t\tmatchlen = -1;\n-\t\t}\n-\t\tif (0 <= matchlen) {\n-\t\t\t/* core.gitproxy = none for kernel.org */\n-\t\t\tif (matchlen == 4 &&\n-\t\t\t    !memcmp(value, \"none\", 4))\n-\t\t\t\tmatchlen = 0;\n-\t\t\tgit_proxy_command = xmemdupz(value, matchlen);\n-\t\t}\n-\t\treturn 0;\n-\t}\n-\n-\treturn git_default_config(var, value, cb);\n-}\n-\n-static int git_use_proxy(const char *host)\n-{\n-\tgit_proxy_command = getenv(\"GIT_PROXY_COMMAND\");\n-\tgit_config(git_proxy_command_options, (void*)host);\n-\treturn (git_proxy_command && *git_proxy_command);\n-}\n-\n-static struct child_process *git_proxy_connect(int fd[2], char *host)\n-{\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tconst char **argv;\n-\tstruct child_process *proxy;\n-\n-\tget_host_and_port(&host, &port);\n-\n-\targv = xmalloc(sizeof(*argv) * 4);\n-\targv[0] = git_proxy_command;\n-\targv[1] = host;\n-\targv[2] = port;\n-\targv[3] = NULL;\n-\tproxy = xcalloc(1, sizeof(*proxy));\n-\tproxy->argv = argv;\n-\tproxy->in = -1;\n-\tproxy->out = -1;\n-\tif (start_command(proxy))\n-\t\tdie(\"cannot start proxy %s\", argv[0]);\n-\tfd[0] = proxy->out; /* read from proxy stdout */\n-\tfd[1] = proxy->in;  /* write to proxy stdin */\n-\treturn proxy;\n-}\n-\n #define MAX_CMD_LEN 1024\n \n static char *get_port(char *host)\ndiff --git a/tcp.c b/tcp.c\nnew file mode 100644\nindex 00000000..f5e1ab37\n--- /dev/null\n+++ b/tcp.c\n@@ -0,0 +1,278 @@\n+#include \"cache.h\"\n+#include \"run-command.h\"\n+\n+#define STR_(s)\t# s\n+#define STR(s)\tSTR_(s)\n+\n+static void get_host_and_port(char **host, const char **port)\n+{\n+\tchar *colon, *end;\n+\n+\tif (*host[0] == '[') {\n+\t\tend = strchr(*host + 1, ']');\n+\t\tif (end) {\n+\t\t\t*end = 0;\n+\t\t\tend++;\n+\t\t\t(*host)++;\n+\t\t} else\n+\t\t\tend = *host;\n+\t} else\n+\t\tend = *host;\n+\tcolon = strchr(end, ':');\n+\n+\tif (colon) {\n+\t\t*colon = 0;\n+\t\t*port = colon + 1;\n+\t}\n+}\n+\n+static void enable_keepalive(int sockfd)\n+{\n+\tint ka = 1;\n+\n+\tif (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)\n+\t\tfprintf(stderr, \"unable to set SO_KEEPALIVE on socket: %s\\n\",\n+\t\t\tstrerror(errno));\n+}\n+\n+#ifndef NO_IPV6\n+\n+static const char *ai_name(const struct addrinfo *ai)\n+{\n+\tstatic char addr[NI_MAXHOST];\n+\tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,\n+\t\t\tNI_NUMERICHOST) != 0)\n+\t\tstrcpy(addr, \"(unknown)\");\n+\n+\treturn addr;\n+}\n+\n+/*\n+ * Returns a connected socket() fd, or else die()s.\n+ */\n+static int git_tcp_connect_sock(char *host, int flags)\n+{\n+\tstruct strbuf error_message = STRBUF_INIT;\n+\tint sockfd = -1;\n+\tconst char *port = STR(DEFAULT_GIT_PORT);\n+\tstruct addrinfo hints, *ai0, *ai;\n+\tint gai;\n+\tint cnt = 0;\n+\n+\tget_host_and_port(&host, &port);\n+\tif (!*port)\n+\t\tport = \"<none>\";\n+\n+\tmemset(&hints, 0, sizeof(hints));\n+\thints.ai_socktype = SOCK_STREAM;\n+\thints.ai_protocol = IPPROTO_TCP;\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"Looking up %s ... \", host);\n+\n+\tgai = getaddrinfo(host, port, &hints, &ai);\n+\tif (gai)\n+\t\tdie(\"Unable to look up %s (port %s) (%s)\", host, port, gai_strerror(gai));\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n+\n+\tfor (ai0 = ai; ai; ai = ai->ai_next, cnt++) {\n+\t\tsockfd = socket(ai->ai_family,\n+\t\t\t\tai->ai_socktype, ai->ai_protocol);\n+\t\tif ((sockfd < 0) ||\n+\t\t    (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) {\n+\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n+\t\t\t\t    host, cnt, ai_name(ai), strerror(errno));\n+\t\t\tif (0 <= sockfd)\n+\t\t\t\tclose(sockfd);\n+\t\t\tsockfd = -1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\tfprintf(stderr, \"%s \", ai_name(ai));\n+\t\tbreak;\n+\t}\n+\n+\tfreeaddrinfo(ai0);\n+\n+\tif (sockfd < 0)\n+\t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n+\n+\tenable_keepalive(sockfd);\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\n\");\n+\n+\tstrbuf_release(&error_message);\n+\n+\treturn sockfd;\n+}\n+\n+#else /* NO_IPV6 */\n+\n+/*\n+ * Returns a connected socket() fd, or else die()s.\n+ */\n+static int git_tcp_connect_sock(char *host, int flags)\n+{\n+\tstruct strbuf error_message = STRBUF_INIT;\n+\tint sockfd = -1;\n+\tconst char *port = STR(DEFAULT_GIT_PORT);\n+\tchar *ep;\n+\tstruct hostent *he;\n+\tstruct sockaddr_in sa;\n+\tchar **ap;\n+\tunsigned int nport;\n+\tint cnt;\n+\n+\tget_host_and_port(&host, &port);\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"Looking up %s ... \", host);\n+\n+\the = gethostbyname(host);\n+\tif (!he)\n+\t\tdie(\"Unable to look up %s (%s)\", host, hstrerror(h_errno));\n+\tnport = strtoul(port, &ep, 10);\n+\tif ( ep == port || *ep ) {\n+\t\t/* Not numeric */\n+\t\tstruct servent *se = getservbyname(port,\"tcp\");\n+\t\tif ( !se )\n+\t\t\tdie(\"Unknown port %s\", port);\n+\t\tnport = se->s_port;\n+\t}\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n+\n+\tfor (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {\n+\t\tmemset(&sa, 0, sizeof sa);\n+\t\tsa.sin_family = he->h_addrtype;\n+\t\tsa.sin_port = htons(nport);\n+\t\tmemcpy(&sa.sin_addr, *ap, he->h_length);\n+\n+\t\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n+\t\tif ((sockfd < 0) ||\n+\t\t    connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n+\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n+\t\t\t\thost,\n+\t\t\t\tcnt,\n+\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr),\n+\t\t\t\tstrerror(errno));\n+\t\t\tif (0 <= sockfd)\n+\t\t\t\tclose(sockfd);\n+\t\t\tsockfd = -1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\tfprintf(stderr, \"%s \",\n+\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr));\n+\t\tbreak;\n+\t}\n+\n+\tif (sockfd < 0)\n+\t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n+\n+\tenable_keepalive(sockfd);\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\n\");\n+\n+\treturn sockfd;\n+}\n+\n+#endif /* NO_IPV6 */\n+\n+\n+void git_tcp_connect(int fd[2], char *host, int flags)\n+{\n+\tint sockfd = git_tcp_connect_sock(host, flags);\n+\n+\tfd[0] = sockfd;\n+\tfd[1] = dup(sockfd);\n+}\n+\n+\n+static char *git_proxy_command;\n+\n+static int git_proxy_command_options(const char *var, const char *value,\n+\t\tvoid *cb)\n+{\n+\tif (!strcmp(var, \"core.gitproxy\")) {\n+\t\tconst char *for_pos;\n+\t\tint matchlen = -1;\n+\t\tint hostlen;\n+\t\tconst char *rhost_name = cb;\n+\t\tint rhost_len = strlen(rhost_name);\n+\n+\t\tif (git_proxy_command)\n+\t\t\treturn 0;\n+\t\tif (!value)\n+\t\t\treturn config_error_nonbool(var);\n+\t\t/* [core]\n+\t\t * ;# matches www.kernel.org as well\n+\t\t * gitproxy = netcatter-1 for kernel.org\n+\t\t * gitproxy = netcatter-2 for sample.xz\n+\t\t * gitproxy = netcatter-default\n+\t\t */\n+\t\tfor_pos = strstr(value, \" for \");\n+\t\tif (!for_pos)\n+\t\t\t/* matches everybody */\n+\t\t\tmatchlen = strlen(value);\n+\t\telse {\n+\t\t\thostlen = strlen(for_pos + 5);\n+\t\t\tif (rhost_len < hostlen)\n+\t\t\t\tmatchlen = -1;\n+\t\t\telse if (!strncmp(for_pos + 5,\n+\t\t\t\t\t  rhost_name + rhost_len - hostlen,\n+\t\t\t\t\t  hostlen) &&\n+\t\t\t\t ((rhost_len == hostlen) ||\n+\t\t\t\t  rhost_name[rhost_len - hostlen -1] == '.'))\n+\t\t\t\tmatchlen = for_pos - value;\n+\t\t\telse\n+\t\t\t\tmatchlen = -1;\n+\t\t}\n+\t\tif (0 <= matchlen) {\n+\t\t\t/* core.gitproxy = none for kernel.org */\n+\t\t\tif (matchlen == 4 &&\n+\t\t\t    !memcmp(value, \"none\", 4))\n+\t\t\t\tmatchlen = 0;\n+\t\t\tgit_proxy_command = xmemdupz(value, matchlen);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n+int git_use_proxy(const char *host)\n+{\n+\tgit_proxy_command = getenv(\"GIT_PROXY_COMMAND\");\n+\tgit_config(git_proxy_command_options, (void*)host);\n+\treturn (git_proxy_command && *git_proxy_command);\n+}\n+\n+struct child_process *git_proxy_connect(int fd[2], char *host)\n+{\n+\tconst char *port = STR(DEFAULT_GIT_PORT);\n+\tconst char **argv;\n+\tstruct child_process *proxy;\n+\n+\tget_host_and_port(&host, &port);\n+\n+\targv = xmalloc(sizeof(*argv) * 4);\n+\targv[0] = git_proxy_command;\n+\targv[1] = host;\n+\targv[2] = port;\n+\targv[3] = NULL;\n+\tproxy = xcalloc(1, sizeof(*proxy));\n+\tproxy->argv = argv;\n+\tproxy->in = -1;\n+\tproxy->out = -1;\n+\tif (start_command(proxy))\n+\t\tdie(\"cannot start proxy %s\", argv[0]);\n+\tfd[0] = proxy->out; /* read from proxy stdout */\n+\tfd[1] = proxy->in;  /* write to proxy stdin */\n+\treturn proxy;\n+}\ndiff --git a/tcp.h b/tcp.h\nnew file mode 100644\nindex 00000000..4de5f712\n--- /dev/null\n+++ b/tcp.h\n@@ -0,0 +1,8 @@\n+#ifndef TCP_H\n+#define TCP_H\n+\n+extern int git_use_proxy(const char *host);\n+extern void git_tcp_connect(int fd[2], char *host, int flags);\n+extern struct child_process *git_proxy_connect(int fd[2], char *host);\n+\n+#endif\n-- \n1.7.9.2\n"},{"id":"186439","messageId":"20120308130505.GB9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 2/5] daemon: make host resolution a separate function","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:05:05Z","receivedAt":"2012-03-08T13:05:05Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Mon, 6 Jun 2011 04:38:45 -0500\n\nThe locate_host() function looks up the IP address and canonical\nhostname of the host named by its argument.  If it succeeds,\n*ip_address and *canon_hostname are freed and replaced by the hosts'\nIP address and canonical hostname, respectively, as strings.  If it\nfails, *ip_address and *canon_hostname are left alone.\n\nThe git daemon uses this functionality to support the %IP and %CH\nplaceholders for its --interpolated-path feature.  Splitting it out as\na separate function would make it easier to tweak, for example to\nunify the ipv6 and ipv4 code paths or to share code with other parts\nof git that make DNS queries.\n\nSigned-off-by; Jonathan Nieder <jrnieder@gmail.com>\n---\nFrom http://thread.gmane.org/gmane.comp.version-control.git/175106/focus=175108\n\nThe commit message was tweaked, but the patch is the same as before.\n\n daemon.c |  112 +++++++++++++++++++++++++++++++++++---------------------------\n 1 file changed, 63 insertions(+), 49 deletions(-)\n\ndiff --git a/daemon.c b/daemon.c\nindex 15ce918a..2a9dfea0 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -441,6 +441,65 @@ static void parse_host_and_port(char *hostport, char **host,\n \t}\n }\n \n+#ifndef NO_IPV6\n+\n+static void locate_host(const char *hostname, char **ip_address,\n+\t\t\t\t\t\tchar **canon_hostname)\n+{\n+\tstruct addrinfo hints;\n+\tstruct addrinfo *ai;\n+\tint gai;\n+\tstatic char addrbuf[HOST_NAME_MAX + 1];\n+\tstruct sockaddr_in *sin_addr;\n+\n+\tmemset(&hints, 0, sizeof(hints));\n+\thints.ai_flags = AI_CANONNAME;\n+\n+\tgai = getaddrinfo(hostname, NULL, &hints, &ai);\n+\tif (gai)\n+\t\treturn;\n+\n+\tsin_addr = (void *)ai->ai_addr;\n+\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n+\tfree(*ip_address);\n+\t*ip_address = xstrdup(addrbuf);\n+\n+\tfree(*canon_hostname);\n+\t*canon_hostname = xstrdup(ai->ai_canonname ?\n+\t\t\t\t  ai->ai_canonname : *ip_address);\n+\n+\tfreeaddrinfo(ai);\n+}\n+\n+#else\n+\n+static void locate_host(const char *hostname, char **ip_address,\n+\t\t\t\t\t\tchar **canon_hostname)\n+{\n+\tstruct hostent *hent;\n+\tstruct sockaddr_in sa;\n+\tchar **ap;\n+\tstatic char addrbuf[HOST_NAME_MAX + 1];\n+\n+\thent = gethostbyname(hostname);\n+\n+\tap = hent->h_addr_list;\n+\tmemset(&sa, 0, sizeof sa);\n+\tsa.sin_family = hent->h_addrtype;\n+\tsa.sin_port = htons(0);\n+\tmemcpy(&sa.sin_addr, *ap, hent->h_length);\n+\n+\tinet_ntop(hent->h_addrtype, &sa.sin_addr,\n+\t\t  addrbuf, sizeof(addrbuf));\n+\n+\tfree(*canon_hostname);\n+\t*canon_hostname = xstrdup(hent->h_name);\n+\tfree(*ip_address);\n+\t*ip_address = xstrdup(addrbuf);\n+}\n+\n+#endif\n+\n /*\n  * Read the host as supplied by the client connection.\n  */\n@@ -476,56 +535,11 @@ static void parse_host_arg(char *extra_args, int buflen)\n \t}\n \n \t/*\n-\t * Locate canonical hostname and its IP address.\n+\t * Locate canonical hostname and its IP address,\n+\t * if possible.\n \t */\n-\tif (hostname) {\n-#ifndef NO_IPV6\n-\t\tstruct addrinfo hints;\n-\t\tstruct addrinfo *ai;\n-\t\tint gai;\n-\t\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\n-\t\tmemset(&hints, 0, sizeof(hints));\n-\t\thints.ai_flags = AI_CANONNAME;\n-\n-\t\tgai = getaddrinfo(hostname, NULL, &hints, &ai);\n-\t\tif (!gai) {\n-\t\t\tstruct sockaddr_in *sin_addr = (void *)ai->ai_addr;\n-\n-\t\t\tinet_ntop(AF_INET, &sin_addr->sin_addr,\n-\t\t\t\t  addrbuf, sizeof(addrbuf));\n-\t\t\tfree(ip_address);\n-\t\t\tip_address = xstrdup(addrbuf);\n-\n-\t\t\tfree(canon_hostname);\n-\t\t\tcanon_hostname = xstrdup(ai->ai_canonname ?\n-\t\t\t\t\t\t ai->ai_canonname : ip_address);\n-\n-\t\t\tfreeaddrinfo(ai);\n-\t\t}\n-#else\n-\t\tstruct hostent *hent;\n-\t\tstruct sockaddr_in sa;\n-\t\tchar **ap;\n-\t\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\n-\t\thent = gethostbyname(hostname);\n-\n-\t\tap = hent->h_addr_list;\n-\t\tmemset(&sa, 0, sizeof sa);\n-\t\tsa.sin_family = hent->h_addrtype;\n-\t\tsa.sin_port = htons(0);\n-\t\tmemcpy(&sa.sin_addr, *ap, hent->h_length);\n-\n-\t\tinet_ntop(hent->h_addrtype, &sa.sin_addr,\n-\t\t\t  addrbuf, sizeof(addrbuf));\n-\n-\t\tfree(canon_hostname);\n-\t\tcanon_hostname = xstrdup(hent->h_name);\n-\t\tfree(ip_address);\n-\t\tip_address = xstrdup(addrbuf);\n-#endif\n-\t}\n+\tif (hostname)\n+\t\tlocate_host(hostname, &ip_address, &canon_hostname);\n }\n \n \n-- \n1.7.9.2\n"},{"id":"186440","messageId":"20120308130629.GC9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 3/5] daemon: move locate_host() to tcp lib","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:06:29Z","receivedAt":"2012-03-08T13:06:29Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Mon, 6 Jun 2011 04:39:29 -0500\n\nKeep the different name resolution functions close together so they\ncan learn from each other and perhaps share code in the future.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nFrom http://thread.gmane.org/gmane.comp.version-control.git/175106/focus=175108\n\nThe original forgot to move HOST_NAME_MAX, causing build failures on systems\nthat lack it.\n\n Makefile |    2 +-\n daemon.c |   66 ++------------------------------------------------------------\n tcp.c    |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h    |    3 +++\n 4 files changed, 63 insertions(+), 65 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 0d0ac31d..927a079a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2149,7 +2149,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n-connect.o tcp.o: tcp.h\n+connect.o daemon.o tcp.o: tcp.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/daemon.c b/daemon.c\nindex 2a9dfea0..3736fe53 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -1,14 +1,11 @@\n #include \"cache.h\"\n #include \"pkt-line.h\"\n+#include \"tcp.h\"\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n #include \"string-list.h\"\n \n-#ifndef HOST_NAME_MAX\n-#define HOST_NAME_MAX 256\n-#endif\n-\n #ifndef NI_MAXSERV\n #define NI_MAXSERV 32\n #endif\n@@ -441,65 +438,6 @@ static void parse_host_and_port(char *hostport, char **host,\n \t}\n }\n \n-#ifndef NO_IPV6\n-\n-static void locate_host(const char *hostname, char **ip_address,\n-\t\t\t\t\t\tchar **canon_hostname)\n-{\n-\tstruct addrinfo hints;\n-\tstruct addrinfo *ai;\n-\tint gai;\n-\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\tstruct sockaddr_in *sin_addr;\n-\n-\tmemset(&hints, 0, sizeof(hints));\n-\thints.ai_flags = AI_CANONNAME;\n-\n-\tgai = getaddrinfo(hostname, NULL, &hints, &ai);\n-\tif (gai)\n-\t\treturn;\n-\n-\tsin_addr = (void *)ai->ai_addr;\n-\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n-\tfree(*ip_address);\n-\t*ip_address = xstrdup(addrbuf);\n-\n-\tfree(*canon_hostname);\n-\t*canon_hostname = xstrdup(ai->ai_canonname ?\n-\t\t\t\t  ai->ai_canonname : *ip_address);\n-\n-\tfreeaddrinfo(ai);\n-}\n-\n-#else\n-\n-static void locate_host(const char *hostname, char **ip_address,\n-\t\t\t\t\t\tchar **canon_hostname)\n-{\n-\tstruct hostent *hent;\n-\tstruct sockaddr_in sa;\n-\tchar **ap;\n-\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\n-\thent = gethostbyname(hostname);\n-\n-\tap = hent->h_addr_list;\n-\tmemset(&sa, 0, sizeof sa);\n-\tsa.sin_family = hent->h_addrtype;\n-\tsa.sin_port = htons(0);\n-\tmemcpy(&sa.sin_addr, *ap, hent->h_length);\n-\n-\tinet_ntop(hent->h_addrtype, &sa.sin_addr,\n-\t\t  addrbuf, sizeof(addrbuf));\n-\n-\tfree(*canon_hostname);\n-\t*canon_hostname = xstrdup(hent->h_name);\n-\tfree(*ip_address);\n-\t*ip_address = xstrdup(addrbuf);\n-}\n-\n-#endif\n-\n /*\n  * Read the host as supplied by the client connection.\n  */\n@@ -539,7 +477,7 @@ static void parse_host_arg(char *extra_args, int buflen)\n \t * if possible.\n \t */\n \tif (hostname)\n-\t\tlocate_host(hostname, &ip_address, &canon_hostname);\n+\t\tgit_locate_host(hostname, &ip_address, &canon_hostname);\n }\n \n \ndiff --git a/tcp.c b/tcp.c\nindex f5e1ab37..9263e0d2 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -1,6 +1,10 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n \n+#ifndef HOST_NAME_MAX\n+#define HOST_NAME_MAX 256\n+#endif\n+\n #define STR_(s)\t# s\n #define STR(s)\tSTR_(s)\n \n@@ -47,6 +51,34 @@ static const char *ai_name(const struct addrinfo *ai)\n \treturn addr;\n }\n \n+void git_locate_host(const char *hostname, char **ip_address,\n+\t\t\t\t\tchar **canon_hostname)\n+{\n+\tstruct addrinfo hints;\n+\tstruct addrinfo *ai;\n+\tint gai;\n+\tstatic char addrbuf[HOST_NAME_MAX + 1];\n+\tstruct sockaddr_in *sin_addr;\n+\n+\tmemset(&hints, 0, sizeof(hints));\n+\thints.ai_flags = AI_CANONNAME;\n+\n+\tgai = getaddrinfo(hostname, NULL, &hints, &ai);\n+\tif (gai)\n+\t\treturn;\n+\n+\tsin_addr = (void *)ai->ai_addr;\n+\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n+\tfree(*ip_address);\n+\t*ip_address = xstrdup(addrbuf);\n+\n+\tfree(*canon_hostname);\n+\t*canon_hostname = xstrdup(ai->ai_canonname ?\n+\t\t\t\t  ai->ai_canonname : *ip_address);\n+\n+\tfreeaddrinfo(ai);\n+}\n+\n /*\n  * Returns a connected socket() fd, or else die()s.\n  */\n@@ -111,6 +143,31 @@ static int git_tcp_connect_sock(char *host, int flags)\n \n #else /* NO_IPV6 */\n \n+void git_locate_host(const char *hostname, char **ip_address,\n+\t\t\t\t\tchar **canon_hostname)\n+{\n+\tstruct hostent *hent;\n+\tstruct sockaddr_in sa;\n+\tchar **ap;\n+\tstatic char addrbuf[HOST_NAME_MAX + 1];\n+\n+\thent = gethostbyname(hostname);\n+\n+\tap = hent->h_addr_list;\n+\tmemset(&sa, 0, sizeof sa);\n+\tsa.sin_family = hent->h_addrtype;\n+\tsa.sin_port = htons(0);\n+\tmemcpy(&sa.sin_addr, *ap, hent->h_length);\n+\n+\tinet_ntop(hent->h_addrtype, &sa.sin_addr,\n+\t\t  addrbuf, sizeof(addrbuf));\n+\n+\tfree(*canon_hostname);\n+\t*canon_hostname = xstrdup(hent->h_name);\n+\tfree(*ip_address);\n+\t*ip_address = xstrdup(addrbuf);\n+}\n+\n /*\n  * Returns a connected socket() fd, or else die()s.\n  */\ndiff --git a/tcp.h b/tcp.h\nindex 4de5f712..bed3cdca 100644\n--- a/tcp.h\n+++ b/tcp.h\n@@ -1,6 +1,9 @@\n #ifndef TCP_H\n #define TCP_H\n \n+extern void git_locate_host(const char *hostname,\n+\t\t\tchar **ip_address, char **canon_hostname);\n+\n extern int git_use_proxy(const char *host);\n extern void git_tcp_connect(int fd[2], char *host, int flags);\n extern struct child_process *git_proxy_connect(int fd[2], char *host);\n-- \n1.7.9.2\n"},{"id":"186441","messageId":"20120308130913.GD9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 4/5] tcp: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:09:13Z","receivedAt":"2012-03-08T13:09:13Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Mon, 6 Jun 2011 04:41:28 -0500\n\nThe new DNS API abstracts away differences between the gethostbyname-\nand getaddrinfo-centric interfaces for looking up a host, making the\ncode to use them in connect.c a little easier to read.\n\nTo make a lookup:\n\n\tresolver_result ai;\n\tdns_resolve(host, port, 0, &ai);\n\t...\n\tdns_free(ai);\n\nTo iterate over responses:\n\n\tresolved_address i;\n\tfor_each_address(i, ai) {\n\t\t...\n\t}\n\nIn the !NO_IPV6 codepath, the git_locate_host function that is used to\nfind the canonical IP and hostname for a git server's public address\n(for virtual hosting) tells getaddrinfo to restrict attention to TCP\nservices after this patch.  That should make no difference because the\nservice parameter is NULL.\n\nNo functional change intended.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThis is the title feature, corresponding to\nhttp://thread.gmane.org/gmane.comp.version-control.git/175106/focus=175111\n\nIt needed changes to adjust to released changes in the code it touches,\nbut nothing is fundamentally different from v1.\n\n Makefile   |    5 ++\n dns-ipv4.c |   33 +++++++++++\n dns-ipv4.h |   69 +++++++++++++++++++++++\n dns-ipv6.c |   49 ++++++++++++++++\n dns-ipv6.h |   31 +++++++++++\n tcp.c      |  182 +++++++++++-------------------------------------------------\n 6 files changed, 218 insertions(+), 151 deletions(-)\n create mode 100644 dns-ipv4.c\n create mode 100644 dns-ipv4.h\n create mode 100644 dns-ipv6.c\n create mode 100644 dns-ipv6.h\n\ndiff --git a/Makefile b/Makefile\nindex 927a079a..8b603895 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1612,6 +1612,11 @@ ifdef NO_TRUSTABLE_FILEMODE\n endif\n ifdef NO_IPV6\n \tBASIC_CFLAGS += -DNO_IPV6\n+\tLIB_OBJS += dns-ipv4.o\n+\tLIB_H += dns-ipv4.h\n+else\n+\tLIB_OBJS += dns-ipv6.o\n+\tLIB_H += dns-ipv6.h\n endif\n ifdef NO_UINTMAX_T\n \tBASIC_CFLAGS += -Duintmax_t=uint32_t\ndiff --git a/dns-ipv4.c b/dns-ipv4.c\nnew file mode 100644\nindex 00000000..911a8569\n--- /dev/null\n+++ b/dns-ipv4.c\n@@ -0,0 +1,33 @@\n+#include \"cache.h\"\n+#include \"dns-ipv4.h\"\n+\n+int dns_resolve(const char *host, const char *port, int flags,\n+\t\tresolver_result *res)\n+{\n+\tchar *ep;\n+\tstruct hostent *he;\n+\tunsigned int nport;\n+\n+\the = gethostbyname(host);\n+\tif (!he && (flags & RESOLVE_FAIL_QUIETLY))\n+\t\treturn -1;\n+\tif (!he)\n+\t\tdie(\"Unable to look up %s (%s)\", host, hstrerror(h_errno));\n+\n+\tif (!port) {\n+\t\tnport = 0;\n+\t\tgoto done;\n+\t}\n+\tnport = strtoul(port, &ep, 10);\n+\tif ( ep == port || *ep ) {\n+\t\t/* Not numeric */\n+\t\tstruct servent *se = getservbyname(port,\"tcp\");\n+\t\tif ( !se )\n+\t\t\tdie(\"Unknown port %s\", port);\n+\t\tnport = se->s_port;\n+\t}\n+done:\n+\tres->he = he;\n+\tres->port = nport;\n+\treturn 0;\n+}\ndiff --git a/dns-ipv4.h b/dns-ipv4.h\nnew file mode 100644\nindex 00000000..6803bcba\n--- /dev/null\n+++ b/dns-ipv4.h\n@@ -0,0 +1,69 @@\n+#ifndef DNS_IPV4_H\n+#define DNS_IPV4_H\n+\n+#ifndef HOST_NAME_MAX\n+#define HOST_NAME_MAX 256\n+#endif\n+\n+struct ipv4_address {\n+\tchar **ap;\n+\tstruct sockaddr_in sa;\n+};\n+\n+struct ipv4_addrinfo {\n+\tstruct hostent *he;\n+\tunsigned int port;\n+};\n+\n+typedef struct ipv4_addrinfo resolver_result;\n+typedef struct ipv4_address resolved_address;\n+\n+enum {\n+\tRESOLVE_CANONNAME = 1,\n+\tRESOLVE_FAIL_QUIETLY = 2\n+};\n+extern int dns_resolve(const char *host, const char *port, int flags,\n+\t\t\tresolver_result *res);\n+\n+static inline const char *dns_name(const resolved_address *addr)\n+{\n+\treturn inet_ntoa(*(struct in_addr *)&addr->sa.sin_addr);\n+}\n+\n+static inline char *dns_ip_address(const resolved_address *addr,\n+\t\t\t\t\tconst resolver_result *ai)\n+{\n+\tchar addrbuf[HOST_NAME_MAX + 1];\n+\tinet_ntop(ai->he->h_addrtype, &addr->sa.sin_addr,\n+\t\t  addrbuf, sizeof(addrbuf));\n+\treturn xstrdup(addrbuf);\n+}\n+\n+static inline int dns_fill_sockaddr_(char *ap,\n+\t\tconst struct ipv4_addrinfo *ai, struct sockaddr_in *sa)\n+{\n+\tif (!ap)\t/* done. */\n+\t\treturn -1;\n+\n+\tmemset(sa, 0, sizeof(*sa));\n+\tsa->sin_family = ai->he->h_addrtype;\n+\tsa->sin_port = htons(ai->port);\n+\tmemcpy(&sa->sin_addr, ap, ai->he->h_length);\n+\treturn 0;\n+}\n+\n+#define for_each_address(addr, ai) \\\n+\tfor ((addr).ap = (ai).he->h_addr_list; \\\n+\t     !dns_fill_sockaddr_(*(addr).ap, &(ai), &(addr).sa); \\\n+\t     (addr).ap++)\n+\n+#define dns_family(addr, ai) ((ai).he->h_addrtype)\n+#define dns_socktype(addr, ai) SOCK_STREAM\n+#define dns_protocol(addr, ai) 0\n+#define dns_addr(addr, ai) ((struct sockaddr *) &(addr).sa)\n+#define dns_addrlen(addr, ai) sizeof((addr).sa)\n+#define dns_canonname(addr, ai) ((ai).he->h_name)\n+\n+#define dns_free(ai) do { /* nothing */ } while (0)\n+\n+#endif\ndiff --git a/dns-ipv6.c b/dns-ipv6.c\nnew file mode 100644\nindex 00000000..ca59ff91\n--- /dev/null\n+++ b/dns-ipv6.c\n@@ -0,0 +1,49 @@\n+#include \"cache.h\"\n+#include \"dns-ipv6.h\"\n+\n+#ifndef HOST_NAME_MAX\n+#define HOST_NAME_MAX 256\n+#endif\n+\n+const char *dns_name(const resolved_address *i)\n+{\n+\tconst struct addrinfo *ai = *i;\n+\tstatic char addr[NI_MAXHOST];\n+\tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,\n+\t\t\tNI_NUMERICHOST) != 0)\n+\t\tstrcpy(addr, \"(unknown)\");\n+\n+\treturn addr;\n+}\n+\n+char *dns_ip_address(const resolved_address *i, const resolver_result *ai0)\n+{\n+\tconst struct addrinfo *ai = *i;\n+\tchar addrbuf[HOST_NAME_MAX + 1];\n+\tstruct sockaddr_in *sin_addr;\n+\n+\tsin_addr = (void *)ai->ai_addr;\n+\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n+\treturn xstrdup(addrbuf);\n+}\n+\n+int dns_resolve(const char *host, const char *port, int flags,\n+\t\tresolver_result *res)\n+{\n+\tstruct addrinfo hints;\n+\tint gai;\n+\n+\tmemset(&hints, 0, sizeof(hints));\n+\tif (flags & RESOLVE_CANONNAME)\n+\t\thints.ai_flags = AI_CANONNAME;\n+\thints.ai_socktype = SOCK_STREAM;\n+\thints.ai_protocol = IPPROTO_TCP;\n+\n+\tgai = getaddrinfo(host, port, &hints, res);\n+\tif (gai && (flags & RESOLVE_FAIL_QUIETLY))\n+\t\treturn -1;\n+\tif (gai)\n+\t\tdie(\"Unable to look up %s (port %s) (%s)\", host, port, gai_strerror(gai));\n+\n+\treturn 0;\n+}\ndiff --git a/dns-ipv6.h b/dns-ipv6.h\nnew file mode 100644\nindex 00000000..16bf84b5\n--- /dev/null\n+++ b/dns-ipv6.h\n@@ -0,0 +1,31 @@\n+#ifndef DNS_IPV6_H\n+#define DNS_IPV6_H\n+\n+typedef struct addrinfo *resolver_result;\n+typedef const struct addrinfo *resolved_address;\n+\n+enum {\n+\tRESOLVE_CANONNAME = 1,\n+\tRESOLVE_FAIL_QUIETLY = 2\n+};\n+extern int dns_resolve(const char *host, const char *port, int flags,\n+\t\t\tresolver_result *res);\n+/* result is in static buffer */\n+extern const char *dns_name(const resolved_address *i);\n+/* result is in malloc'ed buffer */\n+extern char *dns_ip_address(const resolved_address *i,\n+\t\t\t\tconst resolver_result *ai);\n+\n+#define for_each_address(i, ai) \\\n+\tfor (i = ai; i; i = (i)->ai_next)\n+\n+#define dns_family(i, ai) ((i)->ai_family)\n+#define dns_socktype(i, ai) ((i)->ai_socktype)\n+#define dns_protocol(i, ai) ((i)->ai_protocol)\n+#define dns_addr(i, ai) ((i)->ai_addr)\n+#define dns_addrlen(i, ai) ((i)->ai_addrlen)\n+#define dns_canonname(i, ai) ((i)->ai_canonname)\n+\n+#define dns_free(ai) freeaddrinfo(ai)\n+\n+#endif\ndiff --git a/tcp.c b/tcp.c\nindex 9263e0d2..4239daf3 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -1,8 +1,10 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n \n-#ifndef HOST_NAME_MAX\n-#define HOST_NAME_MAX 256\n+#ifndef NO_IPV6\n+#include \"dns-ipv6.h\"\n+#else\n+#include \"dns-ipv4.h\"\n #endif\n \n #define STR_(s)\t# s\n@@ -39,44 +41,27 @@ static void enable_keepalive(int sockfd)\n \t\t\tstrerror(errno));\n }\n \n-#ifndef NO_IPV6\n-\n-static const char *ai_name(const struct addrinfo *ai)\n-{\n-\tstatic char addr[NI_MAXHOST];\n-\tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), NULL, 0,\n-\t\t\tNI_NUMERICHOST) != 0)\n-\t\tstrcpy(addr, \"(unknown)\");\n-\n-\treturn addr;\n-}\n-\n void git_locate_host(const char *hostname, char **ip_address,\n \t\t\t\t\tchar **canon_hostname)\n {\n-\tstruct addrinfo hints;\n-\tstruct addrinfo *ai;\n-\tint gai;\n-\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\tstruct sockaddr_in *sin_addr;\n+\tresolver_result ai;\n+\tresolved_address i;\n \n-\tmemset(&hints, 0, sizeof(hints));\n-\thints.ai_flags = AI_CANONNAME;\n-\n-\tgai = getaddrinfo(hostname, NULL, &hints, &ai);\n-\tif (gai)\n+\tif (dns_resolve(hostname, NULL,\n+\t\t\tRESOLVE_CANONNAME | RESOLVE_FAIL_QUIETLY, &ai))\n \t\treturn;\n \n-\tsin_addr = (void *)ai->ai_addr;\n-\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n-\tfree(*ip_address);\n-\t*ip_address = xstrdup(addrbuf);\n+\tfor_each_address(i, ai) {\n+\t\tfree(*ip_address);\n+\t\t*ip_address = dns_ip_address(&i, &ai);\n \n-\tfree(*canon_hostname);\n-\t*canon_hostname = xstrdup(ai->ai_canonname ?\n-\t\t\t\t  ai->ai_canonname : *ip_address);\n+\t\tfree(*canon_hostname);\n+\t\t*canon_hostname = xstrdup(dns_canonname(i, ai) ?\n+\t\t\t\t\tdns_canonname(i, ai) : *ip_address);\n+\t\tbreak;\n+\t}\n \n-\tfreeaddrinfo(ai);\n+\tdns_free(ai);\n }\n \n /*\n@@ -87,46 +72,42 @@ static int git_tcp_connect_sock(char *host, int flags)\n \tstruct strbuf error_message = STRBUF_INIT;\n \tint sockfd = -1;\n \tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tstruct addrinfo hints, *ai0, *ai;\n-\tint gai;\n-\tint cnt = 0;\n+\tresolver_result ai;\n+\tresolved_address i;\n+\tint cnt = -1;\n \n \tget_host_and_port(&host, &port);\n \tif (!*port)\n \t\tport = \"<none>\";\n \n-\tmemset(&hints, 0, sizeof(hints));\n-\thints.ai_socktype = SOCK_STREAM;\n-\thints.ai_protocol = IPPROTO_TCP;\n-\n \tif (flags & CONNECT_VERBOSE)\n \t\tfprintf(stderr, \"Looking up %s ... \", host);\n \n-\tgai = getaddrinfo(host, port, &hints, &ai);\n-\tif (gai)\n-\t\tdie(\"Unable to look up %s (port %s) (%s)\", host, port, gai_strerror(gai));\n+\tif (dns_resolve(host, port, 0, &ai))\n+\t\tdie(\"BUG: dns_resolve returned error?\");\n \n \tif (flags & CONNECT_VERBOSE)\n \t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n \n-\tfor (ai0 = ai; ai; ai = ai->ai_next, cnt++) {\n-\t\tsockfd = socket(ai->ai_family,\n-\t\t\t\tai->ai_socktype, ai->ai_protocol);\n-\t\tif ((sockfd < 0) ||\n-\t\t    (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)) {\n+\tfor_each_address(i, ai) {\n+\t\tcnt++;\n+\t\tsockfd = socket(dns_family(i, ai),\n+\t\t\t\tdns_socktype(i, ai), dns_protocol(i, ai));\n+\t\tif (sockfd < 0 ||\n+\t\t    connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n \t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\t    host, cnt, ai_name(ai), strerror(errno));\n+\t\t\t\t    host, cnt, dns_name(&i), strerror(errno));\n \t\t\tif (0 <= sockfd)\n \t\t\t\tclose(sockfd);\n \t\t\tsockfd = -1;\n \t\t\tcontinue;\n \t\t}\n \t\tif (flags & CONNECT_VERBOSE)\n-\t\t\tfprintf(stderr, \"%s \", ai_name(ai));\n+\t\t\tfprintf(stderr, \"%s \", dns_name(&i));\n \t\tbreak;\n \t}\n \n-\tfreeaddrinfo(ai0);\n+\tdns_free(ai);\n \n \tif (sockfd < 0)\n \t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n@@ -141,107 +122,6 @@ static int git_tcp_connect_sock(char *host, int flags)\n \treturn sockfd;\n }\n \n-#else /* NO_IPV6 */\n-\n-void git_locate_host(const char *hostname, char **ip_address,\n-\t\t\t\t\tchar **canon_hostname)\n-{\n-\tstruct hostent *hent;\n-\tstruct sockaddr_in sa;\n-\tchar **ap;\n-\tstatic char addrbuf[HOST_NAME_MAX + 1];\n-\n-\thent = gethostbyname(hostname);\n-\n-\tap = hent->h_addr_list;\n-\tmemset(&sa, 0, sizeof sa);\n-\tsa.sin_family = hent->h_addrtype;\n-\tsa.sin_port = htons(0);\n-\tmemcpy(&sa.sin_addr, *ap, hent->h_length);\n-\n-\tinet_ntop(hent->h_addrtype, &sa.sin_addr,\n-\t\t  addrbuf, sizeof(addrbuf));\n-\n-\tfree(*canon_hostname);\n-\t*canon_hostname = xstrdup(hent->h_name);\n-\tfree(*ip_address);\n-\t*ip_address = xstrdup(addrbuf);\n-}\n-\n-/*\n- * Returns a connected socket() fd, or else die()s.\n- */\n-static int git_tcp_connect_sock(char *host, int flags)\n-{\n-\tstruct strbuf error_message = STRBUF_INIT;\n-\tint sockfd = -1;\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tchar *ep;\n-\tstruct hostent *he;\n-\tstruct sockaddr_in sa;\n-\tchar **ap;\n-\tunsigned int nport;\n-\tint cnt;\n-\n-\tget_host_and_port(&host, &port);\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"Looking up %s ... \", host);\n-\n-\the = gethostbyname(host);\n-\tif (!he)\n-\t\tdie(\"Unable to look up %s (%s)\", host, hstrerror(h_errno));\n-\tnport = strtoul(port, &ep, 10);\n-\tif ( ep == port || *ep ) {\n-\t\t/* Not numeric */\n-\t\tstruct servent *se = getservbyname(port,\"tcp\");\n-\t\tif ( !se )\n-\t\t\tdie(\"Unknown port %s\", port);\n-\t\tnport = se->s_port;\n-\t}\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n-\n-\tfor (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {\n-\t\tmemset(&sa, 0, sizeof sa);\n-\t\tsa.sin_family = he->h_addrtype;\n-\t\tsa.sin_port = htons(nport);\n-\t\tmemcpy(&sa.sin_addr, *ap, he->h_length);\n-\n-\t\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n-\t\tif ((sockfd < 0) ||\n-\t\t    connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n-\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\thost,\n-\t\t\t\tcnt,\n-\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr),\n-\t\t\t\tstrerror(errno));\n-\t\t\tif (0 <= sockfd)\n-\t\t\t\tclose(sockfd);\n-\t\t\tsockfd = -1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (flags & CONNECT_VERBOSE)\n-\t\t\tfprintf(stderr, \"%s \",\n-\t\t\t\tinet_ntoa(*(struct in_addr *)&sa.sin_addr));\n-\t\tbreak;\n-\t}\n-\n-\tif (sockfd < 0)\n-\t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n-\n-\tenable_keepalive(sockfd);\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\n\");\n-\n-\treturn sockfd;\n-}\n-\n-#endif /* NO_IPV6 */\n-\n-\n void git_tcp_connect(int fd[2], char *host, int flags)\n {\n \tint sockfd = git_tcp_connect_sock(host, flags);\n-- \n1.7.9.2\n"},{"id":"186442","messageId":"20120308131153.GE9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 5/5] daemon: check for errors retrieving IP address","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:11:53Z","receivedAt":"2012-03-08T13:11:53Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Fri, 17 Jun 2011 05:26:05 -0500\n\nTo retrieve a canonical IP address for possible use in a\n--interpolated-path=%IP string, the git daemon calls inet_ntop to fill\na buffer of size HOST_NAME_MAX+1, ignoring errors, and copies the\nresult.  If the address has length > HOST_NAME_MAX (which was as low\nas 8 in ancient times) then inet_ntop could error out, leading the git\ndaemon to copy uninitialized data.\n\nThis probably never happens in practice because modern systems allow\nsuch long hostnames as 64 chars (POSIX requires at least 255) or have\nno compile-time maximum at all and let git fall back to a buffer size\nof 256.  But it is more comforting not to rely on that.  So:\n\n - in \"ipv4\" code, which uses inet_ntop, use a buffer size of 64 (long\n   enough to hold an ipv6 address if needed).\n\n - in ipv6 code, use the \"getnameinfo\" function that is already being\n   used to convert addresses to text in error messages.  It uses a\n   buffer of length NI_MAXHOST.\n\n - check for errors and make dns_ip_address() and git_locate_host()\n   return NULL when they occur.  strbuf_expand_dict_cb treats NULL as\n   an empty substitution string.\n\nAs a nice side effect, the fallback definition of HOST_NAME_MAX for\nplatforms that don't define it is no longer needed.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThis patch allows getnameinfo and inet_ntop to use a larger buffer or\nfail.  (I was mostly nervous because HOST_NAME_MAX is not guaranteed\nto be large enough to hold an IP address.)\n\nThat's the end of the series.  Thanks for reading.\n\n dns-ipv4.h |   11 +++++------\n dns-ipv6.c |   16 ++++++++--------\n tcp.c      |    6 ++++--\n 3 files changed, 17 insertions(+), 16 deletions(-)\n\ndiff --git a/dns-ipv4.h b/dns-ipv4.h\nindex 6803bcba..c5f1778f 100644\n--- a/dns-ipv4.h\n+++ b/dns-ipv4.h\n@@ -1,9 +1,7 @@\n #ifndef DNS_IPV4_H\n #define DNS_IPV4_H\n \n-#ifndef HOST_NAME_MAX\n-#define HOST_NAME_MAX 256\n-#endif\n+#define ADDRBUFLEN 64\t/* 46 for an ipv6 address, plus a little extra */\n \n struct ipv4_address {\n \tchar **ap;\n@@ -33,9 +31,10 @@ static inline const char *dns_name(const resolved_address *addr)\n static inline char *dns_ip_address(const resolved_address *addr,\n \t\t\t\t\tconst resolver_result *ai)\n {\n-\tchar addrbuf[HOST_NAME_MAX + 1];\n-\tinet_ntop(ai->he->h_addrtype, &addr->sa.sin_addr,\n-\t\t  addrbuf, sizeof(addrbuf));\n+\tchar addrbuf[ADDRBUFLEN];\n+\tif (!inet_ntop(ai->he->h_addrtype, &addr->sa.sin_addr,\n+\t\t  addrbuf, sizeof(addrbuf)))\n+\t\treturn NULL;\n \treturn xstrdup(addrbuf);\n }\n \ndiff --git a/dns-ipv6.c b/dns-ipv6.c\nindex ca59ff91..f2325681 100644\n--- a/dns-ipv6.c\n+++ b/dns-ipv6.c\n@@ -1,8 +1,9 @@\n #include \"cache.h\"\n #include \"dns-ipv6.h\"\n \n-#ifndef HOST_NAME_MAX\n-#define HOST_NAME_MAX 256\n+/* from RFC 2553 */\n+#ifndef NI_MAXHOST\n+#define NI_MAXHOST 1025\n #endif\n \n const char *dns_name(const resolved_address *i)\n@@ -19,12 +20,11 @@ const char *dns_name(const resolved_address *i)\n char *dns_ip_address(const resolved_address *i, const resolver_result *ai0)\n {\n \tconst struct addrinfo *ai = *i;\n-\tchar addrbuf[HOST_NAME_MAX + 1];\n-\tstruct sockaddr_in *sin_addr;\n-\n-\tsin_addr = (void *)ai->ai_addr;\n-\tinet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));\n-\treturn xstrdup(addrbuf);\n+\tchar addr[NI_MAXHOST];\n+\tif (getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr),\n+\t\t\tNULL, 0, NI_NUMERICHOST))\n+\t\treturn NULL;\n+\treturn xstrdup(addr);\n }\n \n int dns_resolve(const char *host, const char *port, int flags,\ndiff --git a/tcp.c b/tcp.c\nindex 4239daf3..83f0313a 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -56,8 +56,10 @@ void git_locate_host(const char *hostname, char **ip_address,\n \t\t*ip_address = dns_ip_address(&i, &ai);\n \n \t\tfree(*canon_hostname);\n-\t\t*canon_hostname = xstrdup(dns_canonname(i, ai) ?\n-\t\t\t\t\tdns_canonname(i, ai) : *ip_address);\n+\t\t*canon_hostname =\n+\t\t\tdns_canonname(i, ai) ? xstrdup(dns_canonname(i, ai)) :\n+\t\t\t*ip_address ? xstrdup(*ip_address) :\n+\t\t\tNULL;\n \t\tbreak;\n \t}\n \n-- \n1.7.9.2\n"},{"id":"186443","messageId":"20120308131620.GF9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 6/5] tcp: make dns_resolve() return an error code","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:16:20Z","receivedAt":"2012-03-08T13:16:20Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Thu, 23 Jun 2011 05:23:57 -0500\n\ngetaddrinfo() and gethostbyname() use error codes to describe their\nerrors.  Pass that error code back to the caller when dns_resolve()\nfails and the RESOLVE_FAIL_QUIETLY flag is set, so callers can save\nthe diagnosis and print it later:\n\n\tint saved_rerrno = dns_resolve(...);\n\t...\n\tif (saved_rerrno)\n\t\tdie(\"resolver failed: %s\", dns_strerror(saved_rerrno));\n\nIn the ipv4 codepath, we assume that h_errno is never 0 on error.\nPOSIX.1-2004 does not specify whether 0 is a valid value for h_errno,\nbut luckily common practice is for h_errno to be a strictly positive\ninteger (HOST_NOT_FOUND = 1, NO_DATA = 2, NO_RECOVERY = 3, or\nTRY_AGAIN = 4).  If gethostbyname errors out with h_errno == 0 on some\nplatform, just let git die with a message indicating a BUG so the bad\nassumption can be corrected.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThis makes saving up errors from a host resolution failure as in\nv1.7.7-rc0~40^2 (connect: only log if all attempts failed, 2011-08-01)\npossible.  By the way, this series is currently against \"maint\" for no\nparticular reason and presumably it would conflict with that patch. ;-)\n\nI'll be happy to rebase against \"master\" some time soon.\n\n dns-ipv4.c |    7 +++++--\n dns-ipv4.h |    5 +++++\n dns-ipv6.c |    2 +-\n dns-ipv6.h |    1 +\n 4 files changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/dns-ipv4.c b/dns-ipv4.c\nindex 911a8569..8820c9e8 100644\n--- a/dns-ipv4.c\n+++ b/dns-ipv4.c\n@@ -9,8 +9,11 @@ int dns_resolve(const char *host, const char *port, int flags,\n \tunsigned int nport;\n \n \the = gethostbyname(host);\n-\tif (!he && (flags & RESOLVE_FAIL_QUIETLY))\n-\t\treturn -1;\n+\tif (!he && (flags & RESOLVE_FAIL_QUIETLY)) {\n+\t\tif (!h_errno)\n+\t\t\tdie(\"BUG: gethostbyname failed but h_errno == 0\");\n+\t\treturn h_errno;\n+\t}\n \tif (!he)\n \t\tdie(\"Unable to look up %s (%s)\", host, hstrerror(h_errno));\n \ndiff --git a/dns-ipv4.h b/dns-ipv4.h\nindex c5f1778f..6f70d639 100644\n--- a/dns-ipv4.h\n+++ b/dns-ipv4.h\n@@ -18,6 +18,10 @@ typedef struct ipv4_address resolved_address;\n \n enum {\n \tRESOLVE_CANONNAME = 1,\n+\t/*\n+\t * Quietly return an error code instead of exiting on error.\n+\t * Callers can use dns_strerror() to get an error string.\n+\t */\n \tRESOLVE_FAIL_QUIETLY = 2\n };\n extern int dns_resolve(const char *host, const char *port, int flags,\n@@ -63,6 +67,7 @@ static inline int dns_fill_sockaddr_(char *ap,\n #define dns_addrlen(addr, ai) sizeof((addr).sa)\n #define dns_canonname(addr, ai) ((ai).he->h_name)\n \n+#define dns_strerror(n) hstrerror(n)\n #define dns_free(ai) do { /* nothing */ } while (0)\n \n #endif\ndiff --git a/dns-ipv6.c b/dns-ipv6.c\nindex f2325681..0b0e0602 100644\n--- a/dns-ipv6.c\n+++ b/dns-ipv6.c\n@@ -41,7 +41,7 @@ int dns_resolve(const char *host, const char *port, int flags,\n \n \tgai = getaddrinfo(host, port, &hints, res);\n \tif (gai && (flags & RESOLVE_FAIL_QUIETLY))\n-\t\treturn -1;\n+\t\treturn gai;\n \tif (gai)\n \t\tdie(\"Unable to look up %s (port %s) (%s)\", host, port, gai_strerror(gai));\n \ndiff --git a/dns-ipv6.h b/dns-ipv6.h\nindex 16bf84b5..4211c9e2 100644\n--- a/dns-ipv6.h\n+++ b/dns-ipv6.h\n@@ -26,6 +26,7 @@ extern char *dns_ip_address(const resolved_address *i,\n #define dns_addrlen(i, ai) ((i)->ai_addrlen)\n #define dns_canonname(i, ai) ((i)->ai_canonname)\n \n+#define dns_strerror(gai) gai_strerror(gai)\n #define dns_free(ai) freeaddrinfo(ai)\n \n #endif\n-- \n1.7.9.2\n"},{"id":"186445","messageId":"20120308132155.GG9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 7/5] transport: optionally honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:21:55Z","receivedAt":"2012-03-08T13:21:55Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Date: Mon, 6 Jun 2011 04:46:20 -0500\n\nSRV records are an extra layer of indirection on top of A/AAAA records\nmapping from domain names and service types to the hostnames of\nmachines that offer that service.  That has a couple of nice effects:\n\n - a single domain can use different hosts for different services\n - a single domain can use multiple hosts for one service\n\nTeach git to perform a SRV lookup whenever resolving a git:// URL.\nThis means:\n\n - if your git server was previously the same machine as your wesnoth\n   server, you can move it to a separate machine without forcing\n   everyone to update their links to the old URLs\n\n - if you have a primary git server and a backup machine that should\n   be used when the primary server goes down, the client can\n   automatically take care of it\n\n - if you have multiple git servers and would like to spread load\n   between them, the client can automatically take care of it\n\nThat is, SRV records let us ask the client to carry out various tasks\nthat would require a proxy on the server side with traditional DNS.\nThe client performs a SRV query to _git._tcp.<domain name> to receive\nits instructions.  RFC 2782 has details.\n\nIdeally reaping these benefits would just involve passing a special\nflag to getaddrinfo().  Since we don't live in such a world, this\npatch uses the BIND 8 API provided by libresolv to parse the response\nfor ourselves.\n\nRFC 2782 requires some non-determinism in the order of hosts\ncontacted; this patch uses drand48() for that.  To avoid causing\ntrouble for platforms that lack the libbind ns_* functions or\ndrand48(), the SRV support is only provided when requested by setting\nthe USE_SRV_RR compile-time option.\n\ngit servers must ensure that they can also be reached by a plain\nA/AAAA lookup to support git clients without SRV support, for example\nby proxying connections to an appropriate server:\n\n\t# in inetd.conf\n\tgit stream tcp nowait.400 nobody /usr/sbin/tcpd \\\n\t\t/bin/nc -q0 gitserver.example.com git\n\nRegression: this uglifies error messages for connection errors a\nlittle.  It would probably be better to leave out the connection count\nwhen we are not trying more than one server.\n\nBased on a patch by Julien Cristau <jcristau@debian.org>.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nAs I mentioned at the gittogether, I think this would be a valuable\nfeature in git.  You can test it out (though this is only the simple\ncase, no load balancing or failover) by doing\n\n\tgit clone git://git.debian.org/~jrnieder-guest/git.git\n\nand watching what happens with wireshark.\n\nThe patch needs documentation.  Maybe a howto and an addendum to the\nprotocol docs would do.  Anyway, I hope it's at least entertaining in\nthe current state.\n\n Makefile |   10 ++\n srv.c    |  321 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n srv.h    |   15 +++\n tcp.c    |  100 ++++++++++++++------\n 4 files changed, 418 insertions(+), 28 deletions(-)\n create mode 100644 srv.c\n create mode 100644 srv.h\n\ndiff --git a/Makefile b/Makefile\nindex 8b603895..56198f11 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -148,6 +148,10 @@ all::\n # Notably on Solaris hstrerror resides in libresolv and on Solaris 7\n # inet_ntop and inet_pton additionally reside there.\n #\n+# Define USE_SRV_RR if you want git to pay attention to SRV resource records\n+# when looking up servers to contact over git protocol.  This implies\n+# NEEDS_RESOLV.\n+#\n # Define NO_MMAP if you want to avoid mmap.\n #\n # Define NO_PTHREADS if you do not have or do not want to use Pthreads.\n@@ -1490,6 +1494,11 @@ endif\n ifdef NEEDS_NSL\n \tEXTLIBS += -lnsl\n endif\n+ifdef USE_SRV_RR\n+\tBASIC_CFLAGS += -DUSE_SRV_RR\n+\tLIB_OBJS += srv.o\n+\tNEEDS_RESOLV = YesPlease\n+endif\n ifdef NEEDS_RESOLV\n \tEXTLIBS += -lresolv\n endif\n@@ -2155,6 +2164,7 @@ builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o url.o http-backend.o: url.h\n connect.o daemon.o tcp.o: tcp.h\n+tcp.o srv.o: srv.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/srv.c b/srv.c\nnew file mode 100644\nindex 00000000..2716206e\n--- /dev/null\n+++ b/srv.c\n@@ -0,0 +1,321 @@\n+#include \"git-compat-util.h\"\n+#include \"strbuf.h\"\n+#include \"srv.h\"\n+\n+#include <arpa/nameser.h>\n+#include <resolv.h>\n+\n+struct parsed_srv_rr {\n+\tint priority;\n+\tint weight;\n+\tint port;\n+\tchar *target;\n+};\n+\n+static void srv_swap(struct parsed_srv_rr *p1, struct parsed_srv_rr *p2)\n+{\n+\tchar *a, *b;\n+\tint size = sizeof(struct parsed_srv_rr);\n+\n+\tfor (a = (char *) p1, b = (char *) p2; size; size--) {\n+\t\tchar t = *a;\n+\t\t*a++ = *b;\n+\t\t*b++ = t;\n+\t}\n+}\n+\n+static int priority_compare(const void *p1, const void *p2)\n+{\n+\tconst struct parsed_srv_rr *a = p1, *b = p2;\n+\n+\t/* can't overflow because priorities are 16 bits wide */\n+\treturn b->priority - a->priority;\n+}\n+\n+static int get_qname_for_srv(struct strbuf *sb, const char *host)\n+{\n+\tconst char prefix[] = \"_git._tcp.\";\n+\tsize_t hostlen;\n+\n+\thostlen = strlen(host);\n+\tif (unsigned_add_overflows(strlen(prefix) + 1, hostlen))\n+\t\treturn error(\"absurdly long hostname\");\n+\n+\tstrbuf_reset(sb);\n+\tstrbuf_grow(sb, strlen(prefix) + hostlen);\n+\tstrbuf_add(sb, prefix, strlen(prefix));\n+\tstrbuf_add(sb, host, hostlen);\n+\treturn 0;\n+}\n+\n+static int srv_parse_rr(const ns_msg *msg,\n+\t\t\tconst ns_rr *rr, struct parsed_srv_rr *res)\n+{\n+\tconst unsigned char *p;\n+\tchar buf[1024];\n+\n+\tif (ns_rr_rdlen(*rr) < 2+2+2 /* priority, weight, port */)\n+\t\treturn error(\"SRV RR is too short\");\n+\n+\tp = ns_rr_rdata(*rr);\n+\tres->priority = *p++ << CHAR_BIT;\n+\tres->priority += *p++;\n+\n+\tres->weight = *p++ << CHAR_BIT;\n+\tres->weight += *p++;\n+\n+\tres->port = *p++ << CHAR_BIT;\n+\tres->port += *p++;\n+\n+\t/*\n+\t * RFC2782 doesn't allow compressed target domain names but we\n+\t * might as well accept them anyway.\n+\t */\n+\tif (dn_expand(ns_msg_base(*msg), ns_msg_end(*msg), p,\n+\t\t\tbuf, sizeof(buf)) < 0)\n+\t\treturn error(\"cannot expand target domain name in SRV RR\");\n+\n+\tres->target = xstrdup(buf);\n+\treturn 0;\n+}\n+\n+static int srv_parse(ns_msg *msg, struct parsed_srv_rr **res)\n+{\n+\tstruct parsed_srv_rr *rrs = NULL;\n+\tint nr_parsed = 0;\n+\tint cnames = 0;\n+\tint i, n;\n+\n+\tn = ns_msg_count(*msg, ns_s_an);\n+\n+\t/* skip CNAME records */\n+\tfor (i = 0; i < n; i++) {\n+\t\tns_rr rr;\n+\t\tif (ns_parserr(msg, ns_s_an, i, &rr)) {\n+\t\t\terror(\"cannot parse DNS RR: %s\", strerror(errno));\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tif (ns_rr_type(rr) != ns_t_cname)\n+\t\t\tbreak;\n+\t}\n+\tcnames = i;\n+\tn -= cnames;\n+\n+\trrs = xmalloc(n * sizeof(*rrs));\n+\tfor (i = 0; i < n; i++) {\n+\t\tns_rr rr;\n+\n+\t\tif (ns_parserr(msg, ns_s_an, cnames + i, &rr)) {\n+\t\t\terror(\"cannot parse DNS RR: %s\", strerror(errno));\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tif (ns_rr_type(rr) != ns_t_srv) {\n+\t\t\terror(\"expected SRV RR, found RR type %d\",\n+\t\t\t\t\t\t(int) ns_rr_type(rr));\n+\t\t\tgoto fail;\n+\t\t}\n+\t\tif (srv_parse_rr(msg, &rr, rrs + i))\n+\t\t\t/* srv_parse_rr writes a message */\n+\t\t\tgoto fail;\n+\t\tnr_parsed++;\n+\t}\n+\n+\t*res = rrs;\n+\treturn n;\n+fail:\n+\tfor (i = 0; i < nr_parsed; i++)\n+\t\tfree(rrs[i].target);\n+\tfree(rrs);\n+\treturn -1;\n+}\n+\n+static int weighted_item(struct parsed_srv_rr *rrs, int n, unsigned int pos)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tunsigned int wt = rrs[i].weight;\n+\n+\t\tif (pos <= wt)\n+\t\t\tbreak;\n+\t\tpos -= wt;\n+\t}\n+\treturn i;\n+}\n+\n+static void shuffle_one(struct parsed_srv_rr *rrs, int n,\n+\t\t\t\tint i, int *j, unsigned int *wt_remaining)\n+{\n+\tunsigned int pos;\n+\tint k;\n+\n+\tpos = (unsigned int) ((*wt_remaining + 1) * drand48());\n+\n+\tif (!pos) {\n+\t\t*wt_remaining -= rrs[i].weight;\n+\t\treturn;\n+\t}\n+\n+\t/* Which item will take the place of rrs[i]? */\n+\tif (*j < i)\n+\t\t*j = i;\n+\tk = *j + weighted_item(rrs + *j, n - *j, pos);\n+\n+\tassert(k < n);\n+\t*wt_remaining -= rrs[k].weight;\n+\n+\tif (k == i)\n+\t\treturn;\n+\n+\tsrv_swap(rrs + i, rrs + k);\n+\n+\t/*\n+\t * If rrs[i] had weight zero, move it to stay in the clump\n+\t * of weight-zero records.  rrs[k] cannot have had weight\n+\t * zero because pos > 0.\n+\t */\n+\tassert(*j <= k);\n+\tif (i < *j) {\n+\t\tsrv_swap(rrs + k, rrs + *j);\n+\t\t(*j)++;\n+\t}\n+}\n+\n+static void weighted_shuffle(struct parsed_srv_rr *rrs, int n)\n+{\n+\tint i, j;\n+\tunsigned int total = 0;\n+\tstatic int seeded;\n+\n+\t/*\n+\t * Calculate total weight and move weight-zero\n+\t * records to the beginning of the array.\n+\t */\n+\tassert(n < (1 << 16));\n+\tfor (i = j = 0; i < n; i++) {\n+\t\tunsigned int wt = rrs[i].weight;\n+\t\tassert(wt < (1 << 16));\n+\n+\t\tif (!wt) {\n+\t\t\tsrv_swap(rrs + i, rrs + j);\n+\t\t\tj++;\n+\t\t}\n+\n+\t\t/*\n+\t\t * In the worst case, n is 2^16 - 1 and\n+\t\t * each weight is 2^16 - 1, making the total\n+\t\t * a little less than 2^32.\n+\t\t */\n+\t\tassert(!unsigned_add_overflows(total, wt + 1));\n+\t\ttotal += wt;\n+\t}\n+\t/* Usual case: all weights are zero. */\n+\tif (!total)\n+\t\treturn;\n+\n+\tif (!seeded) {\n+\t\tseeded = 1;\n+\t\tsrand48(time(NULL));\n+\t}\n+\n+\tfor (i = 0; i < n; i++)\n+\t\t/*\n+\t\t * Now the records starting at rrs[i] could be in any order,\n+\t\t * except those of weight 0 are at the start of the list\n+\t\t * (ending with rrs[j-1]).\n+\t\t *\n+\t\t * Pick an item from rrs[i]..rrs[n] at random, taking weights\n+\t\t * into account, and reorder to make it rrs[i], preserving\n+\t\t * that invariant.\n+\t\t */\n+\t\tshuffle_one(rrs, n, i, &j, &total);\n+}\n+\n+static void sort_rrs(struct parsed_srv_rr *rrs, int n)\n+{\n+\tint i, j, prio;\n+\n+\tqsort(rrs, n, sizeof(*rrs), priority_compare);\n+\n+\t/*\n+\t * Within each priority level, order servers randomly,\n+\t * respecting weight.\n+\t */\n+\tj = 0;\n+\tprio = rrs[j].priority;\n+\tfor (i = 0; i < n; i++) {\n+\t\tif (rrs[i].priority == prio)\n+\t\t\tcontinue;\n+\n+\t\tweighted_shuffle(rrs + j, i - j);\n+\t\tj = i;\n+\t\tprio = rrs[j].priority;\n+\t}\n+\tweighted_shuffle(rrs + j, n - j);\n+}\n+\n+/* Reference: RFC2782. */\n+int get_srv(const char *host, struct host **hosts)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tunsigned char buf[1024];\n+\tns_msg msg;\n+\tint len, n, i, ret;\n+\tstruct parsed_srv_rr *rrs = NULL;\n+\tstruct host *reply = NULL;\n+\n+\t/* if no SRV record is found, fall back to plain address lookup */\n+\tret = 0;\n+\n+\t/* _git._tcp.<host> */\n+\tif (get_qname_for_srv(&sb, host))\n+\t\tgoto out;\n+\tlen = res_query(sb.buf, ns_c_in, ns_t_srv, buf, sizeof(buf));\n+\tif (len < 0)\n+\t\tgoto out;\n+\n+\t/* If a SRV RR cannot be parsed, give up. */\n+\tret = -1;\n+\n+\tif (ns_initparse(buf, len, &msg)) {\n+\t\terror(\"cannot initialize DNS parser: %s\", strerror(errno));\n+\t\tgoto out;\n+\t}\n+\tn = srv_parse(&msg, &rrs);\n+\tif (n < 0)\n+\t\t/* srv_parse writes a message */\n+\t\tgoto out;\n+\tif (!n) {\n+\t\tret = 0;\n+\t\tgoto out;\n+\t}\n+\tassert(n < (1 << 16));\n+\n+\t/* A single RR with target \".\" means \"go away\". */\n+\tif (n == 1 &&\n+\t    (!*rrs[0].target || !strcmp(rrs[0].target, \".\")))\n+\t\tgoto out2;\n+\n+\tsort_rrs(rrs, n);\n+\n+\t/* Success! */\n+\tret = n;\n+\treply = xmalloc(n * sizeof(*reply));\n+\tfor (i = 0; i < n; i++) {\n+\t\tchar buf[32];\n+\t\tsnprintf(buf, sizeof(buf), \"%d\", rrs[i].port);\n+\n+\t\treply[i].hostname = rrs[i].target;\n+\t\treply[i].port = xstrdup(buf);\n+\t}\n+\t*hosts = reply;\n+\tgoto out;\n+\n+out2:\n+\tfor (i = 0; i < n; i++)\n+\t\tfree(rrs[i].target);\n+out:\n+\tfree(rrs);\n+\tstrbuf_release(&sb);\n+\treturn ret;\n+}\ndiff --git a/srv.h b/srv.h\nnew file mode 100644\nindex 00000000..7cea4f4c\n--- /dev/null\n+++ b/srv.h\n@@ -0,0 +1,15 @@\n+#ifndef SRV_H\n+#define SRV_H\n+\n+struct host {\n+\tchar *hostname;\n+\tchar *port;\n+};\n+\n+#ifndef USE_SRV_RR\n+#define get_srv(host, hosts) 0\n+#else\n+extern int get_srv(const char *host, struct host **hosts);\n+#endif\n+\n+#endif\ndiff --git a/tcp.c b/tcp.c\nindex 83f0313a..c27a0d7f 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n+#include \"srv.h\"\n \n #ifndef NO_IPV6\n #include \"dns-ipv6.h\"\n@@ -72,46 +73,85 @@ void git_locate_host(const char *hostname, char **ip_address,\n static int git_tcp_connect_sock(char *host, int flags)\n {\n \tstruct strbuf error_message = STRBUF_INIT;\n-\tint sockfd = -1;\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tresolver_result ai;\n-\tresolved_address i;\n-\tint cnt = -1;\n+\tint sockfd = -1, gai = 0;\n+\tconst char *port = NULL;\n+\tstruct host *hosts = NULL;\n+\tint j, n = 0;\n \n \tget_host_and_port(&host, &port);\n+\tif (!port) {\n+\t\tport = STR(DEFAULT_GIT_PORT);\n+\t\tn = get_srv(host, &hosts);\n+\t}\n+\tif (n < 0)\n+\t\tdie(\"Unable to look up %s\", host);\n \tif (!*port)\n \t\tport = \"<none>\";\n+\tif (!n) {\n+\t\thosts = xmalloc(sizeof(*hosts));\n+\t\thosts[0].hostname = xstrdup(host);\n+\t\thosts[0].port = xstrdup(port);\n+\t\tn = 1;\n+\t}\n \n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"Looking up %s ... \", host);\n+\tfor (j = 0; j < n; j++) {\n+\t\tresolver_result ai;\n+\t\tresolved_address i;\n+\t\tint cnt;\n \n-\tif (dns_resolve(host, port, 0, &ai))\n-\t\tdie(\"BUG: dns_resolve returned error?\");\n+\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\tfprintf(stderr, \"Looking up %s ... \", hosts[j].hostname);\n \n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \", host, port);\n+\t\tgai = dns_resolve(hosts[j].hostname,\n+\t\t\t\thosts[j].port, RESOLVE_FAIL_QUIETLY, &ai);\n+\t\tif (gai) {\n+\t\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\t\tfprintf(stderr, \"failed.\\n\");\n \n-\tfor_each_address(i, ai) {\n-\t\tcnt++;\n-\t\tsockfd = socket(dns_family(i, ai),\n-\t\t\t\tdns_socktype(i, ai), dns_protocol(i, ai));\n-\t\tif (sockfd < 0 ||\n-\t\t    connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n-\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\t    host, cnt, dns_name(&i), strerror(errno));\n-\t\t\tif (0 <= sockfd)\n-\t\t\t\tclose(sockfd);\n-\t\t\tsockfd = -1;\n+\t\t\tif (n == 1 && !strcmp(host, hosts[j].hostname))\n+\t\t\t\tstrbuf_addf(&error_message, \"%s: %s\\n\",\n+\t\t\t\t\thost, dns_strerror(gai));\n+\t\t\telse\n+\t\t\t\tstrbuf_addf(&error_message,\n+\t\t\t\t\t\"%s[%d: %s:%s]: %s\\n\", host, j,\n+\t\t\t\t\thosts[j].hostname, hosts[j].port,\n+\t\t\t\t\tdns_strerror(gai));\n \t\t\tcontinue;\n \t\t}\n+\n \t\tif (flags & CONNECT_VERBOSE)\n-\t\t\tfprintf(stderr, \"%s \", dns_name(&i));\n-\t\tbreak;\n+\t\t\tfprintf(stderr, \"done.\\nConnecting to %s (port %s) ... \",\n+\t\t\t\t\thosts[j].hostname, hosts[j].port);\n+\n+\t\tcnt = -1;\n+\t\tfor_each_address(i, ai) {\n+\t\t\tcnt++;\n+\t\t\tsockfd = socket(dns_family(i, ai),\n+\t\t\t\t\tdns_socktype(i, ai), dns_protocol(i, ai));\n+\t\t\tif (sockfd < 0 ||\n+\t\t\t    connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n+\t\t\t\tstrbuf_addf(&error_message, \"%s[%d: %s]: errno=%s\\n\",\n+\t\t\t\t\t\thosts[j].hostname,\n+\t\t\t\t\t\tcnt,\n+\t\t\t\t\t\tdns_name(&i),\n+\t\t\t\t\t\tstrerror(errno));\n+\t\t\t\tif (0 <= sockfd)\n+\t\t\t\t\tclose(sockfd);\n+\t\t\t\tsockfd = -1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\t\tfprintf(stderr, \"%s \", dns_name(&i));\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tdns_free(ai);\n+\n+\t\tif (sockfd >= 0)\n+\t\t\tbreak;\n \t}\n \n-\tdns_free(ai);\n-\n-\tif (sockfd < 0)\n+\tif (gai || sockfd < 0)\n \t\tdie(\"unable to connect to %s:\\n%s\", host, error_message.buf);\n \n \tenable_keepalive(sockfd);\n@@ -119,8 +159,12 @@ static int git_tcp_connect_sock(char *host, int flags)\n \tif (flags & CONNECT_VERBOSE)\n \t\tfprintf(stderr, \"done.\\n\");\n \n+\tfor (j = 0; j < n; j++) {\n+\t\tfree(hosts[j].hostname);\n+\t\tfree(hosts[j].port);\n+\t}\n+\tfree(hosts);\n \tstrbuf_release(&error_message);\n-\n \treturn sockfd;\n }\n \n-- \n1.7.9.2\n"},{"id":"186446","messageId":"20120308132339.GH9426@burratino","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"[PATCH 8/5] srv: tolerate broken DNS replies","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T13:23:39Z","receivedAt":"2012-03-08T13:23:39Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"At a hotel with a very broken Wi-Fi setup, Richard found his copy\nof git unable to cope:\n\n\t% git clone git://git.kitenet.net/mr\n\tCloning into 'mr'...\n\terror: cannot initialize DNS parser: Message too long\n\tfatal: Unable to look up git.kitenet.net\n\nOther programs gave some warnings but otherwise worked fine.  From a\npacket capture, it seems that the response to a SRV query for\n_git._tcp.git.kitenet.net in this setup was a single A resource record\npointing to the link-local address 169.254.1.1, followed by two\ntrailing bytes: c0 1a.  The trailing bytes cause the underlying parser\nto fail.\n\nIt would not be good to silently tolerate this and similar kinds of\nbrokenness, but working around it would help people on affected\nsystems to recover.  Luckily RFC2782 gives us enough leeway to act as\nwe please for this particular kind of error, so give a warning and\nfall back to an A/AAAA query (which should work).\n\nSimilarly, if we receive non-SRV RRs in response to a SRV query,\nRFC2782 does not say to error out, so in the spirit of graceful\ndegradation let's warn and skip those records.\n\nReported-by: Richard Hartmann <richih.mailinglist@gmail.com>\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThanks for reading.  That's the end of the series.\n\nGood night,\nJonathan\n\n srv.c |   40 ++++++++++++++++++++++++++--------------\n 1 file changed, 26 insertions(+), 14 deletions(-)\n\ndiff --git a/srv.c b/srv.c\nindex 2716206e..829ef762 100644\n--- a/srv.c\n+++ b/srv.c\n@@ -83,7 +83,6 @@ static int srv_parse(ns_msg *msg, struct parsed_srv_rr **res)\n {\n \tstruct parsed_srv_rr *rrs = NULL;\n \tint nr_parsed = 0;\n-\tint cnames = 0;\n \tint i, n;\n \n \tn = ns_msg_count(*msg, ns_s_an);\n@@ -98,30 +97,33 @@ static int srv_parse(ns_msg *msg, struct parsed_srv_rr **res)\n \t\tif (ns_rr_type(rr) != ns_t_cname)\n \t\t\tbreak;\n \t}\n-\tcnames = i;\n-\tn -= cnames;\n \n-\trrs = xmalloc(n * sizeof(*rrs));\n-\tfor (i = 0; i < n; i++) {\n+\trrs = xmalloc((n - i) * sizeof(*rrs));\n+\tfor (; i < n; i++) {\n \t\tns_rr rr;\n \n-\t\tif (ns_parserr(msg, ns_s_an, cnames + i, &rr)) {\n+\t\tif (ns_parserr(msg, ns_s_an, i, &rr)) {\n \t\t\terror(\"cannot parse DNS RR: %s\", strerror(errno));\n \t\t\tgoto fail;\n \t\t}\n \t\tif (ns_rr_type(rr) != ns_t_srv) {\n-\t\t\terror(\"expected SRV RR, found RR type %d\",\n+\t\t\t/*\n+\t\t\t * Maybe the server is playing tricks and returned\n+\t\t\t * an A record.  Let it pass and if we don't get\n+\t\t\t * any SRV RRs, we can fall back to an A lookup.\n+\t\t\t */\n+\t\t\twarning(\"expected SRV RR, found RR type %d\",\n \t\t\t\t\t\t(int) ns_rr_type(rr));\n-\t\t\tgoto fail;\n+\t\t\tcontinue;\n \t\t}\n-\t\tif (srv_parse_rr(msg, &rr, rrs + i))\n+\t\tif (srv_parse_rr(msg, &rr, rrs + nr_parsed))\n \t\t\t/* srv_parse_rr writes a message */\n \t\t\tgoto fail;\n \t\tnr_parsed++;\n \t}\n \n \t*res = rrs;\n-\treturn n;\n+\treturn nr_parsed;\n fail:\n \tfor (i = 0; i < nr_parsed; i++)\n \t\tfree(rrs[i].target);\n@@ -274,13 +276,23 @@ int get_srv(const char *host, struct host **hosts)\n \tif (len < 0)\n \t\tgoto out;\n \n+\t/*\n+\t * If the reply to a SRV query is malformed, fall back to an\n+\t * A query.\n+\t *\n+\t * The RFC2782 usage rules don't say anything about this, but\n+\t * in practice, it seems that some firewalls or DNS servers\n+\t * (think: captive portal) handle A queries sensibly and\n+\t * provide malformed replies in response to SRV queries.\n+\t */\n+\tif (ns_initparse(buf, len, &msg)) {\n+\t\twarning(\"cannot parse SRV response: %s\", strerror(errno));\n+\t\tgoto out;\n+\t}\n+\n \t/* If a SRV RR cannot be parsed, give up. */\n \tret = -1;\n \n-\tif (ns_initparse(buf, len, &msg)) {\n-\t\terror(\"cannot initialize DNS parser: %s\", strerror(errno));\n-\t\tgoto out;\n-\t}\n \tn = srv_parse(&msg, &rrs);\n \tif (n < 0)\n \t\t/* srv_parse writes a message */\n-- \n1.7.9.2\n"},{"id":"186456","messageId":"CABPQNSbSkq3DnxvJ8tDp4KdyaeW1H6GriT9V3X6SLCRjkNR-fw@mail.gmail.com","threadId":"29887","inReplyTo":"20120308130304.GA9426@burratino","subject":"Re: [PATCH 1/5] transport: expose git_tcp_connect() and friends in new tcp.h","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-03-08T15:28:03Z","receivedAt":"2012-03-08T15:28:03Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Thu, Mar 8, 2012 at 2:03 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Date: Mon, 6 Jun 2011 04:37:14 -0500\n>\n> Split off a new tcp.c with the functions git_tcp_connect() and\n> git_proxy_connect() that resolve and connect to a host.\n>\n> Part of a series to teach git to respect DNS SRV records when making\n> new connections.  This is a preliminary step to make the connection\n> library easier to understand before changing it.\n>\n> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n\nNice. Now that this has been libified, someone can probably get rid of\nsome code in imap-send.c (specifically in the imap_open_store\nfunction) on top of your topic...\n"},{"id":"186459","messageId":"CABPQNSYfv19cVQoAoUyXVaF1TpLXTYDRFnHE4vr=X42W771tbA@mail.gmail.com","threadId":"29887","inReplyTo":"20120308130913.GD9426@burratino","subject":"Re: [PATCH 4/5] tcp: unify ipv4 and ipv6 code paths","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-03-08T15:39:02Z","receivedAt":"2012-03-08T15:39:02Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Thu, Mar 8, 2012 at 2:09 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Date: Mon, 6 Jun 2011 04:41:28 -0500\n>\n> The new DNS API abstracts away differences between the gethostbyname-\n> and getaddrinfo-centric interfaces for looking up a host, making the\n> code to use them in connect.c a little easier to read.\n>\n> To make a lookup:\n>\n>        resolver_result ai;\n>        dns_resolve(host, port, 0, &ai);\n>        ...\n>        dns_free(ai);\n>\n> To iterate over responses:\n>\n>        resolved_address i;\n>        for_each_address(i, ai) {\n>                ...\n>        }\n>\n> In the !NO_IPV6 codepath, the git_locate_host function that is used to\n> find the canonical IP and hostname for a git server's public address\n> (for virtual hosting) tells getaddrinfo to restrict attention to TCP\n> services after this patch.  That should make no difference because the\n> service parameter is NULL.\n>\n> No functional change intended.\n>\n> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n> ---\n> This is the title feature, corresponding to\n> http://thread.gmane.org/gmane.comp.version-control.git/175106/focus=175111\n>\n> It needed changes to adjust to released changes in the code it touches,\n> but nothing is fundamentally different from v1.\n>\n>  Makefile   |    5 ++\n>  dns-ipv4.c |   33 +++++++++++\n>  dns-ipv4.h |   69 +++++++++++++++++++++++\n>  dns-ipv6.c |   49 ++++++++++++++++\n>  dns-ipv6.h |   31 +++++++++++\n>  tcp.c      |  182 +++++++++++-------------------------------------------------\n>  6 files changed, 218 insertions(+), 151 deletions(-)\n\nI'm not entirely sure I understand the motivation here. We already had\nwell-tested, implementations of IPv4 and IPv6 tcp-socket setup. Here\nyou unify the code by adding abstraction, but it ends up amounting to\nmore lines of code, with the details scattered around in different\nsource files.\n\nFor me, this means that I have to learn a new API, and to see what\nreally happens when something goes wrong, I have to jump between\nmultiple source files.\n\nAnd I'm not entirely sure what this patch actually improves. If it was\nlikely that we'd get support for yet another IP-stack version, then\nthis would probably be a win. But that's not likely, is it?\n"},{"id":"186461","messageId":"CABPQNSYpRGfu7Ew+KstCFsG4YDSx+i-jzHS1Bw0BA4S2hoz4SA@mail.gmail.com","threadId":"29887","inReplyTo":"20120308132155.GG9426@burratino","subject":"Re: [PATCH 7/5] transport: optionally honor DNS SRV records","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-03-08T16:18:49Z","receivedAt":"2012-03-08T16:18:49Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Thu, Mar 8, 2012 at 2:21 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Date: Mon, 6 Jun 2011 04:46:20 -0500\n>\n> SRV records are an extra layer of indirection on top of A/AAAA records\n> mapping from domain names and service types to the hostnames of\n> machines that offer that service.  That has a couple of nice effects:\n>\n>  - a single domain can use different hosts for different services\n>  - a single domain can use multiple hosts for one service\n>\n> Teach git to perform a SRV lookup whenever resolving a git:// URL.\n> This means:\n>\n>  - if your git server was previously the same machine as your wesnoth\n>   server, you can move it to a separate machine without forcing\n>   everyone to update their links to the old URLs\n>\n>  - if you have a primary git server and a backup machine that should\n>   be used when the primary server goes down, the client can\n>   automatically take care of it\n>\n>  - if you have multiple git servers and would like to spread load\n>   between them, the client can automatically take care of it\n>\n> That is, SRV records let us ask the client to carry out various tasks\n> that would require a proxy on the server side with traditional DNS.\n> The client performs a SRV query to _git._tcp.<domain name> to receive\n> its instructions.  RFC 2782 has details.\n>\n> Ideally reaping these benefits would just involve passing a special\n> flag to getaddrinfo().  Since we don't live in such a world, this\n> patch uses the BIND 8 API provided by libresolv to parse the response\n> for ourselves.\n>\n> RFC 2782 requires some non-determinism in the order of hosts\n> contacted; this patch uses drand48() for that.  To avoid causing\n> trouble for platforms that lack the libbind ns_* functions or\n> drand48(), the SRV support is only provided when requested by setting\n> the USE_SRV_RR compile-time option.\n>\n> git servers must ensure that they can also be reached by a plain\n> A/AAAA lookup to support git clients without SRV support, for example\n> by proxying connections to an appropriate server:\n>\n>        # in inetd.conf\n>        git stream tcp nowait.400 nobody /usr/sbin/tcpd \\\n>                /bin/nc -q0 gitserver.example.com git\n>\n> Regression: this uglifies error messages for connection errors a\n> little.  It would probably be better to leave out the connection count\n> when we are not trying more than one server.\n>\n> Based on a patch by Julien Cristau <jcristau@debian.org>.\n>\n> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n> ---\n> As I mentioned at the gittogether, I think this would be a valuable\n> feature in git.  You can test it out (though this is only the simple\n> case, no load balancing or failover) by doing\n>\n>        git clone git://git.debian.org/~jrnieder-guest/git.git\n>\n> and watching what happens with wireshark.\n>\n> The patch needs documentation.  Maybe a howto and an addendum to the\n> protocol docs would do.  Anyway, I hope it's at least entertaining in\n> the current state.\n\nIt's an interesting feature, but I'm a little bit worried if this\npromotes non-portable setups; won't these repos be unreachable (at\nleast without manually redirecting or also keeping a copy on the\nadvertised URL) on machines where libresolv is unavailable? I'm mainly\nthinking about the \"a single domain can use different hosts for\ndifferent services\"-benefit you mentioned. Multiple hosts for one\nservice would probably be done by simply advertising one of the URLs,\nand get some load-balancing from the clients that DOES have\nlibresolv...\n"},{"id":"186496","messageId":"20120308211008.GA9497@burratino","threadId":"29887","inReplyTo":"CABPQNSYfv19cVQoAoUyXVaF1TpLXTYDRFnHE4vr=X42W771tbA@mail.gmail.com","subject":"Re: [PATCH 4/5] tcp: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T21:10:08Z","receivedAt":"2012-03-08T21:10:08Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Erik Faye-Lund wrote:\n\n> I'm not entirely sure I understand the motivation here. We already had\n> well-tested, implementations of IPv4 and IPv6 tcp-socket setup. Here\n> you unify the code by adding abstraction, but it ends up amounting to\n> more lines of code, with the details scattered around in different\n> source files.\n\nThanks.  It's true: I didn't convey the motivation well at all.\n\nBefore this patch, git_tcp_connect_sock() looks roughly like this:\n\n\t#ifndef NO_IPV6\n\n\tstatic int git_tcp_connect_sock(char *host, int flags)\n\t{\n\t\tget_host_and_port(&host, &port);\n\t\tmemcpy(&hints, tcp_stream_hints, sizeof(hints));\n\t\tgai = getaddrinfo(host, port, &hints, &ai);\n\t\tfor (ai0 = ai; ai; ai = ai->ai_next, cnt++) {\n\t\t\t... socket(), connect(), etc ...\n\t\t}\n\t\tfreeaddrinfo(ai0);\n\t\tenable_keepalive(sockfd);\n\t\treturn sockfd;\n\t}\n\n\t#else\n\n\tstatic int git_tcp_connect_sock(char *host, int flags)\n\t{\n\t\tget_host_and_port(&host, &port);\n\t\the = gethostbyname(host);\n\t\tfor (ap = he->h_addr_list; *ap; ap++, cnt++) {\n\t\t\t... socket(), connect(), etc ...\n\t\t}\n\t\tenable_keepalive(sockfd);\n\t\treturn sockfd;\n\t}\n\n\t#endif\n\nAny change to this procedure has to be made in both places, and in the\npast, that has caused some minor bugs that were unnoticed for a while,\nbut no big deal --- it's not that complicated, so the code duplication\nis tolerable.\n\nAfter this patch and the next two, it looks like so:\n\n\tstatic int git_tcp_connect_sock(char *host, int flags)\n\t{\n\t\tget_host_and_port(&host, &port);\n\t\tgai = dns_resolve(host, port, 0, &ai);\n\t\tfor_each_address(i, ai) {\n\t\t\t... socket(), connect(), etc ...\n\t\t}\n\t\tdns_free(ai0);\n\t\tenable_keepalive(sockfd);\n\t\treturn sockfd;\n\t}\n\nThat is, it is almost identical to the getaddrinfo version.\n\nSo it is not about making the code shorter, but about not repeating\nourselves.\n\nI did this for my sanity while implementing the SRV code: if my\nchanges work with the gethostbyname API, this way it is much more\nlikely this way that they will still work with getaddrinfo, too.  So\nbasically, the point is that I did not want to have to think about the\n!NO_IPV6 codepath while writing patches (and others might not want to\nhave to think about the NO_IPV6 codepath at all --- the same\nadvantages apply for them, too).\n\n\tAPI dictionary in the !NO_IPV6 case\n\t(see dns-ipv6.h and dns-ipv6.c for details):\n\n\tresolver_result = struct addrinfo *\n\tresolved_address = const struct addrinfo *\n\tdns_resolve = getaddrinfo\n\tdns_name = getnameinfo, use in error messages\n\tdns_ip_address = getnameinfo, git-daemon uses for %IP substitution\n\tfor_each_address = for (ai0 = ai; ai; ai = ai->ai_next)\n\n\tdns_family = ->ai_family\n\tdns_socktype = ->ai_socktype\n\tetc\n\n\tdns_strerror = gai_strerror\n\tdns_free = freeaddrinfo\n\nThe dns_name()/dns_ip_address() pair is a little silly.  The former\nreturns its result in a static buffer and the latter uses malloc.\nImprovements welcome.\n\nOf course, git_tcp_connect_sock() is not the only function that uses\nhost resolution facilities.  The benefit scales as more functions get\nconverted.\n\nHope that helps,\nJonathan\n"},{"id":"186500","messageId":"20120308213545.GB9497@burratino","threadId":"29887","inReplyTo":"CABPQNSYpRGfu7Ew+KstCFsG4YDSx+i-jzHS1Bw0BA4S2hoz4SA@mail.gmail.com","subject":"Re: [PATCH 7/5] transport: optionally honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-08T21:35:45Z","receivedAt":"2012-03-08T21:35:45Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Erik Faye-Lund wrote:\n\n> It's an interesting feature, but I'm a little bit worried if this\n> promotes non-portable setups; won't these repos be unreachable (at\n> least without manually redirecting or also keeping a copy on the\n> advertised URL) on machines where libresolv is unavailable? I'm mainly\n> thinking about the \"a single domain can use different hosts for\n> different services\"-benefit you mentioned.\n\nYes.  I think of redirecting with SRV records without a fallback as a\nmisconfiguration (\"don't do that, then\").  Note that it would not only\naffect people without drand48 but people with broken DNS servers.\n\nIn the case of Alioth, what they do is let inetd listen on\ngit.debian.org = vasks.debian.org, using netcat to forward connections\nto wagner.debian.org.  So clients with and without SRV support end up\nfinding their bits shuttled to the same place, with the only\ndifference being a little wasted load on vasks in the no-SRV case.\n\nThis means the Alioth admins don't get the benefit of not having to\nset up a proxy, but they do get the benefit of clients taking on more\nof the work and the service continuing to be available for some\nclients if vasks goes down.\n\nMaybe adding an envvar to disable the SRV handling would make it\neasier for server admins to check the fallback.  Can we do more?\n\n(By the way, what platforms don't support BIND and a random number\ngenerator?)\n\nThanks,\nJonathan\n"},{"id":"186506","messageId":"CAD77+gT_1O2kjvob9Csa2C6vZZYyGrssrdBz8PxNU00wO+6UDQ@mail.gmail.com","threadId":"29887","inReplyTo":"20120308132339.GH9426@burratino","subject":"Re: [PATCH 8/5] srv: tolerate broken DNS replies","fromName":"Richard Hartmann","fromEmail":"richih.mailinglist@gmail.com","sentAt":"2012-03-08T22:28:21Z","receivedAt":"2012-03-08T22:28:21Z","isPatch":true,"sender":{"key":"richih.mailinglist@gmail.com","avatar":"https://avatars.githubusercontent.com/u/754723?v=4"},"body":"On Thu, Mar 8, 2012 at 14:23, Jonathan Nieder <jrnieder@gmail.com> wrote:\n\n> Other programs gave some warnings but otherwise worked fine.\n\nJust ftr, Chrome/Chromium ignores this quickly and loads pages while\nKonqueror takes ages to get around this. Not related to git, but maybe\nan interesting data point in the future. If not, disregard.\n\n\nThanks for hunting and killing this one.\n\n\nRichard\n"},{"id":"186529","messageId":"4F59AC1C.1000406@viscovery.net","threadId":"29887","inReplyTo":"20120308213545.GB9497@burratino","subject":"Re: [PATCH 7/5] transport: optionally honor DNS SRV records","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-03-09T07:07:08Z","receivedAt":"2012-03-09T07:07:08Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 3/8/2012 22:35, schrieb Jonathan Nieder:\n> (By the way, what platforms don't support BIND and a random number\n> generator?)\n\nMinGW, for example:\n\nD:\\Src\\mingw-git>cat foo.c && gcc -c foo.c\n#include <arpa/nameser.h>\n#include <resolv.h>\n#include <stdlib.h>\n\nvoid (*x)(long int) = srand48;\nfoo.c:1:26: error: arpa/nameser.h: No such file or directory\nfoo.c:2:20: error: resolv.h: No such file or directory\nfoo.c:5: error: 'srand48' undeclared here (not in a function)\n\n-- Hannes\n"},{"id":"186534","messageId":"20120309080003.GA2229@burratino","threadId":"29887","inReplyTo":"4F59AC1C.1000406@viscovery.net","subject":"Re: [PATCH 7/5] transport: optionally honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-03-09T08:00:22Z","receivedAt":"2012-03-09T08:00:22Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nJohannes Sixt wrote:\n> Am 3/8/2012 22:35, schrieb Jonathan Nieder:\n\n>> (By the way, what platforms don't support BIND and a random number\n>> generator?)\n>\n> MinGW, for example:\n\nThe ISC seems to provide BIND source code[*] and binaries for Windows,\nthough I'm not sure how well they could fit into a typical development\nenvironment for git's MinGW port.  A good approach for drand48 is\nharder to imagine, mostly because there are too many choices for a\nfree PRNG to use to replace it.  Not a bad problem to have. ;-)\n\nThough all of that would only come up once someone wants to use\nfeatures requiring these facilities on Windows.  Thanks for the\nhints and sorry for the distraction.\n\nCiao,\nJonathan\n\n[*] http://www.isc.org/software/bind\n"},{"id":"193339","messageId":"CABPQNSb9EGOgHb7NtsEtDh2QkjkHYn7YemYsa8Yaqyuce-aDMw@mail.gmail.com","threadId":"29887","inReplyTo":"20120308124857.GA7666@burratino","subject":"Re: [PATCH 0/5] transport: unify ipv4 and ipv6 code paths","fromName":"Erik Faye-Lund","fromEmail":"kusmabite@gmail.com","sentAt":"2012-06-11T18:12:00Z","receivedAt":"2012-06-11T18:12:00Z","isPatch":true,"sender":{"key":"kusmabite@gmail.com","avatar":"https://avatars.githubusercontent.com/u/47073?v=4"},"body":"On Thu, Mar 8, 2012 at 1:48 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Hi,\n>\n> These patches eliminate some ifdef-ery concerning NO_IPV6.  I used\n> them when writing the SRV patch, which applies on top, but it's\n> probably best to think of it as an independent topic.\n>\n> Patch 4 is the heart of the series.  It provides an interface similar\n> to getaddrinfo that can be implemented on top of either gethostbyname\n> or getaddrinfo and puts each implementation in a separate file.  This\n> way, callers can just use the common API and they do not need to have\n> two copies of their code, one for each host resolution API.\n>\n> Patches 1-3 move code around until all the code that patch 4 touches\n> is in one place.\n>\n> Patches 5 is a potential error handling improvement noticed while\n> writing patches 1-4.  It's probably not actually needed but it was a\n> comfort to me.\n>\n> These patches have been in use in Debian since June of last year.  I'd\n> like to see this in mainline early in the 1.7.11 cycle to make coding\n> that touches this area during that cycle more pleasant.  Thoughts of\n> all kinds welcome.\n\nWhat happened to this series?\n"},{"id":"193352","messageId":"7v62ax7jzs.fsf@alter.siamese.dyndns.org","threadId":"29887","inReplyTo":"CABPQNSb9EGOgHb7NtsEtDh2QkjkHYn7YemYsa8Yaqyuce-aDMw@mail.gmail.com","subject":"Re: [PATCH 0/5] transport: unify ipv4 and ipv6 code paths","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-06-11T18:59:19Z","receivedAt":"2012-06-11T18:59:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Erik Faye-Lund <kusmabite@gmail.com> writes:\n\n> On Thu, Mar 8, 2012 at 1:48 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n>> Hi,\n>>\n>> These patches eliminate some ifdef-ery concerning NO_IPV6.  I used\n>> them when writing the SRV patch, which applies on top, but it's\n>> probably best to think of it as an independent topic.\n>>\n>> Patch 4 is the heart of the series.  It provides an interface similar\n>> to getaddrinfo that can be implemented on top of either gethostbyname\n>> or getaddrinfo and puts each implementation in a separate file.  This\n>> way, callers can just use the common API and they do not need to have\n>> two copies of their code, one for each host resolution API.\n>>\n>> Patches 1-3 move code around until all the code that patch 4 touches\n>> is in one place.\n>>\n>> Patches 5 is a potential error handling improvement noticed while\n>> writing patches 1-4.  It's probably not actually needed but it was a\n>> comfort to me.\n>>\n>> These patches have been in use in Debian since June of last year.  I'd\n>> like to see this in mainline early in the 1.7.11 cycle to make coding\n>> that touches this area during that cycle more pleasant.  Thoughts of\n>> all kinds welcome.\n>\n> What happened to this series?\n\nYeah, what happened to it?  I was looking at the diff files Ubuntu\napplies when generating its recent .deb files, and noticed these\npatches today, and was wondering what was going on.\n"},{"id":"193615","messageId":"20120614050203.GE27586@burratino","threadId":"29887","inReplyTo":"CABPQNSb9EGOgHb7NtsEtDh2QkjkHYn7YemYsa8Yaqyuce-aDMw@mail.gmail.com","subject":"Re: [PATCH 0/5] transport: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2012-06-14T05:02:03Z","receivedAt":"2012-06-14T05:02:03Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Erik Faye-Lund wrote:\n> On Thu, Mar 8, 2012 at 1:48 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n\n>> Patch 4 is the heart of the series.  It provides an interface similar\n>> to getaddrinfo that can be implemented on top of either gethostbyname\n>> or getaddrinfo and puts each implementation in a separate file.  This\n>> way, callers can just use the common API and they do not need to have\n>> two copies of their code, one for each host resolution API.\n[...]\n> What happened to this series?\n\nIt seemed nice in principle, but if I understood correctly then the\nAPI was too complicated for anyone but me to like it.  Instead of an\ninterface similar to getaddrinfo, it would probably be better to\nimplement an interface _identical_ to getaddrinfo for the the subset\nof functionality that git uses.\n\nSee [1] for example for proof that that's possible.\n\nI would make some different choices from Russ if doing it myself ---\ne.g., I would want to unconditionally use\n\n\t#define getaddrinfo git_getaddrinfo\n\nwhen NO_IPV6 is set so testing these code paths would be as simple as\nenabling NO_IPV6, even on systems that also provide the modern API in\nlibc.\n\nHoping that clarifies,\nJonathan\n\n[1] http://git.eyrie.org/?p=devel/rra-c-util.git;a=tree;f=portable\n"}]}