mirror of
https://github.com/XTLS/RealiTLScanner.git
synced 2026-09-22 22:28:08 +03:00
209 lines
3.9 KiB
Go
209 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"math"
|
|
"math/big"
|
|
"net"
|
|
"net/netip"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
_ = iota
|
|
HostTypeIP
|
|
HostTypeCIDR
|
|
HostTypeDomain
|
|
)
|
|
|
|
type HostType int
|
|
|
|
type Host struct {
|
|
IP net.IP
|
|
Origin string
|
|
Type HostType
|
|
}
|
|
|
|
func Iterate(reader io.Reader) <-chan Host {
|
|
scanner := bufio.NewScanner(reader)
|
|
hostChan := make(chan Host)
|
|
go func() {
|
|
defer close(hostChan)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" {
|
|
continue
|
|
}
|
|
ip := net.ParseIP(line)
|
|
if ip != nil && (ip.To4() != nil || enableIPv6) {
|
|
// ip address
|
|
hostChan <- Host{
|
|
IP: ip,
|
|
Origin: line,
|
|
Type: HostTypeIP,
|
|
}
|
|
continue
|
|
}
|
|
_, _, err := net.ParseCIDR(line)
|
|
if err == nil {
|
|
// ip cidr
|
|
p, err := netip.ParsePrefix(line)
|
|
if err != nil {
|
|
slog.Warn("Invalid cidr", "cidr", line, "err", err)
|
|
}
|
|
if !p.Addr().Is4() && !enableIPv6 {
|
|
continue
|
|
}
|
|
p = p.Masked()
|
|
addr := p.Addr()
|
|
for {
|
|
if !p.Contains(addr) {
|
|
break
|
|
}
|
|
ip = net.ParseIP(addr.String())
|
|
if ip != nil {
|
|
hostChan <- Host{
|
|
IP: ip,
|
|
Origin: line,
|
|
Type: HostTypeCIDR,
|
|
}
|
|
}
|
|
addr = addr.Next()
|
|
}
|
|
continue
|
|
}
|
|
if ValidateDomainName(line) {
|
|
// domain
|
|
hostChan <- Host{
|
|
IP: nil,
|
|
Origin: line,
|
|
Type: HostTypeDomain,
|
|
}
|
|
continue
|
|
}
|
|
slog.Warn("Not a valid IP, IP CIDR or domain", "line", line)
|
|
}
|
|
if err := scanner.Err(); err != nil && !errors.Is(err, io.EOF) {
|
|
slog.Error("Read file error", "err", err)
|
|
}
|
|
}()
|
|
return hostChan
|
|
}
|
|
func ValidateDomainName(domain string) bool {
|
|
r := regexp.MustCompile(`(?m)^[A-Za-z0-9\-.]+$`)
|
|
return r.MatchString(domain)
|
|
}
|
|
func ExistOnlyOne(arr []string) bool {
|
|
exist := false
|
|
for _, item := range arr {
|
|
if item != "" {
|
|
if exist {
|
|
return false
|
|
} else {
|
|
exist = true
|
|
}
|
|
}
|
|
}
|
|
return exist
|
|
}
|
|
func IterateAddr(addr string) <-chan Host {
|
|
hostChan := make(chan Host)
|
|
_, _, err := net.ParseCIDR(addr)
|
|
if err == nil {
|
|
// is CIDR
|
|
return Iterate(strings.NewReader(addr))
|
|
}
|
|
ip := net.ParseIP(addr)
|
|
if ip == nil {
|
|
ip, err = LookupIP(addr)
|
|
if err != nil {
|
|
close(hostChan)
|
|
slog.Error("Not a valid IP, IP CIDR or domain", "addr", addr)
|
|
return hostChan
|
|
}
|
|
}
|
|
go func() {
|
|
slog.Info("Enable infinite mode", "init", ip.String())
|
|
lowIP := ip
|
|
highIP := ip
|
|
hostChan <- Host{
|
|
IP: ip,
|
|
Origin: addr,
|
|
Type: HostTypeIP,
|
|
}
|
|
for i := 0; i < math.MaxInt; i++ {
|
|
if i%2 == 0 {
|
|
lowIP = NextIP(lowIP, false)
|
|
hostChan <- Host{
|
|
IP: lowIP,
|
|
Origin: lowIP.String(),
|
|
Type: HostTypeIP,
|
|
}
|
|
} else {
|
|
highIP = NextIP(highIP, true)
|
|
hostChan <- Host{
|
|
IP: highIP,
|
|
Origin: highIP.String(),
|
|
Type: HostTypeIP,
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return hostChan
|
|
}
|
|
func LookupIP(addr string) (net.IP, error) {
|
|
ips, err := net.LookupIP(addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to lookup: %w", err)
|
|
}
|
|
var arr []net.IP
|
|
for _, ip := range ips {
|
|
if ip.To4() != nil || enableIPv6 {
|
|
arr = append(arr, ip)
|
|
}
|
|
}
|
|
if len(arr) == 0 {
|
|
return nil, errors.New("no IP found")
|
|
}
|
|
return arr[0], nil
|
|
}
|
|
func RemoveDuplicateStr(strSlice []string) []string {
|
|
allKeys := make(map[string]bool)
|
|
var list []string
|
|
for _, item := range strSlice {
|
|
if _, value := allKeys[item]; !value {
|
|
allKeys[item] = true
|
|
list = append(list, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
func OutWriter(writer io.Writer) chan<- string {
|
|
ch := make(chan string)
|
|
go func() {
|
|
for s := range ch {
|
|
_, _ = io.WriteString(writer, s)
|
|
}
|
|
}()
|
|
return ch
|
|
}
|
|
func NextIP(ip net.IP, increment bool) net.IP {
|
|
// Convert to big.Int and increment
|
|
ipb := big.NewInt(0).SetBytes(ip)
|
|
if increment {
|
|
ipb.Add(ipb, big.NewInt(1))
|
|
} else {
|
|
ipb.Sub(ipb, big.NewInt(1))
|
|
}
|
|
|
|
// Add leading zeros
|
|
b := ipb.Bytes()
|
|
b = append(make([]byte, len(ip)-len(b)), b...)
|
|
return b
|
|
}
|