Browse code

Update sctp package

This commit updates the vendored ishidawataru/sctp and adapts its used
types.

Signed-off-by: Sascha Grunert <sgrunert@suse.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sascha Grunert authored on 2019/05/02 20:23:31
Showing 11 changed files
... ...
@@ -51,8 +51,8 @@ func parseHostContainerAddrs() (host net.Addr, container net.Addr) {
51 51
 		host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort}
52 52
 		container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort}
53 53
 	case "sctp":
54
-		host = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*hostIP)}, Port: *hostPort}
55
-		container = &sctp.SCTPAddr{IP: []net.IP{net.ParseIP(*containerIP)}, Port: *containerPort}
54
+		host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort}
55
+		container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort}
56 56
 	default:
57 57
 		log.Fatalf("unsupported protocol %s", *proto)
58 58
 	}
... ...
@@ -285,7 +285,7 @@ func TestSCTP4Proxy(t *testing.T) {
285 285
 	backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{})
286 286
 	defer backend.Close()
287 287
 	backend.Run()
288
-	frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv4(127, 0, 0, 1)}, Port: 0}
288
+	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0}
289 289
 	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
290 290
 	if err != nil {
291 291
 		t.Fatal(err)
... ...
@@ -298,7 +298,7 @@ func TestSCTP6Proxy(t *testing.T) {
298 298
 	backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{})
299 299
 	defer backend.Close()
300 300
 	backend.Run()
301
-	frontendAddr := &sctp.SCTPAddr{IP: []net.IP{net.IPv6loopback}, Port: 0}
301
+	frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0}
302 302
 	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
303 303
 	if err != nil {
304 304
 		t.Fatal(err)
... ...
@@ -115,16 +115,16 @@ func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart,
115 115
 
116 116
 		m = &mapping{
117 117
 			proto:     proto,
118
-			host:      &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: allocatedHostPort},
118
+			host:      &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort},
119 119
 			container: container,
120 120
 		}
121 121
 
122 122
 		if useProxy {
123 123
 			sctpAddr := container.(*sctp.SCTPAddr)
124
-			if len(sctpAddr.IP) == 0 {
124
+			if len(sctpAddr.IPAddrs) == 0 {
125 125
 				return nil, ErrSCTPAddrNoIP
126 126
 			}
127
-			m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IP[0], sctpAddr.Port, pm.proxyPath)
127
+			m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath)
128 128
 			if err != nil {
129 129
 				return nil, err
130 130
 			}
... ...
@@ -210,10 +210,10 @@ func (pm *PortMapper) Unmap(host net.Addr) error {
210 210
 	case *net.UDPAddr:
211 211
 		return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
212 212
 	case *sctp.SCTPAddr:
213
-		if len(a.IP) == 0 {
213
+		if len(a.IPAddrs) == 0 {
214 214
 			return ErrSCTPAddrNoIP
215 215
 		}
216
-		return pm.Allocator.ReleasePort(a.IP[0], "sctp", a.Port)
216
+		return pm.Allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port)
217 217
 	}
218 218
 	return ErrUnknownBackendAddressType
219 219
 }
... ...
@@ -239,11 +239,11 @@ func getKey(a net.Addr) string {
239 239
 	case *net.UDPAddr:
240 240
 		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
241 241
 	case *sctp.SCTPAddr:
242
-		if len(t.IP) == 0 {
242
+		if len(t.IPAddrs) == 0 {
243 243
 			logrus.Error(ErrSCTPAddrNoIP)
244 244
 			return ""
245 245
 		}
246
-		return fmt.Sprintf("%s:%d/%s", t.IP[0].String(), t.Port, "sctp")
246
+		return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp")
247 247
 	}
248 248
 	return ""
249 249
 }
