|
|
@ -9,6 +9,7 @@ import selectors
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from dataclasses import dataclass
|
|
|
|
import logging as log
|
|
|
|
import logging as log
|
|
|
|
import math
|
|
|
|
import math
|
|
|
|
|
|
|
|
import termcolor # external dep, TODO
|
|
|
|
# Note: get/setrlimit are in resource module
|
|
|
|
# Note: get/setrlimit are in resource module
|
|
|
|
|
|
|
|
|
|
|
|
def setup_logging():
|
|
|
|
def setup_logging():
|
|
|
@ -36,9 +37,30 @@ def hexdump(data: bytes) -> list[str]: # We will be prefixing the lines with sig
|
|
|
|
result.append(line)
|
|
|
|
result.append(line)
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fd types from inode(7)
|
|
|
|
|
|
|
|
fdtypes = {
|
|
|
|
|
|
|
|
S_IFSOCK: 'socket',
|
|
|
|
|
|
|
|
S_IFLNK: 'symlink',
|
|
|
|
|
|
|
|
S_IFREG: 'regular file',
|
|
|
|
|
|
|
|
S_IFBLK: 'blockdev',
|
|
|
|
|
|
|
|
S_IFDIR: 'directory',
|
|
|
|
|
|
|
|
S_IFCHR: 'chardev',
|
|
|
|
|
|
|
|
S_IFIFO: 'fifo',
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def fdinfo(fd: int) -> str:
|
|
|
|
def fdinfo(fd: int) -> str:
|
|
|
|
|
|
|
|
result = []
|
|
|
|
stat = os.fstat(fd)
|
|
|
|
stat = os.fstat(fd)
|
|
|
|
# TODO: return something
|
|
|
|
type = S_IFMT(stat.st_mode)
|
|
|
|
|
|
|
|
result.append(f'type: {fdtypes.get(type, f"UNKNOWN: {type}")}')
|
|
|
|
|
|
|
|
if S_ISREG(stat.st_mode):
|
|
|
|
|
|
|
|
result.append(f'size: {stat.st_size}')
|
|
|
|
|
|
|
|
orig_offset = os.lseek(fd, 0, os.SEEK_CUR)
|
|
|
|
|
|
|
|
os.lseek(fd, 0, os.SEEK_SET)
|
|
|
|
|
|
|
|
head = os.read(fd, 256)
|
|
|
|
|
|
|
|
result.extend(hexdump(head))
|
|
|
|
|
|
|
|
os.lseek(fd, orig_offset, os.SEEK_SET)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def fd_readlink(fd: int, pid: int | None =None) -> str:
|
|
|
|
def fd_readlink(fd: int, pid: int | None =None) -> str:
|
|
|
|
if pid is None:
|
|
|
|
if pid is None:
|
|
|
@ -49,12 +71,66 @@ def fd_readlink(fd: int, pid: int | None =None) -> str:
|
|
|
|
except OSError as e:
|
|
|
|
except OSError as e:
|
|
|
|
return f"Could not read {fd_link}: {e.strerror} (errno {e.errno})"
|
|
|
|
return f"Could not read {fd_link}: {e.strerror} (errno {e.errno})"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dirprintlines(dir: Direction, lines, file=sys.stdout, indent=''):
|
|
|
|
|
|
|
|
if isinstance(lines, str): lines = [lines]
|
|
|
|
|
|
|
|
lines_to_print = [f'{dir.sigil} {indent}{line}' for line in lines]
|
|
|
|
|
|
|
|
if True or COLOR:
|
|
|
|
|
|
|
|
lines_to_print = [termcolor.colored(line, dir.color) for line in lines_to_print]
|
|
|
|
|
|
|
|
print(*lines_to_print, sep='\n', file=file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_fd(dir, fd, /, indent=' '):
|
|
|
|
|
|
|
|
# Here we decide what to show and format it. We leave any particular
|
|
|
|
|
|
|
|
# decisions on other functions.
|
|
|
|
|
|
|
|
dirprintlines(dir, f'link: {fd_readlink(fd)}', indent=indent)
|
|
|
|
|
|
|
|
dirprintlines(dir, fdinfo(fd), indent=indent)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Maps from integers to symbolic names, acc to unix(7)
|
|
|
|
|
|
|
|
sockopts = {getattr(sk, name): name for name in(
|
|
|
|
|
|
|
|
'SO_PASSCRED',
|
|
|
|
|
|
|
|
'SO_PASSSEC',
|
|
|
|
|
|
|
|
# 'SO_PEEK_OFF', # wtf python does not have this
|
|
|
|
|
|
|
|
'SO_PEERCRED',
|
|
|
|
|
|
|
|
'SO_PEERSEC',
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
anclevels = {getattr(sk, name): name for name in(
|
|
|
|
|
|
|
|
'SOL_SOCKET',
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
anctypes = {getattr(sk, name): name for name in(
|
|
|
|
|
|
|
|
'SCM_RIGHTS',
|
|
|
|
|
|
|
|
'SCM_CREDENTIALS',
|
|
|
|
|
|
|
|
# 'SCM_SECURITY', # wtf python does not have this
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_anc(dir, a, /, indent=' ') -> list[int]:
|
|
|
|
|
|
|
|
"returns a possible list of file descriptors"
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
|
|
level, type, data = a
|
|
|
|
|
|
|
|
if level == sk.SOL_SOCKET and type == sk.SCM_RIGHTS:
|
|
|
|
|
|
|
|
# A file descriptor
|
|
|
|
|
|
|
|
fds = array('i')
|
|
|
|
|
|
|
|
fds.frombytes(data)
|
|
|
|
|
|
|
|
dirprintlines(dir, f'Received {len(fds)} file descriptor{"s"*(len(fds)!=1)}', indent=indent)
|
|
|
|
|
|
|
|
result = list(fds)
|
|
|
|
|
|
|
|
for fd in fds:
|
|
|
|
|
|
|
|
show_fd(dir, fd, indent=2*indent)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
dirprintlines(dir, 'Cannot show details.', indent=indent)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def fwd_traffic(frm: sk.socket, to: sk.socket, dir: Direction):
|
|
|
|
def fwd_traffic(frm: sk.socket, to: sk.socket, dir: Direction):
|
|
|
|
print(f'{dir.sigil} there be data from {frm}')
|
|
|
|
|
|
|
|
chunk_size = 640*1024 # … idk, ought to be enough for anyone…
|
|
|
|
chunk_size = 640*1024 # … idk, ought to be enough for anyone…
|
|
|
|
data, anc, flags, _addr = frm.recvmsg(chunk_size, chunk_size)
|
|
|
|
fds = [] # potential fds
|
|
|
|
# TODO: inspect
|
|
|
|
data, anc, flags, addr = frm.recvmsg(chunk_size, chunk_size)
|
|
|
|
|
|
|
|
dirprintlines(dir, f'Forwarding {len(data)} bytes and {len(anc)} ancillary from {addr} to {to}')
|
|
|
|
|
|
|
|
dirprintlines(dir, hexdump(data), indent=' ')
|
|
|
|
|
|
|
|
for i,a in enumerate(anc):
|
|
|
|
|
|
|
|
anclevel, anctype, _ancdata = a
|
|
|
|
|
|
|
|
dirprintlines(dir, f'Ancillary #{i}: level {anclevels[anclevel]}, type {anctypes[anctype]}')
|
|
|
|
|
|
|
|
fds = show_anc(dir, a)
|
|
|
|
to.sendmsg([data], anc, flags)
|
|
|
|
to.sendmsg([data], anc, flags)
|
|
|
|
|
|
|
|
if fds: log.debug(f'Closing fds: {fds}')
|
|
|
|
|
|
|
|
for fd in fds: os.close(fd)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
setup_logging()
|
|
|
|
setup_logging()
|
|
|
|