diff --git a/torrc b/torrc new file mode 100644 index 0000000..50193f2 --- /dev/null +++ b/torrc @@ -0,0 +1,4 @@ +UseBridges 1 +ClientTransportPlugin webrtc exec ./webrtc-client + +Bridge webrtc 0.0.3.0:1 diff --git a/webrtc-client.go b/webrtc-client.go new file mode 100644 index 0000000..09df4fb --- /dev/null +++ b/webrtc-client.go @@ -0,0 +1,173 @@ +package main + +import ( + "fmt" + "log" + "net" + "os" + "os/signal" + "syscall" + + "github.com/keroserene/go-webrtc" + "github.com/keroserene/go-webrtc/data" + + "git.torproject.org/pluggable-transports/goptlib.git" +) + +var ptInfo pt.ClientInfo +var logFile *os.File + +// When a connection handler starts, +1 is written to this channel; when it +// ends, -1 is written. +var handlerChan = make(chan int) + +func handler(conn *pt.SocksConn) error { + handlerChan <- 1 + defer func() { + handlerChan <- -1 + }() + defer conn.Close() + + config := webrtc.NewConfiguration(webrtc.OptionIceServer("stun:stun.l.google.com:19302")) + pc, err := webrtc.NewPeerConnection(config) + if err != nil { + log.Printf("NewPeerConnection: %s", err) + return err + } + + pc.OnNegotiationNeeded = func() { + // log.Println("OnNegotiationNeeded") + go func() { + offer, err := pc.CreateOffer() + if err != nil { + log.Printf("CreateOffer: %s", err) + return + } + fmt.Fprintln(logFile, offer.Serialize()) + pc.SetLocalDescription(offer) + }() + } + pc.OnIceCandidate = func(candidate webrtc.IceCandidate) { + // log.Printf("OnIceCandidate %q", candidate.Candidate) + fmt.Fprintln(logFile, candidate.Serialize()) + } + pc.OnDataChannel = func(channel *data.Channel) { + log.Println("OnDataChannel") + panic("OnDataChannel") + } + + dc, err := pc.CreateDataChannel("test", data.Init{}) + if err != nil { + log.Printf("CreateDataChannel: %s", err) + return err + } + dc.OnOpen = func() { + log.Println("OnOpen channel") + } + dc.OnClose = func() { + log.Println("OnClose channel") + } + dc.OnMessage = func(msg []byte) { + log.Println("OnMessage channel %d %q", len(msg), msg) + } + + // defer close channel? + + err = conn.Grant(&net.TCPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + return err + } + + <-make(chan int) + + return nil +} + +func acceptLoop(ln *pt.SocksListener) error { + defer ln.Close() + for { + conn, err := ln.AcceptSocks() + if err != nil { + if e, ok := err.(net.Error); ok && e.Temporary() { + continue + } + return err + } + go handler(conn) + } +} + +func main() { + var err error + + logFile, err = os.OpenFile("webrtc-client.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + log.Fatal(err) + } + defer logFile.Close() + log.SetOutput(logFile) + + log.Println("starting") + + webrtc.SetLoggingVerbosity(1) + + ptInfo, err = pt.ClientSetup(nil) + if err != nil { + os.Exit(1) + } + + if ptInfo.ProxyURL != nil { + pt.ProxyError("proxy is not supported") + os.Exit(1) + } + + listeners := make([]net.Listener, 0) + for _, methodName := range ptInfo.MethodNames { + switch methodName { + case "webrtc": + ln, err := pt.ListenSocks("tcp", "127.0.0.1:0") + if err != nil { + pt.CmethodError(methodName, err.Error()) + break + } + go acceptLoop(ln) + pt.Cmethod(methodName, ln.Version(), ln.Addr()) + listeners = append(listeners, ln) + default: + pt.CmethodError(methodName, "no such method") + } + } + pt.CmethodsDone() + + var numHandlers int = 0 + var sig os.Signal + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + // wait for first signal + sig = nil + for sig == nil { + select { + case n := <-handlerChan: + numHandlers += n + case sig = <-sigChan: + } + } + for _, ln := range listeners { + ln.Close() + } + + if sig == syscall.SIGTERM { + return + } + + // wait for second signal or no more handlers + sig = nil + for sig == nil && numHandlers != 0 { + select { + case n := <-handlerChan: + numHandlers += n + case sig = <-sigChan: + } + } +}