summaryrefslogtreecommitdiff
path: root/vendor/github.com/ishidawataru/sctp/sctp_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ishidawataru/sctp/sctp_linux.go')
-rw-r--r--vendor/github.com/ishidawataru/sctp/sctp_linux.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/vendor/github.com/ishidawataru/sctp/sctp_linux.go b/vendor/github.com/ishidawataru/sctp/sctp_linux.go
new file mode 100644
index 000000000..f93ab8622
--- /dev/null
+++ b/vendor/github.com/ishidawataru/sctp/sctp_linux.go
@@ -0,0 +1,227 @@
+// +build linux,!386
+
+package sctp
+
+import (
+ "fmt"
+ "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
+}
+
+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 ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
+ af := syscall.AF_INET
+ switch net {
+ case "sctp":
+ hasv6 := func(addr *SCTPAddr) bool {
+ if addr == nil {
+ return false
+ }
+ for _, ip := range addr.IP {
+ if ip.To4() == nil {
+ return true
+ }
+ }
+ return false
+ }
+ if hasv6(laddr) {
+ af = syscall.AF_INET6
+ }
+ case "sctp4":
+ case "sctp6":
+ af = syscall.AF_INET6
+ default:
+ return nil, fmt.Errorf("invalid net: %s", net)
+ }
+
+ sock, err := syscall.Socket(
+ af,
+ syscall.SOCK_STREAM,
+ syscall.IPPROTO_SCTP,
+ )
+ if err != nil {
+ return nil, err
+ }
+ err = setNumOstreams(sock, SCTP_MAX_STREAM)
+ if err != nil {
+ return nil, err
+ }
+ if laddr != nil && len(laddr.IP) != 0 {
+ 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
+}
+
+func (ln *SCTPListener) Accept() (net.Conn, error) {
+ fd, _, err := syscall.Accept4(ln.fd, 0)
+ return NewSCTPConn(fd, nil), err
+}
+
+func (ln *SCTPListener) Close() error {
+ syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
+ return syscall.Close(ln.fd)
+}
+
+func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
+ af := syscall.AF_INET
+ switch net {
+ case "sctp":
+ hasv6 := func(addr *SCTPAddr) bool {
+ if addr == nil {
+ return false
+ }
+ for _, ip := range addr.IP {
+ if ip.To4() == nil {
+ return true
+ }
+ }
+ return false
+ }
+ if hasv6(laddr) || hasv6(raddr) {
+ af = syscall.AF_INET6
+ }
+ case "sctp4":
+ case "sctp6":
+ af = syscall.AF_INET6
+ default:
+ return nil, fmt.Errorf("invalid net: %s", net)
+ }
+ sock, err := syscall.Socket(
+ af,
+ syscall.SOCK_STREAM,
+ syscall.IPPROTO_SCTP,
+ )
+ if err != nil {
+ return nil, err
+ }
+ err = setNumOstreams(sock, SCTP_MAX_STREAM)
+ if err != nil {
+ return nil, err
+ }
+ if laddr != nil {
+ 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
+}