... ...
@@ -255,11 +255,11 @@ func getIPAndPort(a net.Addr) (net.IP, int) {
255 255
 	case *net.UDPAddr:
256 256
 		return t.IP, t.Port
257 257
 	case *sctp.SCTPAddr:
258
-		if len(t.IP) == 0 {
258
+		if len(t.IPAddrs) == 0 {
259 259
 			logrus.Error(ErrSCTPAddrNoIP)
260 260
 			return nil, 0
261 261
 		}
262
-		return t.IP[0], t.Port
262
+		return t.IPAddrs[0].IP, t.Port
263 263
 	}
264 264
 	return nil, 0
265 265
 }
... ...
@@ -90,7 +90,7 @@ func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, er
90 90
 		addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
91 91
 		return &dummyProxy{addr: addr}, nil
92 92
 	case "sctp":
93
-		addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort}
93
+		addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
94 94
 		return &dummyProxy{addr: addr}, nil
95 95
 	default:
96 96
 		return nil, fmt.Errorf("Unknown addr type: %s", proto)
... ...
@@ -99,7 +99,7 @@ func (p PortBinding) HostAddr() (net.Addr, error) {
99 99
 	case TCP:
100 100
 		return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
101 101
 	case SCTP:
102
-		return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
102
+		return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil
103 103
 	default:
104 104
 		return nil, ErrInvalidProtocolBinding(p.Proto.String())
105 105
 	}
... ...
@@ -113,7 +113,7 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) {
113 113
 	case TCP:
114 114
 		return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
115 115
 	case SCTP:
116
-		return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
116
+		return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil
117 117
 	default:
118 118
 		return nil, ErrInvalidProtocolBinding(p.Proto.String())
119 119
 	}
... ...
@@ -48,7 +48,7 @@ golang.org/x/net                        a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1
48 48
 golang.org/x/sys                        d455e41777fca6e8a5a79e34a14b8368bc11d9ba
49 49
 golang.org/x/sync                       1d60e4601c6fd243af51cc01ddf169918a5407ca
50 50
 github.com/pkg/errors                   ba968bfe8b2f7e042a574c888954fccecfa385b4 # v0.8.1
51
-github.com/ishidawataru/sctp            07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
51
+github.com/ishidawataru/sctp            6e2cb1366111dcf547c13531e3a263a067715847
52 52
 
53 53
 gotest.tools                            b6e20af1ed078cd01a6413b734051a292450b4cb # v2.1.0
54 54
 github.com/google/go-cmp                3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0
