mirror of
https://github.com/bettercap/bettercap
synced 2025-07-05 20:42:09 -07:00
197 lines
4.8 KiB
Go
197 lines
4.8 KiB
Go
package zerogod
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
tls_utils "github.com/bettercap/bettercap/v2/tls"
|
|
"github.com/evilsocket/islazy/fs"
|
|
"github.com/evilsocket/islazy/tui"
|
|
"github.com/grandcat/zeroconf"
|
|
yaml "gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Advertiser struct {
|
|
Filename string
|
|
Mapping map[string]zeroconf.ServiceEntry
|
|
Servers map[string]*zeroconf.Server
|
|
Acceptors map[string]*Acceptor
|
|
}
|
|
|
|
type setupResult struct {
|
|
err error
|
|
key string
|
|
server *zeroconf.Server
|
|
}
|
|
|
|
func (mod *ZeroGod) startAdvertiser(fileName string) error {
|
|
if mod.advertiser != nil {
|
|
return fmt.Errorf("advertiser already started for %s", mod.advertiser.Filename)
|
|
}
|
|
|
|
var certFile string
|
|
var keyFile string
|
|
var err error
|
|
|
|
// read tls configuration
|
|
if err, certFile = mod.StringParam("zerogod.advertise.certificate"); err != nil {
|
|
return err
|
|
} else if certFile, err = fs.Expand(certFile); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err, keyFile = mod.StringParam("zerogod.advertise.key"); err != nil {
|
|
return err
|
|
} else if keyFile, err = fs.Expand(keyFile); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !fs.Exists(certFile) || !fs.Exists(keyFile) {
|
|
cfg, err := tls_utils.CertConfigFromModule("zerogod.advertise", mod.SessionModule)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mod.Debug("%+v", cfg)
|
|
mod.Info("generating server TLS key to %s", keyFile)
|
|
mod.Info("generating server TLS certificate to %s", certFile)
|
|
if err := tls_utils.Generate(cfg, certFile, keyFile, false); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
mod.Info("loading server TLS key from %s", keyFile)
|
|
mod.Info("loading server TLS certificate from %s", certFile)
|
|
}
|
|
|
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tlsConfig := tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
InsecureSkipVerify: true,
|
|
}
|
|
|
|
data, err := ioutil.ReadFile(fileName)
|
|
if err != nil {
|
|
return fmt.Errorf("could not read %s: %v", fileName, err)
|
|
}
|
|
|
|
mapping := make(map[string]zeroconf.ServiceEntry)
|
|
if err = yaml.Unmarshal(data, &mapping); err != nil {
|
|
return fmt.Errorf("could not deserialize %s: %v", fileName, err)
|
|
}
|
|
|
|
hostName, err := os.Hostname()
|
|
if err != nil {
|
|
return fmt.Errorf("could not get hostname: %v", err)
|
|
}
|
|
if !strings.HasSuffix(hostName, ".") {
|
|
hostName += "."
|
|
}
|
|
|
|
mod.Info("loaded %d services from %s, advertising with host=%s iface=%s ipv4=%s ipv6=%s",
|
|
len(mapping),
|
|
fileName,
|
|
hostName,
|
|
mod.Session.Interface.Name(),
|
|
mod.Session.Interface.IpAddress,
|
|
mod.Session.Interface.Ip6Address)
|
|
|
|
advertiser := &Advertiser{
|
|
Filename: fileName,
|
|
Mapping: mapping,
|
|
Servers: make(map[string]*zeroconf.Server),
|
|
Acceptors: make(map[string]*Acceptor),
|
|
}
|
|
|
|
svcChan := make(chan setupResult)
|
|
|
|
// paralleize initialization
|
|
for key, svc := range mapping {
|
|
go func(key string, svc zeroconf.ServiceEntry) {
|
|
mod.Info("unregistering instance %s ...", tui.Yellow(fmt.Sprintf("%s.%s.%s", svc.Instance, svc.Service, svc.Domain)))
|
|
|
|
// create a first instance just to deregister it from the network
|
|
server, err := zeroconf.Register(svc.Instance, svc.Service, svc.Domain, svc.Port, svc.Text, nil)
|
|
if err != nil {
|
|
svcChan <- setupResult{err: fmt.Errorf("could not create service %s: %v", svc.Instance, err)}
|
|
return
|
|
}
|
|
server.Shutdown()
|
|
|
|
// give some time to the network to adjust
|
|
time.Sleep(time.Duration(1) * time.Second)
|
|
|
|
// now create it again to actually advertise
|
|
if server, err = zeroconf.Register(svc.Instance, svc.Service, svc.Domain, svc.Port, svc.Text, nil); err != nil {
|
|
svcChan <- setupResult{err: fmt.Errorf("could not create service %s: %v", svc.Instance, err)}
|
|
return
|
|
}
|
|
|
|
mod.Info("advertising service %s", tui.Yellow(svc.Service))
|
|
|
|
svcChan <- setupResult{
|
|
key: key,
|
|
server: server,
|
|
}
|
|
}(key, svc)
|
|
}
|
|
|
|
for res := range svcChan {
|
|
if res.err != nil {
|
|
return res.err
|
|
}
|
|
advertiser.Servers[res.key] = res.server
|
|
if len(advertiser.Servers) == len(mapping) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// now create the tcp acceptors
|
|
for key, svc := range mapping {
|
|
acceptor := NewAcceptor(mod, key, hostName, uint16(svc.Port), &tlsConfig)
|
|
if err := acceptor.Start(); err != nil {
|
|
return err
|
|
}
|
|
advertiser.Acceptors[key] = acceptor
|
|
}
|
|
|
|
mod.advertiser = advertiser
|
|
|
|
mod.Debug("%+v", *mod.advertiser)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (mod *ZeroGod) stopAdvertiser() error {
|
|
if mod.advertiser == nil {
|
|
return errors.New("advertiser not started")
|
|
}
|
|
|
|
mod.Info("stopping %d services ...", len(mod.advertiser.Mapping))
|
|
|
|
for key, server := range mod.advertiser.Servers {
|
|
mod.Info("stopping %s ...", key)
|
|
server.Shutdown()
|
|
}
|
|
|
|
mod.Info("all services stopped")
|
|
|
|
mod.Info("stopping %d acceptors ...", len(mod.advertiser.Acceptors))
|
|
|
|
for _, acceptor := range mod.advertiser.Acceptors {
|
|
acceptor.Stop()
|
|
}
|
|
|
|
mod.Info("all acceptors stopped")
|
|
|
|
mod.advertiser = nil
|
|
return nil
|
|
}
|