arblant
firefox-vpn-client / core / internal / tunapi / socket_darwin.go
socket_darwin.go55 lines · 1.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
package tunapi

import (
	"bytes"
	"encoding/binary"
	"errors"
	"net"

	"golang.org/x/sys/unix"
)

// SocketPath is the service's socket, in a folder only root may write to.
const SocketPath = "/var/run/firefox-vpn-tun.sock"

// LaunchdLabel is the service's launchd job.
const LaunchdLabel = "com.arman.firefoxvpn.tun"

// ServiceFile is the service's launchd job file.
const ServiceFile = "/Library/LaunchDaemons/" + LaunchdLabel + ".plist"

// peer asks the kernel who is at the other end of c.
func peer(c net.Conn) (peerInfo, error) {
	var p peerInfo
	err := fdOf(c, func(fd int) error {
		cred, err := unix.GetsockoptXucred(fd, unix.SOL_LOCAL, unix.LOCAL_PEERCRED)
		if err != nil {
			return err
		}
		pid, err := unix.GetsockoptInt(fd, unix.SOL_LOCAL, unix.LOCAL_PEERPID)
		if err != nil {
			return err
		}
		p = peerInfo{uid: cred.Uid, pid: int32(pid)}
		return nil
	})
	return p, err
}

// ProcessPath is the executable of process pid: the path the kernel keeps
// before its arguments (kern.procargs2: argc, then the path).
func ProcessPath(pid uint32) (string, error) {
	data, err := unix.SysctlRaw("kern.procargs2", int(pid))
	if err != nil {
		return "", err
	}
	if len(data) < 4 {
		return "", errors.New("no process arguments")
	}
	_ = binary.LittleEndian.Uint32(data[:4]) // argc
	path, _, ok := bytes.Cut(data[4:], []byte{0})
	if !ok || len(path) == 0 {
		return "", errors.New("no executable path")
	}
	return string(path), nil
}