Files
phishingclub/backend/vendor/github.com/ysmood/leakless/leakless.go
T
Ronni Skansing f5e6f53d75 update vendor
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2026-05-24 09:57:07 +02:00

156 lines
3.1 KiB
Go

//go:generate go run ./cmd/pack
package leakless
import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net"
"os"
"os/exec"
"path/filepath"
"runtime"
"time"
"github.com/ysmood/leakless/pkg/shared"
"github.com/ysmood/leakless/pkg/utils"
)
var leaklessBinaries = map[string]string{}
// Launcher struct
type Launcher struct {
// Lock for leakless.LockPort, default is 2978
Lock int
pid chan int
err string
}
// New leakless instance
func New() *Launcher {
return &Launcher{
Lock: 2978,
pid: make(chan int),
}
}
// Command will try to download the leakless bin and prefix the exec.Cmd with the leakless options.
func (l *Launcher) Command(name string, arg ...string) *exec.Cmd {
bin := ""
func() {
defer LockPort(l.Lock)()
bin = GetLeaklessBin()
}()
uid := fmt.Sprintf("%x", utils.RandBytes(16))
addr := l.serve(uid)
arg = append([]string{uid, addr, name}, arg...)
return exec.Command(bin, arg...)
}
// Pid signals the pid of the guarded sub-process. The channel may never receive the pid.
func (l *Launcher) Pid() chan int {
return l.pid
}
// Err message from the guard process
func (l *Launcher) Err() string {
return l.err
}
func (l *Launcher) serve(uid string) string {
srv, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic("[leakless] serve error: " + err.Error())
}
go func() {
defer func() { _ = srv.Close() }()
conn, err := srv.Accept()
if err != nil {
l.err = err.Error()
l.pid <- 0
return
}
enc := json.NewEncoder(conn)
err = enc.Encode(shared.Message{UID: uid})
if err != nil {
l.err = err.Error()
l.pid <- 0
return
}
dec := json.NewDecoder(conn)
var msg shared.Message
err = dec.Decode(&msg)
if err == nil {
l.err = msg.Error
l.pid <- msg.PID
}
_ = dec.Decode(&msg)
}()
return srv.Addr().String()
}
var leaklessDir = filepath.Join(os.TempDir(), fmt.Sprintf("leakless-%s-%s", runtime.GOARCH, shared.Version))
// GetLeaklessBin returns the executable path of the guard, if it doesn't exists create one.
func GetLeaklessBin() string {
bin := filepath.Join(leaklessDir, "leakless")
if runtime.GOOS == "windows" {
bin += ".exe"
}
if !utils.FileExists(bin) {
name := utils.GetTarget().BinName()
raw, err := base64.StdEncoding.DecodeString(leaklessBinaries[name])
utils.E(err)
gr, err := gzip.NewReader(bytes.NewBuffer(raw))
utils.E(err)
data, err := ioutil.ReadAll(gr)
utils.E(err)
utils.E(gr.Close())
err = utils.OutputFile(bin, data, nil)
utils.E(err)
utils.E(os.Chmod(bin, 0755))
}
return bin
}
// Support returns true if the OS is supported by leakless.
func Support() bool {
_, has := leaklessBinaries[utils.GetTarget().BinName()]
return has
}
// LockPort uses a tcp port to create a mutex lock for cross-process locking.
// It will poll the port to check if it's free.
func LockPort(port int) func() {
var l net.Listener
for {
var err error
l, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err == nil {
break
}
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
}
return func() {
_ = l.Close()
}
}