chore: wasm further work

This commit is contained in:
braginini
2021-11-14 18:08:33 +01:00
parent 9587b9a930
commit 6457c48281
5 changed files with 340 additions and 8 deletions
+59 -7
View File
@@ -1,14 +1,63 @@
package conn
import "net"
import "golang.zx2c4.com/wireguard/conn"
import (
"github.com/pion/webrtc/v3"
"golang.zx2c4.com/wireguard/conn"
"net"
"sync"
)
func (*WebRTCBind) makeReceive(dcConn *DataChannelConn) conn.ReceiveFunc {
return func(buff []byte) (int, conn.Endpoint, error) {
n, err := dcConn.Read(buff)
if err != nil {
return 0, nil, err
}
addr := dcConn.RemoteAddr().(*DataChannelAddr)
return n, (*WebRTCEndpoint)(addr), err
}
}
// WebRTCBind is an implementation of Wireguard Bind interface backed by WebRTC data channel
type WebRTCBind struct {
id string
pc *webrtc.PeerConnection
conn *DataChannelConn
mu sync.Mutex
}
func (*WebRTCBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
return nil, 0, nil
func NewWebRTCBind(id string, pc *webrtc.PeerConnection) conn.Bind {
return &WebRTCBind{
id: id,
pc: pc,
conn: nil,
mu: sync.Mutex{},
}
}
func (bind *WebRTCBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
bind.mu.Lock()
defer bind.mu.Unlock()
//todo whole webrtc logic from the beginning
//todo create peer connection, offer/answer, wait until connected
dc, err := bind.pc.CreateDataChannel(bind.id, nil)
if err != nil {
return nil, 0, nil
}
dcConn, err := WrapDataChannel(dc)
if err != nil {
dc.Close()
return nil, 0, err
}
bind.conn = dcConn
fns = append(fns, bind.makeReceive(bind.conn))
return fns, 38676, nil
}
func (*WebRTCBind) Close() error {
@@ -19,7 +68,11 @@ func (*WebRTCBind) SetMark(mark uint32) error {
return nil
}
func (*WebRTCBind) Send(b []byte, ep conn.Endpoint) error {
func (bind *WebRTCBind) Send(b []byte, ep conn.Endpoint) error {
_, err := bind.conn.Write(b)
if err != nil {
return err
}
return nil
}
@@ -28,8 +81,7 @@ func (*WebRTCBind) ParseEndpoint(s string) (conn.Endpoint, error) {
}
// WebRTCEndpoint is an implementation of Wireguard's Endpoint interface backed by WebRTC
type WebRTCEndpoint struct {
}
type WebRTCEndpoint DataChannelAddr
func (*WebRTCEndpoint) ClearSrc() {
+34
View File
@@ -0,0 +1,34 @@
package conn
// credits to https://github.com/rtctunnel/rtctunnel
import "sync"
// A Cond is a condition variable like sync.Cond, but using a channel so we can use select.
type Cond struct {
once sync.Once
C chan struct{}
}
// NewCond creates a new condition variable.
func NewCond() *Cond {
return &Cond{C: make(chan struct{})}
}
// Do runs f if the condition hasn't been signaled yet. Afterwards it will be signaled.
func (c *Cond) Do(f func()) {
c.once.Do(func() {
f()
close(c.C)
})
}
// Signal closes the condition variable channel.
func (c *Cond) Signal() {
c.Do(func() {})
}
// Wait waits for the condition variable channel to close.
func (c *Cond) Wait() {
<-c.C
}
+217
View File
@@ -0,0 +1,217 @@
package conn
// credits to https://github.com/rtctunnel/rtctunnel
import (
"context"
"errors"
"github.com/pion/webrtc/v3"
"io"
"net"
"time"
)
var ErrClosedByPeer = errors.New("closed by peer")
type DataChannelAddr struct{}
func (addr DataChannelAddr) Network() string {
return "webrtc"
}
func (addr DataChannelAddr) String() string {
return "webrtc://datachannel"
}
// A DataChannelConn implements the net.Conn interface over a webrtc data channel
type DataChannelConn struct {
dc *webrtc.DataChannel
rr ContextReadCloser
rw ContextWriteCloser
openCond *Cond
closeCond *Cond
closeErr error
}
// WrapDataChannel wraps an rtc data channel and implements the net.Conn
// interface
func WrapDataChannel(rtcDataChannel *webrtc.DataChannel) (*DataChannelConn, error) {
rr, rw := io.Pipe()
conn := &DataChannelConn{
dc: rtcDataChannel,
rr: ContextReadCloser{Context: context.Background(), ReadCloser: rr},
rw: ContextWriteCloser{Context: context.Background(), WriteCloser: rw},
openCond: NewCond(),
closeCond: NewCond(),
}
conn.dc.OnClose(func() {
_ = conn.closeWithError(ErrClosedByPeer)
})
conn.dc.OnOpen(func() {
// for reasons I don't understand, when opened the data channel is not immediately available for use
time.Sleep(50 * time.Millisecond)
conn.openCond.Signal()
})
conn.dc.OnMessage(func(msg webrtc.DataChannelMessage) {
if rw != nil {
_, err := rw.Write(msg.Data)
if err != nil {
_ = conn.closeWithError(err)
rw = nil
}
}
})
select {
case <-conn.closeCond.C:
err := conn.closeErr
if err == nil {
err = errors.New("datachannel closed for unknown reasons")
}
return nil, err
case <-conn.openCond.C:
}
return conn, nil
}
func (dc *DataChannelConn) Read(b []byte) (n int, err error) {
return dc.rr.Read(b)
}
func (dc *DataChannelConn) Write(b []byte) (n int, err error) {
err = dc.dc.Send(b)
if err != nil {
return 0, err
}
return len(b), nil
}
func (dc *DataChannelConn) Close() error {
return dc.closeWithError(nil)
}
func (dc *DataChannelConn) LocalAddr() net.Addr {
return DataChannelAddr{}
}
func (dc *DataChannelConn) RemoteAddr() net.Addr {
return DataChannelAddr{}
}
func (dc *DataChannelConn) SetDeadline(t time.Time) error {
var err error
if e := dc.SetReadDeadline(t); e != nil {
err = e
}
if e := dc.SetWriteDeadline(t); e != nil {
err = e
}
return err
}
func (dc *DataChannelConn) SetReadDeadline(t time.Time) error {
return dc.rr.SetReadDeadline(t)
}
func (dc *DataChannelConn) SetWriteDeadline(t time.Time) error {
return dc.rw.SetWriteDeadline(t)
}
func (dc *DataChannelConn) closeWithError(err error) error {
dc.closeCond.Do(func() {
e := dc.rr.Close()
if err == nil {
err = e
}
e = dc.rw.Close()
if err == nil {
err = e
}
e = dc.dc.Close()
if err == nil {
err = e
}
dc.closeErr = err
})
return err
}
type ContextReadCloser struct {
context.Context
io.ReadCloser
cancel func()
}
func (cr ContextReadCloser) Close() error {
err := cr.ReadCloser.Close()
if cr.cancel != nil {
cr.cancel()
cr.cancel = nil
}
return err
}
func (cr ContextReadCloser) SetReadDeadline(t time.Time) error {
if cr.cancel != nil {
cr.cancel()
cr.cancel = nil
}
cr.Context, cr.cancel = context.WithDeadline(context.Background(), t)
return nil
}
func (cr ContextReadCloser) Read(p []byte) (n int, err error) {
done := make(chan struct{})
go func() {
n, err = cr.ReadCloser.Read(p)
close(done)
}()
select {
case <-done:
return n, err
case <-cr.Context.Done():
return 0, cr.Context.Err()
}
}
type ContextWriteCloser struct {
context.Context
io.WriteCloser
cancel func()
}
func (cw ContextWriteCloser) Close() error {
err := cw.WriteCloser.Close()
if cw.cancel != nil {
cw.cancel()
cw.cancel = nil
}
return err
}
func (cw ContextWriteCloser) SetWriteDeadline(t time.Time) error {
if cw.cancel != nil {
cw.cancel()
cw.cancel = nil
}
cw.Context, cw.cancel = context.WithDeadline(context.Background(), t)
return nil
}
func (cw ContextWriteCloser) Write(p []byte) (n int, err error) {
done := make(chan struct{})
go func() {
n, err = cw.WriteCloser.Write(p)
close(done)
}()
select {
case <-done:
return n, err
case <-cw.Context.Done():
return 0, cw.Context.Err()
}
}