Loading .gitignore +1 −1 Original line number Diff line number Diff line Loading @@ -5,5 +5,5 @@ .DS_Store datadir/ client/snowflake server/snowflake server/server snowflake.log server/snowflake.go +87 −140 Original line number Diff line number Diff line Loading @@ -12,25 +12,22 @@ import ( "syscall" "time" "git.torproject.org/pluggable-transports/goptlib.git" "github.com/keroserene/go-webrtc" "github.com/keroserene/go-webrtc/data" "git.torproject.org/pluggable-transports/goptlib.git" ) var ptMethodName = "snowflake" var ptInfo pt.ServerInfo var logFile *os.File // When a connection handler starts, +1 is written to this channel; when it // ends, -1 is written. // When a datachannel handler starts, +1 is written to this channel; // when it ends, -1 is written. var handlerChan = make(chan int) var signalChan = make(chan *webrtc.SessionDescription) func copyLoop(a, b net.Conn) { var wg sync.WaitGroup wg.Add(2) go func() { io.Copy(b, a) wg.Done() Loading @@ -39,28 +36,25 @@ func copyLoop(a, b net.Conn) { io.Copy(a, b) wg.Done() }() wg.Wait() } type webRTCConn struct { pc *webrtc.PeerConnection dc *data.Channel recvPipe *io.PipeReader pc *webrtc.PeerConnection pr *io.PipeReader } func (c *webRTCConn) Read(b []byte) (int, error) { return c.recvPipe.Read(b) return c.pr.Read(b) } func (c *webRTCConn) Write(b []byte) (int, error) { log.Printf("webrtc Write %d %q", len(b), string(b)) dc.Send(b) c.dc.Send(b) return len(b), nil } func (c *webRTCConn) Close() error { // Data channel closed implicitly? return c.pc.Close() } Loading @@ -84,169 +78,125 @@ func (c *webRTCConn) SetWriteDeadline(t time.Time) error { return fmt.Errorf("SetWriteDeadline not implemented") } type webRTCListener struct { peerConnectionChan chan *webrtc.PeerConnection stopChan chan struct{} } func (ln *webRTCListener) Accept() (net.Conn, error) { offer, ok := <-signalChan if !ok { return nil, fmt.Errorf("signal channel closed") } pc, ok := <-ln.peerConnectionChan if !ok { return nil, fmt.Errorf("PeerConnection channel closed") } func datachannelHandler(conn *webRTCConn) { handlerChan <- 1 defer func() { handlerChan <- -1 }() err := pc.SetRemoteDescription(offer) or, err := pt.DialOr(&ptInfo, "", ptMethodName) // TODO: Extended OR if err != nil { return nil, err log.Printf("Failed to connect to ORPort: " + err.Error()) return } defer or.Close() go func() { answer, err := pc.CreateAnswer() if err != nil { // signal error upwards fmt.Println(err) return pr, pw := io.Pipe() conn.pr = pr dc := conn.dc dc.OnClose = func() { pw.Close() } err = pc.SetLocalDescription(answer) dc.OnMessage = func(msg []byte) { n, err := pw.Write(msg) if err != nil { // signal error upwards fmt.Println(err) return pw.CloseWithError(err) } }() select { case conn := <-ln.connChan: return conn, nil case err := <-ln.errChan: return nil, err } return &webRTCConn{pc: pc, dc: dc, recvPipe: nil}, nil if n != len(msg) { panic("short write") } func (ln *webRTCListener) Close() error { // Stop the PeerConnection factory goroutine. close(ln.stopChan) return nil } func (ln *webRTCListener) Addr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 1} copyLoop(conn, or) } func makePeerConnection(config *webrtc.Configuration) (*webrtc.PeerConnection, error) { pc, err := webrtc.NewPeerConnection(config) if err != nil { log.Printf("NewPeerConnection: %s", err) return nil, err } pc.OnNegotiationNeeded = func() { log.Println("OnNegotiationNeeded") panic("OnNegotiationNeeded") } pc.OnIceCandidate = func(candidate webrtc.IceCandidate) { log.Printf("OnIceCandidate %s", candidate.Serialize()) // Allow candidates to accumulate until OnIceComplete. } pc.OnIceComplete = func() { log.Printf("OnIceComplete") } pc.OnDataChannel = func(channel *data.Channel) { pc.OnDataChannel = func(dc *data.Channel) { log.Println("OnDataChannel") panic("OnDataChannel") datachannelHandler(&webRTCConn{pc: pc, dc: dc}) } return pc, nil } func listenWebRTC(config *webrtc.Configuration) (*webRTCListener, error) { ln := new(webRTCListener) ln.peerConnectionChan = make(chan *webrtc.PeerConnection) ln.stopChan = make(chan struct{}) // This goroutine builds new PeerConnections that await incoming offers. go func() { loop: for { select { case <-ln.stopChan: break loop default: pc, err := makePeerConnection(config) if err != nil { log.Printf("makePeerConnection: %s", err) break func readSignalingMessages(signalChan chan *webrtc.SessionDescription, f *os.File) { s := bufio.NewScanner(f) for s.Scan() { msg := s.Text() sdp := webrtc.DeserializeSessionDescription(msg) if sdp == nil { log.Printf("ignoring invalid signal message %q", msg) continue } ln.peerConnectionChan <- pc signalChan <- sdp } if err := s.Err(); err != nil { log.Printf("signal FIFO: %s", err) } close(ln.peerConnectionChan) }() return ln, nil } func handler(conn net.Conn) error { defer conn.Close() handlerChan <- 1 defer func() { handlerChan <- -1 }() or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "webrtc") func generateAnswer(pc *webrtc.PeerConnection) { fmt.Println("Generating answer...") answer, err := pc.CreateAnswer() // blocking if err != nil { return err fmt.Println(err) return } defer or.Close() copyLoop(conn, or) return nil pc.SetLocalDescription(answer) } func acceptLoop(ln net.Listener) error { defer ln.Close() for { conn, err := ln.Accept() func listenWebRTC(config *webrtc.Configuration, signal string) (*os.File, error) { err := syscall.Mkfifo(signal, 0600) if err != nil { if e, ok := err.(net.Error); ok && e.Temporary() { continue } return err if err.(syscall.Errno) != syscall.EEXIST { return nil, err } go handler(conn) } signalFile, err := os.OpenFile(signal, os.O_RDONLY, 0600) if err != nil { return nil, err } defer signalFile.Close() func readSignalingMessages(f *os.File) { s := bufio.NewScanner(f) for s.Scan() { msg := s.Text() sdp := webrtc.DeserializeSessionDescription(msg) if sdp == nil { log.Printf("ignoring invalid signal message %q", msg) continue var signalChan = make(chan *webrtc.SessionDescription) go func() { for { select { case sdp := <- signalChan: pc, err := makePeerConnection(config) if err != nil { log.Printf("makePeerConnection: %s", err) break } signalChan <- sdp err = pc.SetRemoteDescription(sdp) if err != nil { fmt.Println("ERROR", err) break } fmt.Println("sdp offer successfully received.") go generateAnswer(pc) } close(signalChan) if err := s.Err(); err != nil { log.Printf("signal FIFO: %s", err) } }() go readSignalingMessages(signalChan, signalFile) return signalFile, nil } func main() { var err error logFile, err = os.OpenFile("webrtc-server.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) logFile, err = os.OpenFile("snowflake.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { log.Fatal(err) } Loading @@ -254,7 +204,6 @@ func main() { log.SetOutput(logFile) log.Println("starting") webrtc.SetLoggingVerbosity(1) ptInfo, err = pt.ServerSetup(nil) Loading @@ -264,18 +213,16 @@ func main() { webRTCConfig := webrtc.NewConfiguration(webrtc.OptionIceServer("stun:stun.l.google.com:19302")) listeners := make([]net.Listener, 0) listeners := make([]*os.File, 0) for _, bindaddr := range ptInfo.Bindaddrs { switch bindaddr.MethodName { case "webrtc": // Ignore bindaddr.Addr. ln, err := listenWebRTC(webRTCConfig) case ptMethodName: ln, err := listenWebRTC(webRTCConfig, "signal") // meh if err != nil { pt.SmethodError(bindaddr.MethodName, err.Error()) break } go acceptLoop(ln) pt.Smethod(bindaddr.MethodName, ln.Addr()) pt.Smethod(bindaddr.MethodName, nil) listeners = append(listeners, ln) default: pt.SmethodError(bindaddr.MethodName, "no such method") Loading server/torrc +2 −1 Original line number Diff line number Diff line Loading @@ -3,5 +3,6 @@ ORPort 9001 ExtORPort auto SocksPort 0 ExitPolicy reject *:* DataDirectory datadir ServerTransportPlugin snowflake exec ./snowflake ServerTransportPlugin snowflake exec ./server Loading
.gitignore +1 −1 Original line number Diff line number Diff line Loading @@ -5,5 +5,5 @@ .DS_Store datadir/ client/snowflake server/snowflake server/server snowflake.log
server/snowflake.go +87 −140 Original line number Diff line number Diff line Loading @@ -12,25 +12,22 @@ import ( "syscall" "time" "git.torproject.org/pluggable-transports/goptlib.git" "github.com/keroserene/go-webrtc" "github.com/keroserene/go-webrtc/data" "git.torproject.org/pluggable-transports/goptlib.git" ) var ptMethodName = "snowflake" var ptInfo pt.ServerInfo var logFile *os.File // When a connection handler starts, +1 is written to this channel; when it // ends, -1 is written. // When a datachannel handler starts, +1 is written to this channel; // when it ends, -1 is written. var handlerChan = make(chan int) var signalChan = make(chan *webrtc.SessionDescription) func copyLoop(a, b net.Conn) { var wg sync.WaitGroup wg.Add(2) go func() { io.Copy(b, a) wg.Done() Loading @@ -39,28 +36,25 @@ func copyLoop(a, b net.Conn) { io.Copy(a, b) wg.Done() }() wg.Wait() } type webRTCConn struct { pc *webrtc.PeerConnection dc *data.Channel recvPipe *io.PipeReader pc *webrtc.PeerConnection pr *io.PipeReader } func (c *webRTCConn) Read(b []byte) (int, error) { return c.recvPipe.Read(b) return c.pr.Read(b) } func (c *webRTCConn) Write(b []byte) (int, error) { log.Printf("webrtc Write %d %q", len(b), string(b)) dc.Send(b) c.dc.Send(b) return len(b), nil } func (c *webRTCConn) Close() error { // Data channel closed implicitly? return c.pc.Close() } Loading @@ -84,169 +78,125 @@ func (c *webRTCConn) SetWriteDeadline(t time.Time) error { return fmt.Errorf("SetWriteDeadline not implemented") } type webRTCListener struct { peerConnectionChan chan *webrtc.PeerConnection stopChan chan struct{} } func (ln *webRTCListener) Accept() (net.Conn, error) { offer, ok := <-signalChan if !ok { return nil, fmt.Errorf("signal channel closed") } pc, ok := <-ln.peerConnectionChan if !ok { return nil, fmt.Errorf("PeerConnection channel closed") } func datachannelHandler(conn *webRTCConn) { handlerChan <- 1 defer func() { handlerChan <- -1 }() err := pc.SetRemoteDescription(offer) or, err := pt.DialOr(&ptInfo, "", ptMethodName) // TODO: Extended OR if err != nil { return nil, err log.Printf("Failed to connect to ORPort: " + err.Error()) return } defer or.Close() go func() { answer, err := pc.CreateAnswer() if err != nil { // signal error upwards fmt.Println(err) return pr, pw := io.Pipe() conn.pr = pr dc := conn.dc dc.OnClose = func() { pw.Close() } err = pc.SetLocalDescription(answer) dc.OnMessage = func(msg []byte) { n, err := pw.Write(msg) if err != nil { // signal error upwards fmt.Println(err) return pw.CloseWithError(err) } }() select { case conn := <-ln.connChan: return conn, nil case err := <-ln.errChan: return nil, err } return &webRTCConn{pc: pc, dc: dc, recvPipe: nil}, nil if n != len(msg) { panic("short write") } func (ln *webRTCListener) Close() error { // Stop the PeerConnection factory goroutine. close(ln.stopChan) return nil } func (ln *webRTCListener) Addr() net.Addr { return &net.TCPAddr{IP: net.IPv4zero, Port: 1} copyLoop(conn, or) } func makePeerConnection(config *webrtc.Configuration) (*webrtc.PeerConnection, error) { pc, err := webrtc.NewPeerConnection(config) if err != nil { log.Printf("NewPeerConnection: %s", err) return nil, err } pc.OnNegotiationNeeded = func() { log.Println("OnNegotiationNeeded") panic("OnNegotiationNeeded") } pc.OnIceCandidate = func(candidate webrtc.IceCandidate) { log.Printf("OnIceCandidate %s", candidate.Serialize()) // Allow candidates to accumulate until OnIceComplete. } pc.OnIceComplete = func() { log.Printf("OnIceComplete") } pc.OnDataChannel = func(channel *data.Channel) { pc.OnDataChannel = func(dc *data.Channel) { log.Println("OnDataChannel") panic("OnDataChannel") datachannelHandler(&webRTCConn{pc: pc, dc: dc}) } return pc, nil } func listenWebRTC(config *webrtc.Configuration) (*webRTCListener, error) { ln := new(webRTCListener) ln.peerConnectionChan = make(chan *webrtc.PeerConnection) ln.stopChan = make(chan struct{}) // This goroutine builds new PeerConnections that await incoming offers. go func() { loop: for { select { case <-ln.stopChan: break loop default: pc, err := makePeerConnection(config) if err != nil { log.Printf("makePeerConnection: %s", err) break func readSignalingMessages(signalChan chan *webrtc.SessionDescription, f *os.File) { s := bufio.NewScanner(f) for s.Scan() { msg := s.Text() sdp := webrtc.DeserializeSessionDescription(msg) if sdp == nil { log.Printf("ignoring invalid signal message %q", msg) continue } ln.peerConnectionChan <- pc signalChan <- sdp } if err := s.Err(); err != nil { log.Printf("signal FIFO: %s", err) } close(ln.peerConnectionChan) }() return ln, nil } func handler(conn net.Conn) error { defer conn.Close() handlerChan <- 1 defer func() { handlerChan <- -1 }() or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "webrtc") func generateAnswer(pc *webrtc.PeerConnection) { fmt.Println("Generating answer...") answer, err := pc.CreateAnswer() // blocking if err != nil { return err fmt.Println(err) return } defer or.Close() copyLoop(conn, or) return nil pc.SetLocalDescription(answer) } func acceptLoop(ln net.Listener) error { defer ln.Close() for { conn, err := ln.Accept() func listenWebRTC(config *webrtc.Configuration, signal string) (*os.File, error) { err := syscall.Mkfifo(signal, 0600) if err != nil { if e, ok := err.(net.Error); ok && e.Temporary() { continue } return err if err.(syscall.Errno) != syscall.EEXIST { return nil, err } go handler(conn) } signalFile, err := os.OpenFile(signal, os.O_RDONLY, 0600) if err != nil { return nil, err } defer signalFile.Close() func readSignalingMessages(f *os.File) { s := bufio.NewScanner(f) for s.Scan() { msg := s.Text() sdp := webrtc.DeserializeSessionDescription(msg) if sdp == nil { log.Printf("ignoring invalid signal message %q", msg) continue var signalChan = make(chan *webrtc.SessionDescription) go func() { for { select { case sdp := <- signalChan: pc, err := makePeerConnection(config) if err != nil { log.Printf("makePeerConnection: %s", err) break } signalChan <- sdp err = pc.SetRemoteDescription(sdp) if err != nil { fmt.Println("ERROR", err) break } fmt.Println("sdp offer successfully received.") go generateAnswer(pc) } close(signalChan) if err := s.Err(); err != nil { log.Printf("signal FIFO: %s", err) } }() go readSignalingMessages(signalChan, signalFile) return signalFile, nil } func main() { var err error logFile, err = os.OpenFile("webrtc-server.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) logFile, err = os.OpenFile("snowflake.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { log.Fatal(err) } Loading @@ -254,7 +204,6 @@ func main() { log.SetOutput(logFile) log.Println("starting") webrtc.SetLoggingVerbosity(1) ptInfo, err = pt.ServerSetup(nil) Loading @@ -264,18 +213,16 @@ func main() { webRTCConfig := webrtc.NewConfiguration(webrtc.OptionIceServer("stun:stun.l.google.com:19302")) listeners := make([]net.Listener, 0) listeners := make([]*os.File, 0) for _, bindaddr := range ptInfo.Bindaddrs { switch bindaddr.MethodName { case "webrtc": // Ignore bindaddr.Addr. ln, err := listenWebRTC(webRTCConfig) case ptMethodName: ln, err := listenWebRTC(webRTCConfig, "signal") // meh if err != nil { pt.SmethodError(bindaddr.MethodName, err.Error()) break } go acceptLoop(ln) pt.Smethod(bindaddr.MethodName, ln.Addr()) pt.Smethod(bindaddr.MethodName, nil) listeners = append(listeners, ln) default: pt.SmethodError(bindaddr.MethodName, "no such method") Loading
server/torrc +2 −1 Original line number Diff line number Diff line Loading @@ -3,5 +3,6 @@ ORPort 9001 ExtORPort auto SocksPort 0 ExitPolicy reject *:* DataDirectory datadir ServerTransportPlugin snowflake exec ./snowflake ServerTransportPlugin snowflake exec ./server