Compare commits
27 Commits
941e0541bf
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7331ed2b2b | |||
|
a8364f98ed
|
|||
|
a2dff48936
|
|||
|
e09411f871
|
|||
|
1b16ae243f
|
|||
| 70fb7a3aa8 | |||
|
cfbf4ce504
|
|||
|
17115d97c6
|
|||
|
1dc7a8b22c
|
|||
|
e4fc07871b
|
|||
|
faedb345df
|
|||
|
ee0e36b40d
|
|||
|
790073aa95
|
|||
|
e18859e78f
|
|||
|
aeb855c0b8
|
|||
|
5b8974101e
|
|||
|
dd4e5619c7
|
|||
|
172c32237b
|
|||
|
364e657641
|
|||
|
e4b0ed6668
|
|||
|
f295c402af
|
|||
|
062cb25551
|
|||
|
17dbfac864
|
|||
|
68eb3a055d
|
|||
|
a20d939e0d
|
|||
|
9cda5799e1
|
|||
|
7702b5a431
|
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)
|
||||||
278
blocklist.go
278
blocklist.go
@@ -1,10 +1,14 @@
|
|||||||
package blocklist
|
package blocklist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.kor-elf.net/kor-elf-shield/blocklist/parser"
|
"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 defines the maximum duration for request operations before timing out.
|
||||||
requestTimeout = 20 * time.Second
|
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.
|
// Config defines the configuration for the blocklist.
|
||||||
@@ -34,6 +44,17 @@ type Config struct {
|
|||||||
RequestTimeout time.Duration
|
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.
|
// NewConfig creates a new Config with default values.
|
||||||
// limit is the maximum number of items to process or validate. 0 means no limit.
|
// limit is the maximum number of items to process or validate. 0 means no limit.
|
||||||
func NewConfig(limit uint) Config {
|
func NewConfig(limit uint) Config {
|
||||||
@@ -46,6 +67,8 @@ func NewConfig(limit uint) Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewConfigWithValidator creates a new Config with the specified validator.
|
// NewConfigWithValidator creates a new Config with the specified validator.
|
||||||
|
// limit is the maximum number of items to process or validate. 0 means no limit.
|
||||||
|
// validator is the IP validator to use.
|
||||||
func NewConfigWithValidator(limit uint, validator parser.IPValidator) Config {
|
func NewConfigWithValidator(limit uint, validator parser.IPValidator) Config {
|
||||||
return Config{
|
return Config{
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
@@ -55,32 +78,23 @@ 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.
|
// 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.
|
// 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) {
|
func Get(fileUrl string, parser parser.Parser, c Config) (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.ContextTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), c.ContextTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil)
|
res, err := fetch(fileUrl, ctx, c.RequestTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create request: %w", err)
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Timeout: c.RequestTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("request failed: %w", err)
|
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = res.Body.Close()
|
_ = res.Body.Close()
|
||||||
@@ -92,3 +106,229 @@ func Get(fileUrl string, parser parser.Parser, c Config) (parser.IPs, error) {
|
|||||||
|
|
||||||
return parser.Parse(res.Body, c.Validator, c.Limit)
|
return parser.Parse(res.Body, c.Validator, c.Limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSeparatedIPs 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 GetSeparatedIPs(fileUrl string, parser parser.Parser, c Config) (ipV4 parser.IPs, ipV6 parser.IPs, err error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.ContextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
res, err := fetch(fileUrl, ctx, c.RequestTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser.ParseIPsByVersion(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) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.Config.ContextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
res, err := fetch(fileUrl, ctx, c.Config.RequestTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetZipSeparatedIPs 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 GetZipSeparatedIPs(fileUrl string, parser parser.Parser, c ConfigZip) (ipV4 parser.IPs, ipV6 parser.IPs, err error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.Config.ContextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
res, err := fetch(fileUrl, ctx, c.Config.RequestTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.MaxDownloadSize > 0 && res.ContentLength > c.MaxDownloadSize {
|
||||||
|
return nil, 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, nil, fmt.Errorf("read response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.MaxDownloadSize > 0 && int64(len(body)) > c.MaxDownloadSize {
|
||||||
|
return nil, nil, fmt.Errorf("downloaded file exceeds limit %d bytes", c.MaxDownloadSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isZip(body) {
|
||||||
|
return nil, nil, fmt.Errorf("invalid zip archive")
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseZipSeparatedIPs(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 parseZipSeparatedIPs(body []byte, p parser.Parser, c ConfigZip) (ipV4 parser.IPs, ipV6 parser.IPs, err error) {
|
||||||
|
reader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("open zip archive: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file := findArchiveFile(reader.File)
|
||||||
|
if file == nil {
|
||||||
|
return nil, nil, fmt.Errorf("zip archive does not contain a supported file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.MaxArchiveFileSize > 0 && file.UncompressedSize64 > c.MaxArchiveFileSize {
|
||||||
|
return nil, 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, 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.ParseIPsByVersion(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetch(fileUrl string, ctx context.Context, requestTimeout time.Duration) (*http.Response, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: requestTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request failed: %w", err)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|||||||
74
examples/blocklist.go
Normal file
74
examples/blocklist.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
*/
|
||||||
|
}
|
||||||
41
examples/bruteforceblocker.go
Normal file
41
examples/bruteforceblocker.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
41
examples/ciarmy.go
Normal file
41
examples/ciarmy.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
71
examples/dshield.go
Normal file
71
examples/dshield.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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://dshield.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
url := "https://www.dshield.org/block.txt"
|
||||||
|
extract := parser.NewCIDRTextExtract(0, 2, "\t")
|
||||||
|
pars, err := parser.NewText(extract)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// limit 0 - no limit
|
||||||
|
limit := uint(0)
|
||||||
|
config := blocklist.NewConfig(limit)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// You can also get a range of IP addresses from this service (from to)
|
||||||
|
url := "https://www.dshield.org/block.txt"
|
||||||
|
extract := parser.NewIntervalTextExtract(0, 1, "\t")
|
||||||
|
pars, err := parser.NewText(extract)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// limit 0 - no limit
|
||||||
|
limit := uint(0)
|
||||||
|
config := blocklist.NewConfigWithValidator(limit, &parser.IPRangeValidator{})
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
*/
|
||||||
|
}
|
||||||
41
examples/greensnow.go
Normal file
41
examples/greensnow.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
65
examples/honeypot.go
Normal file
65
examples/honeypot.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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.projecthoneypot.org/list_of_ips.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
type rssItem struct {
|
||||||
|
Title string `xml:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
url := "https://www.projecthoneypot.org/list_of_ips.php?t=d&rss=1"
|
||||||
|
pars, err := parser.NewRss(func(decoder *xml.Decoder, start xml.StartElement) (string, error) {
|
||||||
|
if start.Name.Local != "item" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rssItem rssItem
|
||||||
|
if err := decoder.DecodeElement(&rssItem, &start); err != nil {
|
||||||
|
return "", fmt.Errorf("decode rss item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rssItem.Title == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Split(rssItem.Title, "|")
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(fields[0]), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// limit 0 - no limit
|
||||||
|
limit := uint(0)
|
||||||
|
config := blocklist.NewConfig(limit)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
@@ -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 {
|
type lineJson struct {
|
||||||
@@ -32,9 +32,21 @@ func main() {
|
|||||||
// limit 0 - no limit
|
// limit 0 - no limit
|
||||||
limit := uint(0)
|
limit := uint(0)
|
||||||
config := blocklist.NewConfig(limit)
|
config := blocklist.NewConfig(limit)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
ips, err := blocklist.Get(url, pars, config)
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(ips)
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
}
|
}
|
||||||
|
|||||||
43
examples/stopforumspam.go
Normal file
43
examples/stopforumspam.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.GetZip(url, pars, configZip)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetZipSeparatedIPs(url, pars, configZip)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
41
examples/tor.go
Normal file
41
examples/tor.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in one list
|
||||||
|
ips, err := blocklist.Get(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(ips)
|
||||||
|
|
||||||
|
// Get IPv4 and IPv6 addresses in two lists
|
||||||
|
ipsV4, ipsV6, err := blocklist.GetSeparatedIPs(url, pars, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("IPv4")
|
||||||
|
fmt.Println(ipsV4)
|
||||||
|
fmt.Println("IPv6")
|
||||||
|
fmt.Println(ipsV6)
|
||||||
|
}
|
||||||
@@ -63,3 +63,50 @@ func (p *jsonLinesParser) Parse(body io.Reader, validator IPValidator, limit uin
|
|||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseIPsByVersion parses the JSON Lines data from the given reader
|
||||||
|
// and returns a slice of IP addresses for each IP version.
|
||||||
|
// It also returns any errors that occurred during the process.
|
||||||
|
func (p *jsonLinesParser) ParseIPsByVersion(body io.Reader, validator IPValidator, limit uint) (ipV4 IPs, ipV6 IPs, err error) {
|
||||||
|
decoder := json.NewDecoder(body)
|
||||||
|
ipV4 = make(IPs, 0)
|
||||||
|
ipV6 = make(IPs, 0)
|
||||||
|
for {
|
||||||
|
var item json.RawMessage
|
||||||
|
if err := decoder.Decode(&item); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("decode json item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if item == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := p.extract(item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("extract ip: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = strings.TrimSpace(ip)
|
||||||
|
isValid, ipVersion := validator.IsValidAndReturnVersion(ip)
|
||||||
|
if !isValid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipVersion == IPVersion4 {
|
||||||
|
ipV4 = append(ipV4, ip)
|
||||||
|
} else if ipVersion == IPVersion6 {
|
||||||
|
ipV6 = append(ipV6, ip)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 && uint(len(ipV4))+uint(len(ipV6)) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipV4, ipV6, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,14 +11,25 @@ import (
|
|||||||
type Parser interface {
|
type Parser interface {
|
||||||
// Parse reads the body and returns a slice of IP addresses.
|
// Parse reads the body and returns a slice of IP addresses.
|
||||||
Parse(body io.Reader, validator IPValidator, limit uint) (IPs, error)
|
Parse(body io.Reader, validator IPValidator, limit uint) (IPs, error)
|
||||||
|
|
||||||
|
ParseIPsByVersion(body io.Reader, validator IPValidator, limit uint) (ipV4 IPs, ipV6 IPs, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPValidator interface defines the contract for validating IP addresses.
|
// IPValidator interface defines the contract for validating IP addresses.
|
||||||
type IPValidator interface {
|
type IPValidator interface {
|
||||||
// IsValid checks if the given IP address is valid.
|
// IsValid checks if the given IP address is valid.
|
||||||
IsValid(ip string) bool
|
IsValid(ip string) bool
|
||||||
|
|
||||||
|
IsValidAndReturnVersion(ip string) (bool, IPVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IPVersion int
|
||||||
|
|
||||||
|
const (
|
||||||
|
IPVersion4 IPVersion = iota
|
||||||
|
IPVersion6
|
||||||
|
)
|
||||||
|
|
||||||
// IPs is a slice of IP addresses.
|
// IPs is a slice of IP addresses.
|
||||||
type IPs []string
|
type IPs []string
|
||||||
|
|
||||||
@@ -50,6 +61,48 @@ func (v *DefaultIPValidator) IsValid(value string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidAndReturnVersion checks if the given IP address is valid and returns the IP version.
|
||||||
|
// It returns true if the IP address is not a loopback address and the IP version is either IPv4 or IPv6.
|
||||||
|
func (v *DefaultIPValidator) IsValidAndReturnVersion(value string) (bool, IPVersion) {
|
||||||
|
if value == "" {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip := net.ParseIP(value); ip != nil {
|
||||||
|
if ip.IsLoopback() {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return true, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To16() != nil {
|
||||||
|
return true, IPVersion6
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip, _, err := net.ParseCIDR(value); err == nil {
|
||||||
|
if ip.IsLoopback() {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return true, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To16() != nil {
|
||||||
|
return true, IPVersion6
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
// IPRangeValidator implements IPValidator interface.
|
// IPRangeValidator implements IPValidator interface.
|
||||||
// It validates IP ranges by parsing them using net.ParseIP and checking if the start and end IPs are in the same network.
|
// It validates IP ranges by parsing them using net.ParseIP and checking if the start and end IPs are in the same network.
|
||||||
type IPRangeValidator struct{}
|
type IPRangeValidator struct{}
|
||||||
@@ -89,3 +142,43 @@ func (v *IPRangeValidator) IsValid(value string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *IPRangeValidator) IsValidAndReturnVersion(value string) (bool, IPVersion) {
|
||||||
|
if value == "" {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(value, "-")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
start := net.ParseIP(strings.TrimSpace(parts[0]))
|
||||||
|
end := net.ParseIP(strings.TrimSpace(parts[1]))
|
||||||
|
if start == nil || end == nil {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
|
||||||
|
start4 := start.To4()
|
||||||
|
end4 := end.To4()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case start4 != nil && end4 != nil:
|
||||||
|
if bytes.Compare(start4, end4) <= 0 {
|
||||||
|
return true, IPVersion4
|
||||||
|
}
|
||||||
|
return false, IPVersion4
|
||||||
|
case start4 == nil && end4 == nil:
|
||||||
|
start16 := start.To16()
|
||||||
|
end16 := end.To16()
|
||||||
|
if start16 == nil || end16 == nil {
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
if bytes.Compare(start16, end16) <= 0 {
|
||||||
|
return true, IPVersion6
|
||||||
|
}
|
||||||
|
return false, IPVersion4
|
||||||
|
default:
|
||||||
|
return false, IPVersion4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,3 +66,53 @@ func (p *rssParser) Parse(body io.Reader, validator IPValidator, limit uint) (IP
|
|||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseIPsByVersion parses the RSS data from the given reader
|
||||||
|
// and returns a slice of IP addresses for each IP version.
|
||||||
|
// It also returns any errors that occurred during the process.
|
||||||
|
func (p *rssParser) ParseIPsByVersion(body io.Reader, validator IPValidator, limit uint) (ipV4 IPs, ipV6 IPs, err error) {
|
||||||
|
decoder := xml.NewDecoder(body)
|
||||||
|
ipV4 = make(IPs, 0)
|
||||||
|
ipV6 = make(IPs, 0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
token, err := decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, fmt.Errorf("parse rss: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
start, ok := token.(xml.StartElement)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := p.extract(decoder, start)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("extract rss ip: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = strings.TrimSpace(ip)
|
||||||
|
isValid, ipVersion := validator.IsValidAndReturnVersion(ip)
|
||||||
|
if !isValid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipVersion == IPVersion4 {
|
||||||
|
ipV4 = append(ipV4, ip)
|
||||||
|
} else if ipVersion == IPVersion6 {
|
||||||
|
ipV6 = append(ipV6, ip)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 && uint(len(ipV4))+uint(len(ipV6)) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipV4, ipV6, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -158,3 +158,49 @@ func (p *textParser) Parse(body io.Reader, validator IPValidator, limit uint) (I
|
|||||||
|
|
||||||
return ips, nil
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *textParser) ParseIPsByVersion(body io.Reader, validator IPValidator, limit uint) (ipV4 IPs, ipV6 IPs, err error) {
|
||||||
|
ipV4 = make(IPs, 0)
|
||||||
|
ipV6 = make(IPs, 0)
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(body)
|
||||||
|
|
||||||
|
buf := make([]byte, 0, 64*1024)
|
||||||
|
scanner.Buffer(buf, 1024*1024)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == "" || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, isFound := p.textExtract.Extract(line)
|
||||||
|
if !isFound {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = strings.TrimSpace(ip)
|
||||||
|
isValid, ipVersion := validator.IsValidAndReturnVersion(ip)
|
||||||
|
if !isValid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipVersion == IPVersion4 {
|
||||||
|
ipV4 = append(ipV4, ip)
|
||||||
|
} else if ipVersion == IPVersion6 {
|
||||||
|
ipV6 = append(ipV6, ip)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 && uint(len(ipV4))+uint(len(ipV6)) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("read response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipV4, ipV6, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user