1 /// Some helper functions for using CIDR format network ranges.
2 module arsd.cidr;
3 
4 ///
5 uint addressToUint(string address) {
6 	import std.algorithm.iteration, std.conv;
7 
8 	uint result;
9 	int place = 3;
10 	foreach(part; splitter(address, ".")) {
11 		assert(place >= 0);
12 		result |= to!int(part) << (place * 8);
13 		place--;
14 	}
15 
16 	return result;
17 }
18 
19 ///
20 string uintToAddress(uint addr) {
21 	import std.conv;
22 	string res;
23 	res ~= to!string(addr >> 24);
24 	res ~= ".";
25 	res ~= to!string((addr >> 16) & 0xff);
26 	res ~= ".";
27 	res ~= to!string((addr >> 8) & 0xff);
28 	res ~= ".";
29 	res ~= to!string((addr >> 0) & 0xff);
30 
31 	return res;
32 }
33 
34 ///
35 struct IPv4Block {
36 	this(string cidr) {
37 		import std.algorithm.searching, std.conv;
38 		auto parts = findSplit(cidr, "/");
39 		this.currentAddress = addressToUint(parts[0]);
40 		auto count = to!int(parts[2]);
41 
42 		if(count != 0) {
43 			this.netmask = ((1L << count) - 1) & 0xffffffff;
44 			this.netmask <<= 32-count;
45 		}
46 
47 		this.startingAddress = this.currentAddress & this.netmask;
48 
49 		validate();
50 
51 		restart();
52 	}
53 
54 	this(string address, string netmask) {
55 		this.currentAddress = addressToUint(address);
56 		this.netmask = addressToUint(netmask);
57 		this.startingAddress = this.currentAddress & this.netmask;
58 
59 		validate();
60 
61 		restart();
62 	}
63 
64 	void validate() {
65 		if(!isValid())
66 			throw new Exception("invalid");
67 	}
68 
69 	bool isValid() {
70 		return (startingAddress & netmask) == (currentAddress & netmask);
71 	}
72 
73 	void restart() {
74 		remaining = ~this.netmask - (currentAddress - startingAddress);
75 	}
76 
77 	@property string front() {
78 		return uintToAddress(currentAddress);
79 	}
80 
81 	@property bool empty() {
82 		return remaining < 0;
83 	}
84 
85 	void popFront() {
86 		currentAddress++;
87 		remaining--;
88 	}
89 
90 	string toString() {
91 		import std.conv;
92 		return uintToAddress(startingAddress) ~ "/" ~ to!string(maskBits);
93 	}
94 
95 	int maskBits() {
96 		import core.bitop;
97 		if(netmask == 0)
98 			return 0;
99 		return 32-bsf(netmask);
100 	}
101 
102 	int numberOfAddresses() {
103 		return ~netmask + 1;
104 	}
105 
106 	uint startingAddress;
107 	uint netmask;
108 
109 	uint currentAddress;
110 	int remaining;
111 }
112 
113 version(none)
114 void main() {
115 	// make one with cidr or address + mask notation
116 
117 	// auto i = IPv4Block("192.168.1.0", "255.255.255.0");
118 	auto i = IPv4Block("192.168.1.50/29");
119 
120 	// loop over all addresses in the block
121 	import std.stdio;
122 	foreach(addr; i)
123 		writeln(addr);
124 
125 	// show info about the block too
126 	writefln("%s netmask %s", uintToAddress(i.startingAddress), uintToAddress(i.netmask));
127 	writeln(i);
128 	writeln(i.numberOfAddresses, " addresses in block");
129 }