mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-12 16:39:09 +02:00
chore: wasm further work
This commit is contained in:
@@ -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() {
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user