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