EndGame0/sourcecode/gobalance/pkg/onionbalance/instance.go
2024-10-23 20:50:14 +05:30

158 lines
4.9 KiB
Go

package onionbalance
import (
"errors"
"github.com/sirupsen/logrus"
"gobalance/pkg/btime"
"gobalance/pkg/stem/descriptor"
"strings"
"sync"
"time"
)
type Instance struct {
controller *Controller
OnionAddress string
introSetChangedSincePublished bool
descriptor *ReceivedDescriptor
descriptorMtx sync.RWMutex
IntroSetModifiedTimestamp *time.Time
}
func NewInstance(controller *Controller, onionAddress string) *Instance {
p := Params()
i := &Instance{}
i.controller = controller
if onionAddress != "" {
onionAddress = strings.Replace(onionAddress, ".onion", "", 1)
}
i.OnionAddress = onionAddress
// Onion address does not contain the '.onion'.
logrus.Warnf("Loaded instance %s", onionAddress)
nInstances := p.NInstances()
p.SetNInstances(nInstances + 1)
i.introSetChangedSincePublished = false
i.SetDescriptor(nil)
// When was the intro set of this instance last modified?
i.IntroSetModifiedTimestamp = nil
return i
}
func (i *Instance) SetDescriptor(newDescriptor *ReceivedDescriptor) {
i.descriptorMtx.Lock()
defer i.descriptorMtx.Unlock()
i.descriptor = newDescriptor
}
func (i *Instance) GetDescriptor() *ReceivedDescriptor {
i.descriptorMtx.RLock()
defer i.descriptorMtx.RUnlock()
return i.descriptor
}
// Return True if this instance has this onion address
func (i *Instance) hasOnionAddress(onionAddress string) bool {
// Strip the ".onion" part of the address if it exists since some
// subsystems don't use it (e.g. Tor sometimes omits it from control
// port responses)
myOnionAddress := strings.TrimSuffix(i.OnionAddress, ".onion")
theirOnionAddress := strings.TrimSuffix(onionAddress, ".onion")
return myOnionAddress == theirOnionAddress
}
// FetchDescriptor try fetch a fresh descriptor for this service instance from the HSDirs
func (i *Instance) FetchDescriptor() error {
logrus.Debugf("Trying to fetch a descriptor for instance %s.onion.", i.OnionAddress)
return i.controller.GetHiddenServiceDescriptor(i.OnionAddress)
}
var ErrInstanceHasNoDescriptor = errors.New("InstanceHasNoDescriptor")
var ErrInstanceIsOffline = errors.New("InstanceIsOffline")
// GetIntrosForPublish get a list of stem.descriptor.IntroductionPointV3 objects for this descriptor
// Raise :InstanceHasNoDescriptor: if there is no descriptor for this instance
// Raise :InstanceIsOffline: if the instance is offline.
func (i *Instance) GetIntrosForPublish() ([]descriptor.IntroductionPointV3, error) {
p := Params()
instDescriptor := i.GetDescriptor()
if instDescriptor == nil {
adaptDown := p.AdaptDown()
p.SetAdaptDown(adaptDown + 1)
adaptDownNoDescriptor := p.AdaptDownNoDescriptor()
p.SetAdaptDownNoDescriptor(adaptDownNoDescriptor + 1)
return nil, ErrInstanceHasNoDescriptor
}
if instDescriptor.IsOld() {
adaptDown := p.AdaptDown()
p.SetAdaptDown(adaptDown + 1)
adaptDownInstanceOld := p.AdaptDownInstanceOld()
p.SetAdaptDownInstanceOld(adaptDownInstanceOld + 1)
return nil, ErrInstanceIsOffline
}
adaptUp := p.AdaptUp()
p.SetAdaptUp(adaptUp + 1)
return instDescriptor.GetIntroPoints(), nil
}
// We received a descriptor (with 'descriptor_text') for 'onion_address'.
// Register it to this instance.
func (i *Instance) registerDescriptor(descriptorText, onionAddress string) {
logrus.Debugf("Found instance %s for this new descriptor!", i.OnionAddress)
p := Params()
if onionAddress != i.OnionAddress {
panic("onion_address != i.OnionAddress")
}
// Parse descriptor. If it parsed correctly, we know that this
// descriptor is truly for this instance (since the onion address
// matches)
newDescriptor, err := NewReceivedDescriptor(descriptorText, onionAddress)
if err != nil {
if err == ErrBadDescriptor {
logrus.Warningf("Received bad descriptor for %s. Ignoring.", i.OnionAddress)
return
}
panic(err)
}
// Before replacing the current descriptor with this one, check if the
// introduction point set changed:
// If this is the first descriptor for this instance, the intro point set changed
if i.GetDescriptor() == nil {
logrus.Infof("This is the first time we seen a descriptor for instance %s!", i.OnionAddress)
tmp := btime.Clock.Now().UTC()
i.IntroSetModifiedTimestamp = &tmp
i.SetDescriptor(newDescriptor)
return
}
if i.GetDescriptor() == nil {
panic("i.descriptor == nil")
}
if newDescriptor.introSet.Len() == 0 {
panic("new_descriptor.introSet.Len() == 0")
}
// We already have a descriptor but this is a new one. Check the intro points!
if !newDescriptor.introSet.Equals(*i.GetDescriptor().introSet) {
logrus.Infof("We got a new descriptor for instance %s and the intro set changed!", i.OnionAddress)
tmp := btime.Clock.Now().UTC()
i.IntroSetModifiedTimestamp = &tmp
adaptIntroChanged := p.AdaptIntroChanged()
p.SetAdaptIntroChanged(adaptIntroChanged + 1)
} else {
logrus.Infof("We got a new descriptor for instance %s but the intro set did not change.", i.OnionAddress)
}
i.SetDescriptor(newDescriptor)
}