From: Jaroslav Kysela Date: Sun, 27 Aug 2017 17:48:11 +0000 (+0200) Subject: tcp: rewrite tcp_connect to try more IP addresses, cleanups, fixes #4552 X-Git-Tag: v4.2.4~83 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b829ab46c923f6487dee636b4d4cd1ef61c94826;p=thirdparty%2Ftvheadend.git tcp: rewrite tcp_connect to try more IP addresses, cleanups, fixes #4552 --- diff --git a/src/tcp.c b/src/tcp.c index 8085a5ede..04c1998b0 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -77,27 +77,55 @@ int tcp_connect(const char *hostname, int port, const char *bindaddr, char *errbuf, size_t errbufsize, int timeout) { - int fd, r, res, err; - struct addrinfo *ai, hints; + int fd = -1, r, res, err; + struct addrinfo *ai, *rai = NULL, hints; + struct sockaddr_storage bindip; char portstr[6]; socklen_t errlen = sizeof(err); + errbuf[0] = '\0'; + + bindip.ss_family = AF_UNSPEC; + if (bindaddr && bindaddr[0] != '\0') { + if (tcp_get_ip_from_str(bindaddr, &bindip) == NULL) { + snprintf(errbuf, errbufsize, "Cannot bind to addr '%s'", bindaddr); + return -1; + } + } + snprintf(portstr, 6, "%u", port); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; res = getaddrinfo(hostname, portstr, &hints, &ai); - if (res != 0) { snprintf(errbuf, errbufsize, "%s", gai_strerror(res)); return -1; } - fd = tvh_socket(ai->ai_family, SOCK_STREAM, 0); +again: + if (fd >= 0) { + close(fd); + fd = -1; + } + rai = rai == NULL ? ai : rai->ai_next; + if (rai == NULL) { + if (errbuf[0] == '\0') + snprintf(errbuf, errbufsize, "Invalid or unresolved hostname '%s'", hostname); + goto error; + } + + if (bindip.ss_family == AF_UNSPEC) { + if (rai->ai_family != AF_INET && rai->ai_family != AF_INET6) + goto again; + } else if (rai->ai_family != bindip.ss_family) { + goto again; + } + + fd = tvh_socket(rai->ai_family, SOCK_STREAM, 0); if(fd < 0) { snprintf(errbuf, errbufsize, "Unable to create socket: %s", strerror(errno)); - freeaddrinfo(ai); - return -1; + goto again; } /** @@ -105,30 +133,16 @@ tcp_connect(const char *hostname, int port, const char *bindaddr, */ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); - if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) { - if (bindaddr && bindaddr[0] != '\0') { - struct sockaddr_storage ip; - memset(&ip, 0, sizeof(ip)); - ip.ss_family = ai->ai_family; - if (inet_pton(AF_INET, bindaddr, IP_IN_ADDR(ip)) <= 0 || - bind(fd, (struct sockaddr *)&ip, IP_IN_ADDRLEN(ip)) < 0) { - snprintf(errbuf, errbufsize, "Cannot bind to IPv%s addr '%s'", - ai->ai_family == AF_INET6 ? "6" : "4", - bindaddr); - freeaddrinfo(ai); - close(fd); - return -1; - } + if (bindip.ss_family != AF_UNSPEC) { + if (bind(fd, (struct sockaddr *)&bindip, IP_IN_ADDRLEN(bindip)) < 0) { + snprintf(errbuf, errbufsize, "Cannot bind to IPv%s addr '%s'", + bindip.ss_family == AF_INET6 ? "6" : "4", + bindaddr); + goto error; } - } else { - snprintf(errbuf, errbufsize, "Invalid protocol family"); - freeaddrinfo(ai); - close(fd); - return -1; } - r = connect(fd, ai->ai_addr, ai->ai_addrlen); - freeaddrinfo(ai); + r = connect(fd, rai->ai_addr, rai->ai_addrlen); if(r < 0) { /* timeout < 0 - do not wait at all */ @@ -147,14 +161,13 @@ tcp_connect(const char *hostname, int port, const char *bindaddr, /* minimal timeout is one second */ if (timeout < 1) - timeout = 0; + timeout = 1; while (1) { if (!tvheadend_is_running()) { errbuf[0] = '\0'; tvhpoll_destroy(efd); - close(fd); - return -1; + goto error; } r = tvhpoll_wait(efd, &ev, 1, timeout * 1000); @@ -162,17 +175,15 @@ tcp_connect(const char *hostname, int port, const char *bindaddr, break; if (r == 0) { /* Timeout */ - snprintf(errbuf, errbufsize, "Connection attempt timed out"); + snprintf(errbuf, errbufsize, "Connection attempt to '%s' timed out", hostname); tvhpoll_destroy(efd); - close(fd); - return -1; + goto again; } if (!ERRNO_AGAIN(errno)) { snprintf(errbuf, errbufsize, "poll() error: %s", strerror(errno)); tvhpoll_destroy(efd); - close(fd); - return -1; + goto error; } } @@ -187,18 +198,23 @@ tcp_connect(const char *hostname, int port, const char *bindaddr, if(err != 0) { snprintf(errbuf, errbufsize, "%s", strerror(err)); - close(fd); - return -1; + goto again; } fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); - /* Set the keep-alive active */ err = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&err, errlen); + freeaddrinfo(ai); return fd; + +error: + if (fd >= 0) + close(fd); + freeaddrinfo(ai); + return -1; }