r4243 removed debug function __toString from IPv6Address (mantis #396)
[racktables] / wwwroot / inc / IPv6.php
CommitLineData
21ee3351
AA
1<?php
2
3class IPv6Address
4{
5 const zero_address = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // 16 bytes
6 protected $words = self::zero_address;
7
8function __construct ($bin_str = self::zero_address)
9{
10 if (strlen ($bin_str) < 16)
11 $bin_str .= substr (self::zero_address, 0, 16 - strlen ($bin_str));
12 elseif (strlen ($bin_str) > 16)
13 $bin_str = substr ($bin_str, 0, 16);
14 $this->words = $bin_str;
15}
16
522b6f90
AA
17// returns 16-byte binary string
18function getBin ()
21ee3351
AA
19{
20 return $this->words;
21}
22
23private static function set_word_value (&$haystack, $nword, $hexvalue)
24{
25 // check that $hexvalue is like /^[0-9a-fA-F]*$/
26 for ($i = 0; $i < strlen ($hexvalue); $i++)
27 {
28 $char = ord ($hexvalue[$i]);
29 if (! ($char >= 0x30 && $char <= 0x39 || $char >= 0x41 && $char <= 0x46 || $char >=0x61 && $char <= 0x66))
30 return FALSE;
31 }
32 $haystack = substr_replace ($haystack, pack ('n', hexdec ($hexvalue)), $nword * 2, 2);
33 return TRUE;
34}
35
36// returns bool - was the object modified or not.
37// return true only if address syntax is completely correct.
38function parse ($str_ipv6)
39{
40 $result = self::zero_address;
41 // remove one of double beginning/tailing colons
42 if (substr ($str_ipv6, 0, 2) == '::')
43 $str_ipv6 = substr ($str_ipv6, 1);
44 elseif (substr ($str_ipv6, -2, 2) == '::')
45 $str_ipv6 = substr ($str_ipv6, 0, strlen ($str_ipv6) - 1);
46
47 $tokens = explode (':', $str_ipv6);
48 if (empty ($tokens))
49 return FALSE;
50
51 $last_token = $tokens[count ($tokens) - 1];
52 $split = explode ('.', $last_token);
53 if (count ($split) == 4)
54 {
55 $hex_tokens = array();
56 $hex_tokens[] = dechex ($split[0] * 256 + $split[1]);
57 $hex_tokens[] = dechex ($split[2] * 256 + $split[3]);
58 array_splice ($tokens, -1, 1, $hex_tokens);
59 }
60 if (count ($tokens) > 8)
61 return FALSE;
62 for ($i = 0; $i < count ($tokens); $i++)
63 {
64 if ($tokens[$i] != '')
65 {
66 if (! self::set_word_value ($result, $i, $tokens[$i]))
67 return FALSE;
68 }
69 else
70 {
71 $k = 8; //index in result string (last word)
72 for ($j = count ($tokens) - 1; $j > $i; $j--) // $j is an index in $tokens for reverse walk
73 if ($tokens[$j] == '')
74 break;
75 elseif (! self::set_word_value ($result, --$k, $tokens[$j]))
76 return FALSE;
77 if ($i != $j)
78 return FALSE; //error, more than 1 '::' range
79 break;
80 }
81 }
82 if (! isset ($k) && count ($tokens) != 8)
83 return FALSE;
84 $this->words = $result;
85 return TRUE;
86}
87
88function format ()
89{
90 // maybe this is IPv6-to-IPv4 address?
91 if (substr ($this->words, 0, 12) == "\0\0\0\0\0\0\0\0\0\0\xff\xff")
92 return '::ffff:' . implode ('.', unpack ('C*', substr ($this->words, 12, 4)));
93
94 $result = array();
95 $hole_index = NULL;
96 $max_hole_index = NULL;
97 $hole_length = 0;
98 $max_hole_length = 0;
99
100 for ($i = 0; $i < 8; $i++)
101 {
102 $value = array_shift (unpack ('n', substr ($this->words, $i * 2, 2)));
103 $result[] = dechex ($value & 0xffff);
104 if ($value != 0)
105 {
106 unset ($hole_index);
107 $hole_length = 0;
108 }
109 else
110 {
111 if (! isset ($hole_index))
112 $hole_index = $i;
113 if (++$hole_length >= $max_hole_length)
114 {
115 $max_hole_index = $hole_index;
116 $max_hole_length = $hole_length;
117 }
118 }
119 }
120 if (isset ($max_hole_index))
121 {
122 array_splice ($result, $max_hole_index, $max_hole_length, array (''));
123 if ($max_hole_index == 0 && $max_hole_length == 8)
124 return '::';
125 elseif ($max_hole_index == 0)
126 return ':' . implode (':', $result);
127 elseif ($max_hole_index + $max_hole_length == 8)
128 return implode (':', $result) . ':';
129 }
130 return implode (':', $result);
131}
132
133// returns new object with applied mask, or NULL if mask is incorrect
134function get_first_subnet_address ($prefix_length)
135{
136 if ($prefix_length < 0 || $prefix_length > 128)
137 return NULL;
138 $result = clone $this;
139 if ($prefix_length == 128)
140 return $result;
141 $char_num = intval ($prefix_length / 8);
142 if (0xff00 != $bitmask = 0xff00 >> ($prefix_length % 8))
143 {
144 $result->words[$char_num] = chr (ord ($result->words[$char_num]) & $bitmask);
145 ++$char_num;
146 }
147 for ($i = $char_num; $i < 16; $i++)
148 $result->words[$i] = "\0";
149 return $result;
150}
151
152// returns new object with applied mask, or NULL if mask is incorrect
153function get_last_subnet_address ($prefix_length)
154{
155 if ($prefix_length < 0 || $prefix_length > 128)
156 return NULL;
157 $result = clone $this;
158 if ($prefix_length == 128)
159 return $result;
160 $char_num = intval ($prefix_length / 8);
161 if (0xff != $bitmask = 0xff >> ($prefix_length % 8))
162 {
163 $result->words[$char_num] = chr (ord ($result->words[$char_num]) | $bitmask);
164 ++$char_num;
165 }
166 for ($i = $char_num; $i < 16; $i++)
167 $result->words[$i] = "\xff";
168 return $result;
169}
170
171// returns new object
172function next ()
173{
174 $result = clone $this;
175 for ($i = 15; $i >= 0; $i--)
176 {
177 if ($result->words[$i] == "\xff")
178 $result->words[$i] = "\0";
179 else
180 {
181 $result->words[$i] = chr (ord ($result->words[$i]) + 1);
182 break;
183 }
184 }
185 return $result;
186}
187
188// returns new object
189function prev ()
190{
191 $result = clone $this;
192 for ($i = 15; $i >= 0; $i--)
193 {
194 if ($result->words[$i] == "\0")
195 $result->words[$i] = "\xff";
196 else
197 {
198 $result->words[$i] = chr (ord ($result->words[$i]) - 1);
199 break;
200 }
201 }
202 return $result;
203}
204
205
206} // class IPv6Address
207
208?>