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,17 +24,16 @@ go install github.com/acidvegas/golcg/cmd/golcg@latest
## Usage ## Usage
```bash ```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 ## Arguments
| Argument | Description | | Argument | Description |
| --------------- | ----------------------------- | | --------- | ----------------------------------------------------- |
| `-cidr` | The CIDR range to scan | | `-cidr` | The CIDR range to scan |
| `-shard-num` | The shard number to generate | | `-shard` | Shard specification in INDEX/TOTAL format (e.g., 1/4) |
| `-total-shards` | The total number of shards | | `-seed` | The seed for the LCG |
| `-seed` | The seed for the LCG | | `-state` | The state file to resume from |
| `-state` | The state file to resume from |
## Examples ## Examples
```bash ```bash
@ -47,8 +46,8 @@ golcg -cidr 172.16.0.0/12
golcg -cidr 192.168.0.0/16 golcg -cidr 192.168.0.0/16
# Distributed scanning (2 shards) # 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 1/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 2/2 # Second machine
``` ```
## State Management & Resume Capability ## State Management & Resume Capability
@ -77,7 +76,7 @@ Example of resuming:
state=$(cat /tmp/golcg_12345_192.168.0.0_16_1_4.state) state=$(cat /tmp/golcg_12345_192.168.0.0_16_1_4.state)
# Resume processing # 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. 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)

View File

@ -5,16 +5,47 @@ import (
"fmt" "fmt"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/acidvegas/golcg" "github.com/acidvegas/golcg"
) )
const Version = "1.0.0" 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() { func main() {
cidr := flag.String("cidr", "", "Target IP range in CIDR format") cidr := flag.String("cidr", "", "Target IP range in CIDR format")
shardNum := flag.Int("shard-num", 1, "Shard number (1-based)") shard := flag.String("shard", "", "Shard specification in INDEX/TOTAL format (e.g., 1/4)")
totalShards := flag.Int("total-shards", 1, "Total number of shards")
seed := flag.Int("seed", 0, "Random seed for LCG") seed := flag.Int("seed", 0, "Random seed for LCG")
stateStr := flag.String("state", "", "Resume from specific LCG state") stateStr := flag.String("state", "", "Resume from specific LCG state")
version := flag.Bool("version", false, "Show version information") version := flag.Bool("version", false, "Show version information")
@ -31,6 +62,12 @@ func main() {
os.Exit(1) os.Exit(1)
} }
shardNum, totalShards, err := parseShardArg(*shard)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
var state *uint32 var state *uint32
if *stateStr != "" { if *stateStr != "" {
stateVal, err := strconv.ParseUint(*stateStr, 10, 32) stateVal, err := strconv.ParseUint(*stateStr, 10, 32)
@ -42,7 +79,7 @@ func main() {
state = &stateUint32 state = &stateUint32
} }
stream, err := golcg.IPStream(*cidr, *shardNum, *totalShards, *seed, state) stream, err := golcg.IPStream(*cidr, shardNum, totalShards, *seed, state)
if err != nil { if err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
os.Exit(1) os.Exit(1)

View File

@ -20,7 +20,7 @@ type LCG struct {
func NewLCG(seed int, m uint32) *LCG { func NewLCG(seed int, m uint32) *LCG {
return &LCG{ return &LCG{
M: m, M: 1<<32 - 1,
A: 1664525, A: 1664525,
C: 1013904223, C: 1013904223,
Current: uint32(seed), Current: uint32(seed),
@ -46,17 +46,22 @@ func NewIPRange(cidr string) (*IPRange, error) {
start := ipToUint32(network.IP) start := ipToUint32(network.IP)
ones, bits := network.Mask.Size() ones, bits := network.Mask.Size()
hostBits := uint(bits - ones) 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{ return &IPRange{
Start: start, Start: start,
Total: uint32(total), Total: total,
}, nil }, nil
} }
func (r *IPRange) GetIPAtIndex(index uint32) (string, error) { 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") 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 { 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) stateFile := filepath.Join(os.TempDir(), fileName)
return os.WriteFile(stateFile, []byte(fmt.Sprintf("%d", lcgCurrent)), 0644) 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 lcg.Current = *state
} }
shardSize := ipRange.Total / uint32(totalShards) var shardSize uint32
if ipRange.Total == 0 {
if uint32(shardIndex) < (ipRange.Total % uint32(totalShards)) { shardSize = (1<<32 - 1) / uint32(totalShards)
shardSize++ 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() { go func() {
defer close(out) defer close(out)
remaining := shardSize remaining := shardSize
for remaining > 0 { 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) { if totalShards == 1 || index%uint32(totalShards) == uint32(shardIndex) {
ip, err := ipRange.GetIPAtIndex(index) ip, err := ipRange.GetIPAtIndex(index)
if err != nil { if err != nil {