55 55
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+Copyright (c) 2009 The Go Authors. All rights reserved.
1
+
2
+Redistribution and use in source and binary forms, with or without
3
+modification, are permitted provided that the following conditions are
4
+met:
5
+
6
+   * Redistributions of source code must retain the above copyright
7
+notice, this list of conditions and the following disclaimer.
8
+   * Redistributions in binary form must reproduce the above
9
+copyright notice, this list of conditions and the following disclaimer
10
+in the documentation and/or other materials provided with the
11
+distribution.
12
+   * Neither the name of Google Inc. nor the names of its
13
+contributors may be used to endorse or promote products derived from
14
+this software without specific prior written permission.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 27
new file mode 100644
... ...
@@ -0,0 +1,218 @@
0
+package sctp
1
+
2
+import (
3
+	"net"
4
+	"os"
5
+	"sync"
6
+	"syscall"
7
+)
8
+
9
+//from https://github.com/golang/go
10
+// Boolean to int.
11
+func boolint(b bool) int {
12
+	if b {
13
+		return 1
14
+	}
15
+	return 0
16
+}
17
+
18
+//from https://github.com/golang/go
19
+func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) {
20
+	switch family {
21
+	case syscall.AF_INET:
22
+		if len(ip) == 0 {
23
+			ip = net.IPv4zero
24
+		}
25
+		ip4 := ip.To4()
26
+		if ip4 == nil {
27
+			return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()}
28
+		}
29
+		sa := &syscall.SockaddrInet4{Port: port}
30
+		copy(sa.Addr[:], ip4)
31
+		return sa, nil
32
+	case syscall.AF_INET6:
33
+		// In general, an IP wildcard address, which is either
34
+		// "0.0.0.0" or "::", means the entire IP addressing
35
+		// space. For some historical reason, it is used to
36
+		// specify "any available address" on some operations
37
+		// of IP node.
38
+		//
39
+		// When the IP node supports IPv4-mapped IPv6 address,
40
+		// we allow an listener to listen to the wildcard
41
+		// address of both IP addressing spaces by specifying
42
+		// IPv6 wildcard address.
43
+		if len(ip) == 0 || ip.Equal(net.IPv4zero) {
44
+			ip = net.IPv6zero
45
+		}
46
+		// We accept any IPv6 address including IPv4-mapped
47
+		// IPv6 address.
48
+		ip6 := ip.To16()
49
+		if ip6 == nil {
50
+			return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()}
51
+		}
52
+		//we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host
53
+		//if real Zone handling is required, the zone cache implementation in golang/net should be pulled here
54
+		sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0}
55
+		copy(sa.Addr[:], ip6)
56
+		return sa, nil
57
+	}
58
+	return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()}
59
+}
60
+
61
+//from https://github.com/golang/go
62
+func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) {
63
+	if a == nil {
64
+		return nil, nil
65
+	}
66
+	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
67
+}
68
+
69
+//from https://github.com/golang/go
70
+type ipStackCapabilities struct {
71
+	sync.Once             // guards following
72
+	ipv4Enabled           bool
73
+	ipv6Enabled           bool
74
+	ipv4MappedIPv6Enabled bool
75
+}
76
+
77
+//from https://github.com/golang/go
78
+var ipStackCaps ipStackCapabilities
79
+
80
+//from https://github.com/golang/go
81
+// supportsIPv4 reports whether the platform supports IPv4 networking
82
+// functionality.
83
+func supportsIPv4() bool {
84
+	ipStackCaps.Once.Do(ipStackCaps.probe)
85
+	return ipStackCaps.ipv4Enabled
86
+}
87
+
88
+//from https://github.com/golang/go
89
+// supportsIPv6 reports whether the platform supports IPv6 networking
90
+// functionality.
91
+func supportsIPv6() bool {
92
+	ipStackCaps.Once.Do(ipStackCaps.probe)
93
+	return ipStackCaps.ipv6Enabled
94
+}
95
+
96
+//from https://github.com/golang/go
97
+// supportsIPv4map reports whether the platform supports mapping an
98
+// IPv4 address inside an IPv6 address at transport layer
99
+// protocols. See RFC 4291, RFC 4038 and RFC 3493.
100
+func supportsIPv4map() bool {
101
+	ipStackCaps.Once.Do(ipStackCaps.probe)
102
+	return ipStackCaps.ipv4MappedIPv6Enabled
103
+}
104
+
105
+//from https://github.com/golang/go
106
+// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
107
+// capabilities which are controlled by the IPV6_V6ONLY socket option
108
+// and kernel configuration.
109
+//
110
+// Should we try to use the IPv4 socket interface if we're only
111
+// dealing with IPv4 sockets? As long as the host system understands
112
+// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to
113
+// the IPv6 interface. That simplifies our code and is most
114
+// general. Unfortunately, we need to run on kernels built without
115
+// IPv6 support too. So probe the kernel to figure it out.
116
+func (p *ipStackCapabilities) probe() {
117
+	s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
118
+	switch err {
119
+	case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
120
+	case nil:
121
+		syscall.Close(s)
122
+		p.ipv4Enabled = true
123
+	}
124
+	var probes = []struct {
125
+		laddr net.TCPAddr
126
+		value int
127
+	}{
128
+		// IPv6 communication capability
129
+		{laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1},
130
+		// IPv4-mapped IPv6 address communication capability
131
+		{laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0},
132
+	}
133
+
134
+	for i := range probes {
135
+		s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
136
+		if err != nil {
137
+			continue
138
+		}
139
+		defer syscall.Close(s)
140
+		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
141
+		sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6)
142
+		if err != nil {
143
+			continue
144
+		}
145
+		if err := syscall.Bind(s, sa); err != nil {
146
+			continue
147
+		}
148
+		if i == 0 {
149
+			p.ipv6Enabled = true
150
+		} else {
151
+			p.ipv4MappedIPv6Enabled = true
152
+		}
153
+	}
154
+}
155
+
156
+//from https://github.com/golang/go
157
+//Change: we check the first IP address in the list of candidate SCTP IP addresses
158
+func (a *SCTPAddr) isWildcard() bool {
159
+	if a == nil {
160
+		return true
161
+	}
162
+	if 0 == len(a.IPAddrs) {
163
+		return true
164
+	}
165
+
166
+	return a.IPAddrs[0].IP.IsUnspecified()
167
+}
168
+
169
+func (a *SCTPAddr) family() int {
170
+	if a != nil {
171
+		for _, ip := range a.IPAddrs {
172
+			if ip.IP.To4() == nil {
173
+				return syscall.AF_INET6
174
+			}
175
+		}
176
+	}
177
+	return syscall.AF_INET
178
+}
179
+
180
+//from https://github.com/golang/go
181
+func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) {
182
+	switch network[len(network)-1] {
183
+	case '4':
184
+		return syscall.AF_INET, false
185
+	case '6':
186
+		return syscall.AF_INET6, true
187
+	}
188
+
189
+	if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
190
+		if supportsIPv4map() || !supportsIPv4() {
191
+			return syscall.AF_INET6, false
192
+		}
193
+		if laddr == nil {
194
+			return syscall.AF_INET, false
195
+		}
196
+		return laddr.family(), false
197
+	}
198
+
199
+	if (laddr == nil || laddr.family() == syscall.AF_INET) &&
200
+		(raddr == nil || raddr.family() == syscall.AF_INET) {
201
+		return syscall.AF_INET, false
202
+	}
203
+	return syscall.AF_INET6, false
204
+}
205
+
206
+//from https://github.com/golang/go
207
+//Changes: it is for SCTP only
208
+func setDefaultSockopts(s int, family int, ipv6only bool) error {
209
+	if family == syscall.AF_INET6 {
210
+		// Allow both IP versions even if the OS default
211
+		// is otherwise. Note that some operating systems
212
+		// never admit this option.
213
+		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
214
+	}
215
+	// Allow broadcast.
216
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
217
+}
... ...
@@ -197,37 +197,58 @@ func htons(h uint16) uint16 {
197 197
 
198 198
 var ntohs = htons
199 199
 
200
-func setNumOstreams(fd, num int) error {
201
-	param := InitMsg{
202
-		NumOstreams: uint16(num),
203
-	}
204
-	optlen := unsafe.Sizeof(param)
205
-	_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
200
+// setInitOpts sets options for an SCTP association initialization
201
+// see https://tools.ietf.org/html/rfc4960#page-25
202
+func setInitOpts(fd int, options InitMsg) error {
203
+	optlen := unsafe.Sizeof(options)
204
+	_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen))
206 205
 	return err
207 206
 }
