mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
1060 lines
27 KiB
C
1060 lines
27 KiB
C
/* source: filan.c */
|
|
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* the subroutine filan makes a "FILe descriptor ANalysis". It checks the
|
|
type of file descriptor and tries to retrieve as much info about it as
|
|
possible without modifying its state.
|
|
NOTE: it works on UNIX (kernel) file descriptors, not on libc files! */
|
|
|
|
#include "config.h"
|
|
#include "xioconfig.h" /* what features are enabled */
|
|
|
|
#include "sysincludes.h"
|
|
|
|
#include "mytypes.h"
|
|
#include "compat.h"
|
|
#include "error.h"
|
|
#include "sycls.h"
|
|
#include "sysutils.h"
|
|
|
|
#include "filan.h"
|
|
|
|
|
|
struct sockopt {
|
|
int so;
|
|
char *name;
|
|
};
|
|
|
|
static int filan_streams_analyze(int fd, FILE *outfile);
|
|
|
|
/* global variables for configuring filan */
|
|
bool filan_followsymlinks;
|
|
bool filan_rawoutput;
|
|
|
|
|
|
int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile);
|
|
int tcpan(int fd, FILE *outfile);
|
|
int tcpan2(int fd, FILE *outfile);
|
|
const char *getfiletypestring(int st_mode);
|
|
|
|
static int printtime(FILE *outfile, time_t time);
|
|
|
|
static int headprinted;
|
|
|
|
/* analyse a file system entry, referred by file name */
|
|
int filan_file(const char *filename, FILE *outfile) {
|
|
int fd = -1;
|
|
int result;
|
|
#if HAVE_STAT64
|
|
struct stat64 buf = {0};
|
|
#else
|
|
struct stat buf = {0};
|
|
#endif /* !HAVE_STAT64 */
|
|
|
|
if (filan_followsymlinks) {
|
|
#if HAVE_STAT64
|
|
result = Stat64(filename, &buf);
|
|
#else
|
|
result = Stat(filename, &buf);
|
|
#endif /* !HAVE_STAT64 */
|
|
if (result < 0) {
|
|
Warn3("stat(\"%s\", %p): %s", filename, &buf, strerror(errno));
|
|
}
|
|
} else {
|
|
#if HAVE_STAT64
|
|
result = Lstat64(filename, &buf);
|
|
#else
|
|
result = Lstat(filename, &buf);
|
|
#endif /* !HAVE_STAT64 */
|
|
if (result < 0) {
|
|
Warn3("lstat(\"%s\", %p): %s", filename, &buf, strerror(errno));
|
|
}
|
|
}
|
|
switch (buf.st_mode&S_IFMT) {
|
|
#ifdef S_IFSOCK
|
|
case S_IFSOCK: /* probably, it's useless to make a socket and describe it */
|
|
break;
|
|
#endif /* S_IFSOCK */
|
|
default:
|
|
if ((fd =
|
|
Open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK
|
|
#ifdef O_NOFOLLOW
|
|
|(filan_followsymlinks?0:O_NOFOLLOW)
|
|
#endif
|
|
#ifdef O_LARGEFILE
|
|
|O_LARGEFILE
|
|
#endif
|
|
, 0700))
|
|
< 0) {
|
|
Warn2("open(\"%s\", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_LARGEFILE, 0700): %s",
|
|
filename, strerror(errno));
|
|
}
|
|
}
|
|
|
|
result = filan_stat(&buf, fd, -1, outfile, filename);
|
|
fputc('\n', outfile);
|
|
return result;
|
|
}
|
|
|
|
/* analyze a file descriptor */
|
|
int filan_fd(int fd, FILE *outfile) {
|
|
#if HAVE_STAT64
|
|
struct stat64 buf = {0};
|
|
#else
|
|
struct stat buf = {0};
|
|
#endif /* !HAVE_STAT64 */
|
|
int result;
|
|
|
|
Debug1("checking file descriptor %u", fd);
|
|
#if HAVE_STAT64
|
|
result = Fstat64(fd, &buf);
|
|
#else
|
|
result = Fstat(fd, &buf);
|
|
#endif /* !HAVE_STAT64 */
|
|
if (result < 0) {
|
|
if (errno == EBADF) {
|
|
Debug2("fstat(%d): %s", fd, strerror(errno));
|
|
} else {
|
|
Warn2("fstat(%d): %s", fd, strerror(errno));
|
|
}
|
|
return -1;
|
|
}
|
|
Debug2("fd %d is a %s", fd, getfiletypestring(buf.st_mode));
|
|
|
|
result = filan_stat(&buf, fd, fd, outfile, NULL);
|
|
|
|
if (result >= 0) {
|
|
/* even more dynamic info */
|
|
{ /* see if data is available */
|
|
struct pollfd ufds;
|
|
ufds.fd = fd;
|
|
ufds.events = POLLIN|POLLPRI|POLLOUT
|
|
#ifdef POLLRDNORM
|
|
|POLLRDNORM
|
|
#endif
|
|
#ifdef POLLRDBAND
|
|
|POLLRDBAND
|
|
#endif
|
|
|POLLWRNORM
|
|
#ifdef POLLWRBAND
|
|
|POLLWRBAND
|
|
#endif
|
|
#ifdef POLLMSG
|
|
|POLLMSG
|
|
#endif
|
|
;
|
|
#if HAVE_POLL
|
|
if (Poll(&ufds, 1, 0) < 0) {
|
|
Warn4("poll({%d, %hd, %hd}, 1, 0): %s",
|
|
ufds.fd, ufds.events, ufds.revents, strerror(errno));
|
|
} else {
|
|
fputs("poll: ", outfile);
|
|
if (ufds.revents & POLLIN) fputs("IN,", outfile);
|
|
if (ufds.revents & POLLPRI) fputs("PRI,", outfile);
|
|
if (ufds.revents & POLLOUT) fputs("OUT,", outfile);
|
|
if (ufds.revents & POLLERR) fputs("ERR,", outfile);
|
|
if (ufds.revents & POLLNVAL) fputs("NVAL,", outfile);
|
|
#ifdef FIONREAD
|
|
if (ufds.revents & POLLIN) {
|
|
size_t sizet;
|
|
if ((result = Ioctl(fd, FIONREAD, &sizet) >= 0)) {
|
|
fprintf (outfile, "; FIONREAD="F_Zu, sizet);
|
|
}
|
|
}
|
|
#endif /* defined(FIONREAD) */
|
|
#if _WITH_SOCKET && defined(MSG_DONTWAIT)
|
|
if ((ufds.revents & POLLIN) && isasocket(fd)) {
|
|
char _peername[SOCKADDR_MAX];
|
|
struct sockaddr *pa = (struct sockaddr *)_peername;
|
|
struct msghdr msgh = {0};
|
|
char peekbuff[1]; /* [0] fails with some compilers */
|
|
#if HAVE_STRUCT_IOVEC
|
|
struct iovec iovec;
|
|
#endif
|
|
char ctrlbuff[5120];
|
|
ssize_t bytes;
|
|
|
|
fputs("; ", outfile);
|
|
msgh.msg_name = pa;
|
|
msgh.msg_namelen = sizeof(*pa);
|
|
#if HAVE_STRUCT_IOVEC
|
|
iovec.iov_base = peekbuff;
|
|
iovec.iov_len = sizeof(peekbuff);
|
|
msgh.msg_iov = &iovec;
|
|
msgh.msg_iovlen = 1;
|
|
#endif
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROL
|
|
msgh.msg_control = ctrlbuff;
|
|
#endif
|
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
|
#endif
|
|
#if HAVE_STRUCT_MSGHDR_MSGFLAGS
|
|
msgh.msg_flags = 0;
|
|
#endif
|
|
if ((bytes = Recvmsg(fd, &msgh, MSG_PEEK|MSG_DONTWAIT)) < 0) {
|
|
Warn1("recvmsg(): %s", strerror(errno));
|
|
} else {
|
|
fprintf(outfile, "recvmsg="F_Zd", ", bytes);
|
|
}
|
|
}
|
|
#endif /* _WITH_SOCKET && defined(MSG_DONTWAIT) */
|
|
}
|
|
#endif /* HAVE_POLL */
|
|
}
|
|
}
|
|
fputc('\n', outfile);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int filan_stat(
|
|
#if HAVE_STAT64
|
|
struct stat64 *buf
|
|
#else
|
|
struct stat *buf
|
|
#endif /* !HAVE_STAT64 */
|
|
, int statfd, int dynfd, FILE *outfile,
|
|
const char *filename /* Linux does not (yet) provide an
|
|
freadlink system call, so we need
|
|
the original name for readlink in
|
|
case it is a symlink */
|
|
) {
|
|
char stdevstr[8];
|
|
|
|
/* print header */
|
|
if (!headprinted) {
|
|
if (filan_rawoutput) {
|
|
fputs(" FD type\tdevice\tinode\tmode\tlinks\tuid\tgid"
|
|
#if HAVE_ST_RDEV
|
|
"\trdev"
|
|
#endif
|
|
"\tsize"
|
|
#if HAVE_ST_BLKSIZE
|
|
"\tblksize"
|
|
#endif
|
|
#if HAVE_ST_BLOCKS
|
|
"\tblocks"
|
|
#endif
|
|
"\tatime\t\tmtime\t\tctime\t\tcloexec\tflags"
|
|
#if defined(F_GETOWN)
|
|
"\tsigown"
|
|
#endif
|
|
, outfile);
|
|
} else /* !rawoutput */ {
|
|
fputs(" FD type\tdevice\tinode\tmode\tlinks\tuid\tgid"
|
|
#if HAVE_ST_RDEV
|
|
"\trdev"
|
|
#endif
|
|
"\tsize"
|
|
#if HAVE_ST_BLKSIZE
|
|
"\tblksize"
|
|
#endif
|
|
#if HAVE_ST_BLOCKS
|
|
"\tblocks"
|
|
#endif
|
|
"\tatime\t\t\t\tmtime\t\t\t\tctime\t\t\t\tcloexec\tflags"
|
|
#if defined(F_GETOWN)
|
|
"\tsigown"
|
|
#endif
|
|
, outfile);
|
|
|
|
} /* endif !rawoutput */
|
|
|
|
#if defined(F_GETSIG)
|
|
fputs("\tsigio", outfile);
|
|
#endif /* defined(F_GETSIG) */
|
|
fputc('\n', outfile);
|
|
headprinted = 1;
|
|
}
|
|
if (filan_rawoutput) {
|
|
snprintf(stdevstr, 8, F_dev, buf->st_dev);
|
|
} else {
|
|
snprintf(stdevstr, 8, "%hu,%hu", (unsigned short)(buf->st_dev>>8), (unsigned short)(buf->st_dev&0xff));
|
|
}
|
|
fprintf(outfile, "%4d: %s\t%s\t"
|
|
#if HAVE_STAT64
|
|
F_st64_ino
|
|
#else
|
|
F_st_ino
|
|
#endif /* HAVE_STAT64 */
|
|
"\t"F_mode"\t"F_st_nlink"\t"F_uid"\t"F_gid
|
|
#if HAVE_ST_RDEV
|
|
"\t%hu,%hu"
|
|
#endif
|
|
"\t"
|
|
#if HAVE_STAT64
|
|
F_st64_size
|
|
#else
|
|
F_st_size
|
|
#endif /* HAVE_STAT64 */
|
|
#if HAVE_ST_BLKSIZE
|
|
"\t"F_st_blksize
|
|
#endif
|
|
#if HAVE_ST_BLOCKS
|
|
#if HAVE_STAT64
|
|
"\t"F_st64_blocks
|
|
#else
|
|
"\t"F_st_blocks
|
|
#endif /* HAVE_STAT64 */
|
|
#endif
|
|
,
|
|
(dynfd>=0?dynfd:statfd), getfiletypestring(buf->st_mode),
|
|
stdevstr,
|
|
buf->st_ino,
|
|
buf->st_mode, buf->st_nlink, buf->st_uid,
|
|
buf->st_gid,
|
|
#if HAVE_ST_RDEV
|
|
(unsigned short)(buf->st_rdev>>8), (unsigned short)(buf->st_rdev&0xff),
|
|
#endif
|
|
buf->st_size
|
|
#if HAVE_ST_BLKSIZE
|
|
, buf->st_blksize
|
|
#endif
|
|
#if HAVE_ST_BLOCKS
|
|
, buf->st_blocks /* on Linux, this applies to stat and stat64 */
|
|
#endif
|
|
);
|
|
|
|
printtime(outfile, buf->st_atime);
|
|
printtime(outfile, buf->st_mtime);
|
|
printtime(outfile, buf->st_ctime);
|
|
|
|
#if 0
|
|
{
|
|
fputc('\t', outfile);
|
|
time = asctime(localtime(&buf->st_mtime));
|
|
if (strchr(time, '\n')) *strchr(time, '\n') = '\0';
|
|
fputs(time, outfile);
|
|
|
|
fputc('\t', outfile);
|
|
time = asctime(localtime(&buf->st_ctime));
|
|
if (strchr(time, '\n')) *strchr(time, '\n') = '\0';
|
|
fputs(time, outfile);
|
|
}
|
|
#endif
|
|
|
|
/* here comes dynamic info - it is only meaningful with preexisting FDs */
|
|
if (dynfd >= 0) { /*!indent */
|
|
int cloexec, flags;
|
|
#if defined(F_GETOWN)
|
|
int sigown;
|
|
#endif
|
|
#if defined(F_GETSIG)
|
|
int sigio;
|
|
#endif /* defined(F_GETSIG) */
|
|
|
|
cloexec = Fcntl(dynfd, F_GETFD);
|
|
flags = Fcntl(dynfd, F_GETFL);
|
|
#if defined(F_GETOWN)
|
|
sigown = Fcntl(dynfd, F_GETOWN);
|
|
#endif
|
|
#if defined(F_GETSIG)
|
|
sigio = Fcntl(dynfd, F_GETSIG);
|
|
#endif /* defined(F_GETSIG) */
|
|
fprintf(outfile, "\t%d\tx%06x", cloexec, flags);
|
|
#if defined(F_GETOWN)
|
|
fprintf(outfile, "\t%d", sigown);
|
|
#endif
|
|
#if defined(F_GETSIG)
|
|
fprintf(outfile, "\t%d", sigio);
|
|
#endif /* defined(F_GETSIG) */
|
|
} else {
|
|
fputs("\t\t"
|
|
#if defined(F_GETOWN)
|
|
"\t"
|
|
#endif
|
|
#if defined(F_GETSIG)
|
|
"\t"
|
|
#endif /* defined(F_GETSIG) */
|
|
, outfile);
|
|
}
|
|
|
|
/* ever heard of POSIX streams? here we handle these */
|
|
filan_streams_analyze(statfd, outfile);
|
|
|
|
/* now see for type specific infos */
|
|
if (statfd >= 0) { /*!indent */
|
|
switch (buf->st_mode&S_IFMT) {
|
|
case (S_IFIFO): /* 1, FIFO */
|
|
break;
|
|
case (S_IFCHR): /* 2, character device */
|
|
cdevan(statfd, outfile);
|
|
break;
|
|
case (S_IFDIR): /* 4, directory */
|
|
break;
|
|
case (S_IFBLK): /* 6, block device */
|
|
break;
|
|
case (S_IFREG): /* 8, regular file */
|
|
break;
|
|
#ifdef S_IFLNK
|
|
case (S_IFLNK): /* 10, symbolic link */
|
|
/* we wait for freadlink() sytem call */
|
|
break;
|
|
#endif /* S_IFLNK */
|
|
break;
|
|
#ifdef S_IFSOCK
|
|
case (S_IFSOCK): /* 12, socket */
|
|
#if _WITH_SOCKET
|
|
sockan(statfd, outfile);
|
|
#else
|
|
Warn("SOCKET support not compiled in");
|
|
return -1;
|
|
#endif /* !_WITH_SOCKET */
|
|
break;
|
|
#endif /* S_IFSOCK */
|
|
}
|
|
} else {
|
|
switch (buf->st_mode&S_IFMT) {
|
|
#ifdef S_IFLNK
|
|
case (S_IFLNK): /* 10, symbolic link */
|
|
{
|
|
char linktarget[PATH_MAX+1];
|
|
memset(linktarget, 0, PATH_MAX+1);
|
|
Readlink(filename, linktarget, PATH_MAX);
|
|
fprintf(outfile, "LINKTARGET=%s", linktarget);
|
|
}
|
|
break;
|
|
#endif /* S_IFLNK */
|
|
}
|
|
}
|
|
/* ioctl() */
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if LATER
|
|
int fdinfo(int fd) {
|
|
int result;
|
|
|
|
result = Fcntl(fd, F_GETFD);
|
|
fcntl(fd, F_GETFL, );
|
|
fcntl(fd, F_GETLK, );
|
|
#ifdef F_GETOWN
|
|
fcntl(fd, F_GETOWN, );
|
|
#endif
|
|
#ifdef F_GETSIG
|
|
fcntl(fd, F_GETSIG, );
|
|
#endif
|
|
}
|
|
|
|
|
|
int devinfo(int fd) {
|
|
ioctl();
|
|
}
|
|
#endif
|
|
|
|
|
|
/* returns 0 on success (not a stream descriptor, or no module)
|
|
returns <0 on failure */
|
|
static int filan_streams_analyze(int fd, FILE *outfile) {
|
|
#ifdef I_LIST
|
|
# define SL_NMODS 8 /* max number of module names we can store */
|
|
struct str_list modnames;
|
|
int i;
|
|
|
|
if (!isastream(fd)) {
|
|
fprintf(outfile, "\t(no STREAMS modules)");
|
|
return 0;
|
|
}
|
|
#if 0 /* uncomment for debugging */
|
|
fprintf(outfile, "\tfind=%d", ioctl(fd, I_FIND, "ldterm"));
|
|
#endif
|
|
modnames.sl_nmods = ioctl(fd, I_LIST, 0);
|
|
if (modnames.sl_nmods < 0) {
|
|
fprintf(stderr, "ioctl(%d, I_LIST, 0): %s\n", fd, strerror(errno));
|
|
return -1;
|
|
}
|
|
modnames.sl_modlist = Malloc(modnames.sl_nmods*(sizeof(struct str_mlist)));
|
|
if (modnames.sl_modlist == NULL) {
|
|
fprintf(stderr, "out of memory\n");
|
|
return -1;
|
|
}
|
|
if (ioctl(fd, I_LIST, &modnames) < 0) {
|
|
fprintf(stderr, "ioctl(%d, I_LIST, %p): %s\n",
|
|
fd, &modnames, strerror(errno));
|
|
free(modnames.sl_modlist);
|
|
return -1;
|
|
}
|
|
fprintf(outfile, "\tSTREAMS: ");
|
|
for (i = 0; i < modnames.sl_nmods; ++i) {
|
|
fprintf(outfile, "\"%s\"", modnames.sl_modlist[i].l_name);
|
|
if (i+1 < modnames.sl_nmods) fputc(',', outfile);
|
|
}
|
|
free(modnames.sl_modlist);
|
|
#endif /* defined(I_LIST) */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* character device analysis */
|
|
int cdevan(int fd, FILE *outfile) {
|
|
int ret;
|
|
|
|
#if _WITH_TERMIOS
|
|
if ((ret = Isatty(fd)) < 0) {
|
|
Warn2("isatty(%d): %s", fd, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (ret > 0) {
|
|
struct termios termarg;
|
|
char *name;
|
|
int i;
|
|
|
|
if ((name = Ttyname(fd)) == NULL) {
|
|
/*Warn2("ttyname(%d): %s", fd, strerror(errno));*/
|
|
fputs("\tNULL", outfile);
|
|
} else {
|
|
fprintf(outfile, "\t%s", name);
|
|
}
|
|
if (Tcgetattr(fd, &termarg) < 0) {
|
|
Warn3("tcgetattr(%d, %p): %s", fd, &termarg, strerror(errno));
|
|
return -1;
|
|
}
|
|
fprintf(outfile, " \tIFLAGS=%08x OFLAGS=%08x CFLAGS=%08x LFLAGS=%08x",
|
|
(unsigned int)termarg.c_iflag,
|
|
(unsigned int)termarg.c_oflag,
|
|
(unsigned int)termarg.c_cflag,
|
|
(unsigned int)termarg.c_lflag);
|
|
|
|
/* and the control characters */
|
|
if (filan_rawoutput) {
|
|
for (i=0; i<NCCS; ++i) {
|
|
fprintf(outfile, " cc[%d]=%d", i, termarg.c_cc[i]);
|
|
}
|
|
} else {
|
|
for (i=0; i<NCCS; ++i) {
|
|
int ch;
|
|
unsigned char s[4];
|
|
ch = termarg.c_cc[i];
|
|
if (isprint(ch)) {
|
|
s[0] = ch; s[1]= '\0';
|
|
} else if (ch < ' ') {
|
|
s[0] = '^'; s[1] = ch+'@'; s[2] = '\0';
|
|
} else {
|
|
s[0] = 'x';
|
|
s[1] = (ch>>4)>=10?(ch>>4)-10+'A':(ch>>4)+'0';
|
|
s[2] = (ch&0x0f)>=10?(ch&0x0f)-10+'A':(ch&0x0f)+'0';
|
|
s[3] = '\0';
|
|
}
|
|
fprintf(outfile, " cc[%d]=%s", i, s);
|
|
}
|
|
}
|
|
}
|
|
#endif /* _WITH_TERMIOS */
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if _WITH_SOCKET
|
|
int sockan(int fd, FILE *outfile) {
|
|
#define FILAN_OPTLEN 256
|
|
#define FILAN_NAMELEN 256
|
|
socklen_t optlen;
|
|
int result /*0, i*/;
|
|
char nambuff[FILAN_NAMELEN];
|
|
/* in Linux these optcodes are 'enum', but on AIX they are bits! */
|
|
static const struct sockopt sockopts[] = {
|
|
{SO_DEBUG, "DEBUG"},
|
|
{SO_REUSEADDR, "REUSEADDR"},
|
|
#ifdef SO_PROTOCOL
|
|
{SO_PROTOCOL, "PROTOCOL"},
|
|
#elif defined(SO_PROTOTYPE)
|
|
{SO_PROTOTYPE, "PROTOTYPE"},
|
|
#endif
|
|
{SO_TYPE, "TYPE"},
|
|
{SO_ERROR, "ERROR"},
|
|
{SO_DONTROUTE, "DONTROUTE"},
|
|
{SO_BROADCAST, "BROADCAST"},
|
|
{SO_SNDBUF, "SNDBUF"},
|
|
{SO_RCVBUF, "RCVBUF"},
|
|
{SO_KEEPALIVE, "KEEPALIVE"},
|
|
{SO_OOBINLINE, "OOBINLINE"},
|
|
#ifdef SO_NO_CHECK
|
|
{SO_NO_CHECK, "NO_CHECK"},
|
|
#endif
|
|
#ifdef SO_PRIORITY
|
|
{SO_PRIORITY, "PRIORITY"},
|
|
#endif
|
|
{SO_LINGER, "LINGER"},
|
|
#ifdef SO_BSDCOMPAT
|
|
{SO_BSDCOMPAT, "BSDCOMPAT"},
|
|
#endif
|
|
#ifdef SO_REUSEPORT
|
|
{SO_REUSEPORT, "REUSEPORT"},
|
|
#endif /* defined(SO_REUSEPORT) */
|
|
#ifdef SO_PASSCRED
|
|
{SO_PASSCRED, "PASSCRED"},
|
|
#endif
|
|
#ifdef SO_PEERCRED
|
|
{SO_PEERCRED, "PEERCRED"},
|
|
#endif
|
|
#ifdef SO_RCVLOWAT
|
|
{SO_RCVLOWAT, "RCVLOWAT"},
|
|
#endif
|
|
#ifdef SO_SNDLOWAT
|
|
{SO_SNDLOWAT, "SNDLOWAT"},
|
|
#endif
|
|
#ifdef SO_RCVTIMEO
|
|
{SO_RCVTIMEO, "RCVTIMEO"},
|
|
#endif
|
|
#ifdef SO_SNDTIMEO
|
|
{SO_SNDTIMEO, "SNDTIMEO"},
|
|
#endif
|
|
#ifdef SO_SECURITY_AUTHENTICATION
|
|
{SO_SECURITY_AUTHENTICATION, "SECURITY_AUTHENTICATION"},
|
|
#endif
|
|
#ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
|
|
{SO_SECURITY_ENCRYPTION_TRANSPORT, "SECURITY_ENCRYPTION_TRANSPORT"},
|
|
#endif
|
|
#ifdef SO_SECURITY_ENCRYPTION_NETWORK
|
|
{SO_SECURITY_ENCRYPTION_NETWORK, "SECURITY_ENCRYPTION_NETWORK"},
|
|
#endif
|
|
#ifdef SO_BINDTODEVICE
|
|
{SO_BINDTODEVICE, "BINDTODEVICE"},
|
|
#endif
|
|
#ifdef SO_ATTACH_FILTER
|
|
{SO_ATTACH_FILTER, "ATTACH_FILTER"},
|
|
#endif
|
|
#ifdef SO_DETACH_FILTER
|
|
{SO_DETACH_FILTER, "DETACH_FILTER"},
|
|
#endif
|
|
{0, NULL} } ;
|
|
union {
|
|
char c[FILAN_OPTLEN];
|
|
int i[FILAN_OPTLEN/sizeof(int)];
|
|
} optval;
|
|
const struct sockopt *optname;
|
|
union sockaddr_union sockname, peername; /* the longest I know of */
|
|
socklen_t namelen;
|
|
#if 0 && defined(SIOCGIFNAME)
|
|
/*Linux struct ifreq ifc = {{{ 0 }}};*/
|
|
struct ifreq ifc = {{ 0 }};
|
|
#endif
|
|
|
|
optlen = FILAN_OPTLEN;
|
|
result = Getsockopt(fd, SOL_SOCKET, SO_TYPE, optval.c, &optlen);
|
|
if (result < 0) {
|
|
Debug4("getsockopt(%d, SOL_SOCKET, SO_TYPE, %p, {"F_socklen"}): %s",
|
|
fd, optval.c, optlen, strerror(errno));
|
|
} else {
|
|
# define TYPENAMEMAX 16
|
|
char typename[TYPENAMEMAX];
|
|
sockettype(*optval.i, typename, sizeof(typename));
|
|
|
|
Debug3("fd %d: socket of type %d (\"%s\")", fd, *optval.i,
|
|
typename);
|
|
}
|
|
|
|
optname = sockopts; while (optname->so) {
|
|
optlen = FILAN_OPTLEN;
|
|
result =
|
|
Getsockopt(fd, SOL_SOCKET, optname->so, (void *)optval.c, &optlen);
|
|
if (result < 0) {
|
|
Debug5("getsockopt(%d, SOL_SOCKET, %d, %p, {"F_socklen"}): %s",
|
|
fd, optname->so, optval.c, optlen, strerror(errno));
|
|
fputc('\t', outfile);
|
|
} else if (optlen == sizeof(int)) {
|
|
Debug2("getsockopt(,,, {%d}, %d)",
|
|
*optval.i, optlen);
|
|
/*Info2("%s: %d", optname->name, optval.i);*/
|
|
fprintf(outfile, "%s=%d\t", optname->name, *optval.i);
|
|
} else {
|
|
Debug3("getsockopt(,,, {%d,%d}, %d)",
|
|
optval.i[0], optval.i[1], optlen);
|
|
fprintf(outfile, "%s={%d,%d}\t", optname->name,
|
|
optval.i[0], optval.i[1]);
|
|
}
|
|
++optname;
|
|
}
|
|
|
|
namelen = sizeof(sockname);
|
|
result = Getsockname(fd, (struct sockaddr *)&sockname, &namelen);
|
|
if (result < 0) {
|
|
putc('\n', outfile);
|
|
Warn2("getsockname(%d): %s", fd, strerror(errno));
|
|
return -1;
|
|
}
|
|
fputc('\t', outfile);
|
|
fputs(sockaddr_info((struct sockaddr *)&sockname, namelen, nambuff, sizeof(nambuff)),
|
|
outfile);
|
|
|
|
namelen = sizeof(peername);
|
|
result = Getpeername(fd, (struct sockaddr *)&peername, &namelen);
|
|
if (result < 0) {
|
|
putc('\n', outfile);
|
|
Warn2("getpeername(%d): %s", fd, strerror(errno));
|
|
} else {
|
|
/* only valid if getpeername() succeeded */
|
|
fputs(" <-> ", outfile);
|
|
fprintf(outfile, "%s\t",
|
|
sockaddr_info((struct sockaddr *)&peername, namelen,
|
|
nambuff, sizeof(nambuff)));
|
|
}
|
|
|
|
#if 0 && defined(SIOCGIFNAME)
|
|
if ((result = Ioctl(fd, SIOCGIFNAME, &ifc)) < 0) {
|
|
Warn3("ioctl(%d, SIOCGIFNAME, %p): %s", fd, &ifc, strerror(errno));
|
|
} else {
|
|
fprintf(outfile, "IFNAME=\"%s\"\t", ifc.ifr_name);
|
|
}
|
|
#endif /* SIOCGIFNAME */
|
|
|
|
switch (((struct sockaddr *)&sockname)->sa_family) {
|
|
#if WITH_UNIX
|
|
case AF_UNIX:
|
|
/* no options for unix domain sockets known yet -> no unixan() */
|
|
result = 0;
|
|
break;
|
|
#endif
|
|
#if WITH_IP4
|
|
case AF_INET:
|
|
result = ipan(fd, outfile);
|
|
break;
|
|
#endif
|
|
#if WITH_IP6
|
|
case AF_INET6:
|
|
result = ipan(fd, outfile);
|
|
result |= ip6an(fd, outfile);
|
|
break;
|
|
#endif
|
|
default:
|
|
fputs("**** NO FURTHER ANALYSIS FOR THIS SOCKET TYPE IMPLEMENTED", outfile);
|
|
result = 0;
|
|
}
|
|
return result;
|
|
#undef FILAN_OPTLEN
|
|
#undef FILAN_NAMELEN
|
|
}
|
|
#endif /* _WITH_SOCKET */
|
|
|
|
|
|
#if WITH_IP4 || WITH_IP6
|
|
/* prints the option values for the IP protocol and the IP based protocols */
|
|
/* no distinction between IP4 and IP6 yet */
|
|
int ipan(int fd, FILE *outfile) {
|
|
/* in Linux these optcodes are 'enum', but on AIX they are bits! */
|
|
static const struct sockopt ipopts[] = {
|
|
{IP_TOS, "IP_TOS"},
|
|
{IP_TTL, "IP_TTL"},
|
|
#ifdef IP_HDRINCL
|
|
{IP_HDRINCL, "IP_HDRINCL"},
|
|
#endif
|
|
#ifdef IP_OPTIONS
|
|
{IP_OPTIONS, "IP_OPTIONS"},
|
|
#endif
|
|
#ifdef IP_ROUTER_ALERT
|
|
{IP_ROUTER_ALERT, "IP_ROUTER_ALERT"},
|
|
#endif
|
|
#ifdef IP_RECVOPTS
|
|
{IP_RECVOPTS, "IP_RECVOPTS"},
|
|
#endif
|
|
#ifdef IP_RETOPTS
|
|
{IP_RETOPTS, "IP_RETOPTS"},
|
|
#endif
|
|
#ifdef IP_PKTINFO
|
|
{IP_PKTINFO, "IP_PKTINFO"},
|
|
#endif
|
|
#ifdef IP_PKTOPTIONS
|
|
{IP_PKTOPTIONS, "IP_PKTOPTIONS"},
|
|
#endif
|
|
#ifdef IP_MTU_DISCOVER
|
|
{IP_MTU_DISCOVER, "IP_MTU_DISCOVER"},
|
|
#endif
|
|
#ifdef IP_RECVERR
|
|
{IP_RECVERR, "IP_RECVERR"},
|
|
#endif
|
|
#ifdef IP_RECVTTL
|
|
{IP_RECVTTL, "IP_RECVTTL"},
|
|
#endif
|
|
#ifdef IP_RECVTOS
|
|
{IP_RECVTOS, "IP_RECVTOS"},
|
|
#endif
|
|
#ifdef IP_TRANSPARENT
|
|
{IP_TRANSPARENT, "IP_TRANSPARENT"},
|
|
#endif
|
|
#ifdef IP_MTU
|
|
{IP_MTU, "IP_MTU"},
|
|
#endif
|
|
#ifdef IP_FREEBIND
|
|
{IP_FREEBIND, "IP_FREEBIND"},
|
|
#endif
|
|
#ifdef IP_MULTICAST_TTL
|
|
{IP_MULTICAST_TTL, "IP_MULTICAST_TTL"},
|
|
#endif
|
|
#ifdef IP_MULTICAST_LOOP
|
|
{IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP"},
|
|
#endif
|
|
{0, NULL} } ;
|
|
const struct sockopt *optname;
|
|
int optproto;
|
|
socklen_t optlen = sizeof(optproto);
|
|
|
|
optname = ipopts; while (optname->so) {
|
|
sockoptan(fd, optname, SOL_IP, outfile);
|
|
++optname;
|
|
}
|
|
/* want to pass the fd to the next layer protocol. */
|
|
#if defined(SO_PROTOCOL) || defined(SO_PROTOTYPE)
|
|
if (Getsockopt(fd, SOL_SOCKET,
|
|
#ifdef SO_PROTOCOL
|
|
SO_PROTOCOL,
|
|
#elif defined(SO_PROTOTYPE)
|
|
SO_PROTOTYPE,
|
|
#endif
|
|
&optproto, &optlen) >= 0) {
|
|
switch (optproto) {
|
|
#if WITH_TCP
|
|
case IPPROTO_TCP: tcpan(fd, outfile); break;
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* defined(SO_PROTOCOL) || defined(SO_PROTOTYPE) */
|
|
return 0;
|
|
}
|
|
#endif /* WITH_IP */
|
|
|
|
|
|
#if WITH_IP6
|
|
/* prints the option values for the IPv6 protocol */
|
|
int ip6an(int fd, FILE *outfile) {
|
|
static const struct sockopt ip6opts[] = {
|
|
#ifdef IPV6_V6ONLY
|
|
{IPV6_V6ONLY, "IPV6_V6ONLY"},
|
|
#endif
|
|
{0, NULL} } ;
|
|
const struct sockopt *optname;
|
|
|
|
optname = ip6opts; while (optname->so) {
|
|
sockoptan(fd, optname, SOL_IPV6, outfile);
|
|
++optname;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* WITH_IP6 */
|
|
|
|
|
|
#if WITH_TCP
|
|
int tcpan(int fd, FILE *outfile) {
|
|
static const struct sockopt tcpopts[] = {
|
|
#ifdef TCP_NODELAY
|
|
{ TCP_NODELAY, "TCP_NODELAY" },
|
|
#endif
|
|
#ifdef TCP_MAXSEG
|
|
{ TCP_MAXSEG, "TCP_MAXSEG" },
|
|
#endif
|
|
#ifdef TCP_STDURG
|
|
{ TCP_STDURG, "TCP_STDURG" },
|
|
#endif
|
|
#ifdef TCP_RFC1323
|
|
{ TCP_RFC1323, "TCP_RFC1323" },
|
|
#endif
|
|
#ifdef TCP_CORK
|
|
{ TCP_CORK, "TCP_CORK" },
|
|
#endif
|
|
#ifdef TCP_KEEPIDLE
|
|
{ TCP_KEEPIDLE, "TCP_KEEPIDLE" },
|
|
#endif
|
|
#ifdef TCP_KEEPINTVL
|
|
{ TCP_KEEPINTVL, "TCP_KEEPINTVL" },
|
|
#endif
|
|
#ifdef TCP_KEEPCNT
|
|
{ TCP_KEEPCNT, "TCP_KEEPCNT" },
|
|
#endif
|
|
#ifdef TCP_SYNCNT
|
|
{ TCP_SYNCNT, "TCP_SYNCNT" },
|
|
#endif
|
|
#ifdef TCP_LINGER2
|
|
{ TCP_LINGER2, "TCP_LINGER2" },
|
|
#endif
|
|
#ifdef TCP_DEFER_ACCEPT
|
|
{ TCP_DEFER_ACCEPT, "TCP_ACCEPT" },
|
|
#endif
|
|
#ifdef TCP_WINDOW_CLAMP
|
|
{ TCP_WINDOW_CLAMP, "TCP_WINDOW_CLAMP" },
|
|
#endif
|
|
#ifdef TCP_INFO
|
|
{ TCP_INFO, "TCP_INFO" },
|
|
#endif
|
|
#ifdef TCP_QUICKACK
|
|
{ TCP_QUICKACK, "TCP_QUICKACK" },
|
|
#endif
|
|
#ifdef TCP_MD5SIG
|
|
{ TCP_MD5SIG, "TCP_MD5SIG" },
|
|
#endif
|
|
#ifdef TCP_NOOPT
|
|
{ TCP_NOOPT, "TCP_NOOPT" },
|
|
#endif
|
|
#ifdef TCP_NOPUSH
|
|
{ TCP_NOPUSH, "TCP_NOPUSH" },
|
|
#endif
|
|
#ifdef TCP_SACK_DISABLE
|
|
{ TCP_SACK_DISABLE, "TCP_SACK_DISABLE" },
|
|
#endif
|
|
#ifdef TCP_SIGNATURE_ENABLE
|
|
{ TCP_SIGNATURE_ENABLE, "TCP_SIGNATURE_ENABLE" },
|
|
#endif
|
|
#ifdef TCP_ABORT_THRESHOLD
|
|
{ TCP_ABORT_THRESHOLD, "TCP_ABORT_THRESHOLD" },
|
|
#endif
|
|
#ifdef TCP_CONN_ABORT_THRESHOLD
|
|
{ TCP_CONN_ABORT_THRESHOLD, "TCP_CONN_ABORT_THRESHOLD" },
|
|
#endif
|
|
#ifdef TCP_KEEPINIT
|
|
{ TCP_KEEPINIT, "TCP_KEEPINIT" },
|
|
#endif
|
|
#ifdef TCP_PAWS
|
|
{ TCP_PAWS, "TCP_PAWS" },
|
|
#endif
|
|
#ifdef TCP_SACKENA
|
|
{ TCP_SACKENA, "TCP_SACKENA" },
|
|
#endif
|
|
#ifdef TCP_TSOPTENA
|
|
{ TCP_TSOPTENA, "TCP_TSOPTENA" },
|
|
#endif
|
|
{0, NULL}
|
|
} ;
|
|
const struct sockopt *optname;
|
|
|
|
optname = tcpopts; while (optname->so) {
|
|
sockoptan(fd, optname, SOL_TCP, outfile);
|
|
++optname;
|
|
}
|
|
|
|
#ifdef TCP_INFO
|
|
tcpan2(fd, outfile);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif /* WITH_TCP */
|
|
|
|
#if WITH_TCP && defined(TCP_INFO)
|
|
|
|
int tcpan2(int fd, FILE *outfile) {
|
|
struct tcp_info tcpinfo;
|
|
socklen_t tcpinfolen = sizeof(tcpinfo);
|
|
int result;
|
|
|
|
result = Getsockopt(fd, SOL_TCP, TCP_INFO, &tcpinfo, &tcpinfolen);
|
|
if (result < 0) {
|
|
Debug4("getsockopt(%d, SOL_TCP, TCP_INFO, %p, {"F_Zu"}): %s",
|
|
fd, &tcpinfo, sizeof(tcpinfo), strerror(errno));
|
|
return -1;
|
|
}
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_STATE", tcpinfo.tcpi_state);
|
|
#if 0 /* on BSD these components are prefixed with __ - I get tired... */
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_CA_STATE", tcpinfo.tcpi_ca_state);
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_RETRANSMITS", tcpinfo.tcpi_retransmits);
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_PROBES", tcpinfo.tcpi_probes);
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_BACKOFF", tcpinfo.tcpi_backoff);
|
|
#endif
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_OPTIONS", tcpinfo.tcpi_options);
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_SND_WSCALE", tcpinfo.tcpi_snd_wscale);
|
|
fprintf(outfile, "%s={%u}\t", "TCPI_RCV_WSCALE", tcpinfo.tcpi_rcv_wscale);
|
|
//fprintf(outfile, "%s={%u}\t", "TCPI_DELIVERY_RATE_APP_LIMITED", tcpinfo.tcpi_delivery_rate_app_limited);
|
|
//fprintf(outfile, "%s={%u}\t", "TCPI_FASTOPEN_CLIENT_FAIL", tcpinfo.tcpi_fastopen_client_fail);
|
|
// fprintf(outfile, "%s={%u}\t", "TCPI_", tcpinfo.tcpi_);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* WITH_TCP */
|
|
|
|
|
|
#if _WITH_SOCKET
|
|
int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile) {
|
|
#define FILAN_OPTLEN 256
|
|
union {
|
|
char c[FILAN_OPTLEN];
|
|
int i[FILAN_OPTLEN/sizeof(int)];
|
|
} optval;
|
|
socklen_t optlen;
|
|
int result;
|
|
|
|
optlen = FILAN_OPTLEN;
|
|
result =
|
|
Getsockopt(fd, socklay, optname->so, (void *)optval.c, &optlen);
|
|
if (result < 0) {
|
|
Debug6("getsockopt(%d, %d, %d, %p, {"F_socklen"}): %s",
|
|
fd, socklay, optname->so, optval.c, optlen, strerror(errno));
|
|
fputc('\t', outfile);
|
|
return -1;
|
|
} else if (optlen == 0) {
|
|
Debug1("getsockopt(,,, {}, %d)", optlen);
|
|
fprintf(outfile, "%s=\"\"\t", optname->name);
|
|
} else if (optlen == sizeof(int)) {
|
|
Debug2("getsockopt(,,, {%d}, %d)",
|
|
*optval.i, optlen);
|
|
fprintf(outfile, "%s=%d\t", optname->name, *optval.i);
|
|
} else {
|
|
char outbuf[FILAN_OPTLEN*9+128], *cp = outbuf;
|
|
int i;
|
|
for (i = 0; i < optlen/sizeof(unsigned int); ++i) {
|
|
cp += sprintf(cp, "%08x ", (unsigned int)optval.i[i]);
|
|
}
|
|
*--cp = '\0'; /* delete trailing space */
|
|
Debug2("getsockopt(,,, {%s}, %d)", outbuf, optlen);
|
|
fflush(outfile);
|
|
fprintf(outfile, "%s={%s}\t", optname->name, outbuf);
|
|
}
|
|
return 0;
|
|
#undef FILAN_OPTLEN
|
|
}
|
|
#endif /* _WITH_SOCKET */
|
|
|
|
|
|
#if _WITH_SOCKET
|
|
int isasocket(int fd) {
|
|
int retval;
|
|
#if HAVE_STAT64
|
|
struct stat64 props;
|
|
#else
|
|
struct stat props;
|
|
#endif /* HAVE_STAT64 */
|
|
retval =
|
|
#if HAVE_STAT64
|
|
Fstat64(fd, &props);
|
|
#else
|
|
Fstat(fd, &props);
|
|
#endif
|
|
if (retval < 0) {
|
|
Info3("fstat(%d, %p): %s", fd, &props, strerror(errno));
|
|
return 0;
|
|
}
|
|
/* note: when S_ISSOCK was undefined, it always gives 0 */
|
|
return S_ISSOCK(props.st_mode);
|
|
}
|
|
#endif /* _WITH_SOCKET */
|
|
|
|
|
|
const char *getfiletypestring(int st_mode) {
|
|
const char *s;
|
|
|
|
switch (st_mode&S_IFMT) {
|
|
case S_IFIFO: s = "pipe"; break;
|
|
case S_IFCHR: s = "chrdev"; break;
|
|
case S_IFDIR: s = "dir"; break;
|
|
case S_IFBLK: s = "blkdev"; break;
|
|
case S_IFREG: s = "file"; break;
|
|
case S_IFLNK: s = "symlink"; break;
|
|
case S_IFSOCK: s = "socket"; break;
|
|
/*! AIX: MT? */
|
|
default: s = "undef"; break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static int printtime(FILE *outfile, time_t time) {
|
|
const char *s;
|
|
|
|
if (filan_rawoutput) {
|
|
fprintf(outfile, "\t"F_time, time);
|
|
} else {
|
|
fputc('\t', outfile);
|
|
s = asctime(localtime(&time));
|
|
if (strchr(s, '\n')) *strchr(s, '\n') = '\0';
|
|
fputs(s, outfile);
|
|
}
|
|
return 0;
|
|
}
|