Fixed improper math for large ranges, improved cli arguments & documentation
This commit is contained in:
parent
1f941bc4bb
commit
91f55ed853
24
README.md
24
README.md
@ -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)
|
||||||
|
|
@ -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)
|
||||||
|
43
golcg.go
43
golcg.go
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user