package main import ( "io" "log" "net" "sync" "github.com/ishidawataru/sctp" ) // SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to // handle SCTP traffic forwarding between the frontend and backend addresses. type SCTPProxy struct { listener *sctp.SCTPListener frontendAddr *sctp.SCTPAddr backendAddr *sctp.SCTPAddr } // NewSCTPProxy creates a new SCTPProxy. func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) { // detect version of hostIP to bind only to correct version ipVersion := ipv4 if frontendAddr.IPAddrs[0].IP.To4() == nil { ipVersion = ipv6 } listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr) if err != nil { return nil, err } // If the port in frontendAddr was 0 then ListenSCTP will have a picked // a port to listen on, hence the call to Addr to get that actual port: return &SCTPProxy{ listener: listener, frontendAddr: listener.Addr().(*sctp.SCTPAddr), backendAddr: backendAddr, }, nil } func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) { backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr) if err != nil { log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err) client.Close() return } clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client) backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend) var wg sync.WaitGroup var broker = func(to, from net.Conn) { io.Copy(to, from) from.Close() to.Close() wg.Done() } wg.Add(2) go broker(clientC, backendC) go broker(backendC, clientC) finish := make(chan struct{}) go func() { wg.Wait() close(finish) }() select { case <-quit: case <-finish: } clientC.Close() backendC.Close() <-finish } // Run starts forwarding the traffic using SCTP. func (proxy *SCTPProxy) Run() { quit := make(chan bool) defer close(quit) for { client, err := proxy.listener.Accept() if err != nil { log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) return } go proxy.clientLoop(client.(*sctp.SCTPConn), quit) } } // Close stops forwarding the traffic. func (proxy *SCTPProxy) Close() { proxy.listener.Close() } // FrontendAddr returns the SCTP address on which the proxy is listening. func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } // BackendAddr returns the SCTP proxied address. func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr }