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  }