golcg

- Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README | LICENSE

commit 91f55ed8539065e683c8c5bb46aecd729d90cf06
parent 1f941bc4bb152f1187515e9a6fbb9d3b2096937a
Author: acidvegas <acid.vegas@acid.vegas>
Date: Fri, 6 Dec 2024 13:41:15 -0500

Fixed improper math for large ranges, improved cli arguments & documentation

Diffstat:
MREADME.md | 25++++++++++++-------------
Mcmd/golcg/main.go | 43++++++++++++++++++++++++++++++++++++++++---
Mgolcg.go | 43+++++++++++++++++++++++++++++++------------

3 files changed, 83 insertions(+), 28 deletions(-)

diff --git a/README.md b/README.md
@@ -24,17 +24,16 @@ go install github.com/acidvegas/golcg/cmd/golcg@latest
 
 ## Usage
 ```bash
-golcg -cidr CIDR [-shard-num N] [-total-shards N] [-seed N] [-state STATE]
+golcg -cidr CIDR [-shard INDEX/TOTAL] [-seed N] [-state STATE]
 ```
 
 ## Arguments
-| Argument        | Description                   |
-| --------------- | ----------------------------- |
-| `-cidr`         | The CIDR range to scan        |
-| `-shard-num`    | The shard number to generate  |
-| `-total-shards` | The total number of shards    |
-| `-seed`         | The seed for the LCG          |
-| `-state`        | The state file to resume from |
+| Argument  | Description                                           |
+| --------- | ----------------------------------------------------- |
+| `-cidr`   | The CIDR range to scan                                |
+| `-shard`  | Shard specification in INDEX/TOTAL format (e.g., 1/4) |
+| `-seed`   | The seed for the LCG                                  |
+| `-state`  | The state file to resume from                         |
 
 ## Examples
 ```bash
@@ -47,8 +46,8 @@ golcg -cidr 172.16.0.0/12
 golcg -cidr 192.168.0.0/16
 
 # Distributed scanning (2 shards)
-golcg -cidr 0.0.0.0/0 -shard-num 1 -total-shards 2 # One machine
-golcg -cidr 0.0.0.0/0 -shard-num 2 -total-shards 2 # Second machine
+golcg -cidr 0.0.0.0/0 -shard 1/2  # One machine
+golcg -cidr 0.0.0.0/0 -shard 2/2  # Second machine
 ```
 
 ## State Management & Resume Capability
@@ -77,7 +76,7 @@ Example of resuming:
 state=$(cat /tmp/golcg_12345_192.168.0.0_16_1_4.state)
 
 # Resume processing
