diff --git a/association.go b/association.go index b0b5e4f9..2a3dcc17 100644 --- a/association.go +++ b/association.go @@ -212,6 +212,19 @@ func Server(config Config) (*Association, error) { } } +// DialAssociation connects to the given network address and establishes a +// SCTP association on top. The net.Conn in the config is ignored. +func DialAssociation(network string, raddr *net.UDPAddr, config Config) (*Association, error) { + pConn, err := net.DialUDP(network, nil, raddr) + if err != nil { + return nil, err + } + + config.NetConn = pConn + + return Client(config) +} + // Client opens a SCTP stream over a conn func Client(config Config) (*Association, error) { a := createAssociation(config) diff --git a/examples/ping-pong/Makefile b/examples/ping-pong/Makefile index 5ab5a615..09c40676 100644 --- a/examples/ping-pong/Makefile +++ b/examples/ping-pong/Makefile @@ -1,7 +1,7 @@ all: ping pong -ping: ping.go conn.go +ping: ping.go go build -o $@ -pong: pong.go conn.go +pong: pong.go go build -o $@ -tags $@ \ No newline at end of file diff --git a/examples/ping-pong/conn.go b/examples/ping-pong/conn.go deleted file mode 100644 index 4b5d5e88..00000000 --- a/examples/ping-pong/conn.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "net" - "sync" - "time" -) - -// Reference: https://github.com/pion/sctp/blob/master/association_test.go -// Since UDP is connectionless, as a server, it doesn't know how to reply -// simply using the `Write` method. So, to make it work, `disconnectedPacketConn` -// will infer the last packet that it reads as the reply address for `Write` - -type disconnectedPacketConn struct { - mu sync.RWMutex - rAddr net.Addr - pConn net.PacketConn -} - -// Read -func (c *disconnectedPacketConn) Read(p []byte) (int, error) { - i, rAddr, err := c.pConn.ReadFrom(p) - if err != nil { - return 0, err - } - - c.mu.Lock() - c.rAddr = rAddr - c.mu.Unlock() - - return i, err -} - -// Write writes len(p) bytes from p to the DTLS connection -func (c *disconnectedPacketConn) Write(p []byte) (n int, err error) { - return c.pConn.WriteTo(p, c.RemoteAddr()) -} - -// Close closes the conn and releases any Read calls -func (c *disconnectedPacketConn) Close() error { - return c.pConn.Close() -} - -// LocalAddr is a stub -func (c *disconnectedPacketConn) LocalAddr() net.Addr { - if c.pConn != nil { - return c.pConn.LocalAddr() - } - return nil -} - -// RemoteAddr is a stub -func (c *disconnectedPacketConn) RemoteAddr() net.Addr { - c.mu.RLock() - defer c.mu.RUnlock() - return c.rAddr -} - -// SetDeadline is a stub -func (c *disconnectedPacketConn) SetDeadline(t time.Time) error { - return nil -} - -// SetReadDeadline is a stub -func (c *disconnectedPacketConn) SetReadDeadline(t time.Time) error { - return nil -} - -// SetWriteDeadline is a stub -func (c *disconnectedPacketConn) SetWriteDeadline(t time.Time) error { - return nil -} diff --git a/examples/ping-pong/ping.go b/examples/ping-pong/ping.go index aef6bc02..ea09604c 100644 --- a/examples/ping-pong/ping.go +++ b/examples/ping-pong/ping.go @@ -12,18 +12,13 @@ import ( ) func main() { - conn, err := net.Dial("udp", "127.0.0.1:5678") - if err != nil { - log.Fatal(err) - } - defer conn.Close() - fmt.Println("dialed udp ponger") + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5678} config := sctp.Config{ - NetConn: conn, LoggerFactory: logging.NewDefaultLoggerFactory(), } - a, err := sctp.Client(config) + a, err := sctp.DialAssociation("udp", addr, config) if err != nil { log.Fatal(err) } diff --git a/examples/ping-pong/pong.go b/examples/ping-pong/pong.go index cf63e2c9..6eaaf1e5 100644 --- a/examples/ping-pong/pong.go +++ b/examples/ping-pong/pong.go @@ -13,29 +13,27 @@ import ( ) func main() { - addr := net.UDPAddr{ - IP: net.IPv4(127, 0, 0, 1), - Port: 5678, - } + addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5678} - conn, err := net.ListenUDP("udp", &addr) + config := sctp.Config{ + LoggerFactory: logging.NewDefaultLoggerFactory(), + } + l, err := sctp.ListenAssociation("udp", addr, config) if err != nil { log.Fatal(err) } - defer conn.Close() - fmt.Println("created a udp listener") + defer l.Close() + fmt.Println("created a listener") - config := sctp.Config{ - NetConn: &disconnectedPacketConn{pConn: conn}, - LoggerFactory: logging.NewDefaultLoggerFactory(), - } - a, err := sctp.Server(config) + // Note: You should accept all incoming associations in a loop. + a, err := l.Accept() if err != nil { log.Fatal(err) } defer a.Close() - fmt.Println("created a server") + fmt.Println("accepted an association") + // Note: You should accept all incoming streams in a loop. stream, err := a.AcceptStream() if err != nil { log.Fatal(err) diff --git a/go.mod b/go.mod index 02a0b813..94c2ccd6 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/pion/logging v0.2.2 github.com/pion/transport v0.10.0 + github.com/pion/udp v0.1.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.5.1 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect diff --git a/go.sum b/go.sum index 94dbde20..368021da 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80= github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= +github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI= +github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/listener.go b/listener.go new file mode 100644 index 00000000..f7d6ed35 --- /dev/null +++ b/listener.go @@ -0,0 +1,59 @@ +package sctp + +import ( + "net" + + "github.com/pion/udp" +) + +// ListenAssociation creates a SCTP association listener +func ListenAssociation(network string, laddr *net.UDPAddr, config Config) (*AssociationListener, error) { + lc := udp.ListenConfig{} + parent, err := lc.Listen(network, laddr) + if err != nil { + return nil, err + } + return &AssociationListener{ + config: config, + parent: parent, + }, nil +} + +// NewAssociationListener creates a SCTP association listener +// which accepts connections from an inner Listener. +// The net.Conn in the config is ignored. +func NewAssociationListener(inner net.Listener, config Config) (*AssociationListener, error) { + return &AssociationListener{ + config: config, + parent: inner, + }, nil +} + +// AssociationListener represents a SCTP association listener +type AssociationListener struct { + config Config + parent net.Listener +} + +// Accept waits for and returns the next association to the listener. +// You have to either close or read on all connection that are created. +func (l *AssociationListener) Accept() (*Association, error) { + c, err := l.parent.Accept() + if err != nil { + return nil, err + } + l.config.NetConn = c + return Server(l.config) +} + +// Close closes the listener. +// Any blocked Accept operations will be unblocked and return errors. +// Already Accepted connections are not closed. +func (l *AssociationListener) Close() error { + return l.parent.Close() +} + +// Addr returns the listener's network address. +func (l *AssociationListener) Addr() net.Addr { + return l.parent.Addr() +}