forked from cwtch.im/tapir
137 lines
3.9 KiB
Go
137 lines
3.9 KiB
Go
package tapir
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
|
"golang.org/x/crypto/ed25519"
|
|
"golang.org/x/crypto/nacl/secretbox"
|
|
"io"
|
|
"net"
|
|
"sync"
|
|
)
|
|
|
|
// Service defines the interface for a Tapir Service
|
|
type Service interface {
|
|
Init(acn connectivity.ACN, privateKey ed25519.PrivateKey, identity identity.Identity)
|
|
Connect(hostname string, application Application) (bool, error)
|
|
Listen(application Application) error
|
|
GetConnection(connectionID string) (*Connection, error)
|
|
WaitForCapabilityOrClose(connectionID string, capability string) (*Connection, error)
|
|
Shutdown()
|
|
}
|
|
|
|
// Connection defines a Tapir Connection
|
|
type Connection struct {
|
|
Hostname string
|
|
conn net.Conn
|
|
capabilities sync.Map
|
|
encrypted bool
|
|
key [32]byte
|
|
App Application
|
|
ID identity.Identity
|
|
Outbound bool
|
|
Closed bool
|
|
MaxLength int
|
|
}
|
|
|
|
// NewConnection creates a new Connection
|
|
func NewConnection(id identity.Identity, hostname string, outbound bool, conn net.Conn, app Application) *Connection {
|
|
connection := new(Connection)
|
|
connection.Hostname = hostname
|
|
connection.conn = conn
|
|
connection.App = app
|
|
connection.ID = id
|
|
connection.Outbound = outbound
|
|
connection.MaxLength = 1024
|
|
go connection.App.Init(connection)
|
|
return connection
|
|
}
|
|
|
|
// SetHostname sets the hostname on the connection
|
|
func (c *Connection) SetHostname(hostname string) {
|
|
log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.ID.Hostname(), c.Hostname, hostname)
|
|
c.Hostname = hostname
|
|
}
|
|
|
|
// SetCapability sets a capability on the connection
|
|
func (c *Connection) SetCapability(name string) {
|
|
log.Debugf("[%v -- %v] Setting Capability %v", c.ID.Hostname(), c.Hostname, name)
|
|
c.capabilities.Store(name, true)
|
|
}
|
|
|
|
// HasCapability checks if the connection has a given capability
|
|
func (c *Connection) HasCapability(name string) bool {
|
|
_, ok := c.capabilities.Load(name)
|
|
return ok
|
|
}
|
|
|
|
// Close forcibly closes the connection
|
|
func (c *Connection) Close() {
|
|
c.conn.Close()
|
|
}
|
|
|
|
// Expect blocks and reads a single Tapir packet , from the connection.
|
|
func (c *Connection) Expect() []byte {
|
|
buffer := make([]byte, c.MaxLength)
|
|
n, err := io.ReadFull(c.conn, buffer)
|
|
|
|
if n != c.MaxLength || err != nil {
|
|
log.Errorf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.Hostname, c.ID.Hostname(), n, err)
|
|
c.conn.Close()
|
|
c.Closed = true
|
|
return []byte{}
|
|
}
|
|
|
|
if c.encrypted {
|
|
var decryptNonce [24]byte
|
|
copy(decryptNonce[:], buffer[:24])
|
|
decrypted, ok := secretbox.Open(nil, buffer[24:], &decryptNonce, &c.key)
|
|
if ok {
|
|
copy(buffer, decrypted)
|
|
} else {
|
|
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.Hostname, c.ID.Hostname())
|
|
c.conn.Close()
|
|
c.Closed = true
|
|
return []byte{}
|
|
}
|
|
}
|
|
len, _ := binary.Uvarint(buffer[0:2])
|
|
//cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer)
|
|
return buffer[2 : len+2]
|
|
}
|
|
|
|
// SetEncryptionKey turns on application-level encryption on the connection using the given key.
|
|
func (c *Connection) SetEncryptionKey(key [32]byte) {
|
|
c.key = key
|
|
c.encrypted = true
|
|
}
|
|
|
|
// Send writes a given message to a Tapir packet (of 1024 bytes in length).
|
|
func (c *Connection) Send(message []byte) {
|
|
|
|
buffer := make([]byte, c.MaxLength)
|
|
binary.PutUvarint(buffer[0:2], uint64(len(message)))
|
|
copy(buffer[2:], message)
|
|
|
|
if c.encrypted {
|
|
var nonce [24]byte
|
|
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
|
// TODO: Surface is Error
|
|
c.conn.Close()
|
|
c.Closed = true
|
|
}
|
|
// MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag.
|
|
encrypted := secretbox.Seal(nonce[:], buffer[0:c.MaxLength-40], &nonce, &c.key)
|
|
copy(buffer, encrypted[0:c.MaxLength])
|
|
}
|
|
log.Debugf("[%v -> %v] Wire Send %x", c.ID.Hostname(), c.Hostname, buffer)
|
|
_, err := c.conn.Write(buffer)
|
|
if err != nil {
|
|
c.conn.Close()
|
|
c.Closed = true
|
|
}
|
|
}
|