1package sockaddr 2 3import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7) 8 9type SockAddrType int 10type AttrName string 11 12const ( 13 TypeUnknown SockAddrType = 0x0 14 TypeUnix = 0x1 15 TypeIPv4 = 0x2 16 TypeIPv6 = 0x4 17 18 // TypeIP is the union of TypeIPv4 and TypeIPv6 19 TypeIP = 0x6 20) 21 22type SockAddr interface { 23 // CmpRFC returns 0 if SockAddr exactly matches one of the matched RFC 24 // networks, -1 if the receiver is contained within the RFC network, or 25 // 1 if the address is not contained within the RFC. 26 CmpRFC(rfcNum uint, sa SockAddr) int 27 28 // Contains returns true if the SockAddr arg is contained within the 29 // receiver 30 Contains(SockAddr) bool 31 32 // Equal allows for the comparison of two SockAddrs 33 Equal(SockAddr) bool 34 35 DialPacketArgs() (string, string) 36 DialStreamArgs() (string, string) 37 ListenPacketArgs() (string, string) 38 ListenStreamArgs() (string, string) 39 40 // String returns the string representation of SockAddr 41 String() string 42 43 // Type returns the SockAddrType 44 Type() SockAddrType 45} 46 47// sockAddrAttrMap is a map of the SockAddr type-specific attributes. 48var sockAddrAttrMap map[AttrName]func(SockAddr) string 49var sockAddrAttrs []AttrName 50 51func init() { 52 sockAddrInit() 53} 54 55// New creates a new SockAddr from the string. The order in which New() 56// attempts to construct a SockAddr is: IPv4Addr, IPv6Addr, SockAddrUnix. 57// 58// NOTE: New() relies on the heuristic wherein if the path begins with either a 59// '.' or '/' character before creating a new UnixSock. For UNIX sockets that 60// are absolute paths or are nested within a sub-directory, this works as 61// expected, however if the UNIX socket is contained in the current working 62// directory, this will fail unless the path begins with "./" 63// (e.g. "./my-local-socket"). Calls directly to NewUnixSock() do not suffer 64// this limitation. Invalid IP addresses such as "256.0.0.0/-1" will run afoul 65// of this heuristic and be assumed to be a valid UNIX socket path (which they 66// are, but it is probably not what you want and you won't realize it until you 67// stat(2) the file system to discover it doesn't exist). 68func NewSockAddr(s string) (SockAddr, error) { 69 ipv4Addr, err := NewIPv4Addr(s) 70 if err == nil { 71 return ipv4Addr, nil 72 } 73 74 ipv6Addr, err := NewIPv6Addr(s) 75 if err == nil { 76 return ipv6Addr, nil 77 } 78 79 // Check to make sure the string begins with either a '.' or '/', or 80 // contains a '/'. 81 if len(s) > 1 && (strings.IndexAny(s[0:1], "./") != -1 || strings.IndexByte(s, '/') != -1) { 82 unixSock, err := NewUnixSock(s) 83 if err == nil { 84 return unixSock, nil 85 } 86 } 87 88 return nil, fmt.Errorf("Unable to convert %q to an IPv4 or IPv6 address, or a UNIX Socket", s) 89} 90 91// ToIPAddr returns an IPAddr type or nil if the type conversion fails. 92func ToIPAddr(sa SockAddr) *IPAddr { 93 ipa, ok := sa.(IPAddr) 94 if !ok { 95 return nil 96 } 97 return &ipa 98} 99 100// ToIPv4Addr returns an IPv4Addr type or nil if the type conversion fails. 101func ToIPv4Addr(sa SockAddr) *IPv4Addr { 102 switch v := sa.(type) { 103 case IPv4Addr: 104 return &v 105 default: 106 return nil 107 } 108} 109 110// ToIPv6Addr returns an IPv6Addr type or nil if the type conversion fails. 111func ToIPv6Addr(sa SockAddr) *IPv6Addr { 112 switch v := sa.(type) { 113 case IPv6Addr: 114 return &v 115 default: 116 return nil 117 } 118} 119 120// ToUnixSock returns a UnixSock type or nil if the type conversion fails. 121func ToUnixSock(sa SockAddr) *UnixSock { 122 switch v := sa.(type) { 123 case UnixSock: 124 return &v 125 default: 126 return nil 127 } 128} 129 130// SockAddrAttr returns a string representation of an attribute for the given 131// SockAddr. 132func SockAddrAttr(sa SockAddr, selector AttrName) string { 133 fn, found := sockAddrAttrMap[selector] 134 if !found { 135 return "" 136 } 137 138 return fn(sa) 139} 140 141// String() for SockAddrType returns a string representation of the 142// SockAddrType (e.g. "IPv4", "IPv6", "UNIX", "IP", or "unknown"). 143func (sat SockAddrType) String() string { 144 switch sat { 145 case TypeIPv4: 146 return "IPv4" 147 case TypeIPv6: 148 return "IPv6" 149 // There is no concrete "IP" type. Leaving here as a reminder. 150 // case TypeIP: 151 // return "IP" 152 case TypeUnix: 153 return "UNIX" 154 default: 155 panic("unsupported type") 156 } 157} 158 159// sockAddrInit is called once at init() 160func sockAddrInit() { 161 sockAddrAttrs = []AttrName{ 162 "type", // type should be first 163 "string", 164 } 165 166 sockAddrAttrMap = map[AttrName]func(sa SockAddr) string{ 167 "string": func(sa SockAddr) string { 168 return sa.String() 169 }, 170 "type": func(sa SockAddr) string { 171 return sa.Type().String() 172 }, 173 } 174} 175 176// UnixSockAttrs returns a list of attributes supported by the UnixSock type 177func SockAddrAttrs() []AttrName { 178 return sockAddrAttrs 179} 180 181// Although this is pretty trivial to do in a program, having the logic here is 182// useful all around. Note that this marshals into a *string* -- the underlying 183// string representation of the sockaddr. If you then unmarshal into this type 184// in Go, all will work as expected, but externally you can take what comes out 185// and use the string value directly. 186type SockAddrMarshaler struct { 187 SockAddr 188} 189 190func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) { 191 return json.Marshal(s.SockAddr.String()) 192} 193 194func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error { 195 var str string 196 err := json.Unmarshal(in, &str) 197 if err != nil { 198 return err 199 } 200 sa, err := NewSockAddr(str) 201 if err != nil { 202 return err 203 } 204 s.SockAddr = sa 205 return nil 206} 207