-golcg 192.168.0.0/16 --shard-num 1 --total-shards 4 --seed 12345 --state $state
+golcg -cidr 192.168.0.0/16 -shard 1/4 -seed 12345 -state $state
 ```
 
 Note: When using the `--state` parameter, you must provide the same `--seed` that was used in the original run.
@@ -141,5 +140,4 @@ The sharding system employs an interleaved approach that ensures even distributi
 
 ---
 
-###### Mirrors: [acid.vegas](https://git.acid.vegas/golcg) • [SuperNETs](https://git.supernets.org/acidvegas/golcg) • [GitHub](https://github.com/acidvegas/golcg) • [GitLab](https://gitlab.com/acidvegas/golcg) • [Codeberg](https://codeberg.org/acidvegas/golcg)
-
+###### Mirrors: [acid.vegas](https://git.acid.vegas/golcg) • [SuperNETs](https://git.supernets.org/acidvegas/golcg) • [GitHub](https://github.com/acidvegas/golcg) • [GitLab](https://gitlab.com/acidvegas/golcg) • [Codeberg](https://codeberg.org/acidvegas/golcg)
+\ No newline at end of file
diff --git a/cmd/golcg/main.go b/cmd/golcg/main.go
@@ -5,16 +5,47 @@ import (
 	"fmt"
 	"os"
 	"strconv"
+	"strings"
 
 	"github.com/acidvegas/golcg"
 )
 
 const Version = "1.0.0"
 
+func parseShardArg(shard string) (int, int, error) {
+	if shard == "" {
+		return 1, 1, nil
+	}
+
+	parts := strings.Split(shard, "/")
+	if len(parts) != 2 {
+		return 0, 0, fmt.Errorf("invalid shard format. Expected INDEX/TOTAL, got %s", shard)
+	}
+
+	index, err := strconv.Atoi(parts[0])
+	if err != nil {
+		return 0, 0, fmt.Errorf("invalid shard index: %v", err)
+	}
+
+	total, err := strconv.Atoi(parts[1])
+	if err != nil {
+		return 0, 0, fmt.Errorf("invalid shard total: %v", err)
+	}
+
+	if index < 1 || index > total {
+		return 0, 0, fmt.Errorf("shard index must be between 1 and total")
+	}
+
+	if total < 1 {
+		return 0, 0, fmt.Errorf("total shards must be greater than 0")
+	}
+
+	return index, total, nil
+}
+
 func main() {
 	cidr := flag.String("cidr", "", "Target IP range in CIDR format")
-	shardNum := flag.Int("shard-num", 1, "Shard number (1-based)")
-	totalShards := flag.Int("total-shards", 1, "Total number of shards")
+	shard := flag.String("shard", "", "Shard specification in INDEX/TOTAL format (e.g., 1/4)")
 	seed := flag.Int("seed", 0, "Random seed for LCG")
 	stateStr := flag.String("state", "", "Resume from specific LCG state")
 	version := flag.Bool("version", false, "Show version information")
@@ -31,6 +62,12 @@ func main() {
 		os.Exit(1)
 	}
 
+	shardNum, totalShards, err := parseShardArg(*shard)
+	if err != nil {
+		fmt.Printf("Error: %v\n", err)
+		os.Exit(1)
+	}
+
 	var state *uint32
 	if *stateStr != "" {
 		stateVal, err := strconv.ParseUint(*stateStr, 10, 32)
@@ -42,7 +79,7 @@ func main() {
 		state = &stateUint32
 	}
 
-	stream, err := golcg.IPStream(*cidr, *shardNum, *totalShards, *seed, state)
+	stream, err := golcg.IPStream(*cidr, shardNum, totalShards, *seed, state)
 	if err != nil {
 		fmt.Printf("Error: %v\n", err)
 		os.Exit(1)
diff --git a/golcg.go b/golcg.go
@@ -20,7 +20,7 @@ type LCG struct {
 
 func NewLCG(seed int, m uint32) *LCG {
 	return &LCG{
-		M:       m,
+		M:       1<<32 - 1,
 		A:       1664525,
 		C:       1013904223,
 		Current: uint32(seed),
@@ -46,17 +46,22 @@ func NewIPRange(cidr string) (*IPRange, error) {
 	start := ipToUint32(network.IP)
 	ones, bits := network.Mask.Size()
 	hostBits := uint(bits - ones)
-	broadcast := start | (1<<hostBits - 1)
-	total := broadcast - start + 1
+
+	var total uint32
+	if hostBits == 32 {
+		total = 0
+	} else {
+		total = 1 << hostBits
+	}
 
 	return &IPRange{
 		Start: start,
-		Total: uint32(total),
+		Total: total,
 	}, nil
 }
 
 func (r *IPRange) GetIPAtIndex(index uint32) (string, error) {
-	if index >= r.Total {
+	if r.Total > 0 && index >= r.Total {
 		return "", errors.New("IP index out of range")
 	}
 
@@ -79,7 +84,7 @@ func uint32ToIP(n uint32) net.IP {
 }
 
 func SaveState(seed int, cidr string, shard int, total int, lcgCurrent uint32) error {
-	fileName := fmt.Sprintf("pylcg_%d_%s_%d_%d.state", seed, strings.Replace(cidr, "/", "_", -1), shard, total)
+	fileName := fmt.Sprintf("golcg_%d_%s_%d_%d.state", seed, strings.Replace(cidr, "/", "_", -1), shard, total)
 	stateFile := filepath.Join(os.TempDir(), fileName)
 
 	return os.WriteFile(stateFile, []byte(fmt.Sprintf("%d", lcgCurrent)), 0644)
@@ -103,19 +108,33 @@ func IPStream(cidr string, shardNum, totalShards, seed int, state *uint32) (<-ch
 		lcg.Current = *state
 	}
 
-	shardSize := ipRange.Total / uint32(totalShards)
-
-	if uint32(shardIndex) < (ipRange.Total % uint32(totalShards)) {
-		shardSize++
+	var shardSize uint32
+	if ipRange.Total == 0 {
+		shardSize = (1<<32 - 1) / uint32(totalShards)
+		if uint32(shardIndex) < uint32(totalShards-1) {
+			shardSize++
+		}
+	} else {
+		shardSize = ipRange.Total / uint32(totalShards)
+		if uint32(shardIndex) < ipRange.Total%uint32(totalShards) {
+			shardSize++
+		}
 	}
 
-	out := make(chan string)
+	out := make(chan string, 1000)
 	go func() {
 		defer close(out)
 		remaining := shardSize
 
 		for remaining > 0 {
-			index := lcg.Next() % ipRange.Total
+			next := lcg.Next()
+			var index uint32
+			if ipRange.Total > 0 {
+				index = next % ipRange.Total
+			} else {
+				index = next
+			}
+
 			if totalShards == 1 || index%uint32(totalShards) == uint32(shardIndex) {
 				ip, err := ipRange.GetIPAtIndex(index)
 				if err != nil {