Return the ip resolved by dns, but do not exit on failure, only raise an
    exception. Obeys system preference for IPv4/6 address resolution - this
    can be overridden by the ipv6 flag.
    Tries to connect to the address before considering it useful. If no address
    can be reached, the first one resolved is used as a fallback.

def dns_check(addr, port=80, safe=False, ipv6=None, attempt_connect=True):
    '''
    Return the ip resolved by dns, but do not exit on failure, only raise an
    exception. Obeys system preference for IPv4/6 address resolution - this
    can be overridden by the ipv6 flag.
    Tries to connect to the address before considering it useful. If no address
    can be reached, the first one resolved is used as a fallback.
    '''
    error = False
    lookup = addr
    seen_ipv6 = False
    family = socket.AF_INET6 if ipv6 else socket.AF_INET if ipv6 is False else socket.AF_UNSPEC

    hostnames = []
    try:
        refresh_dns()
        hostnames = socket.getaddrinfo(addr, port, family, socket.SOCK_STREAM)
    except TypeError:
        err = ('Attempt to resolve address \'{0}\' failed. Invalid or unresolveable address').format(lookup)
        raise SaltSystemExit(code=42, msg=err)
    except socket.error:
        error = True

    # If ipv6 is set to True, attempt another lookup using the IPv4 family,
    # just in case we're attempting to lookup an IPv4 IP
    # as an IPv6 hostname.
    if error and ipv6:
        try:
            refresh_dns()
            hostnames = socket.getaddrinfo(addr, port,
                                           socket.AF_INET,
                                           socket.SOCK_STREAM)
        except TypeError:
            err = ('Attempt to resolve address \'{0}\' failed. Invalid or unresolveable address').format(lookup)
            raise SaltSystemExit(code=42, msg=err)
        except socket.error:
            error = True

    try:
        if not hostnames:
            error = True
        else:
            resolved = False
            candidates = []
            for h in hostnames:
                # Input is IP address, passed through unchanged, just return it
                if h[4][0] == addr:
                    resolved = salt.utils.zeromq.ip_bracket(addr)
                    break

                candidate_addr = salt.utils.zeromq.ip_bracket(h[4][0])

                # sometimes /etc/hosts contains ::1 localhost
                if not ipv6 and candidate_addr == '[::1]':
                    continue

                candidates.append(candidate_addr)

                if attempt_connect:
                    try:
                        s = socket.socket(h[0], socket.SOCK_STREAM)
                        s.settimeout(2)
                        s.connect((candidate_addr.strip('[]'), h[4][1]))
                        s.close()

                        resolved = candidate_addr
                        break
                    except socket.error:
                        pass
            if not resolved:
                if candidates:
                    resolved = candidates[0]
                else:
                    error = True
    except TypeError:
        err = ('Attempt to resolve address \'{0}\' failed. Invalid or unresolveable address').format(lookup)
        raise SaltSystemExit(code=42, msg=err)
    except socket.error:
        error = True

    if error:
        err = ('DNS lookup or connection check of \'{0}\' failed.').format(addr)
        if safe:
            if salt.log.is_console_configured():
                # If logging is not configured it also means that either
                # the master or minion instance calling this hasn't even
                # started running
                log.error(err)
            raise SaltClientError()
        raise SaltSystemExit(code=42, msg=err)
    return resolved