2024-11-26 03:28:06 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Python implementation of a Linear Congruential Generator for IP Sharding - Developed by acidvegas in Python (https://git.acid.vegas/pylcg)
|
|
|
|
# pylcg.py
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import ipaddress
|
2024-11-26 05:09:16 +00:00
|
|
|
import random
|
2024-11-26 03:28:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LCG:
|
|
|
|
'''Linear Congruential Generator for deterministic random number generation'''
|
|
|
|
|
|
|
|
def __init__(self, seed: int, m: int = 2**32):
|
|
|
|
self.m = m
|
2024-11-26 05:09:16 +00:00
|
|
|
self.a = 1664525
|
|
|
|
self.c = 1013904223
|
2024-11-26 03:28:06 +00:00
|
|
|
self.current = seed
|
|
|
|
|
|
|
|
|
|
|
|
def next(self) -> int:
|
|
|
|
'''Generate next random number'''
|
|
|
|
|
|
|
|
self.current = (self.a * self.current + self.c) % self.m
|
|
|
|
|
|
|
|
return self.current
|
|
|
|
|
|
|
|
|
|
|
|
class IPRange:
|
|
|
|
'''Memory-efficient IP range iterator'''
|
|
|
|
|
|
|
|
def __init__(self, cidr: str):
|
|
|
|
network = ipaddress.ip_network(cidr)
|
|
|
|
self.start = int(network.network_address)
|
2024-11-26 05:09:16 +00:00
|
|
|
self.total = int(network.broadcast_address) - self.start + 1
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
|
2024-11-26 03:28:06 +00:00
|
|
|
def get_ip_at_index(self, index: int) -> str:
|
|
|
|
'''
|
|
|
|
Get IP at specific index without generating previous IPs
|
|
|
|
|
|
|
|
:param index: The index of the IP to get
|
|
|
|
'''
|
|
|
|
|
|
|
|
if not 0 <= index < self.total:
|
|
|
|
raise IndexError('IP index out of range')
|
|
|
|
|
|
|
|
return str(ipaddress.ip_address(self.start + index))
|
|
|
|
|
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
def ip_stream(cidr: str, shard_num: int = 1, total_shards: int = 1, seed: int = 0):
|
2024-11-26 03:28:06 +00:00
|
|
|
'''
|
2024-11-26 05:09:16 +00:00
|
|
|
Stream random IPs from the CIDR range. Optionally supports sharding.
|
|
|
|
Each IP in the range will be yielded exactly once in a pseudo-random order.
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
:param cidr: Target IP range in CIDR format
|
|
|
|
:param shard_num: Shard number (1-based), defaults to 1
|
|
|
|
:param total_shards: Total number of shards, defaults to 1 (no sharding)
|
|
|
|
:param seed: Random seed for LCG (default: random)
|
2024-11-26 03:28:06 +00:00
|
|
|
'''
|
2024-11-26 05:09:16 +00:00
|
|
|
# Convert to 0-based indexing internally
|
|
|
|
shard_index = shard_num - 1
|
|
|
|
|
|
|
|
# Initialize IP range and LCG
|
|
|
|
ip_range = IPRange(cidr)
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
# Use random seed if none provided
|
|
|
|
if not seed:
|
|
|
|
seed = random.randint(0, 2**32-1)
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
# Initialize LCG
|
|
|
|
lcg = LCG(seed + shard_index)
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
# Calculate how many IPs this shard should generate
|
|
|
|
shard_size = ip_range.total // total_shards
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
# Distribute remainder
|
|
|
|
if shard_index < (ip_range.total % total_shards):
|
|
|
|
shard_size += 1
|
|
|
|
|
|
|
|
# Remaining IPs to yield
|
|
|
|
remaining = shard_size
|
|
|
|
|
|
|
|
while remaining > 0:
|
|
|
|
index = lcg.next() % ip_range.total
|
|
|
|
if total_shards == 1 or index % total_shards == shard_index:
|
|
|
|
yield ip_range.get_ip_at_index(index)
|
|
|
|
remaining -= 1
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(description='Ultra-fast random IP address generator with optional sharding')
|
2024-11-26 03:28:06 +00:00
|
|
|
parser.add_argument('cidr', help='Target IP range in CIDR format')
|
2024-11-26 05:09:16 +00:00
|
|
|
parser.add_argument('--shard-num', type=int, default=1, help='Shard number (1-based)')
|
|
|
|
parser.add_argument('--total-shards', type=int, default=1, help='Total number of shards (default: 1, no sharding)')
|
|
|
|
parser.add_argument('--seed', type=int, default=0, help='Random seed for LCG')
|
2024-11-26 03:28:06 +00:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
if args.total_shards < 1:
|
|
|
|
raise ValueError('Total shards must be at least 1')
|
|
|
|
|
|
|
|
if args.shard_num > args.total_shards:
|
|
|
|
raise ValueError('Shard number must be less than or equal to total shards')
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
if args.shard_num < 1:
|
|
|
|
raise ValueError('Shard number must be at least 1')
|
2024-11-26 03:28:06 +00:00
|
|
|
|
2024-11-26 05:09:16 +00:00
|
|
|
for ip in ip_stream(args.cidr, args.shard_num, args.total_shards, args.seed):
|
2024-11-26 03:28:06 +00:00
|
|
|
print(ip)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2024-11-26 05:09:16 +00:00
|
|
|
main()
|