{"thread":{"id":"27554","subject":"[RFC/PATCH 0/6] git: please honor DNS SRV records","startedAt":"2011-06-06T09:30:20Z","lastAt":"2011-06-06T10:01:47Z","messageCount":9,"participants":["Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"169348","messageId":"20110606093019.GD8015@elie","threadId":"27554","inReplyTo":"20110524230900.GA9440@radis.liafa.jussieu.fr","subject":"[RFC/PATCH 0/6] git: please honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:30:20Z","receivedAt":"2011-06-06T09:30:20Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nJulien Cristau wrote:\n> On Tue, May 24, 2011 at 15:22:55 -0500, Jonathan Nieder wrote:\n\n>> As rfc2782 explains, SRV records allow administrators \"to use serveral\n>> servers for a single domain, to move services from host to host with\n>> little fuss, and to designate some hosts as primary servers for a\n>> service and others as backups\".  Julien Cristau noticed that in the\n>> recent alioth move that second part would have been very handy (since\n>> the git protocol doesn't include a concept of redirects).\n[...]\n> I played with this a bit tonight, came up with\n> http://people.debian.org/~jcristau/git-srv-hack.diff which seems to work\n> for me.  In case somebody wants to test/polish it up...\n\nSo I have played with it a little more.  Very rough, completely\nuntested, in desperate need of a test script, but I think it is ready\nfor some feedback.  To recap, this is really just an evil workaround\nfor the lack of support for redirects in git protocol, but it has a\nnice side effect of making load balancing a little easier for server\nadministrators.\n\nPatches 1-3 are code movement, to give the code that is being changed\na home and make it a little easier to contemplate.  In particular\npatch 3 draws attention to some code from git daemon that makes DNS\nlookups to get the canonical hostname and IP a client contacted.\nShould it pay attention to SRV records?\n\nPatch 5 is another cleanup patch.  It unifies the ipv4 and ipv6\nimplementations of git_tcp_connect by abstracting away some of the\ndifferences between the underlying OS interfaces.  Patch 4 is a tiny\nbugfix noticed in the process.\n\nNone of the above should be necessary for or even has much to do with\npatch 6, the nominal topic of the series.  Sorry about that.\n\nAnyway, patch 6 is an attempt to implement rfc2782, expanded from the\nimplementation of a subset that Julien sent.  It doesn't pay attention\nto the \"additional data\" section of the SRV response to avoid A or\nAAAA lookups, but it probably should.\n\nAt the back of my head there is this nagging feeling that getaddrinfo\nshould accept an ai_flags bit to do this for us automatically, but it\ndoesn't as far as I can tell.  Maybe this can be a stepping stone\ntoward that...\n\nIf nothing else, it's kind of fun.  Thoughts?  Does the general idea\nseem sane?  Am I overlooking some standard function that takes care of\nthis all automatically?\n"},{"id":"169349","messageId":"20110606093714.GF8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 1/6] transport: expose git_tcp_connect and friends in new tcp.h","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:37:14Z","receivedAt":"2011-06-06T09:37:14Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Before adding functionality to them, let's move the functions\ngit_tcp_connect and git_proxy_connect used to resolve a hostname and\nconnect to it to a separate tcp.c file.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Makefile  |    2 +\n connect.c |  270 +------------------------------------------------------------\n tcp.c     |  269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h     |    8 ++\n 4 files changed, 280 insertions(+), 269 deletions(-)\n create mode 100644 tcp.c\n create mode 100644 tcp.h\n\ndiff --git a/Makefile b/Makefile\nindex e40ac0c..fa3a47f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -666,6 +666,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@@ -1958,6 +1959,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 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 2119c3f..9dafcc8 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@@ -150,275 +151,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-#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-\tint sockfd = -1, saved_errno = 0;\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) {\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\tsaved_errno = errno;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tfprintf(stderr, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\thost,\n-\t\t\t\tcnt,\n-\t\t\t\tai_name(ai),\n-\t\t\t\tstrerror(saved_errno));\n-\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 a socket (%s)\", strerror(saved_errno));\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\n\");\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-\tint sockfd = -1, saved_errno = 0;\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\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n-\t\tif (sockfd < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tcontinue;\n-\t\t}\n-\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\tif (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tfprintf(stderr, \"%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(saved_errno));\n-\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 a socket (%s)\", strerror(saved_errno));\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 0000000..89f5c62\n--- /dev/null\n+++ b/tcp.c\n@@ -0,0 +1,269 @@\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 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+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+#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+\tint sockfd = -1, saved_errno = 0;\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) {\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\tsaved_errno = errno;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n+\t\t\tsaved_errno = errno;\n+\t\t\tfprintf(stderr, \"%s[%d: %s]: errno=%s\\n\",\n+\t\t\t\thost,\n+\t\t\t\tcnt,\n+\t\t\t\tai_name(ai),\n+\t\t\t\tstrerror(saved_errno));\n+\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 a socket (%s)\", strerror(saved_errno));\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\n\");\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+\tint sockfd = -1, saved_errno = 0;\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\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n+\t\tif (sockfd < 0) {\n+\t\t\tsaved_errno = errno;\n+\t\t\tcontinue;\n+\t\t}\n+\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\tif (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n+\t\t\tsaved_errno = errno;\n+\t\t\tfprintf(stderr, \"%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(saved_errno));\n+\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 a socket (%s)\", strerror(saved_errno));\n+\n+\tif (flags & CONNECT_VERBOSE)\n+\t\tfprintf(stderr, \"done.\\n\");\n+\n+\treturn sockfd;\n+}\n+\n+#endif /* NO_IPV6 */\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+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 0000000..4de5f71\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.5.3\n"},{"id":"169350","messageId":"20110606093845.GG8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 2/6] daemon: make host resolution into a separate function","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:38:45Z","receivedAt":"2011-06-06T09:38:45Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"For each incoming connection, the git daemon runs a DNS query using\nthe client-supplied hostname parameter, to determine the canonical\nhostname and IP address in case it is needed by the\n--interpolated-path feature (to distinguish between virtual hosts).\nPut the code for this in a separate function so the details of this\nlookup can be easily tweaked.\n\nSigned-off-by; Jonathan Nieder <jrnieder@gmail.com>\n---\n daemon.c |  112 +++++++++++++++++++++++++++++++++++---------------------------\n 1 files changed, 63 insertions(+), 49 deletions(-)\n\ndiff --git a/daemon.c b/daemon.c\nindex 4c8346d..3958cb6 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -432,6 +432,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@@ -467,56 +526,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.5.3\n"},{"id":"169351","messageId":"20110606093929.GH8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 3/6] daemon: move locate_host to tcp.c","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:39:29Z","receivedAt":"2011-06-06T09:39:29Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Keep 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---\n Makefile |    2 +-\n daemon.c |   62 ++------------------------------------------------------------\n tcp.c    |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h    |    3 +++\n 4 files changed, 59 insertions(+), 61 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex fa3a47f..df84157 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1959,7 +1959,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 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 3958cb6..3261616 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -1,5 +1,6 @@\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@@ -432,65 +433,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@@ -530,7 +472,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 89f5c62..2cb90db 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -97,6 +97,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@@ -162,6 +190,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 4de5f71..bed3cdc 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.5.3\n"},{"id":"169352","messageId":"20110606094054.GI8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 4/6] transport: fix index in ipv6 connection failed message","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:40:54Z","receivedAt":"2011-06-06T09:40:54Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ever since v1.5.3-rc0~185^2~1 (Verbose connect messages to show\nthe IP addresses used, 2007-05-23), git's ipv4 connection failure\nmessages have looked something like\n\n\tgit.example.com[2: 127.0.0.1]: errno=Network Unreachable\n\nThe increasing index (\"2\" above) indicates which item in the list of\naddresses returned by gethostbyname was being tried so the operator\ncan tell that git was not just trying to resolve the same hostname\nagain and again.\n\nThe ipv6 codepath prints something similar, but the counter is never\nincremented so the index is always \"0\".  Fix it.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n tcp.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/tcp.c b/tcp.c\nindex 2cb90db..9916272 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -134,7 +134,7 @@ static int git_tcp_connect_sock(char *host, int flags)\n \tconst char *port = STR(DEFAULT_GIT_PORT);\n \tstruct addrinfo hints, *ai0, *ai;\n \tint gai;\n-\tint cnt = 0;\n+\tint cnt;\n \n \tget_host_and_port(&host, &port);\n \tif (!*port)\n@@ -154,7 +154,7 @@ static int git_tcp_connect_sock(char *host, int flags)\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) {\n+\tfor (cnt = 0, 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-- \n1.7.5.3\n"},{"id":"169353","messageId":"20110606094128.GJ8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 5/6] tcp: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:41:28Z","receivedAt":"2011-06-06T09:41:28Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"The 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 used to find\nthe canonical IP and hostname for a git server's public address (for\nvirtual hosting) tells getaddrinfo to restrict attention to TCP\nservices after this patch.  That should make a difference because the\nservice parameter is NULL.\n\nNo functional change intended.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Makefile   |    5 ++\n dns-ipv4.c |   33 +++++++++++\n dns-ipv4.h |   65 ++++++++++++++++++++++\n dns-ipv6.c |   45 +++++++++++++++\n dns-ipv6.h |   31 ++++++++++\n tcp.c      |  180 +++++++++++-------------------------------------------------\n 6 files changed, 211 insertions(+), 148 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 df84157..7d3cb41 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1474,6 +1474,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 0000000..39849c3\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_IGNORE_ERROR))\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 0000000..ef7208f\n--- /dev/null\n+++ b/dns-ipv4.h\n@@ -0,0 +1,65 @@\n+#ifndef DNS_IPV4_H\n+#define DNS_IPV4_H\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_IGNORE_ERROR = 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 0000000..b5f298a\n--- /dev/null\n+++ b/dns-ipv6.c\n@@ -0,0 +1,45 @@\n+#include \"cache.h\"\n+#include \"dns-ipv6.h\"\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_IGNORE_ERROR))\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 0000000..339877f\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_IGNORE_ERROR = 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 9916272..aacc2d1 100644\n--- a/tcp.c\n+++ b/tcp.c\n@@ -1,6 +1,12 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n \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 #define STR(s)\tSTR_(s)\n \n@@ -85,44 +91,27 @@ static void get_host_and_port(char **host, const char **port)\n \t}\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_IGNORE_ERROR, &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@@ -132,52 +121,48 @@ static int git_tcp_connect_sock(char *host, int flags)\n {\n \tint sockfd = -1, saved_errno = 0;\n \tconst char *port = STR(DEFAULT_GIT_PORT);\n-\tstruct addrinfo hints, *ai0, *ai;\n-\tint gai;\n-\tint cnt;\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 (cnt = 0, 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+\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\tsaved_errno = errno;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {\n+\t\tif (connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n \t\t\tsaved_errno = errno;\n \t\t\tfprintf(stderr, \"%s[%d: %s]: errno=%s\\n\",\n \t\t\t\thost,\n \t\t\t\tcnt,\n-\t\t\t\tai_name(ai),\n+\t\t\t\tdns_name(&i),\n \t\t\t\tstrerror(saved_errno));\n \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 a socket (%s)\", strerror(saved_errno));\n@@ -188,107 +173,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-\tint sockfd = -1, saved_errno = 0;\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\tsockfd = socket(he->h_addrtype, SOCK_STREAM, 0);\n-\t\tif (sockfd < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tcontinue;\n-\t\t}\n-\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\tif (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tfprintf(stderr, \"%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(saved_errno));\n-\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 a socket (%s)\", strerror(saved_errno));\n-\n-\tif (flags & CONNECT_VERBOSE)\n-\t\tfprintf(stderr, \"done.\\n\");\n-\n-\treturn sockfd;\n-}\n-\n-#endif /* NO_IPV6 */\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.5.3\n"},{"id":"169354","messageId":"20110606094620.GK8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"[PATCH 6/6] transport: learn to honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:46:20Z","receivedAt":"2011-06-06T09:46:20Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"As rfc2782 explains, SRV records allow administrators \"to use serveral\nservers for a single domain, to move services from host to host with\nlittle fuss, and to designate some hosts as primary servers for a\nservice and others as backups\".  Someone noticed in a recent server\nmove that that second part would have been very handy (since the git\nprotocol doesn't include a concept of redirects).\n\nSadly, getaddrinfo itself does not include a facility to resolve SRV\nrecords automatically.  So this patch uses the libbind functions from\nlibresolv to parse the response for ourselves.  Hopefully after\nreading enough specimens of code like this, someone will be disgusted\nenough to extend libc to take care of this in general.\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 without the libbind ns_* functions or drand48(),\nthe new SRV support is only built when requested with the USE_SRV_RR\ncompile-time option.\n\nBased on a patch by Julien Cristau <jcristau@debian.org>.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nThanks for reading.\n\n Makefile |   10 ++\n srv.c    |  290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n srv.h    |   15 +++\n tcp.c    |   90 +++++++++++++-------\n 4 files changed, 375 insertions(+), 30 deletions(-)\n create mode 100644 srv.c\n create mode 100644 srv.h\n\ndiff --git a/Makefile b/Makefile\nindex 7d3cb41..316b29e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -124,6 +124,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@@ -1357,6 +1361,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@@ -1965,6 +1974,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 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 0000000..222c244\n--- /dev/null\n+++ b/srv.c\n@@ -0,0 +1,290 @@\n+#include \"cache.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 -1;\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 -1;\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 -1;\n+\n+\tres->target = xstrdup(buf);\n+\treturn 0;\n+}\n+\n+static struct parsed_srv_rr *srv_parse(ns_msg *msg, int n)\n+{\n+\tstruct parsed_srv_rr *rrs;\n+\tint nr_parsed = 0;\n+\tint i;\n+\n+\trrs = xmalloc(sizeof(*rrs) * n);\n+\tfor (i = 0; i < n; i++) {\n+\t\tns_rr rr;\n+\n+\t\tif (ns_parserr(msg, ns_s_an, i, &rr) ||\n+\t\t    srv_parse_rr(msg, &rr, rrs + i))\n+\t\t\tgoto fail;\n+\t\tnr_parsed++;\n+\t}\n+\n+\treturn rrs;\n+fail:\n+\tfor (i = 0; i < nr_parsed; i++)\n+\t\tfree(rrs[i].target);\n+\tfree(rrs);\n+\treturn NULL;\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 (wt >= pos)\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) (drand48() * (*wt_remaining + 1));\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 + 1);\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 left.\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 at random, taking weights into account, and\n+\t\t * make it rrs[i], preserving 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+\tif (ns_initparse(buf, len, &msg))\n+\t\tgoto out;\n+\tn = ns_msg_count(msg, ns_s_an);\n+\tif (!n)\n+\t\tgoto out;\n+\tassert(n < (1 << 16));\n+\n+\t/* Parse reply.  If a SRV RR cannot be parsed, give up. */\n+\tret = -1;\n+\trrs = srv_parse(&msg, n);\n+\tif (!rrs)\n+\t\tgoto out;\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(**hosts));\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 0;\n+}\ndiff --git a/srv.h b/srv.h\nnew file mode 100644\nindex 0000000..7cea4f4\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 aacc2d1..53ad2e0 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@@ -120,49 +121,73 @@ void git_locate_host(const char *hostname, char **ip_address,\n static int git_tcp_connect_sock(char *host, int flags)\n {\n \tint sockfd = -1, saved_errno = 0;\n-\tconst char *port = STR(DEFAULT_GIT_PORT);\n+\tconst char *port = NULL;\n+\tstruct host *hosts = NULL;\n \tresolver_result ai;\n \tresolved_address i;\n \tint cnt = -1;\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-\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+\tfor (j = 0; j < n; j++) {\n+\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\tfprintf(stderr, \"Looking up %s ... \", hosts[j].hostname);\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\tsaved_errno = errno;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n-\t\t\tsaved_errno = errno;\n-\t\t\tfprintf(stderr, \"%s[%d: %s]: errno=%s\\n\",\n-\t\t\t\thost,\n-\t\t\t\tcnt,\n-\t\t\t\tdns_name(&i),\n-\t\t\t\tstrerror(saved_errno));\n-\t\t\tclose(sockfd);\n-\t\t\tsockfd = -1;\n+\t\tif (dns_resolve(hosts[j].hostname,\n+\t\t\t\thosts[j].port, RESOLVE_IGNORE_ERROR, &ai)) {\n+\t\t\tif (flags & CONNECT_VERBOSE)\n+\t\t\t\tfprintf(stderr, \"failed.\\n\");\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}\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\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\tsaved_errno = errno;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (connect(sockfd, dns_addr(i, ai), dns_addrlen(i, ai)) < 0) {\n+\t\t\t\tsaved_errno = errno;\n+\t\t\t\tfprintf(stderr, \"%s[%d: %s]: errno=%s\\n\",\n+\t\t\t\t\thosts[j].hostname,\n+\t\t\t\t\tcnt,\n+\t\t\t\t\tdns_name(&i),\n+\t\t\t\t\tstrerror(saved_errno));\n+\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-\tdns_free(ai);\n+\t\tif (sockfd >= 0)\n+\t\t\tbreak;\n+\t}\n \n \tif (sockfd < 0)\n \t\tdie(\"unable to connect a socket (%s)\", strerror(saved_errno));\n@@ -170,6 +195,11 @@ 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 \treturn sockfd;\n }\n \n-- \n1.7.5.3\n"},{"id":"169355","messageId":"20110606094936.GL8015@elie","threadId":"27554","inReplyTo":"20110606093019.GD8015@elie","subject":"Re: [RFC/PATCH 0/6] git: please honor DNS SRV records","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T09:49:36Z","receivedAt":"2011-06-06T09:49:36Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n\n> Does the general idea seem sane?\n\nHere's a diffstat for the curious.\n\nJonathan Nieder (6):\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  transport: fix index in ipv6 connection failed message\n  tcp: unify ipv4 and ipv6 code paths\n  transport: learn to honor DNS SRV records\n\n Makefile   |   17 ++++\n connect.c  |  270 +-------------------------------------------------------\n daemon.c   |   54 +----------\n dns-ipv4.c |   33 +++++++\n dns-ipv4.h |   65 ++++++++++++++\n dns-ipv6.c |   45 +++++++++\n dns-ipv6.h |   31 +++++++\n srv.c      |  290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n srv.h      |   15 +++\n tcp.c      |  236 ++++++++++++++++++++++++++++++++++++++++++++++++\n tcp.h      |   11 +++\n 11 files changed, 749 insertions(+), 318 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 srv.c\n create mode 100644 srv.h\n create mode 100644 tcp.c\n create mode 100644 tcp.h\n"},{"id":"169356","messageId":"20110606100147.GN8015@elie","threadId":"27554","inReplyTo":"20110606094128.GJ8015@elie","subject":"Re: [PATCH 5/6] tcp: unify ipv4 and ipv6 code paths","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-06-06T10:01:47Z","receivedAt":"2011-06-06T10:01:47Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n\n>                  tells getaddrinfo to restrict attention to TCP\n> services after this patch.  That should make a difference because the\n> service parameter is NULL.\n\nSorry, the last sentence should say \"That should _not_ make a\ndifference\".  You would be able to tell the difference between the\nbefore and after with \"strace git daemon\" because of the ai_socktype\nand ai_protocol settings:\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\nbut I don't think the effect is any different --- the ai_socktype and\nprotocol hints are for getservbyname functionality and the code that\ncares about it was already passing those hints.\n"}]}