Skip to content

Instantly share code, notes, and snippets.

@alexec
Last active January 3, 2023 04:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexec/2d611c7cc416b2e96beb6aa056c3a7aa to your computer and use it in GitHub Desktop.
Save alexec/2d611c7cc416b2e96beb6aa056c3a7aa to your computer and use it in GitHub Desktop.
//go:build darwin
package lsof
/*
#include <netinet/in.h>
#include <arpa/inet.h>
#include "fcntl.h"
#include "libproc.h"
*/
import "C"
import (
"encoding/binary"
"unsafe"
)
func ListOpenFiles(pid int) ([]any, error) {
// https://github.com/mellow-io/go-tun2socks/blob/6289c4be8d7c2915a73ba0d303d71a3a135e1574/common/proc/proc_darwin.go
// http://blog.palominolabs.com/2012/06/19/getting-the-files-being-used-by-a-process-on-mac-os-x/index.html
// https://github.com/zosmac/gomon/blob/c6ccca1675e4ca0b933913ca63f61d895a02f301/core/core_darwin.go
var tai C.struct_proc_taskallinfo
bufUsed, err := C.proc_pidinfo(
C.int(pid),
C.PROC_PIDTASKALLINFO,
0,
unsafe.Pointer(&tai),
C.int(unsafe.Sizeof(tai)))
if bufUsed <= 0 {
return nil, err
}
n := int(tai.pbsd.pbi_nfiles)
var fdi C.struct_proc_fdinfo
nbFds := int(unsafe.Sizeof(fdi)) * n
fds := make([]C.struct_proc_fdinfo, nbFds)
bufUsed, err = C.proc_pidinfo(
C.int(pid),
C.PROC_PIDLISTFDS,
0,
unsafe.Pointer(&fds[0]),
C.int(nbFds))
if bufUsed <= 0 {
panic(err)
}
numFds := int(bufUsed) / int(unsafe.Sizeof(fdi))
var openFiles []any
for j := 0; j < numFds; j++ {
if fds[j].proc_fdtype == C.PROX_FDTYPE_SOCKET {
if port, err := portInfo(pid, int(fds[j].proc_fd)); err != nil {
return nil, err
} else {
openFiles = append(openFiles, port)
}
} else if fds[j].proc_fdtype == C.PROX_FDTYPE_VNODE {
if path, err := fdPath(pid, int(fds[j].proc_fd)); err != nil {
return nil, err
} else {
openFiles = append(openFiles, path)
}
}
}
return openFiles, nil
}
func portInfo(pid, fd int) (PortInfo, error) {
var si C.struct_socket_fdinfo
if n, err := C.proc_pidfdinfo(
C.int(pid),
C.int(fd),
C.PROC_PIDFDSOCKETINFO,
unsafe.Pointer(&si),
C.PROC_PIDFDVNODEPATHINFO_SIZE,
); n <= 0 {
return PortInfo{}, err
}
proto := si.psi.soi_protocol
localBuf := make([]byte, 2)
remoteBuf := make([]byte, 2)
var network string
var localPort uint16
var remotePort uint16
var str [int(C.INET_ADDRSTRLEN)]C.char
if proto == C.IPPROTO_TCP || proto == C.IPPROTO_UDP {
if si.psi.soi_kind == C.SOCKINFO_TCP {
priTcp := (*C.struct_tcp_sockinfo)(unsafe.Pointer(&si.psi.soi_proto[0]))
ina46 := (*C.struct_in4in6_addr)(unsafe.Pointer(&priTcp.tcpsi_ini.insi_laddr[0]))
la := ina46.i46a_addr4
C.inet_ntop(
C.AF_INET,
unsafe.Pointer(&la),
&str[0],
C.INET_ADDRSTRLEN)
localPort = uint16(priTcp.tcpsi_ini.insi_lport)
remotePort = uint16(priTcp.tcpsi_ini.insi_fport)
network = "tcp"
} else {
priIn := (*C.struct_in_sockinfo)(unsafe.Pointer(&si.psi.soi_proto[0]))
ina46 := (*C.struct_in4in6_addr)(unsafe.Pointer(&priIn.insi_laddr[0]))
la := ina46.i46a_addr4
C.inet_ntop(
C.AF_INET,
unsafe.Pointer(&la),
&str[0],
C.INET_ADDRSTRLEN)
localPort = uint16(priIn.insi_lport)
remotePort = uint16(priIn.insi_fport)
network = "udp"
}
binary.BigEndian.PutUint16(localBuf, localPort)
binary.BigEndian.PutUint16(remoteBuf, remotePort)
return PortInfo{
Network: network,
Addr: network,
LocalPort: binary.LittleEndian.Uint16(localBuf),
RemotePort: binary.LittleEndian.Uint16(remoteBuf),
}, nil
}
return PortInfo{Network: "unknown"}, nil
}
func fdPath(pid, fd int) (FileInfo, error) {
var fi C.struct_vnode_fdinfowithpath
if n, err := C.proc_pidfdinfo(
C.int(pid),
C.int(fd),
C.PROC_PIDFDVNODEPATHINFO,
unsafe.Pointer(&fi),
C.PROC_PIDFDVNODEPATHINFO_SIZE,
); n <= 0 {
return FileInfo{}, err
}
path := C.GoString(&fi.pvip.vip_path[0])
read := int(fi.pfi.fi_openflags)&1 > 0
write := int(fi.pfi.fi_openflags)&2 > 0
return FileInfo{Path: path, Read: read, Write: write}, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment