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

This commit is contained in:
Dionysus 2024-12-06 13:41:15 -05:00
parent 1f941bc4bb
commit 91f55ed853
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
3 changed files with 82 additions and 28 deletions

View File

@ -24,15 +24,14 @@ 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 |
| `-shard` | Shard specification in INDEX/TOTAL format (e.g., 1/4) |
| `-seed` | The seed for the LCG |
| `-state` | The state file to resume from |
@ -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.
@ -142,4 +141,3 @@ 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)

View File

@ -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)

View File

@ -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)) {
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 {