[
  {
    "path": "README.md",
    "content": "# blcheck\n\nA powerful script for testing a domain or an IP against mailing black lists.\nScript will use dig if it is found. If dig is not found script will use host.\n\n\nFeatures\n--------------------\n\n* More then __100 black lists__ already included!\n* Automatic distinction between __domain or IP__\n* Performs __PTR validation__ (only if domain is supplied, does not work for IP)\n* 3 verbose (-v) levels and a quiet (-q) mode\n* The result of script is the number of servers which blacklisted the domain, so it can be used for any kind of __automated scripts or cronjobs__\n* Informative and pleasant output\n\n\nRequirements\n--------------------\n\n* Any Unix/Linux with POSIX shell.\n* Either dig or host command available.\n\n\nUsage\n--------------------\n\nblcheck [options] <domain\\_or\\_IP>\n\nSupplied domain must be full qualified domain name.\nIf the IP is supplied, the PTR check cannot be executed and will be skipped.\n\n<pre>\n-d dnshost  Use host as DNS server to make lookups\n-l file     Load blacklists from file, separated by space or new line\n-c          Warn if the top level domain of the blacklist has expired\n-v          Verbose mode, can be used multiple times (up to -vvv)\n-q          Quiet modem with absolutely no output (useful for scripts)\n-p          Plain text output (no coloring, no interactive status)\n-h          The help you are just reading\n</pre>\n\nResult of the script is the number of blacklisted entries. So if the supplied\nIP is not blacklisted on any of the servers the result is 0.\n\n\nTODO\n--------------------\n1. Handle domains with multiple DNS entries.\n\nLicence\n--------------------\nMIT License\n\nCopyright (c) 2018 Intellex\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\nCredits\n--------------------\nScript has been written by the [Intellex](http://intellex.rs/en) team.\nContributors:\n\t[Darko Poljak](https://github.com/darko-poljak)\n\n\n"
  },
  {
    "path": "blcheck",
    "content": "#!/bin/bash\n#\n# MIT License\n#\n# Copyright (c) Intellex 2015\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n# =============================================================================\n#\n# title             : blcheck\n# description       : Test any domain against more then 100 black lists.\n# author            : Intellex\n# contributors      : Darko Poljak\n# date              : 2016-03-18\n# version           : 0.6.0\n# usage             : blcheck [options] <domain_or_ip>\n#\n# =============================================================================\n\n# Config {\n\n\n    # How many tries and for how long to wait for DNS queries\n    CONF_DNS_TRIES=2\n    CONF_DNS_DURATION=3\n\n    # Blacklists to check\n    CONF_BLACKLISTS=\"\n        0spam-killlist.fusionzero.com\n        0spam.fusionzero.com\n        access.redhawk.org\n        all.rbl.jp\n        all.spam-rbl.fr\n        all.spamrats.com\n        aspews.ext.sorbs.net\n        b.barracudacentral.org\n        backscatter.spameatingmonkey.net\n        badnets.spameatingmonkey.net\n        bb.barracudacentral.org\n        bl.drmx.org\n        bl.konstant.no\n        bl.nszones.com\n        bl.spamcannibal.org\n        bl.spameatingmonkey.net\n        bl.spamstinks.com\n        black.junkemailfilter.com\n        blackholes.five-ten-sg.com\n        blacklist.sci.kun.nl\n        blacklist.woody.ch\n        bogons.cymru.com\n        bsb.empty.us\n        bsb.spamlookup.net\n        cart00ney.surriel.com\n        cbl.abuseat.org\n        cbl.anti-spam.org.cn\n        cblless.anti-spam.org.cn\n        cblplus.anti-spam.org.cn\n        cdl.anti-spam.org.cn\n        cidr.bl.mcafee.com\n        combined.rbl.msrbl.net\n        db.wpbl.info\n        dev.null.dk\n        dialups.visi.com\n        dnsbl-0.uceprotect.net\n        dnsbl-1.uceprotect.net\n        dnsbl-2.uceprotect.net\n        dnsbl-3.uceprotect.net\n        dnsbl.anticaptcha.net\n        dnsbl.aspnet.hu\n        dnsbl.inps.de\n        dnsbl.justspam.org\n        dnsbl.kempt.net\n        dnsbl.madavi.de\n        dnsbl.rizon.net\n        dnsbl.rv-soft.info\n        dnsbl.rymsho.ru\n        dnsbl.sorbs.net\n        dnsbl.zapbl.net\n        dnsrbl.swinog.ch\n        dul.pacifier.net\n        dyn.nszones.com\n        dyna.spamrats.com\n        fnrbl.fast.net\n        fresh.spameatingmonkey.net\n        hostkarma.junkemailfilter.com\n        images.rbl.msrbl.net\n        ips.backscatterer.org\n        ix.dnsbl.manitu.net\n        korea.services.net\n        l2.bbfh.ext.sorbs.net\n        l3.bbfh.ext.sorbs.net\n        l4.bbfh.ext.sorbs.net\n        list.bbfh.org\n        list.blogspambl.com\n        mail-abuse.blacklist.jippg.org\n        netbl.spameatingmonkey.net\n        netscan.rbl.blockedservers.com\n        no-more-funn.moensted.dk\n        noptr.spamrats.com\n        orvedb.aupads.org\n        pbl.spamhaus.org\n        phishing.rbl.msrbl.net\n        pofon.foobar.hu\n        psbl.surriel.com\n        rbl.abuse.ro\n        rbl.blockedservers.com\n        rbl.dns-servicios.com\n        rbl.efnet.org\n        rbl.efnetrbl.org\n        rbl.iprange.net\n        rbl.schulte.org\n        rbl.talkactive.net\n        rbl2.triumf.ca\n        rsbl.aupads.org\n        sbl-xbl.spamhaus.org\n        sbl.nszones.com\n        sbl.spamhaus.org\n        short.rbl.jp\n        spam.dnsbl.anonmails.de\n        spam.pedantic.org\n        spam.rbl.blockedservers.com\n        spam.rbl.msrbl.net\n        spam.spamrats.com\n        spamrbl.imp.ch\n        spamsources.fabel.dk\n        st.technovision.dk\n        tor.dan.me.uk\n        tor.dnsbl.sectoor.de\n        tor.efnet.org\n        torexit.dan.me.uk\n        truncate.gbudb.net\n        ubl.unsubscore.com\n        uribl.spameatingmonkey.net\n        urired.spameatingmonkey.net\n        virbl.dnsbl.bit.nl\n        virus.rbl.jp\n        virus.rbl.msrbl.net\n        vote.drbl.caravan.ru\n        vote.drbl.gremlin.ru\n        web.rbl.msrbl.net\n        work.drbl.caravan.ru\n        work.drbl.gremlin.ru\n        wormrbl.imp.ch\n        xbl.spamhaus.org\n        zen.spamhaus.org\"\n\n\n#~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ }\n\n\n# Definitions {\n\n    # Common regular expressions\n    REGEX_IP='\\([0-9]\\{1,3\\}\\)\\.\\([0-9]\\{1,3\\}\\)\\.\\([0-9]\\{1,3\\}\\)\\.\\([0-9]\\{1,3\\}\\)'\n    REGEX_DOMAIN='\\([a-zA-Z0-9]\\+\\(-[a-zA-Z0-9]\\+\\)*\\.\\)\\+[a-zA-Z]\\{2,\\}'\n    REGEX_TDL='\\([a-zA-Z0-9]\\+\\(-[a-zA-Z0-9]\\+\\)*\\.\\)[a-zA-Z]\\{2,\\}$'\n\n    # Colors\n    if [[ $- == *i* ]]; then\n        RED=$(tput setaf 1)\n        GREEN=$(tput setaf 2)\n        YELLOW=$(tput setaf 3)\n        CLEAR=$(tput sgr0)\n    else\n        RED=$(tput -T xterm setaf 1)\n        GREEN=$(tput -T xterm setaf 2)\n        YELLOW=$(tput -T xterm setaf 3)\n        CLEAR=$(tput -T xterm sgr0)\n    fi\n\n    # Define spinner\n    SPINNER=\"-\\|/\"\n    #SPINNER=\".oO@*\"\n    #SPINNER=\"▉▊▋▌▍▎▏▎▍▌▋▊▉\"\n    #SPINNER=\"←↖↑↗→↘↓↙\"\n    #SPINNER=\"▁▂▃▄▅▆▇█▇▆▅▄▃▁\"\n    #SPINNER=\"▖▘▝▗\"\n    #SPINNER=\"┤┘┴└├┌┬┐\"\n    #SPINNER=\"◢◣◤◥\"\n    #SPINNER=\"◰◳◲◱\"\n    #SPINNER=\"◴◷◶◵\"\n    #SPINNER=\"◐◓◑◒\"\n\n\n#~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ }\n\n\n# Macros {\n\n    # Verbose printing\n    VERBOSE=0\n    info() {\n        if [ $VERBOSE -ge \"$1\" ]; then\n            echo \"$2\"\n        fi\n    }\n\n    # Error handling\n    error() {\n        echo \"ERROR: $1\" >&2\n        exit 2\n    }\n\n    # Show progress\n    progress() {\n\n        # Bar\n        x=$(($1 % ${#SPINNER} + 1))\n        BAR=$(echo $SPINNER | awk \"{ print substr(\\$0, ${x}, 1) }\")\n        if test -z \"$PLAIN\"; then\n            printf \"\\r \";\n        fi\n\n        # BAR as printf arg so that backslash will be litteraly interpreted\n        printf \"[ %s %3s%% ] checking... %4s / $2  \" \"$BAR\" $(($1 * 100 / $2)) \"$1\";\n    }\n\n    # Resolve the IP\n    resolve() {\n\n        # IP already?\n        IP=$(echo \"$1\" | grep \"^$REGEX_IP$\")\n        if [ \"$IP\" ]; then\n            echo \"$IP\"\n\n        # Resolve domain\n        else\n\n            # Handle special resolve types\n            case \"$2\" in\n                \"ns\" ) TYPE=\"ns\"; REGEX=\"$REGEX_DOMAIN\\.$\";;\n                   * ) TYPE=\"a\";  REGEX=\"$REGEX_IP$\";;\n            esac\n\n            case \"$CMD\" in\n                $CMD_DIG ) \"$CMD\" $DNSSERVER +short -t \"$TYPE\" +time=$CONF_DNS_DURATION +tries=$CONF_DNS_TRIES \"$1\" | grep -om 1 \"$REGEX\";;\n                $CMD_HOST ) \"$CMD\" -t \"$TYPE\" -W $CONF_DNS_DURATION -R $CONF_DNS_TRIES \"$1\" $DNSSERVER | tail -n1 | grep -om 1 \"$REGEX\";;\n            esac\n        fi\n    }\n\n    # Load the blacklist from file\n    loadBlacklists() {\n\n        # Make sure the file is readable\n        if [ ! \"$1\" ]; then\n            error \"Option -l requires an additional parameter\";\n        elif [ ! -r $1 ]; then\n            error \"File $1 cannot be opened for reading, make sure it exists and that you have appropriate privileges\"\n        fi\n\n        CONF_BLACKLISTS=$(cat \"$1\")\n    }\n\n    # Show help\n    showHelp() {\n        cat <<HELP\nblcheck [options] <domain_or_IP>\n\nSupplied domain must be full qualified domain name.\nIf the IP is supplied, the PTR check cannot be executed and will be skipped.\n\n-d dnshost  Use host as DNS server to make lookups\n-l file     Load blacklists from file, separated by space or new line\n-c          Warn if the top level domain of the blacklist has expired\n-v          Verbose mode, can be used multiple times (up to -vvv)\n-q          Quiet modem with absolutely no output (useful for scripts)\n-p          Plain text output (no coloring, no interactive status)\n-h          The help you are just reading\n\nResult of the script is the number of blacklisted entries. So if the supplied\nIP is not blacklisted on any of the servers the result is 0.\n\nHELP\n        exit;\n    }\n\n#~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ }\n\n# Parse the params\nwhile getopts :vqphcl:d: arg; do\n    case \"$arg\" in\n        d) DNSSERVER=$OPTARG;;\n        l) loadBlacklists $OPTARG;;\n        c) VERIFY_BL=TRUE;;\n        v) VERBOSE=$(( (VERBOSE + 1) % 4));;\n        q) VERBOSE=-1;;\n        p) PLAIN=1 RED=\"\" GREEN=\"\" YELLOW=\"\" CLEAR=\"\" ;;\n        h) showHelp;;\n        ?) error \"Unknown option $OPTARG\";;\n    esac\ndone\nshift $((OPTIND - 1))\n\n# Get the domain\nif [ $# -eq 0 ]; then\n    echo \"Missing target domain or IP.\"\n    showHelp\nfi\nTARGET=$1\n\n# Some shells disable parsing backslash in echo statements by default\n# Set the flag to enable echo to behave consistently across platforms\nshopt -s xpg_echo\n\n# Get the command we will use: dig or host\nCMD_DIG=$(which dig)\nCMD_HOST=$(which host)\nif [ \"$CMD_DIG\" ]; then\n    if [[ $DNSSERVER ]]; then\n        DNSSERVER=\"@$DNSSERVER\"\n    fi\n    CMD=$CMD_DIG\nelif [ \"$CMD_HOST\" ]; then\n    CMD=$CMD_HOST\nfi\nif [ ! \"$CMD\" ]; then\n    error \"Either dig or host command is required.\"\nfi\ninfo 3 \"Using $CMD to reslove DNS queries\"\n\n# Parse IP\nIP=$(resolve \"$TARGET\")\nif [ ! \"$IP\" ]; then\n    error \"No DNS record found for $TARGET\"\nelif [ \"$IP\" != \"$TARGET\" ]; then\n    DOMAIN=$TARGET\n    info 2 \"Using $TARGET for target, resolved to $IP\"\nelse\n    info 3 \"Using $TARGET for target\"\nfi\n\n# Reverse the IP\nREVERSED=$(echo \"$IP\" | sed -ne \"s~^$REGEX_IP$~\\4.\\3.\\2.\\1~p\")\ninfo 3 \"Using $REVERSED for reversed IP\"\n\n# Get the PTR\ninfo 3 \"Checking the PTR record\"\ncase \"$CMD\" in\n    $CMD_DIG ) PTR=$(\"$CMD\" $DNSSERVER +short -x \"$IP\" | sed s/\\.$//);;\n    $CMD_HOST ) PTR=$(\"$CMD\" \"$IP\" $DNSSERVER | tail -n1 | grep -o '[^ ]\\+$' | sed s/\\.$//)\nesac\n\n# Validate PTR\nif [ ! \"$PTR\" ]; then\n    info 0 \"${YELLOW}Warning: PTR lookup failed${CLEAR}\"\n\nelse\n\n    # Match against supplied domain\n    info 1 \"PTR resolves to $PTR\"\n    if [ \"$DOMAIN\" ]; then\n        if [ \"$DOMAIN\" != \"$PTR\" ]; then\n            info 0 \"${YELLOW}Warning: PTR record does not match supplied domain: $TARGET != $PTR${CLEAR}\"\n        else\n            info 1 \"${GREEN}PTR record matches supplied domain $PTR${CLEAR}\"\n        fi\n    fi\nfi\n\n# Filter out the blacklists\nBLACKLISTS=\"\"\nfor BL in $CONF_BLACKLISTS; do\n    if [ \"$BL\" ]; then\n\n        # Make sure the domain is a proper one\n        DOMAIN=$(echo \"$BL\" | sed -e 's/^[ \\t]*//' | grep ^\"$REGEX_DOMAIN\"$)\n        if [ ! \"$DOMAIN\" ]; then\n            info 0 \"${YELLOW}Warning: blacklist '$BL' is not valid and will be ignored${CLEAR}\"\n\n        else\n\n            # It is a proper blacklist\n            if [ \"$BLACKLISTS\" ]; then\n                BLACKLISTS=$(echo \"$BLACKLISTS\\n$DOMAIN\")\n            else\n                BLACKLISTS=\"$BL\"\n            fi\n        fi\n    fi\ndone\n\n# Make sure we have at least one blacklist\nCOUNT=$(($(echo \"$BLACKLISTS\" | wc -l)))\nif [ ! \"$BLACKLISTS\" ] || [ \"$COUNT\" -eq 0 ]; then\n    error \"No blacklists have been specified\"\nfi\ninfo 1 \"Matching against $COUNT blacklists\"\n\n# Initialize the counters\nINVALID=0\nPASSED=0\nFAILED=0\n\n# Interate over all blacklists\nI=0;\nfor BL in $BLACKLISTS; do\n    PREFIX=\n    I=$((I + 1))\n\n    # What should we test\n    TEST=\"$REVERSED.$BL.\"\n\n    # Make sure the info is shown if we are cheking the servers\n    if [ \"$VERIFY_BL\" ] && [ $VERBOSE -lt 1 ]; then\n        VERBOSE=1\n    fi\n\n    # For verbose output\n    if [ $VERBOSE -ge 1 ]; then\n\n        # Show percentage\n        STATUS=$(printf \" %3s\" $((I * 100 / COUNT)))\n        STATUS=\"$STATUS%% \"\n\n        # Show additional info\n        if [ $VERBOSE -ge 3 ]; then\n            PREFIX=$(printf \"%-60s\" \"$TEST\")\n        else\n            PREFIX=$(printf \"%-50s\" \"$BL\")\n        fi\n\n        PREFIX=\"$STATUS $PREFIX\"\n        if test -z \"$PLAIN\"; then\n            printf \"%s \\b\" \"$PREFIX\"\n        fi\n\n    elif [ $VERBOSE -ge 0 ]; then\n        if test -z \"$PLAIN\"; then\n            progress \"$I\" \"$COUNT\"\n        fi\n    fi\n\n    # Get the status\n    RESPONSE=$(resolve \"$TEST\")\n    START=$(echo \"$RESPONSE\" | cut -c1-4)\n\n    # Not blacklisted\n    if [ ! \"$RESPONSE\" ]; then\n\n        # Make sure the server is viable\n        ERROR=\"\"\n        if [ \"$VERIFY_BL\" ]; then\n            TDL=$(echo \"$BL\" | grep -om 1 '\\([a-zA-Z0-9]\\+\\(-[a-zA-Z0-9]\\+\\)*\\.\\)[a-zA-Z]\\{2,\\}$')\n            if [ ! \"$(resolve \"$TDL\" ns)\" ]; then\n                if test -z \"$PLAIN\"; then printf \"\\r\"; fi\n                printf \"%s%sUnreachable server%s\\n\" \"$YELLOW\" \"$PREFIX\" \"$CLEAR\";\n                INVALID=$((INVALID + 1))\n                ERROR=TRUE\n            fi\n        fi\n\n        if [ ! \"$ERROR\" ]; then\n            if [ \"$VERIFY_BL\" ] || [ $VERBOSE -ge 1 ]; then\n                if test -z \"$PLAIN\"; then printf \"\\r\"; fi\n                printf \"%s%s✓%s\\n\" \"$CLEAR\" \"$PREFIX\" \"$CLEAR\";\n            fi\n            PASSED=$((PASSED + 1))\n        fi;\n\n    # Invalid response\n    elif [ \"$START\" != \"127.\"  ]; then\n        if [ $VERBOSE -ge 1 ]; then\n            if test -z \"$PLAIN\"; then printf \"\\r\"; fi\n            printf \"%s%sinvalid response (%s)%s\\n\" \"$YELLOW\" \"$PREFIX\" \"$RESPONSE\" \"$CLEAR\";\n        fi;\n        INVALID=$((INVALID + 1))\n\n    # Blacklisted\n    else\n        if [ $VERBOSE -ge 1 ]; then\n            if test -z \"$PLAIN\"; then printf \"\\r\"; fi\n            printf \"%s%sblacklisted (%s)%s\\n\" \"$RED\" \"$PREFIX\" \"$RESPONSE\" \"$CLEAR\";\n        elif [ $VERBOSE -ge 0 ]; then\n            if test -z \"$PLAIN\"; then printf \"\\r                                                          \"; printf \"\\r\"; fi\n            printf \"%s%s%s : %s\\n\" \"$RED\" \"$BL\" \"$CLEAR\" \"$RESPONSE\"\n        fi\n        FAILED=$((FAILED + 1))\n    fi\ndone\n\n# Print results\nif [ $VERBOSE -ge 0 ]; then\n    if test -z \"$PLAIN\"; then\n        printf \"\\r                                                    \\n\"\n    else\n        printf \"                                                     \\n\"\n    fi\n    echo \"----------------------------------------------------------\"\n    echo Results for \"$TARGET\"\n    echo\n    printf \"%-15s\" \"Tested:\";   echo \"${COUNT}\"\n    printf \"%-15s\" \"Passed:\";   echo \"${GREEN}${PASSED}${CLEAR}\"\n    printf \"%-15s\" \"Invalid:\";  echo \"${YELLOW}${INVALID}${CLEAR}\"\n    printf \"%-15s\" \"Blacklisted:\";  echo \"${RED}${FAILED}${CLEAR}\"\n    echo \"----------------------------------------------------------\"\nfi\n\nexit $FAILED;\n\n"
  }
]