package sctp import ( "bytes" "encoding/binary" "fmt" "net" "strconv" "strings" "sync" "sync/atomic" "syscall" "time" "unsafe" ) const ( SOL_SCTP = 132 SCTP_BINDX_ADD_ADDR = 0x01 SCTP_BINDX_REM_ADDR = 0x02 MSG_NOTIFICATION = 0x8000 ) const ( SCTP_RTOINFO = iota SCTP_ASSOCINFO SCTP_INITMSG SCTP_NODELAY SCTP_AUTOCLOSE SCTP_SET_PEER_PRIMARY_ADDR SCTP_PRIMARY_ADDR SCTP_ADAPTATION_LAYER SCTP_DISABLE_FRAGMENTS SCTP_PEER_ADDR_PARAMS SCTP_DEFAULT_SENT_PARAM SCTP_EVENTS SCTP_I_WANT_MAPPED_V4_ADDR SCTP_MAXSEG SCTP_STATUS SCTP_GET_PEER_ADDR_INFO SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK = SCTP_DELAYED_ACK_TIME SCTP_DELAYED_SACK = SCTP_DELAYED_ACK_TIME SCTP_SOCKOPT_BINDX_ADD = 100 SCTP_SOCKOPT_BINDX_REM = 101 SCTP_SOCKOPT_PEELOFF = 102 SCTP_GET_PEER_ADDRS = 108 SCTP_GET_LOCAL_ADDRS = 109 SCTP_SOCKOPT_CONNECTX = 110 SCTP_SOCKOPT_CONNECTX3 = 111 ) const ( SCTP_EVENT_DATA_IO = 1 << iota SCTP_EVENT_ASSOCIATION SCTP_EVENT_ADDRESS SCTP_EVENT_SEND_FAILURE SCTP_EVENT_PEER_ERROR SCTP_EVENT_SHUTDOWN SCTP_EVENT_PARTIAL_DELIVERY SCTP_EVENT_ADAPTATION_LAYER SCTP_EVENT_AUTHENTICATION SCTP_EVENT_SENDER_DRY SCTP_EVENT_ALL = SCTP_EVENT_DATA_IO | SCTP_EVENT_ASSOCIATION | SCTP_EVENT_ADDRESS | SCTP_EVENT_SEND_FAILURE | SCTP_EVENT_PEER_ERROR | SCTP_EVENT_SHUTDOWN | SCTP_EVENT_PARTIAL_DELIVERY | SCTP_EVENT_ADAPTATION_LAYER | SCTP_EVENT_AUTHENTICATION | SCTP_EVENT_SENDER_DRY ) type SCTPNotificationType int const ( SCTP_SN_TYPE_BASE = SCTPNotificationType(iota + (1 << 15)) SCTP_ASSOC_CHANGE SCTP_PEER_ADDR_CHANGE SCTP_SEND_FAILED SCTP_REMOTE_ERROR SCTP_SHUTDOWN_EVENT SCTP_PARTIAL_DELIVERY_EVENT SCTP_ADAPTATION_INDICATION SCTP_AUTHENTICATION_INDICATION SCTP_SENDER_DRY_EVENT ) type NotificationHandler func([]byte) error type EventSubscribe struct { DataIO uint8 Association uint8 Address uint8 SendFailure uint8 PeerError uint8 Shutdown uint8 PartialDelivery uint8 AdaptationLayer uint8 Authentication uint8 SenderDry uint8 } const ( SCTP_CMSG_INIT = iota SCTP_CMSG_SNDRCV SCTP_CMSG_SNDINFO SCTP_CMSG_RCVINFO SCTP_CMSG_NXTINFO ) const ( SCTP_UNORDERED = 1 << iota SCTP_ADDR_OVER SCTP_ABORT SCTP_SACK_IMMEDIATELY SCTP_EOF ) const ( SCTP_MAX_STREAM = 0xffff ) type InitMsg struct { NumOstreams uint16 MaxInstreams uint16 MaxAttempts uint16 MaxInitTimeout uint16 } type SndRcvInfo struct { Stream uint16 SSN uint16 Flags uint16 _ uint16 PPID uint32 Context uint32 TTL uint32 TSN uint32 CumTSN uint32 AssocID int32 } type SndInfo struct { SID uint16 Flags uint16 PPID uint32 Context uint32 AssocID int32 } type GetAddrsOld struct { AssocID int32 AddrNum int32 Addrs uintptr } type NotificationHeader struct { Type uint16 Flags uint16 Length uint32 } type SCTPState uint16 const ( SCTP_COMM_UP = SCTPState(iota) SCTP_COMM_LOST SCTP_RESTART SCTP_SHUTDOWN_COMP SCTP_CANT_STR_ASSOC ) var nativeEndian binary.ByteOrder var sndRcvInfoSize uintptr func init() { i := uint16(1) if *(*byte)(unsafe.Pointer(&i)) == 0 { nativeEndian = binary.BigEndian } else { nativeEndian = binary.LittleEndian } info := SndRcvInfo{} sndRcvInfoSize = unsafe.Sizeof(info) } func toBuf(v interface{}) []byte { var buf bytes.Buffer binary.Write(&buf, nativeEndian, v) return buf.Bytes() } func htons(h uint16) uint16 { if nativeEndian == binary.LittleEndian { return (h << 8 & 0xff00) | (h >> 8 & 0xff) } return h } var ntohs = htons // setInitOpts sets options for an SCTP association initialization // see https://tools.ietf.org/html/rfc4960#page-25 func setInitOpts(fd int, options InitMsg) error { optlen := unsafe.Sizeof(options) _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen)) return err } func setNumOstreams(fd, num int) error { return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)}) } type SCTPAddr struct { IPAddrs []net.IPAddr Port int } func (a *SCTPAddr) ToRawSockAddrBuf() []byte { p := htons(uint16(a.Port)) if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr s := syscall.RawSockaddrInet4{ Family: syscall.AF_INET, Port: p, } copy(s.Addr[:], net.IPv4zero) return toBuf(s) } buf := []byte{} for _, ip := range a.IPAddrs { ipBytes := ip.IP if len(ipBytes) == 0 { ipBytes = net.IPv4zero } if ip4 := ipBytes.To4(); ip4 != nil { s := syscall.RawSockaddrInet4{ Family: syscall.AF_INET, Port: p, } copy(s.Addr[:], ip4) buf = append(buf, toBuf(s)...) } else { var scopeid uint32 ifi, err := net.InterfaceByName(ip.Zone) if err == nil { scopeid = uint32(ifi.Index) } s := syscall.RawSockaddrInet6{ Family: syscall.AF_INET6, Port: p, Scope_id: scopeid, } copy(s.Addr[:], ipBytes) buf = append(buf, toBuf(s)...) } } return buf } func (a *SCTPAddr) String() string { var b bytes.Buffer for n, i := range a.IPAddrs { if i.IP.To4() != nil { b.WriteString(i.String()) } else if i.IP.To16() != nil { b.WriteRune('[') b.WriteString(i.String()) b.WriteRune(']') } if n < len(a.IPAddrs)-1 { b.WriteRune('/') } } b.WriteRune(':') b.WriteString(strconv.Itoa(a.Port)) return b.String() } func (a *SCTPAddr) Network() string { return "sctp" } func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { tcpnet := "" switch network { case "", "sctp": tcpnet = "tcp" case "sctp4": tcpnet = "tcp4" case "sctp6": tcpnet = "tcp6" default: return nil, fmt.Errorf("invalid net: %s", network) } elems := strings.Split(addrs, "/") if len(elems) == 0 { return nil, fmt.Errorf("invalid input: %s", addrs) } ipaddrs := make([]net.IPAddr, 0, len(elems)) for _, e := range elems[:len(elems)-1] { tcpa, err := net.ResolveTCPAddr(tcpnet, e+":") if err != nil { return nil, err } ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1]) if err != nil { return nil, err } if tcpa.IP != nil { ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } else { ipaddrs = nil } return &SCTPAddr{ IPAddrs: ipaddrs, Port: tcpa.Port, }, nil } func SCTPConnect(fd int, addr *SCTPAddr) (int, error) { buf := addr.ToRawSockAddrBuf() param := GetAddrsOld{ AddrNum: int32(len(buf)), Addrs: uintptr(uintptr(unsafe.Pointer(&buf[0]))), } optlen := unsafe.Sizeof(param) _, _, err := getsockopt(fd, SCTP_SOCKOPT_CONNECTX3, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) if err == nil { return int(param.AssocID), nil } else if err != syscall.ENOPROTOOPT { return 0, err } r0, _, err := setsockopt(fd, SCTP_SOCKOPT_CONNECTX, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) return int(r0), err } func SCTPBind(fd int, addr *SCTPAddr, flags int) error { var option uintptr switch flags { case SCTP_BINDX_ADD_ADDR: option = SCTP_SOCKOPT_BINDX_ADD case SCTP_BINDX_REM_ADDR: option = SCTP_SOCKOPT_BINDX_REM default: return syscall.EINVAL } buf := addr.ToRawSockAddrBuf() _, _, err := setsockopt(fd, option, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) return err } type SCTPConn struct { _fd int32 notificationHandler NotificationHandler } func (c *SCTPConn) fd() int { return int(atomic.LoadInt32(&c._fd)) } func NewSCTPConn(fd int, handler NotificationHandler) *SCTPConn { conn := &SCTPConn{ _fd: int32(fd), notificationHandler: handler, } return conn } func (c *SCTPConn) Write(b []byte) (int, error) { return c.SCTPWrite(b, nil) } func (c *SCTPConn) Read(b []byte) (int, error) { n, _, err := c.SCTPRead(b) if n < 0 { n = 0 } return n, err } func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error { return setInitOpts(c.fd(), InitMsg{ NumOstreams: uint16(numOstreams), MaxInstreams: uint16(maxInstreams), MaxAttempts: uint16(maxAttempts), MaxInitTimeout: uint16(maxInitTimeout), }) } func (c *SCTPConn) SubscribeEvents(flags int) error { var d, a, ad, sf, p, sh, pa, ada, au, se uint8 if flags&SCTP_EVENT_DATA_IO > 0 { d = 1 } if flags&SCTP_EVENT_ASSOCIATION > 0 { a = 1 } if flags&SCTP_EVENT_ADDRESS > 0 { ad = 1 } if flags&SCTP_EVENT_SEND_FAILURE > 0 { sf = 1 } if flags&SCTP_EVENT_PEER_ERROR > 0 { p = 1 } if flags&SCTP_EVENT_SHUTDOWN > 0 { sh = 1 } if flags&SCTP_EVENT_PARTIAL_DELIVERY > 0 { pa = 1 } if flags&SCTP_EVENT_ADAPTATION_LAYER > 0 { ada = 1 } if flags&SCTP_EVENT_AUTHENTICATION > 0 { au = 1 } if flags&SCTP_EVENT_SENDER_DRY > 0 { se = 1 } param := EventSubscribe{ DataIO: d, Association: a, Address: ad, SendFailure: sf, PeerError: p, Shutdown: sh, PartialDelivery: pa, AdaptationLayer: ada, Authentication: au, SenderDry: se, } optlen := unsafe.Sizeof(param) _, _, err := setsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) return err } func (c *SCTPConn) SubscribedEvents() (int, error) { param := EventSubscribe{} optlen := unsafe.Sizeof(param) _, _, err := getsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) if err != nil { return 0, err } var flags int if param.DataIO > 0 { flags |= SCTP_EVENT_DATA_IO } if param.Association > 0 { flags |= SCTP_EVENT_ASSOCIATION } if param.Address > 0 { flags |= SCTP_EVENT_ADDRESS } if param.SendFailure > 0 { flags |= SCTP_EVENT_SEND_FAILURE } if param.PeerError > 0 { flags |= SCTP_EVENT_PEER_ERROR } if param.Shutdown > 0 { flags |= SCTP_EVENT_SHUTDOWN } if param.PartialDelivery > 0 { flags |= SCTP_EVENT_PARTIAL_DELIVERY } if param.AdaptationLayer > 0 { flags |= SCTP_EVENT_ADAPTATION_LAYER } if param.Authentication > 0 { flags |= SCTP_EVENT_AUTHENTICATION } if param.SenderDry > 0 { flags |= SCTP_EVENT_SENDER_DRY } return flags, nil } func (c *SCTPConn) SetDefaultSentParam(info *SndRcvInfo) error { optlen := unsafe.Sizeof(*info) _, _, err := setsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(optlen)) return err } func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) { info := &SndRcvInfo{} optlen := unsafe.Sizeof(*info) _, _, err := getsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(&optlen))) return info, err } func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { addr := &SCTPAddr{ IPAddrs: make([]net.IPAddr, n), } switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family { case syscall.AF_INET: addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) tmp := syscall.RawSockaddrInet4{} size := unsafe.Sizeof(tmp) for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]} } case syscall.AF_INET6: addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) tmp := syscall.RawSockaddrInet6{} size := unsafe.Sizeof(tmp) for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) var zone string ifi, err := net.InterfaceByIndex(int(a.Scope_id)) if err == nil { zone = ifi.Name } addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone} } default: return nil, fmt.Errorf("unknown address family: %d", family) } return addr, nil } func sctpGetAddrs(fd, id, optname int) (*SCTPAddr, error) { type getaddrs struct { assocId int32 addrNum uint32 addrs [4096]byte } param := getaddrs{ assocId: int32(id), } optlen := unsafe.Sizeof(param) _, _, err := getsockopt(fd, uintptr(optname), uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) if err != nil { return nil, err } return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), int(param.addrNum)) } func (c *SCTPConn) SCTPGetPrimaryPeerAddr() (*SCTPAddr, error) { type sctpGetSetPrim struct { assocId int32 addrs [128]byte } param := sctpGetSetPrim{ assocId: int32(0), } optlen := unsafe.Sizeof(param) _, _, err := getsockopt(c.fd(), SCTP_PRIMARY_ADDR, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) if err != nil { return nil, err } return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), 1) } func (c *SCTPConn) SCTPLocalAddr(id int) (*SCTPAddr, error) { return sctpGetAddrs(c.fd(), id, SCTP_GET_LOCAL_ADDRS) } func (c *SCTPConn) SCTPRemoteAddr(id int) (*SCTPAddr, error) { return sctpGetAddrs(c.fd(), id, SCTP_GET_PEER_ADDRS) } func (c *SCTPConn) LocalAddr() net.Addr { addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_LOCAL_ADDRS) if err != nil { return nil } return addr } func (c *SCTPConn) RemoteAddr() net.Addr { addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_PEER_ADDRS) if err != nil { return nil } return addr } func (c *SCTPConn) PeelOff(id int) (*SCTPConn, error) { type peeloffArg struct { assocId int32 sd int } param := peeloffArg{ assocId: int32(id), } optlen := unsafe.Sizeof(param) _, _, err := getsockopt(c.fd(), SCTP_SOCKOPT_PEELOFF, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) if err != nil { return nil, err } return &SCTPConn{_fd: int32(param.sd)}, nil } func (c *SCTPConn) SetDeadline(t time.Time) error { return syscall.EOPNOTSUPP } func (c *SCTPConn) SetReadDeadline(t time.Time) error { return syscall.EOPNOTSUPP } func (c *SCTPConn) SetWriteDeadline(t time.Time) error { return syscall.EOPNOTSUPP } type SCTPListener struct { fd int m sync.Mutex } func (ln *SCTPListener) Addr() net.Addr { laddr, err := sctpGetAddrs(ln.fd, 0, SCTP_GET_LOCAL_ADDRS) if err != nil { return nil } return laddr } type SCTPSndRcvInfoWrappedConn struct { conn *SCTPConn } func NewSCTPSndRcvInfoWrappedConn(conn *SCTPConn) *SCTPSndRcvInfoWrappedConn { conn.SubscribeEvents(SCTP_EVENT_DATA_IO) return &SCTPSndRcvInfoWrappedConn{conn} } func (c *SCTPSndRcvInfoWrappedConn) Write(b []byte) (int, error) { if len(b) < int(sndRcvInfoSize) { return 0, syscall.EINVAL } info := (*SndRcvInfo)(unsafe.Pointer(&b[0])) n, err := c.conn.SCTPWrite(b[sndRcvInfoSize:], info) return n + int(sndRcvInfoSize), err } func (c *SCTPSndRcvInfoWrappedConn) Read(b []byte) (int, error) { if len(b) < int(sndRcvInfoSize) { return 0, syscall.EINVAL } n, info, err := c.conn.SCTPRead(b[sndRcvInfoSize:]) if err != nil { return n, err } copy(b, toBuf(info)) return n + int(sndRcvInfoSize), err } func (c *SCTPSndRcvInfoWrappedConn) Close() error { return c.conn.Close() } func (c *SCTPSndRcvInfoWrappedConn) LocalAddr() net.Addr { return c.conn.LocalAddr() } func (c *SCTPSndRcvInfoWrappedConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } func (c *SCTPSndRcvInfoWrappedConn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) } func (c *SCTPSndRcvInfoWrappedConn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } func (c *SCTPSndRcvInfoWrappedConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) }