208 207
 
208
+func setNumOstreams(fd, num int) error {
209
+	return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)})
210
+}
211
+
209 212
 type SCTPAddr struct {
210
-	IP   []net.IP
211
-	Port int
213
+	IPAddrs []net.IPAddr
214
+	Port    int
212 215
 }
213 216
 
214 217
 func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
215
-	buf := []byte{}
216 218
 	p := htons(uint16(a.Port))
217
-	for _, ip := range a.IP {
218
-		if ip.To4() != nil {
219
+	if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr
220
+		s := syscall.RawSockaddrInet4{
221
+			Family: syscall.AF_INET,
222
+			Port:   p,
223
+		}
224
+		copy(s.Addr[:], net.IPv4zero)
225
+		return toBuf(s)
226
+	}
227
+	buf := []byte{}
228
+	for _, ip := range a.IPAddrs {
229
+		ipBytes := ip.IP
230
+		if len(ipBytes) == 0 {
231
+			ipBytes = net.IPv4zero
232
+		}
233
+		if ip4 := ipBytes.To4(); ip4 != nil {
219 234
 			s := syscall.RawSockaddrInet4{
220 235
 				Family: syscall.AF_INET,
221 236
 				Port:   p,
222 237
 			}
223
-			copy(s.Addr[:], ip.To4())
238
+			copy(s.Addr[:], ip4)
224 239
 			buf = append(buf, toBuf(s)...)
225 240
 		} else {
241
+			var scopeid uint32
242
+			ifi, err := net.InterfaceByName(ip.Zone)
243
+			if err == nil {
244
+				scopeid = uint32(ifi.Index)
245
+			}
226 246
 			s := syscall.RawSockaddrInet6{
227
-				Family: syscall.AF_INET6,
228
-				Port:   p,
247
+				Family:   syscall.AF_INET6,
248
+				Port:     p,
249
+				Scope_id: scopeid,
229 250
 			}
230
-			copy(s.Addr[:], ip)
251
+			copy(s.Addr[:], ipBytes)
231 252
 			buf = append(buf, toBuf(s)...)
232 253
 		}
233 254
 	}
... ...
@@ -237,15 +258,15 @@ func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
237 237
 func (a *SCTPAddr) String() string {
238 238
 	var b bytes.Buffer
239 239
 
240
-	for n, i := range a.IP {
241
-		if a.IP[n].To4() != nil {
240
+	for n, i := range a.IPAddrs {
241
+		if i.IP.To4() != nil {
242 242
 			b.WriteString(i.String())
243
-		} else if a.IP[n].To16() != nil {
243
+		} else if i.IP.To16() != nil {
244 244
 			b.WriteRune('[')
245 245
 			b.WriteString(i.String())
246 246
 			b.WriteRune(']')
247 247
 		}
248
-		if n < len(a.IP)-1 {
248
+		if n < len(a.IPAddrs)-1 {
249 249
 			b.WriteRune('/')
250 250
 		}
251 251
 	}
... ...
@@ -260,6 +281,7 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
260 260
 	tcpnet := ""
261 261
 	switch network {
262 262
 	case "", "sctp":
263
+		tcpnet = "tcp"
263 264
 	case "sctp4":
264 265
 		tcpnet = "tcp4"
265 266
 	case "sctp6":
... ...
@@ -271,26 +293,26 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
271 271
 	if len(elems) == 0 {
272 272
 		return nil, fmt.Errorf("invalid input: %s", addrs)
273 273
 	}
274
-	ipaddrs := make([]net.IP, 0, len(elems))
274
+	ipaddrs := make([]net.IPAddr, 0, len(elems))
275 275
 	for _, e := range elems[:len(elems)-1] {
276 276
 		tcpa, err := net.ResolveTCPAddr(tcpnet, e+":")
277 277
 		if err != nil {
278 278
 			return nil, err
279 279
 		}
280
-		ipaddrs = append(ipaddrs, tcpa.IP)
280
+		ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
281 281
 	}
282 282
 	tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1])
283 283
 	if err != nil {
284 284
 		return nil, err
285 285
 	}
286 286
 	if tcpa.IP != nil {
287
-		ipaddrs = append(ipaddrs, tcpa.IP)
287
+		ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone})
288 288
 	} else {
289 289
 		ipaddrs = nil
290 290
 	}
291 291
 	return &SCTPAddr{
292
-		IP:   ipaddrs,
293
-		Port: tcpa.Port,
292
+		IPAddrs: ipaddrs,
293
+		Port:    tcpa.Port,
294 294
 	}, nil
295 295
 }
296 296
 
... ...
@@ -357,15 +379,12 @@ func (c *SCTPConn) Read(b []byte) (int, error) {
357 357
 }
358 358
 
359 359
 func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error {
360
-	param := InitMsg{
360
+	return setInitOpts(c.fd(), InitMsg{
361 361
 		NumOstreams:    uint16(numOstreams),
362 362
 		MaxInstreams:   uint16(maxInstreams),
363 363
 		MaxAttempts:    uint16(maxAttempts),
364 364
 		MaxInitTimeout: uint16(maxInitTimeout),
365
-	}
366
-	optlen := unsafe.Sizeof(param)
367
-	_, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(&param)), uintptr(optlen))
368
-	return err
365
+	})
369 366
 }
