diff --git a/Makefile b/Makefile index db72c45..bee6ec6 100644 --- a/Makefile +++ b/Makefile @@ -789,6 +789,7 @@ ifeq ($(uname_S),Linux) NO_STRLCPY = YesPlease NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease + NEEDS_RESOLV = YesPlease endif ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease diff --git a/connect.c b/connect.c index 2119c3f..e0699e3 100644 --- a/connect.c +++ b/connect.c @@ -7,6 +7,8 @@ #include "remote.h" #include "url.h" +#include + static char *server_capabilities; static int check_ref(const char *name, int len, unsigned int flags) @@ -187,59 +189,131 @@ static const char *ai_name(const struct addrinfo *ai) return addr; } +struct host { + char *hostname; + char *port; +}; + +static int get_srv(const char *host, struct host **hosts) +{ + unsigned char buf[1024]; + ns_msg msg; + ns_rr rr; + int len; + int i = 0, n; + int port; + char *hostname; + char *query; + + if (asprintf(&query, "_git._tcp.%s", host) == -1) + return 0; + + len = res_search(query, ns_c_in, ns_t_srv, buf, sizeof buf); + if (len < 0) + goto out_free; + + ns_initparse(buf, len, &msg); + n = ns_msg_count(msg, ns_s_an); + if (n < 1) + goto out_free; + + for (i = 0; i < n; i++) { + ns_parserr(&msg, ns_s_an, i, &rr); + if (ns_rr_rdlen(rr) < 6) + break; + + if (i == 0) + *hosts = malloc(n * sizeof(struct host)); + + /* bytes 0-1 == priority, bytes 2-3 == weight */ + port = (ns_rr_rdata(rr)[4] << 8) + ns_rr_rdata(rr)[5]; + hostname = malloc(1024); + dn_expand(ns_msg_base(msg), ns_msg_end(msg), ns_rr_rdata(rr) + 6, + hostname, 1024); + (*hosts)[i].hostname = hostname; + asprintf(&((*hosts)[i].port), "%hu", num_port); + } + +out_free: + free(query); + return i; +} + /* * Returns a connected socket() fd, or else die()s. */ static int git_tcp_connect_sock(char *host, int flags) { int sockfd = -1, saved_errno = 0; - const char *port = STR(DEFAULT_GIT_PORT); + const char *port = NULL; struct addrinfo hints, *ai0, *ai; int gai; int cnt = 0; + int i; + int n = 0; + struct host *hosts; get_host_and_port(&host, &port); - if (!*port) - port = ""; - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - if (flags & CONNECT_VERBOSE) - fprintf(stderr, "Looking up %s ... ", host); + if (!port) { + port = STR(DEFAULT_GIT_PORT); + n = get_srv(host, &hosts); + } + if (n == 0) { + hosts = malloc(sizeof(struct host)); + hosts[0].hostname = strdup(host); + hosts[0].port = strdup(port); + n = 1; + } - gai = getaddrinfo(host, port, &hints, &ai); - if (gai) - die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai)); + for (i = 0; i < n; i++) { + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; - if (flags & CONNECT_VERBOSE) - fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "Looking up %s ... ", host); - for (ai0 = ai; ai; ai = ai->ai_next) { - sockfd = socket(ai->ai_family, - ai->ai_socktype, ai->ai_protocol); - if (sockfd < 0) { - saved_errno = errno; + gai = getaddrinfo(hosts[i].hostname, hosts[i].port, &hints, &ai); + if (gai) continue; - } - if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { - saved_errno = errno; - fprintf(stderr, "%s[%d: %s]: errno=%s\n", - host, - cnt, - ai_name(ai), - strerror(saved_errno)); - close(sockfd); - sockfd = -1; - continue; - } + if (flags & CONNECT_VERBOSE) - fprintf(stderr, "%s ", ai_name(ai)); - break; + fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + + for (ai0 = ai; ai; ai = ai->ai_next) { + sockfd = socket(ai->ai_family, + ai->ai_socktype, ai->ai_protocol); + if (sockfd < 0) { + saved_errno = errno; + continue; + } + if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { + saved_errno = errno; + fprintf(stderr, "%s[%d: %s]: errno=%s\n", + host, + cnt, + ai_name(ai), + strerror(saved_errno)); + close(sockfd); + sockfd = -1; + continue; + } + if (flags & CONNECT_VERBOSE) + fprintf(stderr, "%s ", ai_name(ai)); + break; + } + + freeaddrinfo(ai0); + if (sockfd >= 0) + break; } - freeaddrinfo(ai0); + for (i = 0; i < n; i++) { + free(hosts[i].hostname); + free(hosts[i].port); + } + free(hosts); if (sockfd < 0) die("unable to connect a socket (%s)", strerror(saved_errno));