aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/go-sockaddr/sockaddr.go
blob: 826c91c2e3d06b37e3b2e27a063fae3c4ba0481a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package sockaddr

import (
	"encoding/json"
	"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
}

// 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
}