Initial commit
This commit is contained in:
commit
be5dc2fe76
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# State files
|
||||||
|
*.state
|
||||||
|
|
||||||
|
# Go specific
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# IDE specific (optional, but common)
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2025, acidvegas <acid.vegas@acid.vegas>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
173
golcg.go
Normal file
173
golcg.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LCG struct {
|
||||||
|
m uint32
|
||||||
|
a uint32
|
||||||
|
c uint32
|
||||||
|
current uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLCG(seed int, m uint32) *LCG {
|
||||||
|
return &LCG{
|
||||||
|
m: m,
|
||||||
|
a: 1664525,
|
||||||
|
c: 1013904223,
|
||||||
|
current: uint32(seed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LCG) Next() uint32 {
|
||||||
|
l.current = (l.a*l.current + l.c) % l.m
|
||||||
|
return l.current
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPRange struct {
|
||||||
|
start uint32
|
||||||
|
total uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIPRange(cidr string) (*IPRange, error) {
|
||||||
|
_, network, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
start := ipToUint32(network.IP)
|
||||||
|
ones, bits := network.Mask.Size()
|
||||||
|
hostBits := uint(bits - ones)
|
||||||
|
broadcast := start | (1<<hostBits - 1)
|
||||||
|
total := broadcast - start + 1
|
||||||
|
|
||||||
|
return &IPRange{
|
||||||
|
start: start,
|
||||||
|
total: uint32(total),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *IPRange) GetIPAtIndex(index uint32) (string, error) {
|
||||||
|
if index >= r.total {
|
||||||
|
return "", errors.New("IP index out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := uint32ToIP(r.start + index)
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipToUint32(ip net.IP) uint32 {
|
||||||
|
ip = ip.To4()
|
||||||
|
return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint32ToIP(n uint32) net.IP {
|
||||||
|
ip := make(net.IP, 4)
|
||||||
|
ip[0] = byte(n >> 24)
|
||||||
|
ip[1] = byte(n >> 16)
|
||||||
|
ip[2] = byte(n >> 8)
|
||||||
|
ip[3] = byte(n)
|
||||||
|
return 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)
|
||||||
|
stateFile := filepath.Join(os.TempDir(), fileName)
|
||||||
|
|
||||||
|
return os.WriteFile(stateFile, []byte(fmt.Sprintf("%d", lcgCurrent)), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IPStream(cidr string, shardNum, totalShards, seed int, state *uint32) (<-chan string, error) {
|
||||||
|
ipRange, err := NewIPRange(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
shardIndex := shardNum - 1
|
||||||
|
|
||||||
|
if seed == 0 {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
seed = rand.Intn(1<<32 - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
lcg := NewLCG(seed+shardIndex, 1<<32-1)
|
||||||
|
if state != nil {
|
||||||
|
lcg.current = *state
|
||||||
|
}
|
||||||
|
|
||||||
|
shardSize := ipRange.total / uint32(totalShards)
|
||||||
|
|
||||||
|
if uint32(shardIndex) < (ipRange.total % uint32(totalShards)) {
|
||||||
|
shardSize++
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(chan string)
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
remaining := shardSize
|
||||||
|
|
||||||
|
for remaining > 0 {
|
||||||
|
index := lcg.Next() % ipRange.total
|
||||||
|
if totalShards == 1 || index%uint32(totalShards) == uint32(shardIndex) {
|
||||||
|
ip, err := ipRange.GetIPAtIndex(index)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out <- ip
|
||||||
|
remaining--
|
||||||
|
|
||||||
|
if remaining%1000 == 0 {
|
||||||
|
SaveState(seed, cidr, shardNum, totalShards, lcg.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out, 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")
|
||||||
|
seed := flag.Int("seed", 0, "Random seed for LCG")
|
||||||
|
stateStr := flag.String("state", "", "Resume from specific LCG state")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *cidr == "" {
|
||||||
|
fmt.Println("Error: CIDR is required")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var state *uint32
|
||||||
|
if *stateStr != "" {
|
||||||
|
stateVal, err := strconv.ParseUint(*stateStr, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error parsing state: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
stateUint32 := uint32(stateVal)
|
||||||
|
state = &stateUint32
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := IPStream(*cidr, *shardNum, *totalShards, *seed, state)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ip := range stream {
|
||||||
|
fmt.Println(ip)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user