370 367
 
371 368
 func (c *SCTPConn) SubscribeEvents(flags int) error {
... ...
@@ -473,7 +492,7 @@ func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) {
473 473
 
474 474
 func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
475 475
 	addr := &SCTPAddr{
476
-		IP: make([]net.IP, n),
476
+		IPAddrs: make([]net.IPAddr, n),
477 477
 	}
478 478
 
479 479
 	switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family {
... ...
@@ -484,7 +503,7 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
484 484
 		for i := 0; i < n; i++ {
485 485
 			a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer(
486 486
 				uintptr(ptr) + size*uintptr(i)))
487
-			addr.IP[i] = a.Addr[:]
487
+			addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]}
488 488
 		}
489 489
 	case syscall.AF_INET6:
490 490
 		addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
... ...
@@ -493,7 +512,12 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
493 493
 		for i := 0; i < n; i++ {
494 494
 			a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer(
495 495
 				uintptr(ptr) + size*uintptr(i)))
496
-			addr.IP[i] = a.Addr[:]
496
+			var zone string
497
+			ifi, err := net.InterfaceByIndex(int(a.Scope_id))
498
+			if err == nil {
499
+				zone = ifi.Name
500
+			}
501
+			addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone}
497 502
 		}
498 503
 	default:
499 504
 		return nil, fmt.Errorf("unknown address family: %d", family)
