This repository has been archived on 2020-04-20. You can view files and clone it, but cannot push or open issues or pull requests.
libricochet-go/application/application.go

162 lines
4.3 KiB
Go

package application
import (
"git.openprivacy.ca/openprivacy/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go"
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/log"
"net"
"sync"
)
const (
// RicochetPort is the default port used by ricochet applications
RicochetPort = 9878
)
// RicochetApplication bundles many useful constructs that are
// likely standard in a ricochet application
type RicochetApplication struct {
contactManager ContactManagerInterface
v3identity identity.Identity
name string
ls connectivity.ListenService
acn connectivity.ACN
instances []*Instance
lock sync.Mutex
aif InstanceFactory
}
// Init initializes the underlying RicochetApplication datastructure, making it ready for use
func (ra *RicochetApplication) Init(acn connectivity.ACN, name string, v3identity identity.Identity, af InstanceFactory, cm ContactManagerInterface) {
ra.acn = acn
ra.name = name
ra.v3identity = v3identity
ra.aif = af
ra.contactManager = cm
}
// TODO: Reimplement OnJoin, OnLeave Events.
func (ra *RicochetApplication) handleConnection(conn net.Conn) {
rc, err := goricochet.NegotiateVersionInbound(conn)
if err != nil {
log.Errorln("There was an error")
conn.Close()
return
}
ich := connection.HandleInboundConnection(rc)
err = ich.ProcessAuthAsV3Server(ra.v3identity, ra.contactManager.LookupContactV3)
if err != nil {
log.Errorf("There was an error authenticating the connection: %v", err)
conn.Close()
return
}
rai := ra.aif.GetApplicationInstance(rc)
ra.lock.Lock()
ra.instances = append(ra.instances, rai)
ra.lock.Unlock()
rc.Process(rai)
// rc.Process ends when the connection ends.
// Remove it from the application's list of instances
ra.lock.Lock()
for i, x := range ra.instances {
if x == rai {
ra.instances = append(ra.instances[:i], ra.instances[i+1:]...)
break
}
}
ra.lock.Unlock()
}
// HandleApplicationInstance delegates handling of a given Instance to the Application.
func (ra *RicochetApplication) HandleApplicationInstance(rai *Instance) {
ra.lock.Lock()
ra.instances = append(ra.instances, rai)
ra.lock.Unlock()
}
// Open a connection to another Ricochet peer at onionAddress. Infof they are unknown to use, use requestMessage (otherwise can be blank)
func (ra *RicochetApplication) Open(onionAddress string, requestMessage string) (*Instance, error) {
rc, err := goricochet.Open(ra.acn, onionAddress)
if err != nil {
log.Errorf("Error in application.Open(): %v\n", err)
return nil, err
}
och := connection.HandleOutboundConnection(rc)
_, err = och.ProcessAuthAsV3Client(ra.v3identity)
if err != nil {
log.Errorf("There was an error authenticating the connection: %v", err)
return nil, err
}
rai := ra.aif.GetApplicationInstance(rc)
go rc.Process(rai)
ra.HandleApplicationInstance(rai)
return rai, nil
}
// Broadcast performs the given function do() over all application instance (all connected peers)
func (ra *RicochetApplication) Broadcast(do func(rai *Instance)) {
ra.lock.Lock()
for _, rai := range ra.instances {
do(rai)
}
ra.lock.Unlock()
}
// Shutdown stops a RicochetApplication, terminating all child processes and resources
func (ra *RicochetApplication) Shutdown() {
ra.lock.Lock()
ra.ls.Close()
for _, instance := range ra.instances {
instance.Connection.Close()
}
ra.lock.Unlock()
}
// Close kills a connection by a given Onion Address
func (ra *RicochetApplication) Close(onion string) {
ra.lock.Lock()
for _, instance := range ra.instances {
if instance.RemoteHostname == onion {
instance.Connection.Close()
}
}
ra.lock.Unlock()
}
// ConnectionCount returns the number of concurrent connections to the application
func (ra *RicochetApplication) ConnectionCount() int {
ra.lock.Lock()
defer ra.lock.Unlock()
return len(ra.instances)
}
// Run handles a Listen object and Accepts and handles new connections
func (ra *RicochetApplication) Run(ls connectivity.ListenService) {
if !ra.v3identity.Initialized() || ra.contactManager == nil {
return
}
ra.lock.Lock()
ra.ls = ls
ra.lock.Unlock()
var err error
for err == nil {
conn, err := ra.ls.Accept()
if err == nil {
go ra.handleConnection(conn)
} else {
return
}
}
}