Compare commits
18 Commits
68eb3a055d
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 70fb7a3aa8 | |||
|
cfbf4ce504
|
|||
|
17115d97c6
|
|||
|
1dc7a8b22c
|
|||
|
e4fc07871b
|
|||
|
faedb345df
|
|||
|
ee0e36b40d
|
|||
|
790073aa95
|
|||
|
e18859e78f
|
|||
|
aeb855c0b8
|
|||
|
5b8974101e
|
|||
|
dd4e5619c7
|
|||
|
172c32237b
|
|||
|
364e657641
|
|||
|
e4b0ed6668
|
|||
|
f295c402af
|
|||
|
062cb25551
|
|||
|
17dbfac864
|
23
README.md
Normal file
23
README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# blocklist
|
||||
|
||||
Пакет Go для получения IP адресов от различных сервисов.
|
||||
|
||||
## Установка
|
||||
```sh
|
||||
go get git.kor-elf.net/kor-elf-shield/blocklist
|
||||
```
|
||||
|
||||
## Примеры использования
|
||||
- [Пример получения списка IP адресов от Spamhaus](/examples/spamhaus.go)
|
||||
- [Пример получения списка IP адресов от DShield](/examples/dshield.go)
|
||||
- [Пример получения списка IP адресов от HONEYPOT](/examples/honeypot.go)
|
||||
- [Пример получения списка IP адресов от Tor](/examples/tor.go)
|
||||
- [Пример получения списка IP адресов от CIARMY](/examples/ciarmy.go)
|
||||
- [Пример получения списка IP адресов от Daniel Gerzo (BruteforceBlocker)](/examples/bruteforceblocker.go)
|
||||
- [Пример получения списка IP адресов от Blocklist.de](/examples/blocklist.go)
|
||||
- [Пример получения списка IP адресов от GreenSnow](/examples/greensnow.go)
|
||||
- [Пример получения списка IP адресов от StopForumSpam](/examples/stopforumspam.go)
|
||||
|
||||
## Лицензия
|
||||
|
||||
[MIT](https://git.kor-elf.net/kor-elf-shield/blocklist/src/branch/main/LICENSE)
|
||||
152
blocklist.go
152
blocklist.go
@@ -1,10 +1,14 @@
|
||||
package blocklist
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
@@ -16,6 +20,12 @@ const (
|
||||
|
||||
// requestTimeout defines the maximum duration for request operations before timing out.
|
||||
requestTimeout = 20 * time.Second
|
||||
|
||||
// maxDownloadSize defines the maximum allowed size of the downloaded file in bytes.
|
||||
maxDownloadSize int64 = 20 << 20 // 20 MiB
|
||||
|
||||
// maxArchiveFileSize defines the maximum allowed size of the extracted file from ZIP in bytes.
|
||||
maxArchiveFileSize uint64 = 50 << 20 // 50 MiB
|
||||
)
|
||||
|
||||
// Config defines the configuration for the blocklist.
|
||||
@@ -34,6 +44,17 @@ type Config struct {
|
||||
RequestTimeout time.Duration
|
||||
}
|
||||
|
||||
type ConfigZip struct {
|
||||
// Config is the configuration for the blocklist.
|
||||
Config Config
|
||||
|
||||
// MaxDownloadSize defines the maximum allowed size of the downloaded file in bytes.
|
||||
MaxDownloadSize int64
|
||||
|
||||
// MaxArchiveFileSize defines the maximum allowed size of the extracted file from ZIP in bytes.
|
||||
MaxArchiveFileSize uint64
|
||||
}
|
||||
|
||||
// NewConfig creates a new Config with default values.
|
||||
// limit is the maximum number of items to process or validate. 0 means no limit.
|
||||
func NewConfig(limit uint) Config {
|
||||
@@ -57,6 +78,14 @@ func NewConfigWithValidator(limit uint, validator parser.IPValidator) Config {
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfigZip(c Config) ConfigZip {
|
||||
return ConfigZip{
|
||||
Config: c,
|
||||
MaxDownloadSize: maxDownloadSize,
|
||||
MaxArchiveFileSize: maxArchiveFileSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Get fetches data from the given URL, parses the response using the provided parser, and applies the given configuration.
|
||||
// It returns the parsed IPs and any errors that occurred during the process.
|
||||
func Get(fileUrl string, parser parser.Parser, c Config) (parser.IPs, error) {
|
||||
@@ -94,3 +123,126 @@ func Get(fileUrl string, parser parser.Parser, c Config) (parser.IPs, error) {
|
||||
|
||||
return parser.Parse(res.Body, c.Validator, c.Limit)
|
||||
}
|
||||
|
||||
// GetZip fetches data from the given URL, parses the response using the provided parser, and applies the given configuration.
|
||||
// It returns the parsed IPs and any errors that occurred during the process.
|
||||
func GetZip(fileUrl string, parser parser.Parser, c ConfigZip) (parser.IPs, error) {
|
||||
parsedURL, err := url.Parse(fileUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid url: %w", err)
|
||||
}
|
||||
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
|
||||
return nil, fmt.Errorf("invalid url scheme: %s", parsedURL.Scheme)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.Config.ContextTimeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: c.Config.RequestTimeout,
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||
}
|
||||
|
||||
if c.MaxDownloadSize > 0 && res.ContentLength > c.MaxDownloadSize {
|
||||
return nil, fmt.Errorf("downloaded file is too large: content-length %d exceeds limit %d", res.ContentLength, c.MaxDownloadSize)
|
||||
}
|
||||
|
||||
reader := res.Body
|
||||
if c.MaxDownloadSize > 0 {
|
||||
reader = io.NopCloser(io.LimitReader(res.Body, c.MaxDownloadSize+1))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read response body: %w", err)
|
||||
}
|
||||
|
||||
if c.MaxDownloadSize > 0 && int64(len(body)) > c.MaxDownloadSize {
|
||||
return nil, fmt.Errorf("downloaded file exceeds limit %d bytes", c.MaxDownloadSize)
|
||||
}
|
||||
|
||||
if !isZip(body) {
|
||||
return nil, fmt.Errorf("invalid zip archive")
|
||||
}
|
||||
|
||||
return parseZip(body, parser, c)
|
||||
}
|
||||
|
||||
func isZip(body []byte) bool {
|
||||
return len(body) >= 4 &&
|
||||
body[0] == 'P' &&
|
||||
body[1] == 'K' &&
|
||||
body[2] == 0x03 &&
|
||||
body[3] == 0x04
|
||||
}
|
||||
|
||||
func parseZip(body []byte, p parser.Parser, c ConfigZip) (parser.IPs, error) {
|
||||
reader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open zip archive: %w", err)
|
||||
}
|
||||
|
||||
file := findArchiveFile(reader.File)
|
||||
if file == nil {
|
||||
return nil, fmt.Errorf("zip archive does not contain a supported file")
|
||||
}
|
||||
|
||||
if c.MaxArchiveFileSize > 0 && file.UncompressedSize64 > c.MaxArchiveFileSize {
|
||||
return nil, fmt.Errorf("file %q in zip is too large: %d exceeds limit %d", file.Name, file.UncompressedSize64, c.MaxArchiveFileSize)
|
||||
}
|
||||
|
||||
rc, err := file.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open file %q from zip: %w", file.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = rc.Close()
|
||||
}()
|
||||
|
||||
var zipReader io.Reader = rc
|
||||
if c.MaxArchiveFileSize > 0 {
|
||||
zipReader = io.LimitReader(rc, int64(c.MaxArchiveFileSize)+1)
|
||||
}
|
||||
|
||||
return p.Parse(zipReader, c.Config.Validator, c.Config.Limit)
|
||||
}
|
||||
|
||||
func findArchiveFile(files []*zip.File) *zip.File {
|
||||
var fallback *zip.File
|
||||
|
||||
for _, file := range files {
|
||||
if file.FileInfo().IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
if fallback == nil {
|
||||
fallback = file
|
||||
}
|
||||
|
||||
name := strings.ToLower(file.Name)
|
||||
if strings.HasSuffix(name, ".txt") ||
|
||||
strings.HasSuffix(name, ".json") ||
|
||||
strings.HasSuffix(name, ".xml") ||
|
||||
strings.HasSuffix(name, ".rss") {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
50
examples/blocklist.go
Normal file
50
examples/blocklist.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://www.blocklist.de/en/export.html
|
||||
*/
|
||||
|
||||
func main() {
|
||||
// Getting a list of IP addresses that were entered in the last hour
|
||||
// time=seconds
|
||||
url := "https://api.blocklist.de/getlast.php?time=3600"
|
||||
extract := parser.NewDefaultTextExtract(0, "\t")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
|
||||
/*
|
||||
// This second list retrieves all the IP addresses added in the last 48 hours and is usually a
|
||||
// very large list (over 10000 entries), so be sure that you have the resources available to use it
|
||||
url := "http://lists.blocklist.de/lists/all.txt"
|
||||
extract := parser.NewDefaultTextExtract(0, "\t")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
*/
|
||||
}
|
||||
29
examples/bruteforceblocker.go
Normal file
29
examples/bruteforceblocker.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://danger.rulez.sk/index.php/bruteforceblocker/
|
||||
*/
|
||||
|
||||
func main() {
|
||||
url := "https://danger.rulez.sk/projects/bruteforceblocker/blist.php"
|
||||
extract := parser.NewDefaultTextExtract(0, "\t")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
}
|
||||
29
examples/ciarmy.go
Normal file
29
examples/ciarmy.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://www.ciarmy.com/#list
|
||||
*/
|
||||
|
||||
func main() {
|
||||
url := "https://www.ciarmy.com/list/ci-badguys.txt"
|
||||
extract := parser.NewDefaultTextExtract(0, " ")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of bad IP addresses from the service https://dshield.org/
|
||||
* An example of how to get a list of IP addresses from a service https://dshield.org/
|
||||
*/
|
||||
|
||||
func main() {
|
||||
|
||||
29
examples/greensnow.go
Normal file
29
examples/greensnow.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://greensnow.co/
|
||||
*/
|
||||
|
||||
func main() {
|
||||
url := "https://blocklist.greensnow.co/greensnow.txt"
|
||||
extract := parser.NewDefaultTextExtract(0, " ")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of bad IP addresses from the service https://www.projecthoneypot.org/list_of_ips.php
|
||||
* An example of how to get a list of IP addresses from a service https://www.projecthoneypot.org/list_of_ips.php
|
||||
*/
|
||||
|
||||
type rssItem struct {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of bad IP addresses from the service https://www.spamhaus.org/blocklists/do-not-route-or-peer/
|
||||
* An example of how to get a list of IP addresses from a service https://www.spamhaus.org/blocklists/do-not-route-or-peer/
|
||||
*/
|
||||
|
||||
type lineJson struct {
|
||||
|
||||
31
examples/stopforumspam.go
Normal file
31
examples/stopforumspam.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://www.stopforumspam.com/downloads
|
||||
*/
|
||||
|
||||
func main() {
|
||||
url := "https://www.stopforumspam.com/downloads/listed_ip_1.zip"
|
||||
//url := "https://www.stopforumspam.com/downloads/listed_ip_1_ipv6.zip"
|
||||
extract := parser.NewDefaultTextExtract(0, " ")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
configZip := blocklist.NewConfigZip(config)
|
||||
ips, err := blocklist.GetZip(url, pars, configZip)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
}
|
||||
29
examples/tor.go
Normal file
29
examples/tor.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist"
|
||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
||||
)
|
||||
|
||||
/**
|
||||
* An example of how to get a list of IP addresses from a service https://check.torproject.org/torbulkexitlist
|
||||
*/
|
||||
|
||||
func main() {
|
||||
url := "https://check.torproject.org/torbulkexitlist"
|
||||
extract := parser.NewDefaultTextExtract(0, " ")
|
||||
pars, err := parser.NewText(extract)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// limit 0 - no limit
|
||||
limit := uint(0)
|
||||
config := blocklist.NewConfig(limit)
|
||||
ips, err := blocklist.Get(url, pars, config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(ips)
|
||||
}
|
||||
Reference in New Issue
Block a user