2017-04-27 16:20:11 -04:00
|
|
|
package sockaddr
|
|
|
|
|
|
|
|
import (
|
2018-06-29 10:38:44 -04:00
|
|
|
"encoding/json"
|
2017-04-27 16:20:11 -04:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type SockAddrType int
|
|
|
|
type AttrName string
|
|
|
|
|
|
|
|
const (
|
|
|
|
TypeUnknown SockAddrType = 0x0
|
|
|
|
TypeUnix = 0x1
|
|
|
|
TypeIPv4 = 0x2
|
|
|
|
TypeIPv6 = 0x4
|
|
|
|
|
|
|
|
// TypeIP is the union of TypeIPv4 and TypeIPv6
|
|
|
|
TypeIP = 0x6
|
|
|
|
)
|
|
|
|
|
|
|
|
type SockAddr interface {
|
|
|
|
// CmpRFC returns 0 if SockAddr exactly matches one of the matched RFC
|
|
|
|
// networks, -1 if the receiver is contained within the RFC network, or
|
|
|
|
// 1 if the address is not contained within the RFC.
|
|
|
|
CmpRFC(rfcNum uint, sa SockAddr) int
|
|
|
|
|
|
|
|
// Contains returns true if the SockAddr arg is contained within the
|
|
|
|
// receiver
|
|
|
|
Contains(SockAddr) bool
|
|
|
|
|
|
|
|
// Equal allows for the comparison of two SockAddrs
|
|
|
|
Equal(SockAddr) bool
|
|
|
|
|
|
|
|
DialPacketArgs() (string, string)
|
|
|
|
DialStreamArgs() (string, string)
|
|
|
|
ListenPacketArgs() (string, string)
|
|
|
|
ListenStreamArgs() (string, string)
|
|
|
|
|
|
|
|
// String returns the string representation of SockAddr
|
|
|
|
String() string
|
|
|
|
|
|
|
|
// Type returns the SockAddrType
|
|
|
|
Type() SockAddrType
|
|
|
|
}
|
|
|
|
|
|
|
|
// sockAddrAttrMap is a map of the SockAddr type-specific attributes.
|
|
|
|
var sockAddrAttrMap map[AttrName]func(SockAddr) string
|
|
|
|
var sockAddrAttrs []AttrName
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
sockAddrInit()
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new SockAddr from the string. The order in which New()
|
|
|
|
// attempts to construct a SockAddr is: IPv4Addr, IPv6Addr, SockAddrUnix.
|
|
|
|
//
|
|
|
|
// NOTE: New() relies on the heuristic wherein if the path begins with either a
|
|
|
|
// '.' or '/' character before creating a new UnixSock. For UNIX sockets that
|
|
|
|
// are absolute paths or are nested within a sub-directory, this works as
|
|
|
|
// expected, however if the UNIX socket is contained in the current working
|
|
|
|
// directory, this will fail unless the path begins with "./"
|
|
|
|
// (e.g. "./my-local-socket"). Calls directly to NewUnixSock() do not suffer
|
|
|
|
// this limitation. Invalid IP addresses such as "256.0.0.0/-1" will run afoul
|
|
|
|
// of this heuristic and be assumed to be a valid UNIX socket path (which they
|
|
|
|
// are, but it is probably not what you want and you won't realize it until you
|
|
|
|
// stat(2) the file system to discover it doesn't exist).
|
|
|
|
func NewSockAddr(s string) (SockAddr, error) {
|
|
|
|
ipv4Addr, err := NewIPv4Addr(s)
|
|
|
|
if err == nil {
|
|
|
|
return ipv4Addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ipv6Addr, err := NewIPv6Addr(s)
|
|
|
|
if err == nil {
|
|
|
|
return ipv6Addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to make sure the string begins with either a '.' or '/', or
|
|
|
|
// contains a '/'.
|
|
|
|
if len(s) > 1 && (strings.IndexAny(s[0:1], "./") != -1 || strings.IndexByte(s, '/') != -1) {
|
|
|
|
unixSock, err := NewUnixSock(s)
|
|
|
|
if err == nil {
|
|
|
|
return unixSock, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("Unable to convert %q to an IPv4 or IPv6 address, or a UNIX Socket", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToIPAddr returns an IPAddr type or nil if the type conversion fails.
|
|
|
|
func ToIPAddr(sa SockAddr) *IPAddr {
|
|
|
|
ipa, ok := sa.(IPAddr)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &ipa
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToIPv4Addr returns an IPv4Addr type or nil if the type conversion fails.
|
|
|
|
func ToIPv4Addr(sa SockAddr) *IPv4Addr {
|
|
|
|
switch v := sa.(type) {
|
|
|
|
case IPv4Addr:
|
|
|
|
return &v
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToIPv6Addr returns an IPv6Addr type or nil if the type conversion fails.
|
|
|
|
func ToIPv6Addr(sa SockAddr) *IPv6Addr {
|
|
|
|
switch v := sa.(type) {
|
|
|
|
case IPv6Addr:
|
|
|
|
return &v
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToUnixSock returns a UnixSock type or nil if the type conversion fails.
|
|
|
|
func ToUnixSock(sa SockAddr) *UnixSock {
|
|
|
|
switch v := sa.(type) {
|
|
|
|
case UnixSock:
|
|
|
|
return &v
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SockAddrAttr returns a string representation of an attribute for the given
|
|
|
|
// SockAddr.
|
|
|
|
func SockAddrAttr(sa SockAddr, selector AttrName) string {
|
|
|
|
fn, found := sockAddrAttrMap[selector]
|
|
|
|
if !found {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return fn(sa)
|
|
|
|
}
|
|
|
|
|
|
|
|
// String() for SockAddrType returns a string representation of the
|
|
|
|
// SockAddrType (e.g. "IPv4", "IPv6", "UNIX", "IP", or "unknown").
|
|
|
|
func (sat SockAddrType) String() string {
|
|
|
|
switch sat {
|
|
|
|
case TypeIPv4:
|
|
|
|
return "IPv4"
|
|
|
|
case TypeIPv6:
|
|
|
|
return "IPv6"
|
|
|
|
// There is no concrete "IP" type. Leaving here as a reminder.
|
|
|
|
// case TypeIP:
|
|
|
|
// return "IP"
|
|
|
|
case TypeUnix:
|
|
|
|
return "UNIX"
|
|
|
|
default:
|
|
|
|
panic("unsupported type")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sockAddrInit is called once at init()
|
|
|
|
func sockAddrInit() {
|
|
|
|
sockAddrAttrs = []AttrName{
|
|
|
|
"type", // type should be first
|
|
|
|
"string",
|
|
|
|
}
|
|
|
|
|
|
|
|
sockAddrAttrMap = map[AttrName]func(sa SockAddr) string{
|
|
|
|
"string": func(sa SockAddr) string {
|
|
|
|
return sa.String()
|
|
|
|
},
|
|
|
|
"type": func(sa SockAddr) string {
|
|
|
|
return sa.Type().String()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnixSockAttrs returns a list of attributes supported by the UnixSock type
|
|
|
|
func SockAddrAttrs() []AttrName {
|
|
|
|
return sockAddrAttrs
|
|
|
|
}
|
2018-06-29 10:38:44 -04:00
|
|
|
|
|
|
|
// Although this is pretty trivial to do in a program, having the logic here is
|
|
|
|
// useful all around. Note that this marshals into a *string* -- the underlying
|
|
|
|
// string representation of the sockaddr. If you then unmarshal into this type
|
|
|
|
// in Go, all will work as expected, but externally you can take what comes out
|
|
|
|
// and use the string value directly.
|
|
|
|
type SockAddrMarshaler struct {
|
|
|
|
SockAddr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) {
|
|
|
|
return json.Marshal(s.SockAddr.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error {
|
|
|
|
var str string
|
|
|
|
err := json.Unmarshal(in, &str)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sa, err := NewSockAddr(str)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.SockAddr = sa
|
|
|
|
return nil
|
|
|
|
}
|