feat: support SCTP & UDP as endpoint type (#352)
* feat: support SCTP & UDP as endpoint type * update README * modify endpoint type test for sctp & udp
This commit is contained in:
305
vendor/github.com/ishidawataru/sctp/sctp_linux.go
generated
vendored
Normal file
305
vendor/github.com/ishidawataru/sctp/sctp_linux.go
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
// +build linux,!386
|
||||
// Copyright 2019 Wataru Ishida. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||
// FIXME: syscall.SYS_SETSOCKOPT is undefined on 386
|
||||
r0, r1, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT,
|
||||
uintptr(fd),
|
||||
SOL_SCTP,
|
||||
optname,
|
||||
optval,
|
||||
optlen,
|
||||
0)
|
||||
if errno != 0 {
|
||||
return r0, r1, errno
|
||||
}
|
||||
return r0, r1, nil
|
||||
}
|
||||
|
||||
func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||
// FIXME: syscall.SYS_GETSOCKOPT is undefined on 386
|
||||
r0, r1, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT,
|
||||
uintptr(fd),
|
||||
SOL_SCTP,
|
||||
optname,
|
||||
optval,
|
||||
optlen,
|
||||
0)
|
||||
if errno != 0 {
|
||||
return r0, r1, errno
|
||||
}
|
||||
return r0, r1, nil
|
||||
}
|
||||
|
||||
type rawConn struct {
|
||||
sockfd int
|
||||
}
|
||||
|
||||
func (r rawConn) Control(f func(fd uintptr)) error {
|
||||
f(uintptr(r.sockfd))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r rawConn) Read(f func(fd uintptr) (done bool)) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (r rawConn) Write(f func(fd uintptr) (done bool)) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
|
||||
var cbuf []byte
|
||||
if info != nil {
|
||||
cmsgBuf := toBuf(info)
|
||||
hdr := &syscall.Cmsghdr{
|
||||
Level: syscall.IPPROTO_SCTP,
|
||||
Type: SCTP_CMSG_SNDRCV,
|
||||
}
|
||||
|
||||
// bitwidth of hdr.Len is platform-specific,
|
||||
// so we use hdr.SetLen() rather than directly setting hdr.Len
|
||||
hdr.SetLen(syscall.CmsgSpace(len(cmsgBuf)))
|
||||
cbuf = append(toBuf(hdr), cmsgBuf...)
|
||||
}
|
||||
return syscall.SendmsgN(c.fd(), b, cbuf, nil, 0)
|
||||
}
|
||||
|
||||
func parseSndRcvInfo(b []byte) (*SndRcvInfo, error) {
|
||||
msgs, err := syscall.ParseSocketControlMessage(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Level == syscall.IPPROTO_SCTP {
|
||||
switch m.Header.Type {
|
||||
case SCTP_CMSG_SNDRCV:
|
||||
return (*SndRcvInfo)(unsafe.Pointer(&m.Data[0])), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) {
|
||||
oob := make([]byte, 254)
|
||||
for {
|
||||
n, oobn, recvflags, _, err := syscall.Recvmsg(c.fd(), b, oob, 0)
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
if n == 0 && oobn == 0 {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
if recvflags&MSG_NOTIFICATION > 0 && c.notificationHandler != nil {
|
||||
if err := c.notificationHandler(b[:n]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
} else {
|
||||
var info *SndRcvInfo
|
||||
if oobn > 0 {
|
||||
info, err = parseSndRcvInfo(oob[:oobn])
|
||||
}
|
||||
return n, info, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SCTPConn) Close() error {
|
||||
if c != nil {
|
||||
fd := atomic.SwapInt32(&c._fd, -1)
|
||||
if fd > 0 {
|
||||
info := &SndRcvInfo{
|
||||
Flags: SCTP_EOF,
|
||||
}
|
||||
c.SCTPWrite(nil, info)
|
||||
syscall.Shutdown(int(fd), syscall.SHUT_RDWR)
|
||||
return syscall.Close(int(fd))
|
||||
}
|
||||
}
|
||||
return syscall.EBADF
|
||||
}
|
||||
|
||||
func (c *SCTPConn) SetWriteBuffer(bytes int) error {
|
||||
return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)
|
||||
}
|
||||
|
||||
func (c *SCTPConn) GetWriteBuffer() (int, error) {
|
||||
return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF)
|
||||
}
|
||||
|
||||
func (c *SCTPConn) SetReadBuffer(bytes int) error {
|
||||
return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes)
|
||||
}
|
||||
|
||||
func (c *SCTPConn) GetReadBuffer() (int, error) {
|
||||
return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
|
||||
}
|
||||
|
||||
// ListenSCTP - start listener on specified address/port
|
||||
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
||||
return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
|
||||
}
|
||||
|
||||
// ListenSCTPExt - start listener on specified address/port with given SCTP options
|
||||
func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) {
|
||||
return listenSCTPExtConfig(network, laddr, options, nil)
|
||||
}
|
||||
|
||||
// listenSCTPExtConfig - start listener on specified address/port with given SCTP options and socket configuration
|
||||
func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) {
|
||||
af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen")
|
||||
sock, err := syscall.Socket(
|
||||
af,
|
||||
syscall.SOCK_STREAM,
|
||||
syscall.IPPROTO_SCTP,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// close socket on error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.Close(sock)
|
||||
}
|
||||
}()
|
||||
if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if control != nil {
|
||||
rc := rawConn{sockfd: sock}
|
||||
if err = control(network, laddr.String(), rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = setInitOpts(sock, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if laddr != nil {
|
||||
// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
|
||||
if len(laddr.IPAddrs) == 0 {
|
||||
if af == syscall.AF_INET {
|
||||
laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
|
||||
} else if af == syscall.AF_INET6 {
|
||||
laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
|
||||
}
|
||||
}
|
||||
err = SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = syscall.Listen(sock, syscall.SOMAXCONN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SCTPListener{
|
||||
fd: sock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AcceptSCTP waits for and returns the next SCTP connection to the listener.
|
||||
func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) {
|
||||
fd, _, err := syscall.Accept4(ln.fd, 0)
|
||||
return NewSCTPConn(fd, nil), err
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection connection to the listener.
|
||||
func (ln *SCTPListener) Accept() (net.Conn, error) {
|
||||
return ln.AcceptSCTP()
|
||||
}
|
||||
|
||||
func (ln *SCTPListener) Close() error {
|
||||
syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
|
||||
return syscall.Close(ln.fd)
|
||||
}
|
||||
|
||||
// DialSCTP - bind socket to laddr (if given) and connect to raddr
|
||||
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
||||
return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM})
|
||||
}
|
||||
|
||||
// DialSCTPExt - same as DialSCTP but with given SCTP options
|
||||
func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) {
|
||||
return dialSCTPExtConfig(network, laddr, raddr, options, nil)
|
||||
}
|
||||
|
||||
// dialSCTPExtConfig - same as DialSCTP but with given SCTP options and socket configuration
|
||||
func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) {
|
||||
af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial")
|
||||
sock, err := syscall.Socket(
|
||||
af,
|
||||
syscall.SOCK_STREAM,
|
||||
syscall.IPPROTO_SCTP,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// close socket on error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
syscall.Close(sock)
|
||||
}
|
||||
}()
|
||||
if err = setDefaultSockopts(sock, af, ipv6only); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if control != nil {
|
||||
rc := rawConn{sockfd: sock}
|
||||
if err = control(network, laddr.String(), rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = setInitOpts(sock, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if laddr != nil {
|
||||
// If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address
|
||||
if len(laddr.IPAddrs) == 0 {
|
||||
if af == syscall.AF_INET {
|
||||
laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero})
|
||||
} else if af == syscall.AF_INET6 {
|
||||
laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero})
|
||||
}
|
||||
}
|
||||
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = SCTPConnect(sock, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSCTPConn(sock, nil), nil
|
||||
}
|
Reference in New Issue
Block a user