diff --git a/README.md b/README.md index 58d25c4..a5b24a0 100644 --- 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 index 5772934..0eef15d 100644 --- 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 index 8dc9cfb..f721093 100644 --- 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<= 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 {