Source file
src/net/ip.go
1
2
3
4
5
6
7
8
9
10
11
12
13 package net
14
15 import (
16 "internal/bytealg"
17 "internal/strconv"
18 "internal/stringslite"
19 "net/netip"
20 )
21
22
23 const (
24 IPv4len = 4
25 IPv6len = 16
26 )
27
28
29
30
31
32
33
34
35
36
37 type IP []byte
38
39
40
41
42
43 type IPMask []byte
44
45
46 type IPNet struct {
47 IP IP
48 Mask IPMask
49 }
50
51
52
53 func IPv4(a, b, c, d byte) IP {
54 p := make(IP, IPv6len)
55 copy(p, v4InV6Prefix)
56 p[12] = a
57 p[13] = b
58 p[14] = c
59 p[15] = d
60 return p
61 }
62
63 var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
64
65
66
67 func IPv4Mask(a, b, c, d byte) IPMask {
68 p := make(IPMask, IPv4len)
69 p[0] = a
70 p[1] = b
71 p[2] = c
72 p[3] = d
73 return p
74 }
75
76
77
78
79 func CIDRMask(ones, bits int) IPMask {
80 if bits != 8*IPv4len && bits != 8*IPv6len {
81 return nil
82 }
83 if ones < 0 || ones > bits {
84 return nil
85 }
86 l := bits / 8
87 m := make(IPMask, l)
88 n := uint(ones)
89 for i := 0; i < l; i++ {
90 if n >= 8 {
91 m[i] = 0xff
92 n -= 8
93 continue
94 }
95 m[i] = ^byte(0xff >> n)
96 n = 0
97 }
98 return m
99 }
100
101
102 var (
103 IPv4bcast = IPv4(255, 255, 255, 255)
104 IPv4allsys = IPv4(224, 0, 0, 1)
105 IPv4allrouter = IPv4(224, 0, 0, 2)
106 IPv4zero = IPv4(0, 0, 0, 0)
107 )
108
109
110 var (
111 IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
112 IPv6unspecified = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
113 IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
114 IPv6interfacelocalallnodes = IP{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
115 IPv6linklocalallnodes = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
116 IPv6linklocalallrouters = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}
117 )
118
119
120
121 func (ip IP) IsUnspecified() bool {
122 return ip.Equal(IPv4zero) || ip.Equal(IPv6unspecified)
123 }
124
125
126 func (ip IP) IsLoopback() bool {
127 if ip4 := ip.To4(); ip4 != nil {
128 return ip4[0] == 127
129 }
130 return ip.Equal(IPv6loopback)
131 }
132
133
134
135
136
137
138
139 func (ip IP) IsPrivate() bool {
140 if ip4 := ip.To4(); ip4 != nil {
141
142
143
144
145
146
147 return ip4[0] == 10 ||
148 (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
149 (ip4[0] == 192 && ip4[1] == 168)
150 }
151
152
153 return len(ip) == IPv6len && ip[0]&0xfe == 0xfc
154 }
155
156
157 func (ip IP) IsMulticast() bool {
158 if ip4 := ip.To4(); ip4 != nil {
159 return ip4[0]&0xf0 == 0xe0
160 }
161 return len(ip) == IPv6len && ip[0] == 0xff
162 }
163
164
165
166 func (ip IP) IsInterfaceLocalMulticast() bool {
167 return len(ip) == IPv6len && ip[0] == 0xff && ip[1]&0x0f == 0x01
168 }
169
170
171
172 func (ip IP) IsLinkLocalMulticast() bool {
173 if ip4 := ip.To4(); ip4 != nil {
174 return ip4[0] == 224 && ip4[1] == 0 && ip4[2] == 0
175 }
176 return len(ip) == IPv6len && ip[0] == 0xff && ip[1]&0x0f == 0x02
177 }
178
179
180
181 func (ip IP) IsLinkLocalUnicast() bool {
182 if ip4 := ip.To4(); ip4 != nil {
183 return ip4[0] == 169 && ip4[1] == 254
184 }
185 return len(ip) == IPv6len && ip[0] == 0xfe && ip[1]&0xc0 == 0x80
186 }
187
188
189
190
191
192
193
194
195
196 func (ip IP) IsGlobalUnicast() bool {
197 return (len(ip) == IPv4len || len(ip) == IPv6len) &&
198 !ip.Equal(IPv4bcast) &&
199 !ip.IsUnspecified() &&
200 !ip.IsLoopback() &&
201 !ip.IsMulticast() &&
202 !ip.IsLinkLocalUnicast()
203 }
204
205
206 func isZeros(p IP) bool {
207 for i := 0; i < len(p); i++ {
208 if p[i] != 0 {
209 return false
210 }
211 }
212 return true
213 }
214
215
216
217 func (ip IP) To4() IP {
218 if len(ip) == IPv4len {
219 return ip
220 }
221 if len(ip) == IPv6len &&
222 isZeros(ip[0:10]) &&
223 ip[10] == 0xff &&
224 ip[11] == 0xff {
225 return ip[12:16]
226 }
227 return nil
228 }
229
230
231
232 func (ip IP) To16() IP {
233 if len(ip) == IPv4len {
234 return IPv4(ip[0], ip[1], ip[2], ip[3])
235 }
236 if len(ip) == IPv6len {
237 return ip
238 }
239 return nil
240 }
241
242
243 var (
244 classAMask = IPv4Mask(0xff, 0, 0, 0)
245 classBMask = IPv4Mask(0xff, 0xff, 0, 0)
246 classCMask = IPv4Mask(0xff, 0xff, 0xff, 0)
247 )
248
249
250
251
252 func (ip IP) DefaultMask() IPMask {
253 if ip = ip.To4(); ip == nil {
254 return nil
255 }
256 switch {
257 case ip[0] < 0x80:
258 return classAMask
259 case ip[0] < 0xC0:
260 return classBMask
261 default:
262 return classCMask
263 }
264 }
265
266 func allFF(b []byte) bool {
267 for _, c := range b {
268 if c != 0xff {
269 return false
270 }
271 }
272 return true
273 }
274
275
276 func (ip IP) Mask(mask IPMask) IP {
277 if len(mask) == IPv6len && len(ip) == IPv4len && allFF(mask[:12]) {
278 mask = mask[12:]
279 }
280 if len(mask) == IPv4len && len(ip) == IPv6len && bytealg.Equal(ip[:12], v4InV6Prefix) {
281 ip = ip[12:]
282 }
283 n := len(ip)
284 if n != len(mask) {
285 return nil
286 }
287 out := make(IP, n)
288 for i := 0; i < n; i++ {
289 out[i] = ip[i] & mask[i]
290 }
291 return out
292 }
293
294
295
296
297
298
299
300 func (ip IP) String() string {
301 if len(ip) == 0 {
302 return "<nil>"
303 }
304
305 if len(ip) != IPv4len && len(ip) != IPv6len {
306 return "?" + hexString(ip)
307 }
308
309 var buf []byte
310 switch len(ip) {
311 case IPv4len:
312 const maxCap = len("255.255.255.255")
313 buf = make([]byte, 0, maxCap)
314 case IPv6len:
315 const maxCap = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
316 buf = make([]byte, 0, maxCap)
317 }
318 buf = ip.appendTo(buf)
319 return string(buf)
320 }
321
322 func hexString(b []byte) string {
323 s := make([]byte, len(b)*2)
324 for i, tn := range b {
325 s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf]
326 }
327 return string(s)
328 }
329
330
331
332 func ipEmptyString(ip IP) string {
333 if len(ip) == 0 {
334 return ""
335 }
336 return ip.String()
337 }
338
339
340
341 func (ip IP) appendTo(b []byte) []byte {
342
343 if p4 := ip.To4(); len(p4) == IPv4len {
344 ip = p4
345 }
346 addr, _ := netip.AddrFromSlice(ip)
347 return addr.AppendTo(b)
348 }
349
350
351
352
353 func (ip IP) AppendText(b []byte) ([]byte, error) {
354 if len(ip) == 0 {
355 return b, nil
356 }
357 if len(ip) != IPv4len && len(ip) != IPv6len {
358 return b, &AddrError{Err: "invalid IP address", Addr: hexString(ip)}
359 }
360
361 return ip.appendTo(b), nil
362 }
363
364
365
366
367 func (ip IP) MarshalText() ([]byte, error) {
368
369 b, err := ip.AppendText(make([]byte, 0, 24))
370 if err != nil {
371 return nil, err
372 }
373 return b, nil
374 }
375
376
377
378 func (ip *IP) UnmarshalText(text []byte) error {
379 if len(text) == 0 {
380 *ip = nil
381 return nil
382 }
383 s := string(text)
384 x := ParseIP(s)
385 if x == nil {
386 return &ParseError{Type: "IP address", Text: s}
387 }
388 *ip = x
389 return nil
390 }
391
392
393
394
395 func (ip IP) Equal(x IP) bool {
396 if len(ip) == len(x) {
397 return bytealg.Equal(ip, x)
398 }
399 if len(ip) == IPv4len && len(x) == IPv6len {
400 return bytealg.Equal(x[0:12], v4InV6Prefix) && bytealg.Equal(ip, x[12:])
401 }
402 if len(ip) == IPv6len && len(x) == IPv4len {
403 return bytealg.Equal(ip[0:12], v4InV6Prefix) && bytealg.Equal(ip[12:], x)
404 }
405 return false
406 }
407
408 func (ip IP) matchAddrFamily(x IP) bool {
409 return ip.To4() != nil && x.To4() != nil || ip.To16() != nil && ip.To4() == nil && x.To16() != nil && x.To4() == nil
410 }
411
412
413
414 func simpleMaskLength(mask IPMask) int {
415 var n int
416 for i, v := range mask {
417 if v == 0xff {
418 n += 8
419 continue
420 }
421
422
423 for v&0x80 != 0 {
424 n++
425 v <<= 1
426 }
427
428 if v != 0 {
429 return -1
430 }
431 for i++; i < len(mask); i++ {
432 if mask[i] != 0 {
433 return -1
434 }
435 }
436 break
437 }
438 return n
439 }
440
441
442
443
444 func (m IPMask) Size() (ones, bits int) {
445 ones, bits = simpleMaskLength(m), len(m)*8
446 if ones == -1 {
447 return 0, 0
448 }
449 return
450 }
451
452
453 func (m IPMask) String() string {
454 if len(m) == 0 {
455 return "<nil>"
456 }
457 return hexString(m)
458 }
459
460 func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
461 if ip = n.IP.To4(); ip == nil {
462 ip = n.IP
463 if len(ip) != IPv6len {
464 return nil, nil
465 }
466 }
467 m = n.Mask
468 switch len(m) {
469 case IPv4len:
470 if len(ip) != IPv4len {
471 return nil, nil
472 }
473 case IPv6len:
474 if len(ip) == IPv4len {
475 m = m[12:]
476 }
477 default:
478 return nil, nil
479 }
480 return
481 }
482
483
484 func (n *IPNet) Contains(ip IP) bool {
485 nn, m := networkNumberAndMask(n)
486 if x := ip.To4(); x != nil {
487 ip = x
488 }
489 l := len(ip)
490 if l != len(nn) {
491 return false
492 }
493 for i := 0; i < l; i++ {
494 if nn[i]&m[i] != ip[i]&m[i] {
495 return false
496 }
497 }
498 return true
499 }
500
501
502 func (n *IPNet) Network() string { return "ip+net" }
503
504
505
506
507
508
509
510 func (n *IPNet) String() string {
511 if n == nil {
512 return "<nil>"
513 }
514 nn, m := networkNumberAndMask(n)
515 if nn == nil || m == nil {
516 return "<nil>"
517 }
518 l := simpleMaskLength(m)
519 if l == -1 {
520 return nn.String() + "/" + m.String()
521 }
522 return nn.String() + "/" + strconv.Itoa(l)
523 }
524
525
526
527
528
529
530
531 func ParseIP(s string) IP {
532 if addr, valid := parseIP(s); valid {
533 return IP(addr[:])
534 }
535 return nil
536 }
537
538 func parseIP(s string) ([16]byte, bool) {
539 ip, err := netip.ParseAddr(s)
540 if err != nil || ip.Zone() != "" {
541 return [16]byte{}, false
542 }
543 return ip.As16(), true
544 }
545
546
547
548
549
550
551
552
553
554 func ParseCIDR(s string) (IP, *IPNet, error) {
555 addr, mask, found := stringslite.Cut(s, "/")
556 if !found {
557 return nil, nil, &ParseError{Type: "CIDR address", Text: s}
558 }
559
560 ipAddr, err := netip.ParseAddr(addr)
561 if err != nil || ipAddr.Zone() != "" {
562 return nil, nil, &ParseError{Type: "CIDR address", Text: s}
563 }
564
565 n, i, ok := dtoi(mask)
566 if !ok || i != len(mask) || n < 0 || n > ipAddr.BitLen() {
567 return nil, nil, &ParseError{Type: "CIDR address", Text: s}
568 }
569 m := CIDRMask(n, ipAddr.BitLen())
570 addr16 := ipAddr.As16()
571 return IP(addr16[:]), &IPNet{IP: IP(addr16[:]).Mask(m), Mask: m}, nil
572 }
573
574 func copyIP(x IP) IP {
575 y := make(IP, len(x))
576 copy(y, x)
577 return y
578 }
579
View as plain text