github.com/iDigitalFlame/xmt@v0.5.4/device/address.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package device 18 19 import ( 20 "net" 21 22 "github.com/iDigitalFlame/xmt/data" 23 "github.com/iDigitalFlame/xmt/util" 24 ) 25 26 // Address represents an encoded IPv4 or IPv6 address. 27 // 28 // The address struct was built on the great work from the great inet.af/netaddr 29 // package thanks and great work y'all! 30 // 31 // GoDoc: https://pkg.go.dev/inet.af/netaddr 32 // 33 // https://tailscale.com/blog/netaddr-new-ip-type-for-go/ 34 type Address struct { 35 hi, low uint64 36 } 37 38 // Len returns the size of this IP address. It returns '32' for IPv4 and '128' 39 // for IPv6. 40 func (a Address) Len() int { 41 if a.Is4() { 42 return 32 43 } 44 return 128 45 } 46 47 // Is4 returns true if this struct represents an IPv4 based address or an IPv4 48 // address wrapped in an IPv6 address. 49 func (a Address) Is4() bool { 50 return a.hi == 0 && a.low>>32 == 0xFFFF 51 } 52 53 // Is6 returns true if this struct represents an IPv6 based address. 54 func (a Address) Is6() bool { 55 return a.low > 0 && a.low>>32 != 0xFFFF 56 } 57 58 // IP returns a 'net.IP' copy of this address. 59 // 60 // This may be zero or empty depending on the type of address value this struct 61 // contains. 62 func (a Address) IP() net.IP { 63 if a.Is4() { 64 return net.IP{byte(a.low >> 24), byte(a.low >> 16), byte(a.low >> 8), byte(a.low)} 65 } 66 return net.IP{ 67 byte(a.hi >> 56), byte(a.hi >> 48), byte(a.hi >> 40), byte(a.hi >> 32), 68 byte(a.hi >> 24), byte(a.hi >> 16), byte(a.hi >> 8), byte(a.hi), 69 byte(a.low >> 56), byte(a.low >> 48), byte(a.low >> 40), byte(a.low >> 32), 70 byte(a.low >> 24), byte(a.low >> 16), byte(a.low >> 8), byte(a.low), 71 } 72 } 73 74 // Set will set the internal values of this address to the specified 'net.IP' 75 // address. 76 func (a *Address) Set(i net.IP) { 77 if len(i) == 0 { 78 return 79 } 80 if len(i) == 4 { 81 _ = i[3] 82 a.hi, a.low = 0, 0xFFFF00000000|uint64(i[0])<<24|uint64(i[1])<<16|uint64(i[2])<<8|uint64(i[3]) 83 return 84 } 85 _ = i[15] 86 a.hi = uint64(i[7]) | uint64(i[6])<<8 | uint64(i[5])<<16 | uint64(i[4])<<24 | 87 uint64(i[3])<<32 | uint64(i[2])<<40 | uint64(i[1])<<48 | uint64(i[0])<<56 88 a.low = uint64(i[15]) | uint64(i[14])<<8 | uint64(i[13])<<16 | uint64(i[12])<<24 | 89 uint64(i[11])<<32 | uint64(i[10])<<40 | uint64(i[9])<<48 | uint64(i[8])<<56 90 } 91 92 // String returns the string form of the IP address. 93 func (a Address) String() string { 94 if a.IsUnspecified() { 95 if a.Is4() { 96 return emptyIP 97 } 98 return "::" 99 } 100 var n int 101 if a.Is4() { 102 var b [15]byte 103 n = write(&b, uint8(a.low>>24), n) 104 b[n] = '.' 105 n++ 106 n = write(&b, uint8(a.low>>16), n) 107 b[n] = '.' 108 n++ 109 n = write(&b, uint8(a.low>>8), n) 110 b[n] = '.' 111 n++ 112 n = write(&b, uint8(a.low), n) 113 return string(b[:n]) 114 } 115 var ( 116 b [39]byte 117 s, e, i uint8 = 255, 255, 0 118 ) 119 for ; i < 8; i++ { 120 j := i 121 for j < 8 && a.grab(j) == 0 { 122 j++ 123 } 124 if l := j - i; l >= 2 && l > e-s { 125 s, e = i, j 126 } 127 } 128 for i = 0; i < 8; i++ { 129 if i == s { 130 b[n] = ':' 131 b[n+1] = ':' 132 n += 2 133 if i = e; i >= 8 { 134 break 135 } 136 } else if i > 0 { 137 b[n] = ':' 138 n++ 139 } 140 n = writeHex(&b, a.grab(i), n) 141 } 142 return string(b[:n]) 143 } 144 145 // IsLoopback reports whether this is a loopback address. 146 func (a Address) IsLoopback() bool { 147 return (a.Is4() && uint8(a.low>>24) == 0x7F) || (a.Is6() && a.hi == 0 && a.low == 1) 148 } 149 150 // IsMulticast reports whether this is a multicast address. 151 func (a Address) IsMulticast() bool { 152 return (a.Is4() && uint8(a.low>>24) == 0xFE) || (a.Is6() && uint8(a.hi>>56) == 0xFF) 153 } 154 155 // IsBroadcast reports whether this is a broadcast address. 156 func (a Address) IsBroadcast() bool { 157 return a.Is4() && a.low == 0xFFFFFFFFFFFF 158 } 159 func (a Address) grab(i uint8) uint16 { 160 r := a.hi 161 if (i/4)%2 == 1 { 162 r = a.low 163 } 164 return uint16(r >> ((3 - i%4) * 16)) 165 } 166 167 // IsUnspecified reports whether ip is an unspecified address, either the IPv4 168 // address "0.0.0.0" or the IPv6 address "::". 169 func (a Address) IsUnspecified() bool { 170 return a.hi == 0 && a.low == 0 171 } 172 173 // SetBytes will set the internal values of this address to the specified bytes 174 // contained in the byte array. 175 // 176 // This function will attempt to detect zeros to determin if this is just a 177 // shortened IPv4 or IPv6 address. 178 func (a *Address) SetBytes(b [16]byte) { 179 x := false 180 for i := 4; i < 16; i++ { 181 if b[i] > 0 { 182 x = true 183 } 184 } 185 if !x { 186 a.hi, a.low = 0, 0xFFFF00000000|uint64(b[0])<<24|uint64(b[1])<<16|uint64(b[2])<<8|uint64(b[3]) 187 return 188 } 189 a.hi = uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 190 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 191 a.low = uint64(b[15]) | uint64(b[14])<<8 | uint64(b[13])<<16 | uint64(b[12])<<24 | 192 uint64(b[11])<<32 | uint64(b[10])<<40 | uint64(b[9])<<48 | uint64(b[8])<<56 193 } 194 195 // IsGlobalUnicast reports whether this is a global unicast address. 196 // 197 // The identification of global unicast addresses uses address type identification 198 // as defined in RFC 1122, RFC 4632 and RFC 4291 with the exception of IPv4 199 // directed broadcast addresses. 200 // 201 // It returns true even if this is in IPv4 private address space or local IPv6 202 // unicast address space. 203 func (a Address) IsGlobalUnicast() bool { 204 return !a.IsUnspecified() && !a.IsBroadcast() && !a.IsLoopback() && !a.IsMulticast() && !a.IsLinkLocalUnicast() 205 } 206 207 // IsLinkLocalUnicast reports whether this is a link-local unicast address. 208 func (a Address) IsLinkLocalUnicast() bool { 209 return (a.Is4() && uint32(a.low>>16) == 0xFFFFA9FE) || (a.Is6() && uint16(a.hi>>48) == 0xFE80) 210 } 211 func write(b *[15]byte, v uint8, n int) int { 212 if v >= 100 { 213 b[n] = util.HexTable[v/100] 214 n++ 215 } 216 if v >= 10 { 217 b[n] = util.HexTable[v/10%10] 218 n++ 219 } 220 b[n] = util.HexTable[v%10] 221 return n + 1 222 } 223 224 // IsLinkLocalMulticast reports whether this is a link-local multicast address. 225 func (a Address) IsLinkLocalMulticast() bool { 226 return (a.Is4() && a.low>>8 == 0xFFFFE00000) || (a.Is6() && uint16(a.hi>>48) == 0xFF02) 227 } 228 func writeHex(b *[39]byte, v uint16, n int) int { 229 if v >= 0x1000 { 230 b[n] = util.HexTable[v>>12] 231 n++ 232 } 233 if v >= 0x100 { 234 b[n] = util.HexTable[v>>8&0xF] 235 n++ 236 } 237 if v >= 0x10 { 238 b[n] = util.HexTable[v>>4&0xF] 239 n++ 240 } 241 b[n] = util.HexTable[v&0xF] 242 return n + 1 243 } 244 245 // MarshalStream writes the data of this Address to the supplied Writer. 246 func (a Address) MarshalStream(w data.Writer) error { 247 if err := w.WriteUint64(a.hi); err != nil { 248 return err 249 } 250 return w.WriteUint64(a.low) 251 } 252 253 // UnmarshalStream reads the data of this Address from the supplied Reader. 254 func (a *Address) UnmarshalStream(r data.Reader) error { 255 if err := r.ReadUint64(&a.hi); err != nil { 256 return err 257 } 258 return r.ReadUint64(&a.low) 259 }