Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions modules/dns_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"ipmap/config"
"net"
"strings"
"time"
)

Expand All @@ -18,6 +19,19 @@ func ReverseDNS(ip string) string {
d := net.Dialer{
Timeout: time.Second * 2,
}
// Use custom DNS servers if configured
if len(config.DNSServers) > 0 {
for _, dns := range config.DNSServers {
dnsAddr := strings.TrimSpace(dns)
if !strings.Contains(dnsAddr, ":") {
dnsAddr = dnsAddr + ":53"
}
conn, err := d.DialContext(ctx, network, dnsAddr)
if err == nil {
return conn, nil
}
}
}
return d.DialContext(ctx, network, address)
},
}
Expand Down
145 changes: 125 additions & 20 deletions modules/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,128 @@ import (
"ipmap/config"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"

"github.com/corpix/uarand"
)

// Reusable HTTP client with connection pooling
var httpClient *http.Client
// HTTP client with lazy initialization
var (
httpClient *http.Client
httpClientOnce sync.Once
lastProxyURL string
lastDNSServers string
clientMu sync.RWMutex
)

// GetHTTPClient returns the HTTP client, creating or recreating if config changed
func GetHTTPClient() *http.Client {
clientMu.RLock()
currentProxy := config.ProxyURL
currentDNS := strings.Join(config.DNSServers, ",")
needsRecreate := httpClient != nil && (lastProxyURL != currentProxy || lastDNSServers != currentDNS)
clientMu.RUnlock()

if needsRecreate {
clientMu.Lock()
// Double-check after acquiring write lock
if lastProxyURL != currentProxy || lastDNSServers != currentDNS {
httpClient = createHTTPClientWithConfig()
lastProxyURL = currentProxy
lastDNSServers = currentDNS
config.VerboseLog("HTTP client recreated with new config (Proxy: %s, DNS: %s)", currentProxy, currentDNS)
}
clientMu.Unlock()
return httpClient
}

httpClientOnce.Do(func() {
clientMu.Lock()
defer clientMu.Unlock()
httpClient = createHTTPClientWithConfig()
lastProxyURL = config.ProxyURL
lastDNSServers = strings.Join(config.DNSServers, ",")
})

return httpClient
}

// createCustomDialer creates a dialer with optional custom DNS servers
func createCustomDialer() *net.Dialer {
dialer := &net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}
return dialer
}

// createDialContext creates a DialContext function with optional custom DNS
func createDialContext() func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := createCustomDialer()

if len(config.DNSServers) == 0 {
return dialer.DialContext
}

// Custom DNS resolver
resolver := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{Timeout: 5 * time.Second}
// Use first available custom DNS server
for _, dns := range config.DNSServers {
dnsAddr := strings.TrimSpace(dns)
if !strings.Contains(dnsAddr, ":") {
dnsAddr = dnsAddr + ":53"
}
conn, err := d.DialContext(ctx, "udp", dnsAddr)
if err == nil {
return conn, nil
}
}
// Fallback to default
return d.DialContext(ctx, network, address)
},
}

config.VerboseLog("Using custom DNS servers: %v", config.DNSServers)

return func(ctx context.Context, network, addr string) (net.Conn, error) {
// Split host and port
host, port, err := net.SplitHostPort(addr)
if err != nil {
return dialer.DialContext(ctx, network, addr)
}

// Resolve using custom DNS
ips, err := resolver.LookupIPAddr(ctx, host)
if err != nil || len(ips) == 0 {
// Fallback to normal resolution
return dialer.DialContext(ctx, network, addr)
}

func init() {
httpClient = createHTTPClient()
// Try each resolved IP
for _, ip := range ips {
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))
if err == nil {
return conn, nil
}
}

// Fallback
return dialer.DialContext(ctx, network, addr)
}
}

func createHTTPClient() *http.Client {
func createHTTPClientWithConfig() *http.Client {
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
// Allow more cipher suites for compatibility
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
Expand All @@ -36,21 +138,25 @@ func createHTTPClient() *http.Client {
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
},
},
// Connection pooling
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
// Timeouts
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
// Custom dialer with timeout
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
// Enable HTTP/2
ForceAttemptHTTP2: true,
DialContext: createDialContext(),
ForceAttemptHTTP2: true,
}

// Configure proxy if specified
if config.ProxyURL != "" {
proxyURL, err := url.Parse(config.ProxyURL)
if err != nil {
config.ErrorLog("Invalid proxy URL '%s': %v", config.ProxyURL, err)
} else {
transport.Proxy = http.ProxyURL(proxyURL)
config.VerboseLog("Using proxy: %s", config.ProxyURL)
}
}

