160 lines
4.3 KiB
Go
160 lines
4.3 KiB
Go
package application
|
|
|
|
import (
|
|
"git.openprivacy.ca/openprivacy/libricochet-go"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/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 {
|
|
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
|
|
}
|
|
}
|
|
}
|