View file File name : dig.py Content :""" Compendium of generic DNS utilities. The 'dig' command line tool must be installed in order to use this module. """ import logging import re import salt.utils.network import salt.utils.path log = logging.getLogger(__name__) __virtualname__ = "dig" def __virtual__(): """ Only load module if dig binary is present """ if salt.utils.path.which("dig"): return __virtualname__ return ( False, "The dig execution module cannot be loaded: the dig binary is not in the path.", ) def check_ip(addr): """ Check if address is a valid IP. returns True if valid, otherwise False. CLI Example: .. code-block:: bash salt ns1 dig.check_ip 127.0.0.1 salt ns1 dig.check_ip 1111:2222:3333:4444:5555:6666:7777:8888 """ try: addr = addr.rsplit("/", 1) except AttributeError: # Non-string passed return False if salt.utils.network.is_ipv4(addr[0]): try: if 1 <= int(addr[1]) <= 32: return True except ValueError: # Non-int subnet notation return False except IndexError: # No subnet notation used (i.e. just an IPv4 address) return True if salt.utils.network.is_ipv6(addr[0]): try: if 8 <= int(addr[1]) <= 128: return True except ValueError: # Non-int subnet notation return False except IndexError: # No subnet notation used (i.e. just an IPv4 address) return True return False def A(host, nameserver=None): """ Return the A record for ``host``. Always returns a list. CLI Example: .. code-block:: bash salt ns1 dig.A www.google.com """ dig = ["dig", "+short", str(host), "A"] if nameserver is not None: dig.append("@{}".format(nameserver)) cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False if cmd["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", cmd["retcode"], ) return [] # make sure all entries are IPs return [x for x in cmd["stdout"].split("\n") if check_ip(x)] def AAAA(host, nameserver=None): """ Return the AAAA record for ``host``. Always returns a list. CLI Example: .. code-block:: bash salt ns1 dig.AAAA www.google.com """ dig = ["dig", "+short", str(host), "AAAA"] if nameserver is not None: dig.append("@{}".format(nameserver)) cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False if cmd["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", cmd["retcode"], ) return [] # make sure all entries are IPs return [x for x in cmd["stdout"].split("\n") if check_ip(x)] def NS(domain, resolve=True, nameserver=None): """ Return a list of IPs of the nameservers for ``domain`` If ``resolve`` is False, don't resolve names. CLI Example: .. code-block:: bash salt ns1 dig.NS google.com """ dig = ["dig", "+short", str(domain), "NS"] if nameserver is not None: dig.append("@{}".format(nameserver)) cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False if cmd["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", cmd["retcode"], ) return [] if resolve: ret = [] for ns_host in cmd["stdout"].split("\n"): for ip_addr in A(ns_host, nameserver): ret.append(ip_addr) return ret return cmd["stdout"].split("\n") def SPF(domain, record="SPF", nameserver=None): """ Return the allowed IPv4 ranges in the SPF record for ``domain``. If record is ``SPF`` and the SPF record is empty, the TXT record will be searched automatically. If you know the domain uses TXT and not SPF, specifying that will save a lookup. CLI Example: .. code-block:: bash salt ns1 dig.SPF google.com """ spf_re = re.compile(r"(?:\+|~)?(ip[46]|include):(.+)") cmd = ["dig", "+short", str(domain), record] if nameserver is not None: cmd.append("@{}".format(nameserver)) result = __salt__["cmd.run_all"](cmd, python_shell=False) # In this case, 0 is not the same as False if result["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", result["retcode"], ) return [] if result["stdout"] == "" and record == "SPF": # empty string is successful query, but nothing to return. So, try TXT # record. return SPF(domain, "TXT", nameserver) sections = re.sub('"', "", result["stdout"]).split() if not sections or sections[0] != "v=spf1": return [] if sections[1].startswith("redirect="): # Run a lookup on the part after 'redirect=' (9 chars) return SPF(sections[1][9:], "SPF", nameserver) ret = [] for section in sections[1:]: try: mechanism, address = spf_re.match(section).groups() except AttributeError: # Regex was not matched continue if mechanism == "include": ret.extend(SPF(address, "SPF", nameserver)) elif mechanism in ("ip4", "ip6") and check_ip(address): ret.append(address) return ret def MX(domain, resolve=False, nameserver=None): """ Return a list of lists for the MX of ``domain``. If the ``resolve`` argument is True, resolve IPs for the servers. It's limited to one IP, because although in practice it's very rarely a round robin, it is an acceptable configuration and pulling just one IP lets the data be similar to the non-resolved version. If you think an MX has multiple IPs, don't use the resolver here, resolve them in a separate step. CLI Example: .. code-block:: bash salt ns1 dig.MX google.com """ dig = ["dig", "+short", str(domain), "MX"] if nameserver is not None: dig.append("@{}".format(nameserver)) cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False if cmd["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", cmd["retcode"], ) return [] stdout = [x.split() for x in cmd["stdout"].split("\n")] if resolve: return [(lambda x: [x[0], A(x[1], nameserver)[0]])(x) for x in stdout] return stdout def TXT(host, nameserver=None): """ Return the TXT record for ``host``. Always returns a list. CLI Example: .. code-block:: bash salt ns1 dig.TXT google.com """ dig = ["dig", "+short", str(host), "TXT"] if nameserver is not None: dig.append("@{}".format(nameserver)) cmd = __salt__["cmd.run_all"](dig, python_shell=False) if cmd["retcode"] != 0: log.warning( "dig returned exit code '%s'. Returning empty list as fallback.", cmd["retcode"], ) return [] return [i for i in cmd["stdout"].split("\n")] # Let lowercase work, since that is the convention for Salt functions a = A aaaa = AAAA ns = NS spf = SPF mx = MX