return &http.Client{
Expand All @@ -59,7 +165,6 @@ func createHTTPClient() *http.Client {
if len(via) >= 10 {
return http.ErrUseLastResponse
}
// Preserve headers on redirect
for key, val := range via[0].Header {
if _, ok := req.Header[key]; !ok {
req.Header[key] = val
Expand Down Expand Up @@ -118,7 +223,7 @@ func RequestFuncWithRetry(ip string, url string, timeout int, maxRetries int) []
req.Header.Set("Sec-Ch-Ua-Mobile", "?0")
req.Header.Set("Sec-Ch-Ua-Platform", `"Windows"`)

resp, err := httpClient.Do(req)
resp, err := GetHTTPClient().Do(req)

if err != nil {
cancel() // Cancel on error
Expand Down
9 changes: 9 additions & 0 deletions modules/resolve_site.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ func ResolveSite(IPAddress []string, Websites [][]string, DomainTitle string, IP
config.VerboseLog("Starting scan with %d concurrent workers", workerCount)
sem := make(chan struct{}, workerCount)

// Create rate limiter
rateLimiter := NewRateLimiter(config.RateLimit, config.RateLimit*2)
if rateLimiter.IsEnabled() {
config.VerboseLog("Rate limiting enabled: %d requests/second", config.RateLimit)
}

// Create progress bar
bar := progressbar.NewOptions(len(IPAddress),
progressbar.OptionEnableColorCodes(true),
Expand All @@ -41,6 +47,9 @@ func ResolveSite(IPAddress []string, Websites [][]string, DomainTitle string, IP
defer wg.Done()
defer func() { <-sem }()

// Wait for rate limiter before making request
rateLimiter.Wait()

site := GetSite(ip, domain, timeout)
if len(site) > 0 {

Expand Down
27 changes: 13 additions & 14 deletions tools/find_asn.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,36 @@ import (
"time"
)

var (
IPBlocks []string
IPAddress []string
Websites [][]string
)

func FindASN(asn string, domain string, domainTitle string, con bool, export bool, timeout int, interruptData *modules.InterruptData) {
// Use local variables instead of global to avoid race conditions
var ipBlocks []string
var ipAddress []string
var websites [][]string

re := regexp.MustCompile(`(?m)route:\s+([0-9\.\/]+)$`)
for _, match := range re.FindAllStringSubmatch(modules.FindIPBlocks(asn), -1) {
IPBlocks = append(IPBlocks, match[1])
ipBlocks = append(ipBlocks, match[1])
}

for _, block := range IPBlocks {
for _, block := range ipBlocks {
ips, err := modules.CalcIPAddress(block)
if err != nil {
return
}

IPAddress = append(IPAddress, ips...)
ipAddress = append(ipAddress, ips...)
}

// Update interrupt data with IP blocks
if interruptData != nil {
interruptData.IPBlocks = IPBlocks
interruptData.IPBlocks = ipBlocks
}

fmt.Println("ASN: " + asn +
"\nIP Block: " + strconv.Itoa(len(IPBlocks)) +
"\nIP Address: " + strconv.Itoa(len(IPAddress)) +
"\nIP Block: " + strconv.Itoa(len(ipBlocks)) +
"\nIP Address: " + strconv.Itoa(len(ipAddress)) +
"\nStart Time: " + time.Now().Local().String() +
"\nEnd Time: " + time.Now().Add((time.Millisecond*time.Duration(timeout))*time.Duration(len(IPAddress))).Local().String())
"\nEnd Time: " + time.Now().Add((time.Millisecond*time.Duration(timeout))*time.Duration(len(ipAddress))).Local().String())

modules.ResolveSite(IPAddress, Websites, domainTitle, IPBlocks, domain, con, export, timeout, interruptData)
modules.ResolveSite(ipAddress, websites, domainTitle, ipBlocks, domain, con, export, timeout, interruptData)
}
18 changes: 11 additions & 7 deletions tools/find_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ import (
"time"
)

func FindIP(IPBlocks []string, domain string, domainTitle string, con bool, export bool, timeout int, interruptData *modules.InterruptData) {
for _, block := range IPBlocks {
func FindIP(ipBlocks []string, domain string, domainTitle string, con bool, export bool, timeout int, interruptData *modules.InterruptData) {
// Use local variables instead of global to avoid race conditions
var ipAddress []string
var websites [][]string

for _, block := range ipBlocks {
ips, err := modules.CalcIPAddress(block)
if err != nil {
return
}

IPAddress = append(IPAddress, ips...)
ipAddress = append(ipAddress, ips...)
}

fmt.Println("IP Block: " + strconv.Itoa(len(IPBlocks)) +
"\nIP Address: " + strconv.Itoa(len(IPAddress)) +
fmt.Println("IP Block: " + strconv.Itoa(len(ipBlocks)) +
"\nIP Address: " + strconv.Itoa(len(ipAddress)) +
"\nStart Time: " + time.Now().Local().String() +
"\nEnd Time: " + time.Now().Add((time.Millisecond*time.Duration(timeout))*time.Duration(len(IPAddress))).Local().String())
"\nEnd Time: " + time.Now().Add((time.Millisecond*time.Duration(timeout))*time.Duration(len(ipAddress))).Local().String())

modules.ResolveSite(IPAddress, Websites, domainTitle, IPBlocks, domain, con, export, timeout, interruptData)
modules.ResolveSite(ipAddress, websites, domainTitle, ipBlocks, domain, con, export, timeout, interruptData)
}
Loading