... ...
@@ -3,7 +3,6 @@
3 3
 package sctp
4 4
 
5 5
 import (
6
-	"fmt"
7 6
 	"io"
8 7
 	"net"
9 8
 	"sync/atomic"
... ...
@@ -115,31 +114,14 @@ func (c *SCTPConn) Close() error {
115 115
 	return syscall.EBADF
116 116
 }
117 117
 
118
+// ListenSCTP - start listener on specified address/port
118 119
 func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
119
-	af := syscall.AF_INET
120
-	switch net {
121
-	case "sctp":
122
-		hasv6 := func(addr *SCTPAddr) bool {
123
-			if addr == nil {
124
-				return false
125
-			}
126
-			for _, ip := range addr.IP {
127
-				if ip.To4() == nil {
128
-					return true
129
-				}
130
-			}
131
-			return false
132
-		}
133
-		if hasv6(laddr) {
134
-			af = syscall.AF_INET6
135
-		}
136
-	case "sctp4":
137
-	case "sctp6":
138
-		af = syscall.AF_INET6
139
-	default:
140
-		return nil, fmt.Errorf("invalid net: %s", net)
141
-	}
120
+	return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
121
+}
142 122
 
123
+// ListenSCTPExt - start listener on specified address/port with given SCTP options
124
+func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
125
+	af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen")
143 126
 	sock, err := syscall.Socket(
144 127
 		af,
145 128
 		syscall.SOCK_STREAM,
... ...
@@ -148,11 +130,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
148 148
 	if err != nil {
149 149
 		return nil, err
150 150
 	}
151
-	err = setNumOstreams(sock, SCTP_MAX_STREAM)
151
+
152
+	// close socket on error
153
+	defer func() {
154
+		if err != nil {
155
+			syscall.Close(sock)
156
+		}
157
+	}()
158
+	if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
159
+		return nil, err
160
+	}
161
+	err = setInitOpts(sock, options)
152 162
 	if err != nil {
153 163
 		return nil, err
154 164
 	}
