diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7470079 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +/wgortc + +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# End of https://www.toptal.com/developers/gitignore/api/go \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e94b07e --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# wgortc (Wireguard Over Webrtc) + +## How to Use + +```go +``` \ No newline at end of file diff --git a/bind.go b/bind.go new file mode 100644 index 0000000..1dc4e60 --- /dev/null +++ b/bind.go @@ -0,0 +1,210 @@ +package wgortc + +import ( + "bytes" + "encoding/json" + "errors" + "net" + "net/http" + "sync" + "sync/atomic" + + "github.com/donovanhide/eventsource" + "github.com/lainio/err2" + "github.com/lainio/err2/try" + "github.com/pion/ice/v2" + "github.com/pion/webrtc/v3" + "github.com/shynome/wgortc/mux" + "golang.zx2c4.com/wireguard/conn" +) + +type Bind struct { + id string + signaler *signaler + + pcs []*webrtc.PeerConnection + pcsL sync.Locker + + api *webrtc.API + mux ice.UDPMux + + connectionStream *eventsource.Stream + + ICEServers []webrtc.ICEServer + + msgCh chan packetMsg + + closed uint32 +} + +var _ conn.Bind = (*Bind)(nil) + +func NewBind(id string, signaler string) *Bind { + return &Bind{ + id: id, + signaler: newSignaler(signaler), + + pcsL: &sync.Mutex{}, + + closed: 0, + } +} + +func (b *Bind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) { + defer err2.Handle(&err) + + fns = append(fns, b.receiveFunc) + + b.msgCh = make(chan packetMsg, b.BatchSize()-1) + + settingEngine := webrtc.SettingEngine{} + if mux.WithUDPMux != nil { + b.mux = try.To1(mux.WithUDPMux(settingEngine, port)) + actualPort = port + } + b.api = webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine)) + + req := try.To1(b.signaler.newReq(http.MethodGet, b.id, http.NoBody)) + b.connectionStream = try.To1(eventsource.SubscribeWithRequest("", req)) + go func() { + for ev := range b.connectionStream.Events { + go b.handleConnect(ev) + } + }() + + atomic.StoreUint32(&b.closed, 0) + return +} + +type packetMsg struct { + data *[]byte + ep conn.Endpoint +} + +func (b *Bind) receiveFunc(packets [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) { + if b.isClosed() { + return 0, net.ErrClosed + } + for i := 0; i < b.BatchSize(); i++ { + msg, ok := <-b.msgCh + if !ok { + return 0, net.ErrClosed + } + sizes[i] = copy(packets[i], *msg.data) + eps[i] = msg.ep + n += 1 + } + return +} + +func (b *Bind) receiveMsg(ep conn.Endpoint) func(msg webrtc.DataChannelMessage) { + return func(msg webrtc.DataChannelMessage) { + if b.isClosed() { + return + } + b.msgCh <- packetMsg{ + data: &msg.Data, + ep: ep, + } + } +} + +func (b *Bind) handleConnect(ev eventsource.Event) { + var pc *webrtc.PeerConnection + defer err2.Catch(func(err error) { + if pc != nil { + pc.Close() + } + }) + + var offer webrtc.SessionDescription + try.To(json.Unmarshal([]byte(ev.Data()), &offer)) + sdp := try.To1(offer.Unmarshal()) + if sdp.Origin.Username == "" { + return + } + + config := webrtc.Configuration{ + ICEServers: b.ICEServers, + } + pc = try.To1(b.api.NewPeerConnection(config)) + pc.OnDataChannel(func(dc *webrtc.DataChannel) { + ep := b.NewEndpoint(sdp.Origin.Username) + ep.init.Do(func() { + ep.dc = dc + ep.dsiableReconnect = true + }) + dc.OnMessage(b.receiveMsg(ep)) + }) + + try.To(pc.SetRemoteDescription(offer)) + answer := try.To1(pc.CreateAnswer(nil)) + gatherComplete := webrtc.GatheringCompletePromise(pc) + try.To(pc.SetLocalDescription(answer)) + <-gatherComplete + roffer := pc.LocalDescription() + + body := try.To1(json.Marshal(roffer)) + req := try.To1(b.signaler.newReq(http.MethodDelete, b.id, bytes.NewReader(body))) + req.Header.Set("X-Event-Id", ev.Id()) + try.To1(b.signaler.doReq(req)) + + return +} + +func (b *Bind) isClosed() bool { + return atomic.LoadUint32(&b.closed) != 0 +} + +func (b *Bind) Close() (err error) { + defer err2.Handle(&err) + + atomic.StoreUint32(&b.closed, 1) + + if len(b.pcs) > 0 { + func() { + b.pcsL.Lock() + defer b.pcsL.Unlock() + for _, pc := range b.pcs { + if pc != nil { + try.To(pc.Close()) + } + } + }() + } + if b.mux != nil { + try.To(b.mux.Close()) + } + if b.connectionStream != nil { + b.connectionStream.Close() + } + if b.msgCh != nil { + close(b.msgCh) + } + return +} + +func (b *Bind) ParseEndpoint(s string) (ep conn.Endpoint, err error) { + return b.NewEndpoint(s), nil +} + +func (b *Bind) Send(bufs [][]byte, ep conn.Endpoint) (err error) { + if b.isClosed() { + return net.ErrClosed + } + sender, ok := ep.(*Endpoint) + if !ok { + return ErrEndpointImpl + } + for _, buf := range bufs { + if err := sender.Send(buf); err != nil { + return err + } + } + return nil +} + +var ErrEndpointImpl = errors.New("endpoint is not wgortc.Endpoint") + +func (b *Bind) SetMark(mark uint32) error { return nil } +func (b *Bind) BatchSize() int { return 1 } diff --git a/cmd/wgortc/wgortc.go b/cmd/wgortc/wgortc.go new file mode 100644 index 0000000..06c6d0d --- /dev/null +++ b/cmd/wgortc/wgortc.go @@ -0,0 +1,33 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/lainio/err2/try" + "github.com/shynome/wgortc" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun" +) + +func main() { + tdevn := flag.String("tun", "wgortc", "tun name") + id := flag.String("id", "", "webrtc id") + signaler := flag.String("signaler", "https://test:test@signaler.slive.fun/", "signaler server") + loglevel := flag.Int("log", 0, "log level. slient:0 error:1 verbose:2") + flag.Parse() + + if *id == "" { + fmt.Fprintln(os.Stderr, "id is required") + os.Exit(1) + return + } + + tdev := try.To1(tun.CreateTUN(*tdevn, device.DefaultMTU)) + bind := wgortc.NewBind(*id, *signaler) + logger := device.NewLogger(*loglevel, *id) + dev := device.NewDevice(tdev, bind, logger) + + <-dev.Wait() +} diff --git a/endpoint.go b/endpoint.go new file mode 100644 index 0000000..cc9231f --- /dev/null +++ b/endpoint.go @@ -0,0 +1,123 @@ +package wgortc + +import ( + "bytes" + "encoding/json" + "errors" + "net/http" + "net/netip" + "sync" + "time" + + "github.com/lainio/err2" + "github.com/lainio/err2/try" + "github.com/pion/webrtc/v3" + "golang.zx2c4.com/wireguard/conn" +) + +type Endpoint struct { + id string + dc *webrtc.DataChannel + err error + init *sync.Once + + bind *Bind + + dsiableReconnect bool +} + +var _ conn.Endpoint = (*Endpoint)(nil) + +func (b *Bind) NewEndpoint(id string) *Endpoint { + return &Endpoint{ + id: id, + bind: b, + init: &sync.Once{}, + } +} + +// used for mac2 cookie calculations +func (ep *Endpoint) DstToBytes() []byte { + return []byte(ep.id) +} +func (ep *Endpoint) DstToString() string { return ep.id } // returns the destination address (ip:port) + +func (*Endpoint) ClearSrc() {} // clears the source address +func (*Endpoint) SrcToString() string { return "" } // returns the local source address (ip:port) +func (*Endpoint) DstIP() netip.Addr { return netip.Addr{} } +func (*Endpoint) SrcIP() netip.Addr { return netip.Addr{} } + +func (ep *Endpoint) connect() { + var pc *webrtc.PeerConnection + defer err2.Catch(func(err error) { + if pc != nil { + pc.Close() + } + ep.err = err + }) + + b := ep.bind + config := webrtc.Configuration{ + ICEServers: b.ICEServers, + } + pc = try.To1(b.api.NewPeerConnection(config)) + + dcinit := webrtc.DataChannelInit{ + Ordered: refVal(false), + MaxRetransmits: refVal(uint16(0)), + } + dc := try.To1(pc.CreateDataChannel("wgortc", &dcinit)) + dc.OnMessage(b.receiveMsg(ep)) + + offer := try.To1(pc.CreateOffer(nil)) + try.To(pc.SetLocalDescription(offer)) + offer = *pc.LocalDescription() + + sdp := try.To1(offer.Unmarshal()) + sdp.Origin.Username = b.id + offer.SDP = string(try.To1(sdp.Marshal())) + + body := try.To1(json.Marshal(offer)) + req := try.To1(b.signaler.newReq(http.MethodPost, ep.id, bytes.NewReader(body))) + res := try.To1(b.signaler.doReq(req)) + + var roffer webrtc.SessionDescription + try.To(json.NewDecoder(res.Body).Decode(&roffer)) + try.To(pc.SetRemoteDescription(roffer)) + + try.To(waitDC(dc, 10*time.Second)) + ep.dc = dc + + go func() { + b.pcsL.Lock() + defer b.pcsL.Unlock() + b.pcs = append(b.pcs, pc) + }() + +} +func (ep *Endpoint) resetWhenWrong(err *error) { + if ep.dsiableReconnect { + return + } + if *err == nil { + return + } + if ep.bind.isClosed() { // if bind is closed, don't reset + return + } + ep.init = &sync.Once{} + ep.err = nil +} +func (ep *Endpoint) Send(data []byte) (err error) { + ep.init.Do(ep.connect) + defer ep.resetWhenWrong(&err) + if err = ep.err; err != nil { + return + } + if ep.dc == nil { + return ErrEndpointDataChannelNotReady + } + return ep.dc.Send(data) +} + +var ErrEndpointDataChannelNotReady = errors.New("endpoint dc is not ready") diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..a290e73 --- /dev/null +++ b/example/main.go @@ -0,0 +1,88 @@ +package main + +import ( + "flag" + "io" + "log" + "net" + "net/http" + "net/netip" + + "github.com/lainio/err2/try" + "github.com/shynome/wgortc" + "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun/netstack" +) + +func main() { + runServer := flag.Bool("server", false, "") + flag.Parse() + if *runServer { + dev := startServer() + // <-dev.Wait() + dev.Close() + return + } else { + startClient() + } +} + +var signaler = "https://test:test@signaler.slive.fun" +var loglevel = device.LogLevelVerbose + +func startServer() (dev *device.Device) { + tdev, tnet, err := netstack.CreateNetTUN( + []netip.Addr{netip.MustParseAddr("192.168.4.29")}, + []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("8.8.4.4")}, + 1420, + ) + try.To(err) + var bind conn.Bind + bind = wgortc.NewBind("server", signaler) + // bind = conn.NewDefaultBind() + dev = device.NewDevice(tdev, bind, device.NewLogger(loglevel, "server")) + dev.IpcSet(`private_key=003ed5d73b55806c30de3f8a7bdab38af13539220533055e635690b8b87ad641 +listen_port=0 +public_key=f928d4f6c1b86c12f2562c10b07c555c5c57fd00f59e90c8d8d88767271cbf7c +allowed_ip=192.168.4.28/32 +`) + try.To(dev.Up()) + + listener := try.To1(tnet.ListenTCP(&net.TCPAddr{Port: 80})) + mux := http.NewServeMux() + mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + log.Printf("> %s - %s - %s", request.RemoteAddr, request.URL.String(), request.UserAgent()) + io.WriteString(writer, "Hello from userspace TCP!") + }) + go func() { + try.To(http.Serve(listener, mux)) + }() + return +} + +func startClient() (dev *device.Device, tnet *netstack.Net) { + tun, tnet, err := netstack.CreateNetTUN( + []netip.Addr{netip.MustParseAddr("192.168.4.28")}, + []netip.Addr{netip.MustParseAddr("8.8.8.8")}, + 1420) + try.To(err) + bind := wgortc.NewBind("client", signaler) + dev = device.NewDevice(tun, bind, device.NewLogger(loglevel, "client")) + err = dev.IpcSet(`private_key=087ec6e14bbed210e7215cdc73468dfa23f080a1bfb8665b2fd809bd99d28379 +public_key=c4c8e984c5322c8184c72265b92b250fdb63688705f504ba003c88f03393cf28 +allowed_ip=0.0.0.0/0 +endpoint=server +`) + try.To(dev.Up()) + + client := http.Client{ + Transport: &http.Transport{ + DialContext: tnet.DialContext, + }, + } + resp := try.To1(client.Get("http://192.168.4.29/")) + body := try.To1(io.ReadAll(resp.Body)) + log.Println(string(body)) + return +} diff --git a/example/main_test.go b/example/main_test.go new file mode 100644 index 0000000..bd6f67f --- /dev/null +++ b/example/main_test.go @@ -0,0 +1,74 @@ +package main + +import ( + "io" + "net/http" + "os" + "strings" + "sync" + "testing" + + "github.com/lainio/err2" + "github.com/lainio/err2/try" + "golang.zx2c4.com/wireguard/device" +) + +func TestServer(t *testing.T) { + if strings.HasSuffix(os.Args[0], "__debug_bin") == false { + return + } + dev := startServer() + <-dev.Wait() +} + +func TestClient(t *testing.T) { + if strings.HasSuffix(os.Args[0], "__debug_bin") == false { + return + } + dev, _ := startClient() + defer dev.Close() +} + +func TestNet(t *testing.T) { + dev := startServer() + defer dev.Close() + dev2, _ := startClient() + defer dev2.Close() +} + +func TestDevClose(t *testing.T) { + dev := startServer() + // time.Sleep(time.Second * 5) + dev.Close() +} + +func TestMain(m *testing.M) { + loglevel = device.LogLevelError + m.Run() +} + +func BenchmarkNet(b *testing.B) { + dev := startServer() + defer dev.Close() + dev2, tnet := startClient() + defer dev2.Close() + + client := http.Client{ + Transport: &http.Transport{ + DialContext: tnet.DialContext, + }, + } + try.To1(client.Get("http://192.168.4.29/")) + + var wg sync.WaitGroup + wg.Add(b.N) + for i := 0; i < b.N; i++ { + go func() { + defer wg.Done() + defer err2.Catch() + resp := try.To1(client.Get("http://192.168.4.29/")) + _ = try.To1(io.ReadAll(resp.Body)) + }() + } + wg.Wait() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ee0b804 --- /dev/null +++ b/go.mod @@ -0,0 +1,37 @@ +module github.com/shynome/wgortc + +go 1.20 + +require ( + github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 + github.com/lainio/err2 v0.9.0 + github.com/pion/ice/v2 v2.3.2 + github.com/pion/webrtc/v3 v3.1.59 + golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b +) + +require ( + github.com/google/btree v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/pion/datachannel v1.5.5 // indirect + github.com/pion/dtls/v2 v2.2.6 // indirect + github.com/pion/interceptor v0.1.12 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/mdns v0.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.10 // indirect + github.com/pion/rtp v1.7.13 // indirect + github.com/pion/sctp v1.8.6 // indirect + github.com/pion/sdp/v3 v3.0.6 // indirect + github.com/pion/srtp/v2 v2.0.12 // indirect + github.com/pion/stun v0.4.0 // indirect + github.com/pion/transport/v2 v2.1.0 // indirect + github.com/pion/turn/v2 v2.1.0 // indirect + github.com/pion/udp/v2 v2.0.1 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect + gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..651785a --- /dev/null +++ b/go.sum @@ -0,0 +1,195 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao= +github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lainio/err2 v0.9.0 h1:4ILuU6dczlPMAIZQSmLZhCjc1AWHJ8e+b67N7sTX6Po= +github.com/lainio/err2 v0.9.0/go.mod h1:5d/fy7+ytZJEybxIPN/LGEfvnU4y+JWE1tZAWRECJqY= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= +github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= +github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4= +github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY= +github.com/pion/ice/v2 v2.3.2 h1:vh+fi4RkZ8H5fB4brZ/jm3j4BqFgMmNs+aB3X52Hu7M= +github.com/pion/ice/v2 v2.3.2/go.mod h1:AMIpuJqcpe+UwloocNebmTSWhCZM1TUCo9v7nW50jX0= +github.com/pion/interceptor v0.1.12 h1:CslaNriCFUItiXS5o+hh5lpL0t0ytQkFnUcbbCs2Zq8= +github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA= +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/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U= +github.com/pion/mdns v0.0.7/go.mod h1:4iP2UbeFhLI/vWju/bw6ZfwjJzk0z8DNValjGxR/dD8= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= +github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= +github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= +github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= +github.com/pion/sctp v1.8.6 h1:CUex11Vkt9YS++VhLf8b55O3VqKrWL6W3SDwX4jAqsI= +github.com/pion/sctp v1.8.6/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= +github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= +github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= +github.com/pion/srtp/v2 v2.0.12 h1:WrmiVCubGMOAObBU1vwWjG0H3VSyQHawKeer2PVA5rY= +github.com/pion/srtp/v2 v2.0.12/go.mod h1:C3Ep44hlOo2qEYaq4ddsmK5dL63eLehXFbHaZ9F5V9Y= +github.com/pion/stun v0.4.0 h1:vgRrbBE2htWHy7l3Zsxckk7rkjnjOsSM7PHZnBwo8rk= +github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw= +github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= +github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI= +github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc= +github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0= +github.com/pion/transport/v2 v2.1.0 h1:tLBmDy/sfPu4UG9QsiKiI7Zav+i9zhUYvg7VlCUpIV8= +github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ= +github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI= +github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs= +github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54= +github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8= +github.com/pion/webrtc/v3 v3.1.59 h1:B3YFo8q6dwBYKA2LUjWRChP59Qtt+xvv1Ul7UPDp6Zc= +github.com/pion/webrtc/v3 v3.1.59/go.mod h1:rJGgStRoFyFOWJULHLayaimsG+jIEoenhJ5MB5gIFqw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY= +gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA= diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..fb1d616 --- /dev/null +++ b/helpers.go @@ -0,0 +1,47 @@ +package wgortc + +import ( + "context" + "errors" + "time" + + "github.com/pion/webrtc/v3" +) + +func refVal[T any](v T) *T { return &v } + +var ErrDataChannelClosed = errors.New("DataChannel state is closed") + +func waitDC(dc *webrtc.DataChannel, timeout time.Duration) (err error) { + switch dc.ReadyState() { + case webrtc.DataChannelStateOpen: + return + case webrtc.DataChannelStateClosing: + fallthrough + case webrtc.DataChannelStateClosed: + return ErrDataChannelClosed + case webrtc.DataChannelStateConnecting: + break + } + + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + ctx, cancelWith := context.WithCancelCause(ctx) + + dc.OnOpen(func() { + cancelWith(nil) + }) + dc.OnError(func(err error) { + cancelWith(err) + }) + + <-ctx.Done() + + switch err = context.Cause(ctx); err { + case context.Canceled: + return nil + } + + return +} diff --git a/mux/mux.go b/mux/mux.go new file mode 100644 index 0000000..6744a6b --- /dev/null +++ b/mux/mux.go @@ -0,0 +1,8 @@ +package mux + +import ( + "github.com/pion/ice/v2" + "github.com/pion/webrtc/v3" +) + +var WithUDPMux func(engine webrtc.SettingEngine, port uint16) (ice.UDPMux, error) diff --git a/mux/mux_std.go b/mux/mux_std.go new file mode 100644 index 0000000..b0ca02d --- /dev/null +++ b/mux/mux_std.go @@ -0,0 +1,18 @@ +//go:build !(js || wasip1) + +package mux + +import ( + "github.com/pion/ice/v2" + "github.com/pion/webrtc/v3" +) + +func init() { + WithUDPMux = func(engine webrtc.SettingEngine, port uint16) (mux ice.UDPMux, err error) { + if mux, err = ice.NewMultiUDPMuxFromPort(int(port)); err != nil { + return + } + engine.SetICEUDPMux(mux) + return + } +} diff --git a/signaler.go b/signaler.go new file mode 100644 index 0000000..fa33620 --- /dev/null +++ b/signaler.go @@ -0,0 +1,55 @@ +package wgortc + +import ( + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/lainio/err2/try" +) + +type signaler struct { + endpoint *url.URL + client *http.Client +} + +func newSignaler(endpoint string) *signaler { + u := try.To1(url.Parse(endpoint)) + return &signaler{ + endpoint: u, + client: &http.Client{ + // Timeout: 10 * time.Second, + }, + } +} + +func (s *signaler) newReq(method string, topic string, body io.Reader) (req *http.Request, err error) { + if req, err = http.NewRequest(method, s.endpoint.String(), body); err != nil { + return + } + q := req.URL.Query() + q.Set("t", topic) + req.URL.RawQuery = q.Encode() + u := s.endpoint.User + pass, _ := u.Password() + req.SetBasicAuth(u.Username(), pass) + return +} + +func (p *signaler) doReq(req *http.Request) (res *http.Response, err error) { + res, err = p.client.Do(req) + if err != nil { + return + } + if strings.HasPrefix(res.Status, "2") { + return + } + var errText []byte + if errText, err = io.ReadAll(res.Body); err != nil { + return + } + err = fmt.Errorf("server err. status: %s. content: %s", res.Status, errText) + return +}