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