From 45baf065154f6492eed1ccafdba3a45511154752 Mon Sep 17 00:00:00 2001 From: Rob Fuller <679319+mubix@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:31:30 -0400 Subject: [PATCH] Update main.go --- main.go | 111 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/main.go b/main.go index f7e1caa..ce240c1 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "context" "encoding/binary" "flag" @@ -9,39 +10,29 @@ import ( "math/rand" "net" "os" + "strings" "syscall" "time" "github.com/armon/go-socks5" ) -// Global variables holding the numeric representation of the IP range. -var ipRangeStart, ipRangeEnd uint32 +var ipList []net.IP -// ipToUint32 converts a net.IP (assumed to be IPv4) to a uint32 representation. func ipToUint32(ip net.IP) uint32 { - ip = ip.To4() - return binary.BigEndian.Uint32(ip) + return binary.BigEndian.Uint32(ip.To4()) } -// uint32ToIP converts a uint32 to a net.IP (IPv4). func uint32ToIP(n uint32) net.IP { ip := make(net.IP, 4) binary.BigEndian.PutUint32(ip, n) return ip } -// randomIP computes a random IP within the global ipRangeStart to ipRangeEnd values. func randomIP() net.IP { - // Calculate total number of addresses in the range. - total := ipRangeEnd - ipRangeStart + 1 - // Pick a random offset. - offset := uint32(rand.Intn(int(total))) - return uint32ToIP(ipRangeStart + offset) + return ipList[rand.Intn(len(ipList))] } -// customDialer creates an outbound connection bound to a random IP from the chosen range. -// It also uses the IP_FREEBIND socket option, which allows binding to non-local addresses. func customDialer(ctx context.Context, network, addr string) (net.Conn, error) { localIP := randomIP() localAddr := &net.TCPAddr{ @@ -50,11 +41,9 @@ func customDialer(ctx context.Context, network, addr string) (net.Conn, error) { dialer := &net.Dialer{ LocalAddr: localAddr, Timeout: 10 * time.Second, - // Use Control to enable IP_FREEBIND on the socket. Control: func(network, address string, c syscall.RawConn) error { var err error c.Control(func(fd uintptr) { - // Set the IP_FREEBIND option. err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_FREEBIND, 1) }) return err @@ -63,55 +52,81 @@ func customDialer(ctx context.Context, network, addr string) (net.Conn, error) { return dialer.DialContext(ctx, network, addr) } -// validateIPRange checks that the provided IP strings are valid IPv4 addresses and that start <= end. -func validateIPRange(startStr, endStr string) (net.IP, net.IP, error) { - startIP := net.ParseIP(startStr) - if startIP == nil || startIP.To4() == nil { - return nil, nil, fmt.Errorf("start IP (%s) is not a valid IPv4 address", startStr) - } - endIP := net.ParseIP(endStr) - if endIP == nil || endIP.To4() == nil { - return nil, nil, fmt.Errorf("end IP (%s) is not a valid IPv4 address", endStr) +func validateIPRange(startStr, endStr string) ([]net.IP, error) { + startIP := net.ParseIP(startStr).To4() + endIP := net.ParseIP(endStr).To4() + if startIP == nil || endIP == nil { + return nil, fmt.Errorf("invalid IPv4 addresses") } - // Convert to numeric form for comparison. startVal := ipToUint32(startIP) endVal := ipToUint32(endIP) if startVal > endVal { - return nil, nil, fmt.Errorf("start IP (%s) must be less than or equal to end IP (%s)", startStr, endStr) + return nil, fmt.Errorf("start IP must be <= end IP") + } + + var ips []net.IP + for i := startVal; i <= endVal; i++ { + ips = append(ips, uint32ToIP(i)) + } + return ips, nil +} + +func loadIPsFromFile(filePath string) ([]net.IP, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var ips []net.IP + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if ip := net.ParseIP(line).To4(); ip != nil { + ips = append(ips, ip) + } else { + log.Printf("Ignoring invalid IP: %s", line) + } } - return startIP.To4(), endIP.To4(), nil + if err := scanner.Err(); err != nil { + return nil, err + } + + if len(ips) == 0 { + return nil, fmt.Errorf("no valid IPs found in file") + } + return ips, nil } func main() { - // Define command-line flags for start and end IP. startFlag := flag.String("start", "", "Start IP of the range (e.g., 10.1.0.0)") endFlag := flag.String("end", "", "End IP of the range (e.g., 10.100.255.255)") + fileFlag := flag.String("file", "", "File containing a list of IP addresses (one per line)") portFlag := flag.Int("port", 1080, "Port on which the SOCKS5 proxy will listen") flag.Parse() - // Check that the start and end flags were provided. - if *startFlag == "" || *endFlag == "" { - fmt.Println("Usage: scoreproxy -start -end [-port ]") - os.Exit(1) - } - - // Validate the IP range. - startIP, endIP, err := validateIPRange(*startFlag, *endFlag) - if err != nil { - log.Fatalf("Invalid IP range: %v", err) + var err error + + switch { + case *fileFlag != "": + ipList, err = loadIPsFromFile(*fileFlag) + if err != nil { + log.Fatalf("Failed loading IPs from file: %v", err) + } + log.Printf("Loaded %d IPs from file", len(ipList)) + case *startFlag != "" && *endFlag != "": + ipList, err = validateIPRange(*startFlag, *endFlag) + if err != nil { + log.Fatalf("Invalid IP range: %v", err) + } + log.Printf("Using IP range with %d IPs", len(ipList)) + default: + log.Fatalf("Usage: -start and -end for IP range OR -file for list of IPs") } - // Compute the numeric representations for use in randomIP(). - ipRangeStart = ipToUint32(startIP) - ipRangeEnd = ipToUint32(endIP) - log.Printf("Using IP range from %s (%d) to %s (%d)", - startIP, ipRangeStart, endIP, ipRangeEnd) - - // Seed the random number generator. rand.Seed(time.Now().UnixNano()) - // Configure the SOCKS5 server to use our custom dialer. conf := &socks5.Config{ Dial: customDialer, } @@ -121,11 +136,9 @@ func main() { log.Fatalf("Error creating SOCKS5 server: %v", err) } - // Prepare the listen address using the provided port. listenAddr := fmt.Sprintf("0.0.0.0:%d", *portFlag) log.Printf("Starting SOCKS5 server on %s", listenAddr) if err := server.ListenAndServe("tcp", listenAddr); err != nil { log.Fatalf("Error starting SOCKS5 server: %v", err) } } -