155
-	if laddr != nil && len(laddr.IP) != 0 {
165
+
166
+	if laddr != nil {
167
+		// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
168
+		if len(laddr.IPAddrs) == 0 {
169
+			if af == syscall.AF_INET {
170
+				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
171
+			} else if af == syscall.AF_INET6 {
172
+				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
173
+			}
174
+		}
156 175
 		err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
157 176
 		if err != nil {
158 177
 			return nil, err
... ...
@@ -167,40 +168,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
167 167
 	}, nil
168 168
 }
169 169
 
170
-func (ln *SCTPListener) Accept() (net.Conn, error) {
170
+// AcceptSCTP waits for and returns the next SCTP connection to the listener.
171
+func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
171 172
 	fd, _, err := syscall.Accept4(ln.fd, 0)
172 173
 	return NewSCTPConn(fd, nil), err
173 174
 }
174 175
 
176
+// Accept waits for and returns the next connection connection to the listener.
177
+func (ln *SCTPListener) Accept() (net.Conn, error) {
178
+	return ln.AcceptSCTP()
179
+}
180
+
175 181
 func (ln *SCTPListener) Close() error {
176 182
 	syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
177 183
 	return syscall.Close(ln.fd)
178 184
 }
179 185
 
186
+// DialSCTP - bind socket to laddr (if given) and connect to raddr
180 187
 func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
181
-	af := syscall.AF_INET
182
-	switch net {
183
-	case "sctp":
184
-		hasv6 := func(addr *SCTPAddr) bool {
185
-			if addr == nil {
186
-				return false
187
-			}
188
-			for _, ip := range addr.IP {
189
-				if ip.To4() == nil {
190
-					return true
191
-				}
192
-			}
193
-			return false
194
-		}
195
-		if hasv6(laddr) || hasv6(raddr) {
196
-			af = syscall.AF_INET6
197
-		}
198
-	case "sctp4":
199
-	case "sctp6":
200
-		af = syscall.AF_INET6
201
-	default:
202
-		return nil, fmt.Errorf("invalid net: %s", net)
203
-	}
188
+	return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
189
+}
190
+
191
+// DialSCTPExt - same as DialSCTP but with given SCTP options
192
+func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
193
+	af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial")
204 194
 	sock, err := syscall.Socket(
205 195
 		af,
206 196
 		syscall.SOCK_STREAM,
... ...
@@ -209,11 +200,29 @@ func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
209 209
 	if err != nil {
210 210
 		return nil, err
211 211
 	}
212
-	err = setNumOstreams(sock, SCTP_MAX_STREAM)
212
+
213
+	// close socket on error
214
+	defer func() {
215
+		if err != nil {
216
+			syscall.Close(sock)
217
+		}
218
+	}()
219
+	if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
220
+		return nil, err
221
+	}
222
+	err = setInitOpts(sock, options)
213 223
 	if err != nil {
214 224
 		return nil, err
215 225
 	}
216 226
 	if laddr != nil {
227
+		// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
228
+		if len(laddr.IPAddrs) == 0 {
229
+			if af == syscall.AF_INET {
230
+				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
231
+			} else if af == syscall.AF_INET6 {
232
+				laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
233
+			}
234
+		}
217 235
 		err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
218 236
 		if err != nil {
219 237
 			return nil, err
... ...
@@ -34,10 +34,18 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
34 34
 	return nil, ErrUnsupported
35 35
 }
36 36
 
37
+func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
38
+	return nil, ErrUnsupported
39
+}
40
+
37 41
 func (ln *SCTPListener) Accept() (net.Conn, error) {
38 42
 	return nil, ErrUnsupported
39 43
 }
40 44
 
45
+func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
46
+	return nil, ErrUnsupported
47
+}
48
+
41 49
 func (ln *SCTPListener) Close() error {
42 50
 	return ErrUnsupported
43 51
 }
... ...
@@ -45,3 +53,7 @@ func (ln *SCTPListener) Close() error {
45 45
 func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
46 46
 	return nil, ErrUnsupported
47 47
 }
48
+
49
+func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
50
+	return nil, ErrUnsupported
51
+}