diff --git a/connection.go b/connection.go new file mode 100644 index 0000000..982d871 --- /dev/null +++ b/connection.go @@ -0,0 +1,34 @@ +package tunnel + +type Connection struct { + RX <-chan *DataFrame + TX chan<- *DataFrame + ID ID +} + +func (c *Connection) Read(p []byte) (int, error) { + df, ok := <-c.RX + if !ok { + return 0, c.Close() + } + return df.Read(p) +} + +func (c *Connection) Write(p []byte) (int, error) { + defer recover() + df := &DataFrame{ + ID: c.ID, + Type: TypeNormal, + } + n, err := df.Write(p) + if err != nil { + return 0, err + } + c.TX <- df + return n, nil +} + +func (c *Connection) Close() error { + defer close(c.TX) + return nil +} diff --git a/consts.go b/consts.go new file mode 100644 index 0000000..f29ed70 --- /dev/null +++ b/consts.go @@ -0,0 +1,9 @@ +package tunnel + +const ( + TypeNormal Type = iota + TypeRequest + TypeConnected + TypeClosed + TypeInvalid Type = 0xff +) diff --git a/dataframe.go b/dataframe.go new file mode 100644 index 0000000..d154cc7 --- /dev/null +++ b/dataframe.go @@ -0,0 +1,38 @@ +package tunnel + +import ( + "compress/gzip" + "encoding/gob" + "io" +) + +type DataFrame struct { + ID ID + Type Type + Payload []byte +} + +func (d *DataFrame) Encode(w io.Writer) error { + gz := gzip.NewWriter(w) + defer gz.Close() + gob := gob.NewEncoder(gz) + return gob.Encode(d) +} + +func (d *DataFrame) Decode(r io.Reader) error { + gz, err := gzip.NewReader(r) + if err != nil { + return err + } + defer gz.Close() + gob := gob.NewDecoder(gz) + return gob.Decode(d) +} + +func (d *DataFrame) Read(p []byte) (int, error) { + return copy(p, d.Payload), nil +} + +func (d *DataFrame) Write(p []byte) (int, error) { + return copy(d.Payload, p), nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a162ca2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.sense-t.eu.org/senset/tunnel + +go 1.19 diff --git a/manager.go b/manager.go new file mode 100644 index 0000000..65e8037 --- /dev/null +++ b/manager.go @@ -0,0 +1,72 @@ +package tunnel + +import ( + "io" +) + +type Manager struct { + Tunnel io.ReadWriter + Connections map[ID]*Connection + incoming chan *DataFrame + outgoing chan *DataFrame + accept chan *DataFrame + closed chan bool +} + +func NewManager(tun io.ReadWriter) *Manager { + return &Manager{ + Tunnel: tun, + Connections: make(map[ID]*Connection), + incoming: make(chan *DataFrame, 1024), + outgoing: make(chan *DataFrame, 1024), + accept: make(chan *DataFrame, 1024), + } +} + +func (m *Manager) Run() { + for { + select { + case <-m.closed: + return + case df := <-m.incoming: + go df.Encode(m.Tunnel) + default: + go func() { + df := new(DataFrame) + df.Decode(m.Tunnel) + switch df.Type { + case TypeRequest: + m.accept <- df + case TypeClosed: + connection, ok := m.Connections[df.ID] + if ok { + connection.Close() + delete(m.Connections, df.ID) + } + case TypeNormal: + connection, ok := m.Connections[df.ID] + if ok { + connection.RX = m.outgoing + m.outgoing <- df + } + } + }() + } + } +} + +func (m *Manager) Connect() + +func (m *Manager) Accept() + +func (m *Manager) Close() error { + defer close(m.closed) + defer close(m.accept) + defer close(m.incoming) + defer close(m.outgoing) + for _, connection := range m.Connections { + connection.Close() + } + m.closed <- true + return nil +} diff --git a/types.go b/types.go new file mode 100644 index 0000000..bb1d030 --- /dev/null +++ b/types.go @@ -0,0 +1,4 @@ +package tunnel + +type ID string +type Type uint8