ZeroTierOne/go/pkg/zerotier/inetaddress.go
2019-09-23 15:18:52 -07:00

124 lines
2.9 KiB
Go

/*
* Copyright (c)2019 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2023-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
package zerotier
import (
"bytes"
"encoding/json"
"net"
"strconv"
"strings"
"unsafe"
)
// InetAddress implements net.Addr but has a ZeroTier-like string representation
type InetAddress struct {
IP net.IP
Port int
}
// Less returns true if this IP/port is lexicographically less than another
func (i *InetAddress) Less(i2 *InetAddress) bool {
c := bytes.Compare(i.IP, i2.IP)
if c < 0 {
return true
}
if c == 0 {
return i.Port < i2.Port
}
return false
}
// NewInetAddressFromString parses an IP[/port] format address
func NewInetAddressFromString(s string) *InetAddress {
i := new(InetAddress)
ss := strings.Split(s, "/")
if len(ss) > 0 {
i.IP = net.ParseIP(ss[0])
i4 := i.IP.To4()
if len(i4) == 4 { // down-convert IPv4-in-6 IPs to native IPv4 as this is what all our code expects
i.IP = i4
}
if len(ss) > 1 {
p64, _ := strconv.ParseUint(s, 10, 64)
i.Port = int(p64 & 0xffff)
}
}
return i
}
// NewInetAddressFromSockaddr parses a sockaddr_in or sockaddr_in6 C structure (may crash if given something other than these!)
// This is a convenience wrapper around the CGO functions in node.go.
func NewInetAddressFromSockaddr(sa unsafe.Pointer) *InetAddress {
i := new(InetAddress)
if uintptr(sa) != 0 {
ua := sockaddrStorageToUDPAddr2(sa)
if ua != nil {
i.IP = ua.IP
i.Port = ua.Port
}
}
return i
}
// Network returns "udp" to implement net.Addr
func (i *InetAddress) Network() string {
return "udp"
}
// String returns this address in ZeroTier-canonical IP/port format
func (i *InetAddress) String() string {
return i.IP.String() + "/" + strconv.FormatInt(int64(i.Port), 10)
}
// Family returns the address family (AFInet etc.) or 0 if none
func (i *InetAddress) Family() int {
switch len(i.IP) {
case 4:
return AFInet
case 16:
return AFInet6
}
return 0
}
// Valid returns true if both the IP and port have valid values
func (i *InetAddress) Valid() bool {
return (len(i.IP) == 4 || len(i.IP) == 16) && (i.Port > 0 && i.Port < 65536)
}
// MarshalJSON marshals this MAC as a string
func (i *InetAddress) MarshalJSON() ([]byte, error) {
s := i.String()
return json.Marshal(&s)
}
// UnmarshalJSON unmarshals this MAC from a string
func (i *InetAddress) UnmarshalJSON(j []byte) error {
var s string
err := json.Unmarshal(j, &s)
if err != nil {
return err
}
*i = *NewInetAddressFromString(s)
return nil
}
// key returns a short array suitable for use as a map[] key for this IP
func (i *InetAddress) key() (k [3]uint64) {
copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], i.IP)
k[2] = uint64(i.Port)
return
}