├── .github └── workflows │ └── release.yml ├── LICENSE ├── README.md ├── build.sh ├── cmd └── dnsbl-check │ └── main.go ├── dnsblprovider ├── dnsblprovider.go ├── general.go └── io │ ├── io.go │ └── io_test.go ├── go.mod ├── iputils ├── iputils.go └── iputils_test.go ├── providerlist ├── providerlist.go └── providerlist_test.go ├── providers └── stringutils └── stringutils.go /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | 14 | - name: Set up Go 1.x 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: ^1.16 18 | id: go 19 | 20 | - name: Check out code into the Go module directory 21 | uses: actions/checkout@v2 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Get dependencies 26 | run: | 27 | go get -v -t -d ./... 28 | if [ -f Gopkg.toml ]; then 29 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 30 | dep ensure 31 | fi 32 | 33 | - name: Test 34 | run: | 35 | ./build.sh test 36 | 37 | - name: Build 38 | run: | 39 | ./build.sh build 40 | 41 | - run: | 42 | set -x 43 | assets=() 44 | for asset in build/*; do 45 | assets+=("-a" "$asset") 46 | done 47 | tag_name="${GITHUB_REF##*/}" 48 | hub release create "${assets[@]}" -m "$tag_name" "$tag_name" 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Lajos Koszti 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNSBL Check 2 | 3 | Checks if the given IP address(es) are listed at the given dnsrbl provider(s). 4 | 5 | DNSRBL lists are used to fight against e-mail spams. They detect and list IP addresses that are used to send unwanted e-mails. When an e-mail sent from a blacklisted address, the anti-spam software will mark the message as Spam. 6 | 7 | This software is useful for sysadmins who operates MTA (Mail Transfer Agent) softwares and want to see if their IP address they use to send e-mail is listed on an RBL list - therefore they can see a reason why some clients doesn't receive e-mail. 8 | 9 | ## Providers 10 | 11 | Providers must be listed in a file, one line should be one provider. 12 | Empty lines are ignored. 13 | Lines started with `#` are ignored. 14 | 15 | The file name must be passed with the `-p` parameter to the command or `-` for standard input. 16 | 17 | ## Addresses 18 | 19 | IP addresses or domain names which needs to be tested against the providers. 20 | The addresses must be passed with the `-i` parameter to the command. Multiple address can be listed, separate them by comma (`,`). 21 | 22 | ## Build 23 | 24 | ```sh 25 | go build 26 | ``` 27 | 28 | ## Execute 29 | 30 | Test if the IP address `1.2.3.4` is blacklisted in providers listed in `providers` file (see [how to get provider list](#getting-provider-list) section): 31 | 32 | Variations for the same operation: 33 | 34 | ```sh 35 | ./dnsbl-check -i 1.2.3.4 -p ipv4providers 36 | ``` 37 | 38 | ```sh 39 | ./dnsbl-check -i 1.2.3.4 -p - < ipv4providers 40 | ``` 41 | 42 | ```sh 43 | cat ipv4providers | ./dnsbl-check -i 1.2.3.4 -p - 44 | ``` 45 | 46 | ## Output 47 | 48 | The program returns every result in a new line, fields are separated by TAB character `\t`. 49 | 50 | The line starts with the status: `OK` or `FAIL` or `ERR` 51 | 52 | - `OK` returned if no listing found for the address 53 | - `FAIL` returned if listing found for the address 54 | - `ERR` returned if the address lookup failed 55 | 56 | Second field is the address 57 | Third field is the provider 58 | Fourth field is filled only if the status is either `FAIL` or `ERR`. If the status is `FAIL` and no reason returned from te provider, the `unknown reason` text will be shown. If the status is `ERR` the error message will be shown here. 59 | 60 | ```text 61 | OK 127.0.0.2 dyn.rbl.polspam.pl 62 | FAIL 127.0.0.2 bl.spamcop.net Blocked - see https://www.spamcop.net/bl.shtml?127.0.0.2 63 | ERR 127.0.0.2 spam.dnsbl.anonmails.de lookup 2.0.0.127.spam.dnsbl.anonmails.de on 127.0.0.53:53: server misbehaving 64 | ``` 65 | 66 | ## Getting provider list 67 | 68 | List of providers coming from [http://multirbl.valli.org/list/](http://multirbl.valli.org/list/) 69 | 70 | ### IPv4 providers 71 | 72 | To get ipv4 blacklist providers run the following command: 73 | 74 | ```sh 75 | awk '$5 == "b" && $2 == "ipv4" && $1 != "(hidden)" { print $1 }' < providers > ipv4providers 76 | ``` 77 | 78 | Then you can test if a provider is working - responds to a test query (query the address [127.0.0.2](https://datatracker.ietf.org/doc/html/rfc5782#section-5)): 79 | 80 | ```sh 81 | ./dnsbl-check -p ipv4providers -i 127.0.0.2 | awk '$1 == "FAIL" { print $3 }' > ipv4verified 82 | ``` 83 | 84 | Then with that list you can check if your IP address (1.2.3.4) is blacklisted: 85 | 86 | ```sh 87 | ./dnsbl-check -p ip4verified -i 1.2.3.4 88 | ``` 89 | 90 | It can be piped into one command: 91 | 92 | ```sh 93 | awk '$5 == "b" && $2 == "ipv4" && $1 != "(hidden)" { print $1 }' < providers | \ 94 | ./dnsbl-check -p - -i 127.0.0.2 | awk '$1 == "FAIL" { print $3 }' | \ 95 | ./dnsbl-check -p - -i 1.2.3.4 96 | ``` 97 | 98 | However it's recommended to keep the used provider list separately, to save the resources of the providers. 99 | 100 | ### Domain providers 101 | 102 | Similar to the IPv4 providers, but we filter the multirbl list to items which are maintaining balck lists of domains: 103 | 104 | ```sh 105 | awk '$5 == "b" && $4 == "dom" && $1 != "(hidden)" { print $1 }' < providers > domain_providers 106 | ``` 107 | 108 | Then check if the provider is looking good query the address [TEST](https://datatracker.ietf.org/doc/html/rfc5782#section-5) 109 | 110 | ```sh 111 | ./dnsbl-check -p domain_providers -i TEST | awk '$1 == "FAIL" { print$3 }' > domain_providers_verified 112 | ``` 113 | 114 | You can check the address `INVALID` as well, which should return `OK`: 115 | 116 | ```sh 117 | ./dnsbl-check -p domain_providers -i TEST | awk '$1 == "FAIL" { print $3 }' | \ 118 | ./dnsbl-check -p - -i INVALID | awk '$1 == "OK" { print $3 }' > domain_providers_verified 119 | 120 | ``` 121 | 122 | Then you can query your domain: 123 | 124 | ```sh 125 | ./dnsbl-check -p domain_providers_verified -i mail.example.com 126 | ``` 127 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | build() { 6 | VERSION=$(git describe --tags) 7 | BUILD=$(date +%FT%T%z) 8 | ARCHLIST="$1" 9 | OSLIST="$2" 10 | BUILD_DIR="$3" 11 | 12 | echo "$VERSION" 13 | echo "$BUILD" 14 | 15 | mkdir -p "$BUILD_DIR" 16 | 17 | cd "$CUR_PATH/cmd/dnsbl-check" 18 | 19 | for os in $OSLIST 20 | do 21 | for arch in $ARCHLIST 22 | do 23 | echo "building $os.$arch" 24 | if go tool dist list | grep -q "^${os}/${arch}$" 25 | then 26 | GOOS="$os" GOARCH="$arch" go build -ldflags "-w -s -X main.version=${VERSION} -X main.build=${BUILD}" -o "$BUILD_DIR/$FILE_NAME.$os.$arch" 27 | fi 28 | done 29 | done 30 | } 31 | 32 | remove() { 33 | BUILD_DIR="$1" 34 | for os in $OSLIST 35 | do 36 | for arch in $ARCHLIST 37 | do 38 | if [ -f "$BUILD_DIR/$FILE_NAME.$os.$arch" ] 39 | then 40 | echo "removing $os.$arch" 41 | rm "$BUILD_DIR/$FILE_NAME.$os.$arch" 42 | fi 43 | done 44 | done 45 | } 46 | 47 | REMOVE=0 48 | OSLIST="linux darwin" 49 | ARCHLIST="amd64 arm64 arm" 50 | CUR_PATH="$(dirname "$(realpath "$0")")" 51 | BUILD_DIR="$CUR_PATH/build" 52 | FILE_NAME="dnsbl-check" 53 | BUILD=0 54 | 55 | if [ $# -lt 1 ];then 56 | echo "Command required, available commands are \"test\" and \"build\"" >&2 57 | exit 1 58 | fi 59 | 60 | subcommand="$1" 61 | shift 62 | case "$subcommand" in 63 | "test") 64 | go test -v -tags test ./... 65 | return 66 | ;; 67 | "build") 68 | BUILD=1; 69 | ;; 70 | *) 71 | echo "Invalid command, available commands are \"test\" and \"build\"" >&2 72 | exit 1 73 | ;; 74 | esac 75 | 76 | while getopts "ra:o:s:b:" opt 77 | do 78 | case "$opt" in 79 | "r") 80 | REMOVE=1 81 | ;; 82 | "a") 83 | ARCHLIST="$OPTARG" 84 | ;; 85 | "o") 86 | OSLIST="$OPTARG" 87 | ;; 88 | "b") 89 | BUILD_DIR="$OPTARG" 90 | ;; 91 | [?]) 92 | exit 1 93 | ;; 94 | esac 95 | done 96 | 97 | if [ $REMOVE -eq 1 ];then 98 | remove "$BUILD_DIR" 99 | else 100 | build "$ARCHLIST" "$OSLIST" "$BUILD_DIR" 101 | fi 102 | -------------------------------------------------------------------------------- /cmd/dnsbl-check/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/Ajnasz/dnsbl-check/dnsblprovider" 9 | "github.com/Ajnasz/dnsbl-check/dnsblprovider/io" 10 | "github.com/Ajnasz/dnsbl-check/providerlist" 11 | ) 12 | 13 | func processLookupResult(result io.LookupResult) { 14 | if result.Error != nil { 15 | fmt.Println(fmt.Sprintf("ERR\t%s\t%s\t%s", result.Address, result.Provider.GetName(), result.Error)) 16 | return 17 | } 18 | if result.IsBlacklisted { 19 | var reason string 20 | 21 | if result.Reason == "" { 22 | reason = "unknown reason" 23 | } else { 24 | reason = result.Reason 25 | } 26 | 27 | fmt.Println(fmt.Sprintf("FAIL\t%s\t%s\t%s", result.Address, result.Provider.GetName(), reason)) 28 | } else { 29 | fmt.Println(fmt.Sprintf("OK\t%s\t%s", result.Address, result.Provider.GetName())) 30 | } 31 | } 32 | 33 | func main() { 34 | var domainsFile = flag.String("p", "", "path to file which stores list of dnsbl checks, empty or - for stdin") 35 | var addressesParam = flag.String("i", "", "IP Address to check, separate by comma for a list") 36 | 37 | flag.Parse() 38 | list, err := providerlist.GetProvidersChan(*domainsFile) 39 | 40 | if err != nil { 41 | fmt.Fprintln(os.Stderr, "Error reading domains") 42 | os.Exit(1) 43 | } 44 | 45 | var providers []dnsblprovider.DNSBLProvider 46 | 47 | for item := range list { 48 | provider := dnsblprovider.GeneralProvider{ 49 | URL: item, 50 | } 51 | 52 | providers = append(providers, provider) 53 | } 54 | 55 | addresses := providerlist.GetAddresses(*addressesParam) 56 | for result := range io.LookupMany(addresses, providers) { 57 | processLookupResult(result) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dnsblprovider/dnsblprovider.go: -------------------------------------------------------------------------------- 1 | package dnsblprovider 2 | 3 | // DNSBLProvider interface should be implemented to be able to query a provider 4 | type DNSBLProvider interface { 5 | GetName() string 6 | IsBlacklisted(string) (bool, error) 7 | GetReason(string) (string, error) 8 | } 9 | -------------------------------------------------------------------------------- /dnsblprovider/general.go: -------------------------------------------------------------------------------- 1 | package dnsblprovider 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strings" 7 | 8 | "github.com/Ajnasz/dnsbl-check/iputils" 9 | ) 10 | 11 | func isNoHostError(err error) bool { 12 | if serr, ok := err.(*net.DNSError); ok { 13 | return serr.IsNotFound 14 | } 15 | 16 | return false 17 | } 18 | 19 | // GeneralProvider implements DNSBLProvider 20 | // URL is a required property which should be the ending of the dnsbl hostname 21 | type GeneralProvider struct { 22 | URL string 23 | } 24 | 25 | // getAddress returns the address what should be queried 26 | // Combines the IP address (octets reversed) and the provider URL 27 | func (provider GeneralProvider) getAddress(address string) string { 28 | if provider.isIPAddress(address) { 29 | return fmt.Sprintf("%s.%s", iputils.ReverseIPv4Address(address), provider.URL) 30 | } 31 | 32 | return fmt.Sprintf("%s.%s", address, provider.URL) 33 | } 34 | 35 | func (provider GeneralProvider) isIPAddress(address string) bool { 36 | return net.ParseIP(address) != nil 37 | } 38 | 39 | // GetName returns the name of the provider 40 | // Now it's the URL 41 | func (provider GeneralProvider) GetName() string { 42 | return provider.URL 43 | } 44 | 45 | // GetReason returns the block reason for an IP address 46 | func (provider GeneralProvider) GetReason(address string) (string, error) { 47 | texts, err := net.LookupTXT(provider.getAddress(address)) 48 | if err != nil { 49 | if isNoHostError(err) { 50 | return "", nil 51 | } 52 | 53 | return "", err 54 | } 55 | 56 | return strings.Join(texts, ""), nil 57 | } 58 | 59 | // IsBlacklisted returns if the IP address listed at a provider 60 | func (provider GeneralProvider) IsBlacklisted(address string) (bool, error) { 61 | names, err := net.LookupIP(provider.getAddress(address)) 62 | 63 | if err != nil { 64 | if isNoHostError(err) { 65 | return false, nil 66 | } 67 | 68 | return false, nil 69 | } 70 | 71 | if len(names) == 0 { 72 | return false, nil 73 | } 74 | 75 | return true, nil 76 | } 77 | -------------------------------------------------------------------------------- /dnsblprovider/io/io.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Ajnasz/dnsbl-check/dnsblprovider" 7 | ) 8 | 9 | // LookupResult stores the query result with reason 10 | type LookupResult struct { 11 | IsBlacklisted bool 12 | Address string 13 | Reason string 14 | Provider dnsblprovider.DNSBLProvider 15 | Error error 16 | } 17 | 18 | // Lookup will check if an address is listed on a balcklist, returns a structured result 19 | func Lookup(address string, provider dnsblprovider.DNSBLProvider) LookupResult { 20 | isListed, err := provider.IsBlacklisted(address) 21 | if err != nil { 22 | return LookupResult{ 23 | Provider: provider, 24 | Address: address, 25 | Error: err, 26 | } 27 | } 28 | 29 | if isListed { 30 | desc, err := provider.GetReason(address) 31 | 32 | return LookupResult{ 33 | Error: err, 34 | Address: address, 35 | IsBlacklisted: true, 36 | Provider: provider, 37 | Reason: desc, 38 | } 39 | } 40 | 41 | return LookupResult{ 42 | Address: address, 43 | IsBlacklisted: false, 44 | Provider: provider, 45 | } 46 | } 47 | 48 | // LookupMany will check multiple addresses on multiple providers, return the result in a chan 49 | func LookupMany(addresses []string, providers []dnsblprovider.DNSBLProvider) chan LookupResult { 50 | var wg sync.WaitGroup 51 | results := make(chan LookupResult) 52 | 53 | for _, address := range addresses { 54 | wg.Add(len(providers)) 55 | 56 | for _, provider := range providers { 57 | go func(address string, provider dnsblprovider.DNSBLProvider) { 58 | defer wg.Done() 59 | results <- Lookup(address, provider) 60 | }(address, provider) 61 | } 62 | } 63 | 64 | go func() { 65 | wg.Wait() 66 | close(results) 67 | }() 68 | 69 | return results 70 | } 71 | -------------------------------------------------------------------------------- /dnsblprovider/io/io_test.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import "testing" 4 | 5 | type provider struct { 6 | retval bool 7 | err error 8 | name string 9 | reason string 10 | } 11 | 12 | func (p provider) IsBlacklisted(adress string) (bool, error) { 13 | return p.retval, p.err 14 | } 15 | 16 | func (p provider) GetName() string { 17 | return p.name 18 | } 19 | 20 | func (p provider) GetReason(string) (string, error) { 21 | return p.reason, nil 22 | } 23 | 24 | func Test_Lookup(t *testing.T) { 25 | p := provider{ 26 | retval: true, 27 | } 28 | ret := Lookup("127.0.0.1", p) 29 | 30 | if ret.IsBlacklisted == false { 31 | t.Errorf("Expected to be false") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Ajnasz/dnsbl-check 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /iputils/iputils.go: -------------------------------------------------------------------------------- 1 | package iputils 2 | 3 | import "strings" 4 | 5 | func reverseStringByToken(str string, delimiter string) string { 6 | if str == "" { 7 | return "" 8 | } 9 | 10 | before, after, found := strings.Cut(str, delimiter) 11 | 12 | if !found { 13 | return before 14 | } 15 | 16 | return reverseStringByToken(after, delimiter) + delimiter + before 17 | } 18 | 19 | // ReverseIPv4Address will return ip address octets in reverse order: 20 | // 1.2.3.4 will be 4.3.2.1 21 | func ReverseIPv4Address(str string) string { 22 | return reverseStringByToken(str, ".") 23 | } 24 | -------------------------------------------------------------------------------- /iputils/iputils_test.go: -------------------------------------------------------------------------------- 1 | package iputils 2 | 3 | import "testing" 4 | 5 | func Test_ReverseStringByToken(t *testing.T) { 6 | testCases := []struct { 7 | Input string 8 | Expected string 9 | Delimiter string 10 | }{ 11 | { 12 | Input: "", 13 | Expected: "", 14 | Delimiter: " ", 15 | }, 16 | { 17 | Input: "foo", 18 | Expected: "foo", 19 | Delimiter: " ", 20 | }, 21 | { 22 | Input: "foo bar", 23 | Expected: "bar foo", 24 | Delimiter: " ", 25 | }, 26 | { 27 | Input: "foo bar baz", 28 | Expected: "baz bar foo", 29 | Delimiter: " ", 30 | }, 31 | { 32 | Input: "1.2.3.4", 33 | Expected: "4.3.2.1", 34 | Delimiter: ".", 35 | }, 36 | } 37 | 38 | for _, testCase := range testCases { 39 | actual := reverseStringByToken(testCase.Input, testCase.Delimiter) 40 | 41 | if actual != testCase.Expected { 42 | t.Errorf("expected %q got %q", testCase.Expected, actual) 43 | } 44 | } 45 | } 46 | 47 | func Benchmark_ReverseStringByToken(b *testing.B) { 48 | noop := func(string) {} 49 | for i := 0; i < b.N; i++ { 50 | str := reverseStringByToken("foo bar", " ") 51 | noop(str) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /providerlist/providerlist.go: -------------------------------------------------------------------------------- 1 | package providerlist 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | "github.com/Ajnasz/dnsbl-check/stringutils" 10 | ) 11 | 12 | func negate(f func(string) bool) func(string) bool { 13 | return func(str string) bool { 14 | r := f(str) 15 | return !r 16 | } 17 | } 18 | 19 | func isCommentLine(line string) bool { 20 | return strings.HasPrefix(line, "#") 21 | } 22 | 23 | func isEmptyString(str string) bool { 24 | return str == "" 25 | } 26 | 27 | func readLines(reader io.Reader) []string { 28 | scanner := bufio.NewScanner(reader) 29 | scanner.Split(bufio.ScanLines) 30 | var text []string 31 | for scanner.Scan() { 32 | text = append(text, scanner.Text()) 33 | } 34 | 35 | return text 36 | } 37 | 38 | func getProvidersFromFile(fn string) ([]string, error) { 39 | file, err := os.Open(fn) 40 | if err != nil { 41 | return nil, err 42 | } 43 | defer file.Close() 44 | 45 | return readLines(file), nil 46 | } 47 | 48 | func getProvidersFromStdin() ([]string, error) { 49 | text := readLines(os.Stdin) 50 | 51 | return text, nil 52 | } 53 | 54 | func getProviders(fn string) ([]string, error) { 55 | var lines []string 56 | var err error 57 | 58 | if fn == "" || fn == "-" { 59 | lines, err = getProvidersFromStdin() 60 | } else { 61 | lines, err = getProvidersFromFile(fn) 62 | } 63 | 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | trimmed := stringutils.Map(lines, strings.TrimSpace) 69 | noEmpty := stringutils.Filter(trimmed, negate(isEmptyString)) 70 | noComment := stringutils.Filter(noEmpty, negate(isCommentLine)) 71 | 72 | return noComment, nil 73 | } 74 | 75 | func getLinesChan(reader io.Reader) (chan string, chan struct{}) { 76 | scanner := bufio.NewScanner(reader) 77 | scanner.Split(bufio.ScanLines) 78 | 79 | text := make(chan string) 80 | done := make(chan struct{}) 81 | 82 | go func() { 83 | defer func() { 84 | close(text) 85 | done <- struct{}{} 86 | close(done) 87 | }() 88 | for scanner.Scan() { 89 | text <- scanner.Text() 90 | } 91 | }() 92 | 93 | return text, done 94 | } 95 | 96 | func getProvidersFromFileChan(fn string) (chan string, error) { 97 | file, err := os.Open(fn) 98 | if err != nil { 99 | return nil, err 100 | } 101 | 102 | text, done := getLinesChan(file) 103 | 104 | go func() { 105 | defer file.Close() 106 | <-done 107 | }() 108 | 109 | return text, nil 110 | } 111 | 112 | func getProvidersFromStdinChan() (chan string, error) { 113 | text, done := getLinesChan(os.Stdin) 114 | 115 | go func() { 116 | <-done 117 | }() 118 | 119 | return text, nil 120 | } 121 | 122 | // GetProvidersChan returns a channel which will emit available providers 123 | func GetProvidersChan(fn string) (chan string, error) { 124 | var lines chan string 125 | var err error 126 | 127 | if fn == "" || fn == "-" { 128 | lines, err = getProvidersFromStdinChan() 129 | } else { 130 | lines, err = getProvidersFromFileChan(fn) 131 | } 132 | 133 | if err != nil { 134 | return nil, err 135 | } 136 | 137 | trimmed := stringutils.MapChan(strings.TrimSpace, lines) 138 | noEmpty := stringutils.FilterChan(negate(isEmptyString), trimmed) 139 | noComment := stringutils.FilterChan(negate(isCommentLine), noEmpty) 140 | 141 | return noComment, nil 142 | } 143 | 144 | // GetAddresses will split the comma separated addresses by the comma and return them as a slice 145 | func GetAddresses(addresses string) []string { 146 | return stringutils.Filter(strings.Split(addresses, ","), negate(isEmptyString)) 147 | } 148 | -------------------------------------------------------------------------------- /providerlist/providerlist_test.go: -------------------------------------------------------------------------------- 1 | package providerlist 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_negate(t *testing.T) { 8 | if negate(isEmptyString)("") != false { 9 | t.Errorf("It should be false") 10 | } 11 | 12 | if negate(isEmptyString)("a") != true { 13 | t.Errorf("It should be true") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /providers: -------------------------------------------------------------------------------- 1 | 0spamtrust.fusionzero.com ipv4 - - w 2 | bl.0spam.org ipv4 - - b 3 | 0spam.fusionzero.com ipv4 - - b 4 | nbl.0spam.org ipv4 - - b 5 | 0spam-n.fusionzero.com ipv4 - - b 6 | url.0spam.org - - dom b 7 | 0spamurl.fusionzero.com - - dom b 8 | uribl.zeustracker.abuse.ch - - dom b 9 | ipbl.zeustracker.abuse.ch ipv4 - - b 10 | contacts.abuse.net - - dom i 11 | rbl.abuse.ro ipv4 - - b 12 | uribl.abuse.ro - - dom b 13 | (hidden) ipv4 ipv6 - b 14 | (hidden) ipv4 ipv6 - b 15 | (hidden) ipv4 ipv6 - b 16 | (hidden) ipv4 ipv6 dom b 17 | (hidden) ipv4 ipv6 - b 18 | (hidden) ipv4 - - b 19 | (hidden) ipv4 ipv6 dom w 20 | abuse-contacts.abusix.org ipv4 - - i 21 | spam.dnsbl.anonmails.de ipv4 - - b 22 | dnsbl.anticaptcha.net ipv4 - - b 23 | dnsbl6.anticaptcha.net - ipv6 - b 24 | orvedb.aupads.org ipv4 - - b 25 | rsbl.aupads.org ipv4 - - b 26 | block.ascams.com ipv4 - - b 27 | superblock.ascams.com ipv4 - - b 28 | aspews.ext.sorbs.net ipv4 - - b 29 | ips.backscatterer.org ipv4 - - b 30 | b.barracudacentral.org ipv4 - - b 31 | bb.barracudacentral.org ipv4 - - b 32 | list.bbfh.org ipv4 - - b 33 | l1.bbfh.ext.sorbs.net ipv4 - - b 34 | l2.bbfh.ext.sorbs.net ipv4 - - b 35 | l3.bbfh.ext.sorbs.net ipv4 - - b 36 | l4.bbfh.ext.sorbs.net ipv4 - - b 37 | all.ascc.dnsbl.bit.nl ipv4 - - i 38 | all.v6.ascc.dnsbl.bit.nl - ipv6 - i 39 | all.dnsbl.bit.nl ipv4 - - i 40 | ipv6.all.dnsbl.bit.nl - ipv6 - i 41 | bitonly.dnsbl.bit.nl ipv4 ipv6 - i 42 | blackholes.tepucom.nl ipv4 - - b 43 | blacklist.netcore.co.in - - dom b 44 | rbl.blakjak.net ipv4 - - b 45 | netscan.rbl.blockedservers.com ipv4 - - b 46 | rbl.blockedservers.com ipv4 - - b 47 | spam.rbl.blockedservers.com ipv4 - - b 48 | list.blogspambl.com ipv4 - - b 49 | bsb.empty.us ipv4 - dom b 50 | bsb.spamlookup.net ipv4 - dom b 51 | query.bondedsender.org ipv4 - - w 52 | plus.bondedsender.org ipv4 - - w 53 | dnsbl1.dnsbl.borderware.com ipv4 - - b 54 | dnsbl2.dnsbl.borderware.com ipv4 - - b 55 | dnsbl3.dnsbl.borderware.com ipv4 - - b 56 | dul.dnsbl.borderware.com ipv4 - - b 57 | black.dnsbl.brukalai.lt ipv4 - dom b 58 | light.dnsbl.brukalai.lt ipv4 - dom b 59 | white.dnsbl.brukalai.lt ipv4 - dom w 60 | blacklist.sci.kun.nl ipv4 - - b 61 | whitelist.sci.kun.nl ipv4 - - w 62 | dul.blackhole.cantv.net ipv4 - dom b 63 | hog.blackhole.cantv.net ipv4 - dom b 64 | rhsbl.blackhole.cantv.net ipv4 - dom b 65 | rot.blackhole.cantv.net ipv4 - dom b 66 | spam.blackhole.cantv.net ipv4 - dom b 67 | cbl.abuseat.org ipv4 - - b 68 | rbl.choon.net ipv4 - - b 69 | rwl.choon.net ipv4 - - w 70 | ipv6.rbl.choon.net - ipv6 - b 71 | ipv6.rwl.choon.net - ipv6 - w 72 | zz.countries.nerd.dk ipv4 - - i 73 | dnsbl.cyberlogic.net ipv4 - - b 74 | bogons.cymru.com ipv4 - - b 75 | v4.fullbogons.cymru.com ipv4 - - b 76 | v6.fullbogons.cymru.com - ipv6 - b 77 | origin.asn.cymru.com ipv4 - - i 78 | origin6.asn.cymru.com - ipv6 - i 79 | peer.asn.cymru.com ipv4 - - i 80 | tor.dan.me.uk ipv4 ipv6 - i 81 | torexit.dan.me.uk ipv4 ipv6 - b 82 | dnsbl.darklist.de ipv4 - - b 83 | openproxy.bls.digibase.ca ipv4 - - b 84 | proxyabuse.bls.digibase.ca ipv4 - - b 85 | spambot.bls.digibase.ca ipv4 - - b 86 | rbl.dns-servicios.com ipv4 - - b 87 | dnsbl.abyan.es ipv4 - dom b 88 | dnsbl.beetjevreemd.nl ipv4 ipv6 - b 89 | dnsbl.calivent.com.pe ipv4 - - b 90 | dnsbl.isx.fr ipv4 - - b 91 | dnsbl.mcu.edu.tw ipv4 - - b 92 | dnsbl.net.ua ipv4 - - b 93 | dnsbl.rv-soft.info ipv4 - - b 94 | dnsblchile.org ipv4 - - b 95 | dwl.dnswl.org - - dom w 96 | list.dnswl.org ipv4 ipv6 - w 97 | vote.drbl.caravan.ru ipv4 - - b 98 | work.drbl.caravan.ru ipv4 - - b 99 | vote.drbl.gremlin.ru ipv4 - - b 100 | work.drbl.gremlin.ru ipv4 - - b 101 | bl.drmx.org ipv4 - - b 102 | dnsbl.dronebl.org ipv4 ipv6 - b 103 | dul.pacifier.net ipv4 - - b 104 | rbl.efnet.org ipv4 - - b 105 | rbl.efnetrbl.org ipv4 - - b 106 | tor.efnet.org ipv4 - - b 107 | rbl.fasthosts.co.uk ipv4 - - b 108 | bl.fmb.la ipv4 - dom b 109 | communicado.fmb.la - - dom b 110 | nsbl.fmb.la - - dom b 111 | sa.fmb.la - - dom c 112 | short.fmb.la - - dom b 113 | fnrbl.fast.net ipv4 - - b 114 | forbidden.icm.edu.pl ipv4 - - b 115 | 88.blocklist.zap ipv4 - - b 116 | hil.habeas.com ipv4 - - b 117 | accredit.habeas.com ipv4 - - w 118 | sa-accredit.habeas.com ipv4 - - w 119 | hul.habeas.com ipv4 - - w 120 | sohul.habeas.com ipv4 - - w 121 | hostkarma.junkemailfilter.com ipv4 - dom c 122 | black.junkemailfilter.com ipv4 - dom b 123 | nobl.junkemailfilter.com ipv4 - dom c 124 | dnsbl.cobion.com ipv4 - - b 125 | spamrbl.imp.ch ipv4 - - b 126 | wormrbl.imp.ch ipv4 - - b 127 | rbl.interserver.net ipv4 - - b 128 | (hidden) ipv4 - - b 129 | (hidden) ipv4 - - b 130 | (hidden) - - dom b 131 | rbl.iprange.net ipv4 - - b 132 | rbl.ircbl.org ipv4 - - b 133 | iadb.isipp.com ipv4 - - w 134 | iadb2.isipp.com ipv4 - - w 135 | iddb.isipp.com - - dom w 136 | wadb.isipp.com ipv4 - - w 137 | whitelist.rbl.ispa.at ipv4 - - w 138 | mail-abuse.blacklist.jippg.org ipv4 - - b 139 | dnsbl.justspam.org ipv4 - - b 140 | dnsbl.kempt.net ipv4 - - b 141 | spamlist.or.kr ipv4 - - b 142 | bl.konstant.no ipv4 - - b 143 | krn.korumail.com ipv4 - - c 144 | admin.bl.kundenserver.de ipv4 - - b 145 | relays.bl.kundenserver.de ipv4 - - b 146 | schizo-bl.kundenserver.de ipv4 - - b 147 | spamblock.kundenserver.de ipv4 - - b 148 | worms-bl.kundenserver.de ipv4 - - b 149 | spamguard.leadmon.net ipv4 - - b 150 | rbl.lugh.ch ipv4 - - b 151 | dnsbl.madavi.de ipv4 - - b 152 | niprbl.mailcleaner.net ipv4 - - b 153 | uribl.mailcleaner.net - - dom b 154 | blacklist.mailrelay.att.net ipv4 - - b 155 | bl.mailspike.net ipv4 - - b 156 | rep.mailspike.net ipv4 - - c 157 | wl.mailspike.net ipv4 - - w 158 | z.mailspike.net ipv4 - - b 159 | bl.mav.com.br ipv4 - - b 160 | cidr.bl.mcafee.com ipv4 - - b 161 | rbl.metunet.com ipv4 - - b 162 | dnsbl.forefront.microsoft.com ipv4 - - b 163 | bl.mipspace.com ipv4 - - b 164 | combined.rbl.msrbl.net ipv4 - - b 165 | images.rbl.msrbl.net ipv4 - - b 166 | phishing.rbl.msrbl.net ipv4 - - b 167 | spam.rbl.msrbl.net ipv4 - - b 168 | virus.rbl.msrbl.net ipv4 - - b 169 | web.rbl.msrbl.net ipv4 - - b 170 | (hidden) ipv4 - - c 171 | relays.nether.net ipv4 - - b 172 | trusted.nether.net ipv4 - - w 173 | unsure.nether.net ipv4 - - b 174 | ix.dnsbl.manitu.net ipv4 - - b 175 | dbl.nordspam.com - - dom b 176 | bl.nordspam.com ipv4 ipv6 - b 177 | bl.nosolicitado.org ipv4 - - b 178 | bl.worst.nosolicitado.org ipv4 - - b 179 | wl.nszones.com ipv4 - - w 180 | dyn.nszones.com ipv4 - - b 181 | sbl.nszones.com ipv4 - - b 182 | bl.nszones.com ipv4 - - b 183 | ubl.nszones.com - - dom b 184 | bl.octopusdns.com ipv4 - - b 185 | blacklist.mail.ops.asp.att.net ipv4 - - b 186 | blacklist.sequoia.ops.asp.att.net ipv4 - - b 187 | spam.pedantic.org ipv4 - - b 188 | pofon.foobar.hu ipv4 ipv6 - b 189 | ispmx.pofon.foobar.hu ipv4 ipv6 - w 190 | uribl.pofon.foobar.hu - - dom b 191 | bl.rbl.polspam.pl ipv4 - - b 192 | bl-h1.rbl.polspam.pl ipv4 - - b 193 | bl-h2.rbl.polspam.pl ipv4 - - b 194 | bl-h3.rbl.polspam.pl ipv4 - - b 195 | bl-h4.rbl.polspam.pl ipv4 - - b 196 | bl6.rbl.polspam.pl - ipv6 - b 197 | cnkr.rbl.polspam.pl ipv4 - - b 198 | dyn.rbl.polspam.pl ipv4 - - b 199 | ip4.white.polspam.pl ipv4 - - w 200 | ip6.white.polspam.pl - ipv6 - w 201 | lblip4.rbl.polspam.pl ipv4 - - b 202 | lblip6.rbl.polspam.pl - ipv6 - b 203 | rblip4.rbl.polspam.pl ipv4 - - b 204 | rblip6.rbl.polspam.pl - ipv6 - b 205 | rhsbl.rbl.polspam.pl - - dom b 206 | rhsbl-h.rbl.polspam.pl - - dom b 207 | (hidden) ipv4 - - b 208 | (hidden) ipv4 - - b 209 | safe.dnsbl.prs.proofpoint.com ipv4 - - b 210 | psbl.surriel.com ipv4 - - b 211 | whitelist.surriel.com ipv4 - - w 212 | rbl.rbldns.ru ipv4 - - b 213 | rbl.schulte.org ipv4 - - b 214 | rbl.zenon.net ipv4 - - b 215 | rbl.realtimeblacklist.com ipv4 - - b 216 | access.redhawk.org ipv4 - - b 217 | eswlrev.dnsbl.rediris.es ipv4 ipv6 - w 218 | mtawlrev.dnsbl.rediris.es ipv4 ipv6 - w 219 | abuse.rfc-clueless.org - - dom b 220 | bogusmx.rfc-clueless.org - - dom b 221 | dsn.rfc-clueless.org - - dom b 222 | elitist.rfc-clueless.org - - dom b 223 | fulldom.rfc-clueless.org - - dom b 224 | postmaster.rfc-clueless.org - - dom b 225 | mailsl.dnsbl.rjek.com - - dom b 226 | urlsl.dnsbl.rjek.com - - dom b 227 | asn.routeviews.org ipv4 - - i 228 | aspath.routeviews.org ipv4 - - i 229 | dnsbl.rymsho.ru ipv4 - - b 230 | rhsbl.rymsho.ru - - dom b 231 | all.s5h.net ipv4 ipv6 - b 232 | public.sarbl.org - - dom b 233 | rhsbl.scientificspam.net - - dom b 234 | bl.scientificspam.net ipv4 - - b 235 | reputation-domain.rbl.scrolloutf1.com - - dom c 236 | reputation-ip.rbl.scrolloutf1.com ipv4 - - c 237 | reputation-ns.rbl.scrolloutf1.com - - dom c 238 | query.senderbase.org ipv4 - - i 239 | sa.senderbase.org ipv4 - - i 240 | rf.senderbase.org ipv4 - - i 241 | bl.score.senderscore.com ipv4 - - b 242 | score.senderscore.com ipv4 - - c 243 | singular.ttk.pte.hu ipv4 - - b 244 | blackholes.scconsult.com ipv4 - - b 245 | dnsbl.sorbs.net ipv4 - - b 246 | problems.dnsbl.sorbs.net ipv4 - - b 247 | proxies.dnsbl.sorbs.net ipv4 - - b 248 | relays.dnsbl.sorbs.net ipv4 - - b 249 | safe.dnsbl.sorbs.net ipv4 - - b 250 | nomail.rhsbl.sorbs.net - - dom b 251 | badconf.rhsbl.sorbs.net - - dom b 252 | dul.dnsbl.sorbs.net ipv4 - - b 253 | zombie.dnsbl.sorbs.net ipv4 - - b 254 | block.dnsbl.sorbs.net ipv4 - - b 255 | escalations.dnsbl.sorbs.net ipv4 - - b 256 | http.dnsbl.sorbs.net ipv4 - - b 257 | misc.dnsbl.sorbs.net ipv4 - - b 258 | smtp.dnsbl.sorbs.net ipv4 - - b 259 | socks.dnsbl.sorbs.net ipv4 - - b 260 | rhsbl.sorbs.net - - dom b 261 | spam.dnsbl.sorbs.net ipv4 - - b 262 | recent.spam.dnsbl.sorbs.net ipv4 - - b 263 | new.spam.dnsbl.sorbs.net ipv4 - - b 264 | old.spam.dnsbl.sorbs.net ipv4 - - b 265 | web.dnsbl.sorbs.net ipv4 - - b 266 | korea.services.net ipv4 - - b 267 | geobl.spameatingmonkey.net ipv4 - - i 268 | origin.asn.spameatingmonkey.net ipv4 - - i 269 | backscatter.spameatingmonkey.net ipv4 - - b 270 | bl.spameatingmonkey.net ipv4 - - b 271 | fresh.spameatingmonkey.net - - dom b 272 | fresh10.spameatingmonkey.net - - dom b 273 | fresh15.spameatingmonkey.net - - dom b 274 | fresh30.spameatingmonkey.net - - dom b 275 | freshzero.spameatingmonkey.net - - dom b 276 | bl.ipv6.spameatingmonkey.net - ipv6 - b 277 | netbl.spameatingmonkey.net ipv4 - - b 278 | uribl.spameatingmonkey.net - - dom b 279 | urired.spameatingmonkey.net - - dom b 280 | netblockbl.spamgrouper.to ipv4 - - b 281 | all.spam-rbl.fr ipv4 - - b 282 | bl.spamcop.net ipv4 - - b 283 | sbl.spamdown.org ipv4 - - b 284 | dbl.spamhaus.org - - dom b 285 | _vouch.dwl.spamhaus.org - - dom w 286 | pbl.spamhaus.org ipv4 ipv6 - b 287 | sbl.spamhaus.org ipv4 ipv6 - b 288 | sbl-xbl.spamhaus.org ipv4 ipv6 - b 289 | swl.spamhaus.org ipv4 ipv6 - w 290 | xbl.spamhaus.org ipv4 ipv6 - b 291 | zen.spamhaus.org ipv4 ipv6 - b 292 | feb.spamlab.com ipv4 - - b 293 | rbl.spamlab.com ipv4 - - b 294 | all.spamrats.com ipv4 - - b 295 | auth.spamrats.com ipv4 - - b 296 | dyna.spamrats.com ipv4 - - b 297 | noptr.spamrats.com ipv4 - - b 298 | spam.spamrats.com ipv4 - - b 299 | spamsources.fabel.dk ipv4 - - b 300 | abuse.spfbl.net ipv4 ipv6 dom i 301 | dnsbl.spfbl.net ipv4 ipv6 dom b 302 | score.spfbl.net ipv4 ipv6 dom c 303 | dnswl.spfbl.net ipv4 ipv6 dom w 304 | bl.suomispam.net ipv4 ipv6 - b 305 | dbl.suomispam.net - - dom b 306 | gl.suomispam.net ipv4 ipv6 - b 307 | multi.surbl.org ipv4 - dom b 308 | srn.surgate.net ipv4 - - c 309 | dnsrbl.swinog.ch ipv4 - - b 310 | uribl.swinog.ch - - dom b 311 | rbl.tdk.net ipv4 - - b 312 | st.technovision.dk ipv4 - - b 313 | dob.sibl.support-intelligence.net - - dom b 314 | dbl.tiopan.com - - dom b 315 | bl.tiopan.com ipv4 - - b 316 | dnsbl.tornevall.org ipv4 - - b 317 | r.mail-abuse.com ipv4 - - b 318 | q.mail-abuse.com ipv4 - - b 319 | rbl2.triumf.ca ipv4 - - b 320 | wbl.triumf.ca ipv4 - - w 321 | truncate.gbudb.net ipv4 - - b 322 | dunk.dnsbl.tuxad.de ipv4 - - b 323 | hartkore.dnsbl.tuxad.de ipv4 - - b 324 | dnsbl-0.uceprotect.net ipv4 - - b 325 | dnsbl-1.uceprotect.net ipv4 - - b 326 | dnsbl-2.uceprotect.net ipv4 - - b 327 | dnsbl-3.uceprotect.net ipv4 - - b 328 | ubl.unsubscore.com ipv4 - - b 329 | black.uribl.com - - dom b 330 | grey.uribl.com - - dom b 331 | multi.uribl.com - - dom b 332 | red.uribl.com - - dom b 333 | white.uribl.com - - dom w 334 | free.v4bl.org ipv4 - - b 335 | ip.v4bl.org ipv4 - - b 336 | ips.whitelisted.org ipv4 - - w 337 | blacklist.woody.ch ipv4 - - b 338 | ipv6.blacklist.woody.ch - ipv6 - b 339 | uri.blacklist.woody.ch - - dom b 340 | db.wpbl.info ipv4 - - b 341 | bl.blocklist.de ipv4 - - b 342 | dnsbl.zapbl.net ipv4 - - b 343 | rhsbl.zapbl.net - - dom b 344 | d.bl.zenrbl.pl - - dom b 345 | ip4.bl.zenrbl.pl ipv4 - - b 346 | -------------------------------------------------------------------------------- /stringutils/stringutils.go: -------------------------------------------------------------------------------- 1 | package stringutils 2 | 3 | // Filter filters the slice of string by the given test func and keeps only 4 | // those entries where the test returned true 5 | func Filter(lines []string, test func(string) bool) []string { 6 | var out []string 7 | 8 | for _, line := range lines { 9 | if test(line) { 10 | out = append(out, line) 11 | } 12 | } 13 | 14 | return out 15 | } 16 | 17 | // Map returns the output conv function which executed on all entires 18 | func Map(lines []string, conv func(string) string) []string { 19 | var out []string 20 | 21 | for _, line := range lines { 22 | out = append(out, conv(line)) 23 | } 24 | 25 | return out 26 | } 27 | 28 | // MapChan enables to alter the input strings and emit them from a channel 29 | func MapChan(conv func(string) string, lines chan string) chan string { 30 | out := make(chan string) 31 | 32 | go func() { 33 | defer close(out) 34 | for line := range lines { 35 | out <- conv(line) 36 | } 37 | }() 38 | 39 | return out 40 | } 41 | 42 | // FilterChan reads items from a string channel, and emits only those where the 43 | // test function returned true 44 | func FilterChan(test func(string) bool, lines chan string) chan string { 45 | out := make(chan string) 46 | 47 | go func() { 48 | defer close(out) 49 | for line := range lines { 50 | if test(line) { 51 | out <- line 52 | } 53 | } 54 | }() 55 | 56 | return out 57 | } 58 | --------------------------------------------------------------------------------