lib: dns: Clean up allocated structure on error exit.
[sfrench/samba-autobuild/.git] / lib / addns / dnssock.c
index 9e769097ba961543e92ce9ec886ed378344fb0ee..a45e3255a54e0ba9b9e09e28bcc50b944d8db97e 100644 (file)
@@ -27,6 +27,7 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include "system/select.h"
+#include "../lib/util/debug.h"
 
 static int destroy_dns_connection(struct dns_connection *conn)
 {
@@ -40,76 +41,118 @@ static DNS_ERROR dns_tcp_open( const char *nameserver,
                               TALLOC_CTX *mem_ctx,
                               struct dns_connection **result )
 {
-       uint32_t ulAddress;
-       struct hostent *pHost;
-       struct sockaddr_in s_in;
+       struct addrinfo hints;
+       struct addrinfo *ai_result = NULL;
+       struct addrinfo *rp;
        struct dns_connection *conn;
-       int res;
+       int ret;
+       char service[16];
+
+       snprintf(service, sizeof(service), "%d", DNS_TCP_PORT);
 
        if (!(conn = talloc(mem_ctx, struct dns_connection))) {
                return ERROR_DNS_NO_MEMORY;
        }
 
-       if ( (ulAddress = inet_addr( nameserver )) == INADDR_NONE ) {
-               if ( (pHost = gethostbyname( nameserver )) == NULL ) {
-                       TALLOC_FREE(conn);
-                       return ERROR_DNS_INVALID_NAME_SERVER;
-               }
-               memcpy( &ulAddress, pHost->h_addr, pHost->h_length );
-       }
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = 0;
+       hints.ai_protocol = IPPROTO_TCP;
 
-       conn->s = socket( PF_INET, SOCK_STREAM, 0 );
-       if (conn->s == -1) {
+       ret = getaddrinfo(nameserver, service, &hints, &ai_result);
+       if (ret != 0) {
+               DEBUG(1,("dns_tcp_open: getaddrinfo: %s\n", gai_strerror(ret)));
                TALLOC_FREE(conn);
-               return ERROR_DNS_CONNECTION_FAILED;
+               return ERROR_DNS_INVALID_NAME_SERVER;
        }
 
-       talloc_set_destructor(conn, destroy_dns_connection);
+       for (rp = ai_result; rp != NULL; rp = rp->ai_next) {
+               conn->s = socket(rp->ai_family,
+                               rp->ai_socktype,
+                               rp->ai_protocol);
+               if (conn->s == -1) {
+                       continue;
+               }
+               do {
+                       ret = connect(conn->s, rp->ai_addr, rp->ai_addrlen);
+               } while ((ret == -1) && (errno == EINTR));
+               if (ret != -1) {
+                       /* Successful connect */
+                       break;
+               }
+               close(conn->s);
+       }
 
-       s_in.sin_family = AF_INET;
-       s_in.sin_addr.s_addr = ulAddress;
-       s_in.sin_port = htons( DNS_TCP_PORT );
+       freeaddrinfo(ai_result);
 
-       res = connect(conn->s, (struct sockaddr*)&s_in, sizeof( s_in ));
-       if (res == -1) {
+       /* Failed to connect with any address */
+       if (rp == NULL) {
                TALLOC_FREE(conn);
                return ERROR_DNS_CONNECTION_FAILED;
        }
 
-       conn->hType = DNS_TCP;
+       talloc_set_destructor(conn, destroy_dns_connection);
 
+       conn->hType = DNS_TCP;
        *result = conn;
        return ERROR_DNS_SUCCESS;
 }
 
 /********************************************************************
-********************************************************************/
+ * ********************************************************************/
 
 static DNS_ERROR dns_udp_open( const char *nameserver,
                               TALLOC_CTX *mem_ctx,
                               struct dns_connection **result )
 {
-       unsigned long ulAddress;
-       struct hostent *pHost;
-       struct sockaddr_in RecvAddr;
+       struct addrinfo hints;
+       struct addrinfo *ai_result = NULL;
+       struct addrinfo *rp;
+       struct sockaddr_storage RecvAddr;
        struct dns_connection *conn;
+       int ret;
+       socklen_t RecvAddrLen;
+       char service[16];
+
+       snprintf(service, sizeof(service), "%d", DNS_UDP_PORT);
 
        if (!(conn = talloc(NULL, struct dns_connection))) {
                return ERROR_DNS_NO_MEMORY;
        }
 
-       if ( (ulAddress = inet_addr( nameserver )) == INADDR_NONE ) {
-               if ( (pHost = gethostbyname( nameserver )) == NULL ) {
-                       TALLOC_FREE(conn);
-                       return ERROR_DNS_INVALID_NAME_SERVER;
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_flags = 0;
+       hints.ai_protocol = IPPROTO_UDP;
+
+       ret = getaddrinfo(nameserver, service, &hints, &ai_result);
+       if (ret != 0) {
+               DEBUG(1,("dns_ucp_open:getaddrinfo: %s\n", gai_strerror(ret)));
+               TALLOC_FREE(conn);
+               return ERROR_DNS_INVALID_NAME_SERVER;
+       }
+
+       for (rp = ai_result; rp != NULL; rp = rp->ai_next) {
+               conn->s = socket(rp->ai_family,
+                               rp->ai_socktype,
+                               rp->ai_protocol);
+               if (conn->s == -1) {
+                       continue;
+               }
+               ret = connect(conn->s, rp->ai_addr, rp->ai_addrlen);
+               if (ret != -1) {
+                       /* Successful connect */
+                       break;
                }
-               memcpy( &ulAddress, pHost->h_addr, pHost->h_length );
+               close(conn->s);
        }
 
-       /* Create a socket for sending data */
+       freeaddrinfo(ai_result);
 
-       conn->s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
-       if (conn->s == -1) {
+       /* Failed to connect with any address */
+       if (rp == NULL) {
                TALLOC_FREE(conn);
                return ERROR_DNS_CONNECTION_FAILED;
        }
@@ -117,16 +160,18 @@ static DNS_ERROR dns_udp_open( const char *nameserver,
        talloc_set_destructor(conn, destroy_dns_connection);
 
        /* Set up the RecvAddr structure with the IP address of
-          the receiver (in this example case "123.456.789.1")
-          and the specified port number. */
+          the receiver and the specified port number. */
 
-       ZERO_STRUCT(RecvAddr);
-       RecvAddr.sin_family = AF_INET;
-       RecvAddr.sin_port = htons( DNS_UDP_PORT );
-       RecvAddr.sin_addr.s_addr = ulAddress;
+       RecvAddrLen = sizeof(RecvAddr);
+       if (getpeername(conn->s,
+                       (struct sockaddr *)&RecvAddr,
+                       &RecvAddrLen) == -1) {
+               TALLOC_FREE(conn);
+               return ERROR_DNS_CONNECTION_FAILED;
+       }
 
        conn->hType = DNS_UDP;
-       memcpy( &conn->RecvAddr, &RecvAddr, sizeof( struct sockaddr_in ) );
+       memcpy(&conn->RecvAddr, &RecvAddr, sizeof(struct sockaddr_storage));
 
        *result = conn;
        return ERROR_DNS_SUCCESS;
@@ -135,7 +180,7 @@ static DNS_ERROR dns_udp_open( const char *nameserver,
 /********************************************************************
 ********************************************************************/
 
-DNS_ERROR dns_open_connection( const char *nameserver, int32 dwType,
+DNS_ERROR dns_open_connection( const char *nameserver, int32_t dwType,
                    TALLOC_CTX *mem_ctx,
                    struct dns_connection **conn )
 {
@@ -149,13 +194,17 @@ DNS_ERROR dns_open_connection( const char *nameserver, int32 dwType,
        return ERROR_DNS_INVALID_PARAMETER;
 }
 
-static DNS_ERROR write_all(int fd, uint8 *data, size_t len)
+static DNS_ERROR write_all(int fd, uint8_t *data, size_t len)
 {
        size_t total = 0;
 
        while (total < len) {
 
-               ssize_t ret = write(fd, data + total, len - total);
+               ssize_t ret;
+
+               do {
+                       ret = write(fd, data + total, len - total);
+               } while ((ret == -1) && (errno == EINTR));
 
                if (ret <= 0) {
                        /*
@@ -173,10 +222,10 @@ static DNS_ERROR write_all(int fd, uint8 *data, size_t len)
 static DNS_ERROR dns_send_tcp(struct dns_connection *conn,
                              const struct dns_buffer *buf)
 {
-       uint16 len = htons(buf->offset);
+       uint16_t len = htons(buf->offset);
        DNS_ERROR err;
 
-       err = write_all(conn->s, (uint8 *)&len, sizeof(len));
+       err = write_all(conn->s, (uint8_t *)&len, sizeof(len));
        if (!ERR_DNS_IS_OK(err)) return err;
 
        return write_all(conn->s, buf->data, buf->offset);
@@ -187,9 +236,11 @@ static DNS_ERROR dns_send_udp(struct dns_connection *conn,
 {
        ssize_t ret;
 
-       ret = sendto(conn->s, buf->data, buf->offset, 0,
+       do {
+               ret = sendto(conn->s, buf->data, buf->offset, 0,
                     (struct sockaddr *)&conn->RecvAddr,
                     sizeof(conn->RecvAddr));
+       } while ((ret == -1) && (errno == EINTR));
 
        if (ret != buf->offset) {
                return ERROR_DNS_SOCKET_ERROR;
@@ -211,7 +262,7 @@ DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf)
        return ERROR_DNS_INVALID_PARAMETER;
 }
 
-static DNS_ERROR read_all(int fd, uint8 *data, size_t len)
+static DNS_ERROR read_all(int fd, uint8_t *data, size_t len)
 {
        size_t total = 0;
 
@@ -225,12 +276,21 @@ static DNS_ERROR read_all(int fd, uint8 *data, size_t len)
                pfd.events = POLLIN|POLLHUP;
 
                fd_ready = poll(&pfd, 1, 10000);
+               if (fd_ready == -1) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       return ERROR_DNS_SOCKET_ERROR;
+               }
                if ( fd_ready == 0 ) {
                        /* read timeout */
                        return ERROR_DNS_SOCKET_ERROR;
                }
 
-               ret = read(fd, data + total, len - total);
+               do {
+                       ret = read(fd, data + total, len - total);
+               } while ((ret == -1) && (errno == EINTR));
+
                if (ret <= 0) {
                        /* EOF or error */
                        return ERROR_DNS_SOCKET_ERROR;
@@ -248,29 +308,30 @@ static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
 {
        struct dns_buffer *buf;
        DNS_ERROR err;
-       uint16 len;
+       uint16_t len;
 
        if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
                return ERROR_DNS_NO_MEMORY;
        }
 
-       err = read_all(conn->s, (uint8 *)&len, sizeof(len));
+       err = read_all(conn->s, (uint8_t *)&len, sizeof(len));
        if (!ERR_DNS_IS_OK(err)) {
                return err;
        }
 
        buf->size = ntohs(len);
 
-       if (buf->size) {
-               if (!(buf->data = talloc_array(buf, uint8, buf->size))) {
-                       TALLOC_FREE(buf);
-                       return ERROR_DNS_NO_MEMORY;
-               }
-       } else {
-               buf->data = NULL;
+       if (buf->size == 0) {
+               *presult = buf;
+               return ERROR_DNS_SUCCESS;
+       }
+
+       if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) {
+               TALLOC_FREE(buf);
+               return ERROR_DNS_NO_MEMORY;
        }
 
-       err = read_all(conn->s, buf->data, buf->size);
+       err = read_all(conn->s, buf->data, talloc_get_size(buf->data));
        if (!ERR_DNS_IS_OK(err)) {
                TALLOC_FREE(buf);
                return err;
@@ -295,12 +356,14 @@ static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
         * UDP based DNS can only be 512 bytes
         */
 
-       if (!(buf->data = talloc_array(buf, uint8, 512))) {
+       if (!(buf->data = talloc_array(buf, uint8_t, 512))) {
                TALLOC_FREE(buf);
                return ERROR_DNS_NO_MEMORY;
        }
 
-       received = recv(conn->s, (void *)buf->data, 512, 0);
+       do {
+               received = recv(conn->s, (void *)buf->data, 512, 0);
+       } while ((received == -1) && (errno == EINTR));
 
        if (received == -1) {
                TALLOC_FREE(buf);