Version 1.8.0.0
[socat.git] / filan.c
blob36def50c81418de2a4164eba73b09dc0a4e082d1
1 /* source: filan.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* the subroutine filan makes a "FILe descriptor ANalysis". It checks the
6 type of file descriptor and tries to retrieve as much info about it as
7 possible without modifying its state.
8 NOTE: it works on UNIX (kernel) file descriptors, not on libc files! */
10 #include "config.h"
11 #include "xioconfig.h" /* what features are enabled */
13 #include "sysincludes.h"
15 #include "mytypes.h"
16 #include "compat.h"
17 #include "error.h"
18 #include "sycls.h"
19 #include "sysutils.h"
21 #include "filan.h"
24 struct sockopt {
25 int so;
26 char *name;
29 static int filan_streams_analyze(int fd, FILE *outfile);
31 /* global variables for configuring filan */
32 bool filan_followsymlinks;
33 bool filan_rawoutput;
36 int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile);
37 int tcpan(int fd, FILE *outfile);
38 int tcpan2(int fd, FILE *outfile);
39 const char *getfiletypestring(int st_mode);
41 static int printtime(FILE *outfile, time_t time);
43 static int headprinted;
45 /* analyse a file system entry, referred by file name */
46 int filan_file(const char *filename, FILE *outfile) {
47 int fd = -1;
48 int result;
49 #if HAVE_STAT64
50 struct stat64 buf = {0};
51 #else
52 struct stat buf = {0};
53 #endif /* !HAVE_STAT64 */
55 if (filan_followsymlinks) {
56 #if HAVE_STAT64
57 result = Stat64(filename, &buf);
58 #else
59 result = Stat(filename, &buf);
60 #endif /* !HAVE_STAT64 */
61 if (result < 0) {
62 Warn3("stat(\"%s\", %p): %s", filename, &buf, strerror(errno));
64 } else {
65 #if HAVE_STAT64
66 result = Lstat64(filename, &buf);
67 #else
68 result = Lstat(filename, &buf);
69 #endif /* !HAVE_STAT64 */
70 if (result < 0) {
71 Warn3("lstat(\"%s\", %p): %s", filename, &buf, strerror(errno));
74 switch (buf.st_mode&S_IFMT) {
75 #ifdef S_IFSOCK
76 case S_IFSOCK: /* probably, it's useless to make a socket and describe it */
77 break;
78 #endif /* S_IFSOCK */
79 default:
80 if ((fd =
81 Open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK
82 #ifdef O_NOFOLLOW
83 |(filan_followsymlinks?0:O_NOFOLLOW)
84 #endif
85 #ifdef O_LARGEFILE
86 |O_LARGEFILE
87 #endif
88 , 0700))
89 < 0) {
90 Warn2("open(\"%s\", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_LARGEFILE, 0700): %s",
91 filename, strerror(errno));
95 result = filan_stat(&buf, fd, -1, outfile, filename);
96 fputc('\n', outfile);
97 return result;
100 /* analyze a file descriptor */
101 int filan_fd(int fd, FILE *outfile) {
102 #if HAVE_STAT64
103 struct stat64 buf = {0};
104 #else
105 struct stat buf = {0};
106 #endif /* !HAVE_STAT64 */
107 int result;
109 Debug1("checking file descriptor %u", fd);
110 #if HAVE_STAT64
111 result = Fstat64(fd, &buf);
112 #else
113 result = Fstat(fd, &buf);
114 #endif /* !HAVE_STAT64 */
115 if (result < 0) {
116 if (errno == EBADF) {
117 Debug2("fstat(%d): %s", fd, strerror(errno));
118 } else {
119 Warn2("fstat(%d): %s", fd, strerror(errno));
121 return -1;
123 Debug2("fd %d is a %s", fd, getfiletypestring(buf.st_mode));
125 result = filan_stat(&buf, fd, fd, outfile, NULL);
127 if (result >= 0) {
128 /* even more dynamic info */
129 { /* see if data is available */
130 struct pollfd ufds;
131 ufds.fd = fd;
132 ufds.events = POLLIN|POLLPRI|POLLOUT
133 #ifdef POLLRDNORM
134 |POLLRDNORM
135 #endif
136 #ifdef POLLRDBAND
137 |POLLRDBAND
138 #endif
139 |POLLWRNORM
140 #ifdef POLLWRBAND
141 |POLLWRBAND
142 #endif
143 #ifdef POLLMSG
144 |POLLMSG
145 #endif
147 #if HAVE_POLL
148 if (Poll(&ufds, 1, 0) < 0) {
149 Warn4("\tpoll({%d, %hd, %hd}, 1, 0): %s",
150 ufds.fd, ufds.events, ufds.revents, strerror(errno));
151 } else {
152 fputs("\tpoll: ", outfile);
153 if (ufds.revents & POLLIN) fputs("IN,", outfile);
154 if (ufds.revents & POLLPRI) fputs("PRI,", outfile);
155 if (ufds.revents & POLLOUT) fputs("OUT,", outfile);
156 if (ufds.revents & POLLERR) fputs("ERR,", outfile);
157 if (ufds.revents & POLLNVAL) fputs("NVAL,", outfile);
158 #ifdef FIONREAD
159 if (ufds.revents & POLLIN) {
160 size_t sizet;
161 if ((result = Ioctl(fd, FIONREAD, &sizet) >= 0)) {
162 fprintf (outfile, "; FIONREAD="F_Zu, sizet);
165 #endif /* defined(FIONREAD) */
166 #if _WITH_SOCKET && defined(MSG_DONTWAIT)
167 if ((ufds.revents & POLLIN) && isasocket(fd)) {
168 char _peername[SOCKADDR_MAX];
169 struct sockaddr *pa = (struct sockaddr *)_peername;
170 struct msghdr msgh = {0};
171 char peekbuff[1]; /* [0] fails with some compilers */
172 #if HAVE_STRUCT_IOVEC
173 struct iovec iovec;
174 #endif
175 char ctrlbuff[5120];
176 ssize_t bytes;
178 fputs("; ", outfile);
179 msgh.msg_name = pa;
180 msgh.msg_namelen = sizeof(*pa);
181 #if HAVE_STRUCT_IOVEC
182 iovec.iov_base = peekbuff;
183 iovec.iov_len = sizeof(peekbuff);
184 msgh.msg_iov = &iovec;
185 msgh.msg_iovlen = 1;
186 #endif
187 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
188 msgh.msg_control = ctrlbuff;
189 #endif
190 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
191 msgh.msg_controllen = sizeof(ctrlbuff);
192 #endif
193 #if HAVE_STRUCT_MSGHDR_MSGFLAGS
194 msgh.msg_flags = 0;
195 #endif
196 if ((bytes = Recvmsg(fd, &msgh, MSG_PEEK|MSG_DONTWAIT)) < 0) {
197 Warn1("recvmsg(): %s", strerror(errno));
198 } else {
199 fprintf(outfile, "recvmsg="F_Zd", ", bytes);
202 #endif /* _WITH_SOCKET && defined(MSG_DONTWAIT) */
204 #endif /* HAVE_POLL */
207 fputc('\n', outfile);
208 return 0;
212 int filan_stat(
213 #if HAVE_STAT64
214 struct stat64 *buf
215 #else
216 struct stat *buf
217 #endif /* !HAVE_STAT64 */
218 , int statfd, int dynfd, FILE *outfile,
219 const char *filename /* Linux does not (yet) provide an
220 freadlink system call, so we need
221 the original name for readlink in
222 case it is a symlink */
224 char stdevstr[8];
226 /* print header */
227 if (!headprinted) {
228 if (filan_rawoutput) {
229 fputs(" FD type\tdevice\tinode\tmode\tlinks\tuid\tgid"
230 #if HAVE_ST_RDEV
231 "\trdev"
232 #endif
233 "\tsize"
234 #if HAVE_ST_BLKSIZE
235 "\tblksize"
236 #endif
237 #if HAVE_ST_BLOCKS
238 "\tblocks"
239 #endif
240 "\tatime\t\tmtime\t\tctime\t\tcloexec\tflags"
241 #if defined(F_GETOWN)
242 "\tsigown"
243 #endif
244 , outfile);
245 } else /* !rawoutput */ {
246 fputs(" FD type\tdevice\tinode\tmode\tlinks\tuid\tgid"
247 #if HAVE_ST_RDEV
248 "\trdev"
249 #endif
250 "\tsize"
251 #if HAVE_ST_BLKSIZE
252 "\tblksize"
253 #endif
254 #if HAVE_ST_BLOCKS
255 "\tblocks"
256 #endif
257 "\tatime\t\t\t\tmtime\t\t\t\tctime\t\t\t\tcloexec\tflags"
258 #if defined(F_GETOWN)
259 "\tsigown"
260 #endif
261 , outfile);
263 } /* endif !rawoutput */
265 #if defined(F_GETSIG)
266 fputs("\tsigio", outfile);
267 #endif /* defined(F_GETSIG) */
268 fputc('\n', outfile);
269 headprinted = 1;
271 if (filan_rawoutput) {
272 snprintf(stdevstr, 8, F_dev, buf->st_dev);
273 } else {
274 snprintf(stdevstr, 8, "%hu,%hu", (unsigned short)(buf->st_dev>>8), (unsigned short)(buf->st_dev&0xff));
276 fprintf(outfile, "%4d: %s\t%s\t"
277 #if HAVE_STAT64
278 F_st64_ino
279 #else
280 F_st_ino
281 #endif /* HAVE_STAT64 */
282 "\t"F_mode"\t"F_st_nlink"\t"F_uid"\t"F_gid
283 #if HAVE_ST_RDEV
284 "\t%hu,%hu"
285 #endif
286 "\t"
287 #if HAVE_STAT64
288 F_st64_size
289 #else
290 F_st_size
291 #endif /* HAVE_STAT64 */
292 #if HAVE_ST_BLKSIZE
293 "\t"F_st_blksize
294 #endif
295 #if HAVE_ST_BLOCKS
296 #if HAVE_STAT64
297 "\t"F_st64_blocks
298 #else
299 "\t"F_st_blocks
300 #endif /* HAVE_STAT64 */
301 #endif
303 (dynfd>=0?dynfd:statfd), getfiletypestring(buf->st_mode),
304 stdevstr,
305 buf->st_ino,
306 buf->st_mode, buf->st_nlink, buf->st_uid,
307 buf->st_gid,
308 #if HAVE_ST_RDEV
309 (unsigned short)(buf->st_rdev>>8), (unsigned short)(buf->st_rdev&0xff),
310 #endif
311 buf->st_size
312 #if HAVE_ST_BLKSIZE
313 , buf->st_blksize
314 #endif
315 #if HAVE_ST_BLOCKS
316 , buf->st_blocks /* on Linux, this applies to stat and stat64 */
317 #endif
320 printtime(outfile, buf->st_atime);
321 printtime(outfile, buf->st_mtime);
322 printtime(outfile, buf->st_ctime);
324 #if 0
326 fputc('\t', outfile);
327 time = asctime(localtime(&buf->st_mtime));
328 if (strchr(time, '\n')) *strchr(time, '\n') = '\0';
329 fputs(time, outfile);
331 fputc('\t', outfile);
332 time = asctime(localtime(&buf->st_ctime));
333 if (strchr(time, '\n')) *strchr(time, '\n') = '\0';
334 fputs(time, outfile);
336 #endif
338 /* here comes dynamic info - it is only meaningful with preexisting FDs */
339 if (dynfd >= 0) { /*!indent */
340 int cloexec, flags;
341 #if defined(F_GETOWN)
342 int sigown;
343 #endif
344 #if defined(F_GETSIG)
345 int sigio;
346 #endif /* defined(F_GETSIG) */
348 cloexec = Fcntl(dynfd, F_GETFD);
349 flags = Fcntl(dynfd, F_GETFL);
350 #if defined(F_GETOWN)
351 sigown = Fcntl(dynfd, F_GETOWN);
352 #endif
353 #if defined(F_GETSIG)
354 sigio = Fcntl(dynfd, F_GETSIG);
355 #endif /* defined(F_GETSIG) */
356 fprintf(outfile, "\t%d\tx%06x", cloexec, flags);
357 #if defined(F_GETOWN)
358 fprintf(outfile, "\t%d", sigown);
359 #endif
360 #if defined(F_GETSIG)
361 fprintf(outfile, "\t%d", sigio);
362 #endif /* defined(F_GETSIG) */
363 } else {
364 fputs("\t\t"
365 #if defined(F_GETOWN)
366 "\t"
367 #endif
368 #if defined(F_GETSIG)
369 "\t"
370 #endif /* defined(F_GETSIG) */
371 , outfile);
374 /* ever heard of POSIX streams? here we handle these */
375 filan_streams_analyze(statfd, outfile);
377 /* now see for type specific infos */
378 if (statfd >= 0) { /*!indent */
379 switch (buf->st_mode&S_IFMT) {
380 case (S_IFIFO): /* 1, FIFO */
381 #if defined(F_GETPIPE_SZ)
382 fprintf(outfile, "\tF_GETPIPE_SZ=%d", Fcntl(statfd, F_GETPIPE_SZ));
383 #endif
384 break;
385 case (S_IFCHR): /* 2, character device */
386 cdevan(statfd, outfile);
387 break;
388 case (S_IFDIR): /* 4, directory */
389 break;
390 case (S_IFBLK): /* 6, block device */
391 break;
392 case (S_IFREG): /* 8, regular file */
393 break;
394 #ifdef S_IFLNK
395 case (S_IFLNK): /* 10, symbolic link */
396 /* we wait for freadlink() sytem call */
397 break;
398 #endif /* S_IFLNK */
399 break;
400 #ifdef S_IFSOCK
401 case (S_IFSOCK): /* 12, socket */
402 #if _WITH_SOCKET
403 sockan(statfd, outfile);
404 #else
405 Warn("SOCKET support not compiled in");
406 return -1;
407 #endif /* !_WITH_SOCKET */
408 break;
409 #endif /* S_IFSOCK */
411 } else {
412 switch (buf->st_mode&S_IFMT) {
413 #ifdef S_IFLNK
414 case (S_IFLNK): /* 10, symbolic link */
416 char linktarget[PATH_MAX+1];
417 memset(linktarget, 0, PATH_MAX+1);
418 if (Readlink(filename, linktarget, PATH_MAX) < 0) {
419 Warn3("readlink(\"%s\", linktarget, %d): %s",
420 filename, PATH_MAX, strerror(errno));
421 } else {
422 fprintf(outfile, "LINKTARGET=%s", linktarget);
425 break;
426 #endif /* S_IFLNK */
429 /* ioctl() */
430 return 0;
434 #if LATER
435 int fdinfo(int fd) {
436 int result;
438 result = Fcntl(fd, F_GETFD);
439 fcntl(fd, F_GETFL, );
440 fcntl(fd, F_GETLK, );
441 #ifdef F_GETOWN
442 fcntl(fd, F_GETOWN, );
443 #endif
444 #ifdef F_GETSIG
445 fcntl(fd, F_GETSIG, );
446 #endif
450 int devinfo(int fd) {
451 ioctl();
453 #endif
456 /* returns 0 on success (not a stream descriptor, or no module)
457 returns <0 on failure */
458 static int filan_streams_analyze(int fd, FILE *outfile) {
459 #ifdef I_LIST
460 # define SL_NMODS 8 /* max number of module names we can store */
461 struct str_list modnames;
462 int i;
464 if (!isastream(fd)) {
465 fprintf(outfile, "\t(no STREAMS modules)");
466 return 0;
468 #if 0 /* uncomment for debugging */
469 fprintf(outfile, "\tfind=%d", ioctl(fd, I_FIND, "ldterm"));
470 #endif
471 modnames.sl_nmods = ioctl(fd, I_LIST, 0);
472 if (modnames.sl_nmods < 0) {
473 fprintf(stderr, "ioctl(%d, I_LIST, 0): %s\n", fd, strerror(errno));
474 return -1;
476 modnames.sl_modlist = Malloc(modnames.sl_nmods*(sizeof(struct str_mlist)));
477 if (modnames.sl_modlist == NULL) {
478 fprintf(stderr, "out of memory\n");
479 return -1;
481 if (ioctl(fd, I_LIST, &modnames) < 0) {
482 fprintf(stderr, "ioctl(%d, I_LIST, %p): %s\n",
483 fd, &modnames, strerror(errno));
484 free(modnames.sl_modlist);
485 return -1;
487 fprintf(outfile, "\tSTREAMS: ");
488 for (i = 0; i < modnames.sl_nmods; ++i) {
489 fprintf(outfile, "\"%s\"", modnames.sl_modlist[i].l_name);
490 if (i+1 < modnames.sl_nmods) fputc(',', outfile);
492 free(modnames.sl_modlist);
493 #endif /* defined(I_LIST) */
494 return 0;
498 /* character device analysis */
499 int cdevan(int fd, FILE *outfile) {
500 int ret;
502 #if _WITH_TERMIOS
503 if ((ret = Isatty(fd)) < 0) {
504 Warn2("isatty(%d): %s", fd, strerror(errno));
505 return -1;
507 if (ret > 0) {
508 struct termios termarg;
509 char *name;
510 int i;
512 if ((name = Ttyname(fd)) == NULL) {
513 /*Warn2("ttyname(%d): %s", fd, strerror(errno));*/
514 fputs("\tNULL", outfile);
515 } else {
516 fprintf(outfile, "\t%s", name);
518 if (Tcgetattr(fd, &termarg) < 0) {
519 Warn3("tcgetattr(%d, %p): %s", fd, &termarg, strerror(errno));
520 return -1;
522 fprintf(outfile, " \tIFLAGS=%08x OFLAGS=%08x CFLAGS=%08x LFLAGS=%08x",
523 (unsigned int)termarg.c_iflag,
524 (unsigned int)termarg.c_oflag,
525 (unsigned int)termarg.c_cflag,
526 (unsigned int)termarg.c_lflag);
528 /* and the control characters */
529 if (filan_rawoutput) {
530 for (i=0; i<NCCS; ++i) {
531 fprintf(outfile, " cc[%d]=%d", i, termarg.c_cc[i]);
533 } else {
534 for (i=0; i<NCCS; ++i) {
535 int ch;
536 unsigned char s[4];
537 ch = termarg.c_cc[i];
538 if (isprint(ch)) {
539 s[0] = ch; s[1]= '\0';
540 } else if (ch < ' ') {
541 s[0] = '^'; s[1] = ch+'@'; s[2] = '\0';
542 } else {
543 s[0] = 'x';
544 s[1] = (ch>>4)>=10?(ch>>4)-10+'A':(ch>>4)+'0';
545 s[2] = (ch&0x0f)>=10?(ch&0x0f)-10+'A':(ch&0x0f)+'0';
546 s[3] = '\0';
548 fprintf(outfile, " cc[%d]=%s", i, s);
552 #endif /* _WITH_TERMIOS */
553 return 0;
557 #if _WITH_SOCKET
558 int sockan(int fd, FILE *outfile) {
559 #define FILAN_OPTLEN 256
560 #define FILAN_NAMELEN 256
561 socklen_t optlen;
562 int result /*0, i*/;
563 char nambuff[FILAN_NAMELEN];
564 /* in Linux these optcodes are 'enum', but on AIX they are bits! */
565 static const struct sockopt sockopts[] = {
566 {SO_DEBUG, "DEBUG"},
567 {SO_REUSEADDR, "REUSEADDR"},
568 #ifdef SO_PROTOCOL
569 {SO_PROTOCOL, "PROTOCOL"},
570 #elif defined(SO_PROTOTYPE)
571 {SO_PROTOTYPE, "PROTOTYPE"},
572 #endif
573 {SO_TYPE, "TYPE"},
574 {SO_ERROR, "ERROR"},
575 {SO_DONTROUTE, "DONTROUTE"},
576 {SO_BROADCAST, "BROADCAST"},
577 {SO_SNDBUF, "SNDBUF"},
578 {SO_RCVBUF, "RCVBUF"},
579 {SO_KEEPALIVE, "KEEPALIVE"},
580 {SO_OOBINLINE, "OOBINLINE"},
581 #ifdef SO_NO_CHECK
582 {SO_NO_CHECK, "NO_CHECK"},
583 #endif
584 #ifdef SO_PRIORITY
585 {SO_PRIORITY, "PRIORITY"},
586 #endif
587 {SO_LINGER, "LINGER"},
588 #ifdef SO_BSDCOMPAT
589 {SO_BSDCOMPAT, "BSDCOMPAT"},
590 #endif
591 #ifdef SO_REUSEPORT
592 {SO_REUSEPORT, "REUSEPORT"},
593 #endif /* defined(SO_REUSEPORT) */
594 #ifdef SO_PASSCRED
595 {SO_PASSCRED, "PASSCRED"},
596 #endif
597 #ifdef SO_PEERCRED
598 {SO_PEERCRED, "PEERCRED"},
599 #endif
600 #ifdef SO_RCVLOWAT
601 {SO_RCVLOWAT, "RCVLOWAT"},
602 #endif
603 #ifdef SO_SNDLOWAT
604 {SO_SNDLOWAT, "SNDLOWAT"},
605 #endif
606 #ifdef SO_RCVTIMEO
607 {SO_RCVTIMEO, "RCVTIMEO"},
608 #endif
609 #ifdef SO_SNDTIMEO
610 {SO_SNDTIMEO, "SNDTIMEO"},
611 #endif
612 #ifdef SO_SECURITY_AUTHENTICATION
613 {SO_SECURITY_AUTHENTICATION, "SECURITY_AUTHENTICATION"},
614 #endif
615 #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
616 {SO_SECURITY_ENCRYPTION_TRANSPORT, "SECURITY_ENCRYPTION_TRANSPORT"},
617 #endif
618 #ifdef SO_SECURITY_ENCRYPTION_NETWORK
619 {SO_SECURITY_ENCRYPTION_NETWORK, "SECURITY_ENCRYPTION_NETWORK"},
620 #endif
621 #ifdef SO_BINDTODEVICE
622 {SO_BINDTODEVICE, "BINDTODEVICE"},
623 #endif
624 #ifdef SO_ATTACH_FILTER
625 {SO_ATTACH_FILTER, "ATTACH_FILTER"},
626 #endif
627 #ifdef SO_DETACH_FILTER
628 {SO_DETACH_FILTER, "DETACH_FILTER"},
629 #endif
630 {0, NULL} } ;
631 union {
632 char c[FILAN_OPTLEN];
633 int i[FILAN_OPTLEN/sizeof(int)];
634 } optval;
635 const struct sockopt *optname;
636 union sockaddr_union sockname, peername; /* the longest I know of */
637 socklen_t namelen;
638 #if 0 && defined(SIOCGIFNAME)
639 /*Linux struct ifreq ifc = {{{ 0 }}};*/
640 struct ifreq ifc = {{ 0 }};
641 #endif
643 optlen = FILAN_OPTLEN;
644 result = Getsockopt(fd, SOL_SOCKET, SO_TYPE, optval.c, &optlen);
645 if (result < 0) {
646 Debug4("getsockopt(%d, SOL_SOCKET, SO_TYPE, %p, {"F_socklen"}): %s",
647 fd, optval.c, optlen, strerror(errno));
648 } else {
649 # define TYPENAMEMAX 16
650 char typename[TYPENAMEMAX];
651 sockettype(*optval.i, typename, sizeof(typename));
653 Debug3("fd %d: socket of type %d (\"%s\")", fd, *optval.i,
654 typename);
657 optname = sockopts; while (optname->so) {
658 optlen = FILAN_OPTLEN;
659 result =
660 Getsockopt(fd, SOL_SOCKET, optname->so, (void *)optval.c, &optlen);
661 if (result < 0) {
662 Debug5("getsockopt(%d, SOL_SOCKET, %d, %p, {"F_socklen"}): %s",
663 fd, optname->so, optval.c, optlen, strerror(errno));
664 fputc('\t', outfile);
665 } else if (optlen == sizeof(int)) {
666 Debug2("getsockopt(,,, {%d}, %d)",
667 *optval.i, optlen);
668 /*Info2("%s: %d", optname->name, optval.i);*/
669 fprintf(outfile, "%s=%d\t", optname->name, *optval.i);
670 } else {
671 Debug3("getsockopt(,,, {%d,%d}, %d)",
672 optval.i[0], optval.i[1], optlen);
673 fprintf(outfile, "%s={%d,%d}\t", optname->name,
674 optval.i[0], optval.i[1]);
676 ++optname;
679 namelen = sizeof(sockname);
680 result = Getsockname(fd, (struct sockaddr *)&sockname, &namelen);
681 if (result < 0) {
682 putc('\n', outfile);
683 Warn2("getsockname(%d): %s", fd, strerror(errno));
684 return -1;
686 fputc('\t', outfile);
687 fputs(sockaddr_info((struct sockaddr *)&sockname, namelen, nambuff, sizeof(nambuff)),
688 outfile);
690 namelen = sizeof(peername);
691 result = Getpeername(fd, (struct sockaddr *)&peername, &namelen);
692 if (result < 0) {
693 putc('\n', outfile);
694 Warn2("getpeername(%d): %s", fd, strerror(errno));
695 } else {
696 /* only valid if getpeername() succeeded */
697 fputs(" <-> ", outfile);
698 fprintf(outfile, "%s\t",
699 sockaddr_info((struct sockaddr *)&peername, namelen,
700 nambuff, sizeof(nambuff)));
703 #if 0 && defined(SIOCGIFNAME)
704 if ((result = Ioctl(fd, SIOCGIFNAME, &ifc)) < 0) {
705 Warn3("ioctl(%d, SIOCGIFNAME, %p): %s", fd, &ifc, strerror(errno));
706 } else {
707 fprintf(outfile, "IFNAME=\"%s\"\t", ifc.ifr_name);
709 #endif /* SIOCGIFNAME */
711 switch (((struct sockaddr *)&sockname)->sa_family) {
712 #if WITH_UNIX
713 case AF_UNIX:
714 /* no options for unix domain sockets known yet -> no unixan() */
715 result = 0;
716 break;
717 #endif
718 #if WITH_IP4
719 case AF_INET:
720 result = ipan(fd, outfile);
721 break;
722 #endif
723 #if WITH_IP6
724 case AF_INET6:
725 result = ipan(fd, outfile);
726 result |= ip6an(fd, outfile);
727 break;
728 #endif
729 default:
730 fputs("**** NO FURTHER ANALYSIS FOR THIS SOCKET TYPE IMPLEMENTED", outfile);
731 result = 0;
733 return result;
734 #undef FILAN_OPTLEN
735 #undef FILAN_NAMELEN
737 #endif /* _WITH_SOCKET */
740 #if WITH_IP4 || WITH_IP6
741 /* prints the option values for the IP protocol and the IP based protocols */
742 /* no distinction between IP4 and IP6 yet */
743 int ipan(int fd, FILE *outfile) {
744 /* in Linux these optcodes are 'enum', but on AIX they are bits! */
745 static const struct sockopt ipopts[] = {
746 {IP_TOS, "IP_TOS"},
747 {IP_TTL, "IP_TTL"},
748 #ifdef IP_HDRINCL
749 {IP_HDRINCL, "IP_HDRINCL"},
750 #endif
751 #ifdef IP_OPTIONS
752 {IP_OPTIONS, "IP_OPTIONS"},
753 #endif
754 #ifdef IP_ROUTER_ALERT
755 {IP_ROUTER_ALERT, "IP_ROUTER_ALERT"},
756 #endif
757 #ifdef IP_RECVOPTS
758 {IP_RECVOPTS, "IP_RECVOPTS"},
759 #endif
760 #ifdef IP_RETOPTS
761 {IP_RETOPTS, "IP_RETOPTS"},
762 #endif
763 #ifdef IP_PKTINFO
764 {IP_PKTINFO, "IP_PKTINFO"},
765 #endif
766 #ifdef IP_PKTOPTIONS
767 {IP_PKTOPTIONS, "IP_PKTOPTIONS"},
768 #endif
769 #ifdef IP_MTU_DISCOVER
770 {IP_MTU_DISCOVER, "IP_MTU_DISCOVER"},
771 #endif
772 #ifdef IP_RECVERR
773 {IP_RECVERR, "IP_RECVERR"},
774 #endif
775 #ifdef IP_RECVTTL
776 {IP_RECVTTL, "IP_RECVTTL"},
777 #endif
778 #ifdef IP_RECVTOS
779 {IP_RECVTOS, "IP_RECVTOS"},
780 #endif
781 #ifdef IP_TRANSPARENT
782 {IP_TRANSPARENT, "IP_TRANSPARENT"},
783 #endif
784 #ifdef IP_MTU
785 {IP_MTU, "IP_MTU"},
786 #endif
787 #ifdef IP_FREEBIND
788 {IP_FREEBIND, "IP_FREEBIND"},
789 #endif
790 #ifdef IP_MULTICAST_TTL
791 {IP_MULTICAST_TTL, "IP_MULTICAST_TTL"},
792 #endif
793 #ifdef IP_MULTICAST_LOOP
794 {IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP"},
795 #endif
796 {0, NULL} } ;
797 const struct sockopt *optname;
798 int optproto;
799 socklen_t optlen = sizeof(optproto);
801 optname = ipopts; while (optname->so) {
802 sockoptan(fd, optname, SOL_IP, outfile);
803 ++optname;
805 /* want to pass the fd to the next layer protocol. */
806 #if defined(SO_PROTOCOL) || defined(SO_PROTOTYPE)
807 if (Getsockopt(fd, SOL_SOCKET,
808 #ifdef SO_PROTOCOL
809 SO_PROTOCOL,
810 #elif defined(SO_PROTOTYPE)
811 SO_PROTOTYPE,
812 #endif
813 &optproto, &optlen) >= 0) {
814 switch (optproto) {
815 #if WITH_TCP
816 case IPPROTO_TCP: tcpan(fd, outfile); break;
817 #endif
820 #endif /* defined(SO_PROTOCOL) || defined(SO_PROTOTYPE) */
821 return 0;
823 #endif /* WITH_IP */
826 #if WITH_IP6
827 /* prints the option values for the IPv6 protocol */
828 int ip6an(int fd, FILE *outfile) {
829 static const struct sockopt ip6opts[] = {
830 #ifdef IPV6_V6ONLY
831 {IPV6_V6ONLY, "IPV6_V6ONLY"},
832 #endif
833 {0, NULL} } ;
834 const struct sockopt *optname;
836 optname = ip6opts; while (optname->so) {
837 sockoptan(fd, optname, SOL_IPV6, outfile);
838 ++optname;
840 return 0;
842 #endif /* WITH_IP6 */
845 #if WITH_TCP
846 int tcpan(int fd, FILE *outfile) {
847 static const struct sockopt tcpopts[] = {
848 #ifdef TCP_NODELAY
849 { TCP_NODELAY, "TCP_NODELAY" },
850 #endif
851 #ifdef TCP_MAXSEG
852 { TCP_MAXSEG, "TCP_MAXSEG" },
853 #endif
854 #ifdef TCP_STDURG
855 { TCP_STDURG, "TCP_STDURG" },
856 #endif
857 #ifdef TCP_RFC1323
858 { TCP_RFC1323, "TCP_RFC1323" },
859 #endif
860 #ifdef TCP_CORK
861 { TCP_CORK, "TCP_CORK" },
862 #endif
863 #ifdef TCP_KEEPIDLE
864 { TCP_KEEPIDLE, "TCP_KEEPIDLE" },
865 #endif
866 #ifdef TCP_KEEPINTVL
867 { TCP_KEEPINTVL, "TCP_KEEPINTVL" },
868 #endif
869 #ifdef TCP_KEEPCNT
870 { TCP_KEEPCNT, "TCP_KEEPCNT" },
871 #endif
872 #ifdef TCP_SYNCNT
873 { TCP_SYNCNT, "TCP_SYNCNT" },
874 #endif
875 #ifdef TCP_LINGER2
876 { TCP_LINGER2, "TCP_LINGER2" },
877 #endif
878 #ifdef TCP_DEFER_ACCEPT
879 { TCP_DEFER_ACCEPT, "TCP_ACCEPT" },
880 #endif
881 #ifdef TCP_WINDOW_CLAMP
882 { TCP_WINDOW_CLAMP, "TCP_WINDOW_CLAMP" },
883 #endif
884 #ifdef TCP_INFO
885 { TCP_INFO, "TCP_INFO" },
886 #endif
887 #ifdef TCP_QUICKACK
888 { TCP_QUICKACK, "TCP_QUICKACK" },
889 #endif
890 #ifdef TCP_MD5SIG
891 { TCP_MD5SIG, "TCP_MD5SIG" },
892 #endif
893 #ifdef TCP_NOOPT
894 { TCP_NOOPT, "TCP_NOOPT" },
895 #endif
896 #ifdef TCP_NOPUSH
897 { TCP_NOPUSH, "TCP_NOPUSH" },
898 #endif
899 #ifdef TCP_SACK_DISABLE
900 { TCP_SACK_DISABLE, "TCP_SACK_DISABLE" },
901 #endif
902 #ifdef TCP_SIGNATURE_ENABLE
903 { TCP_SIGNATURE_ENABLE, "TCP_SIGNATURE_ENABLE" },
904 #endif
905 #ifdef TCP_ABORT_THRESHOLD
906 { TCP_ABORT_THRESHOLD, "TCP_ABORT_THRESHOLD" },
907 #endif
908 #ifdef TCP_CONN_ABORT_THRESHOLD
909 { TCP_CONN_ABORT_THRESHOLD, "TCP_CONN_ABORT_THRESHOLD" },
910 #endif
911 #ifdef TCP_KEEPINIT
912 { TCP_KEEPINIT, "TCP_KEEPINIT" },
913 #endif
914 #ifdef TCP_PAWS
915 { TCP_PAWS, "TCP_PAWS" },
916 #endif
917 #ifdef TCP_SACKENA
918 { TCP_SACKENA, "TCP_SACKENA" },
919 #endif
920 #ifdef TCP_TSOPTENA
921 { TCP_TSOPTENA, "TCP_TSOPTENA" },
922 #endif
923 {0, NULL}
925 const struct sockopt *optname;
927 optname = tcpopts; while (optname->so) {
928 sockoptan(fd, optname, SOL_TCP, outfile);
929 ++optname;
932 #ifdef TCP_INFO
933 tcpan2(fd, outfile);
934 #endif
935 return 0;
937 #endif /* WITH_TCP */
939 #if WITH_TCP && defined(TCP_INFO)
941 int tcpan2(int fd, FILE *outfile) {
942 struct tcp_info tcpinfo;
943 socklen_t tcpinfolen = sizeof(tcpinfo);
944 int result;
946 result = Getsockopt(fd, SOL_TCP, TCP_INFO, &tcpinfo, &tcpinfolen);
947 if (result < 0) {
948 Debug4("getsockopt(%d, SOL_TCP, TCP_INFO, %p, {"F_Zu"}): %s",
949 fd, &tcpinfo, sizeof(tcpinfo), strerror(errno));
950 return -1;
952 fprintf(outfile, "%s={%u}\t", "TCPI_STATE", tcpinfo.tcpi_state);
953 #if 0 /* on BSD these components are prefixed with __ - I get tired... */
954 fprintf(outfile, "%s={%u}\t", "TCPI_CA_STATE", tcpinfo.tcpi_ca_state);
955 fprintf(outfile, "%s={%u}\t", "TCPI_RETRANSMITS", tcpinfo.tcpi_retransmits);
956 fprintf(outfile, "%s={%u}\t", "TCPI_PROBES", tcpinfo.tcpi_probes);
957 fprintf(outfile, "%s={%u}\t", "TCPI_BACKOFF", tcpinfo.tcpi_backoff);
958 #endif
959 fprintf(outfile, "%s={%u}\t", "TCPI_OPTIONS", tcpinfo.tcpi_options);
960 fprintf(outfile, "%s={%u}\t", "TCPI_SND_WSCALE", tcpinfo.tcpi_snd_wscale);
961 fprintf(outfile, "%s={%u}\t", "TCPI_RCV_WSCALE", tcpinfo.tcpi_rcv_wscale);
962 #if LATER
963 fprintf(outfile, "%s={%u}\t", "TCPI_DELIVERY_RATE_APP_LIMITED", tcpinfo.tcpi_delivery_rate_app_limited);
964 fprintf(outfile, "%s={%u}\t", "TCPI_FASTOPEN_CLIENT_FAIL", tcpinfo.tcpi_fastopen_client_fail);
965 fprintf(outfile, "%s={%u}\t", "TCPI_", tcpinfo.tcpi_);
966 #endif
968 return 0;
971 #endif /* WITH_TCP */
974 #if _WITH_SOCKET
975 int sockoptan(int fd, const struct sockopt *optname, int socklay, FILE *outfile) {
976 #define FILAN_OPTLEN 256
977 union {
978 char c[FILAN_OPTLEN];
979 int i[FILAN_OPTLEN/sizeof(int)];
980 } optval;
981 socklen_t optlen;
982 int result;
984 optlen = FILAN_OPTLEN;
985 result =
986 Getsockopt(fd, socklay, optname->so, (void *)optval.c, &optlen);
987 if (result < 0) {
988 Debug6("getsockopt(%d, %d, %d, %p, {"F_socklen"}): %s",
989 fd, socklay, optname->so, optval.c, optlen, strerror(errno));
990 fputc('\t', outfile);
991 return -1;
992 } else if (optlen == 0) {
993 Debug1("getsockopt(,,, {}, %d)", optlen);
994 fprintf(outfile, "%s=\"\"\t", optname->name);
995 } else if (optlen == sizeof(int)) {
996 Debug2("getsockopt(,,, {%d}, %d)",
997 *optval.i, optlen);
998 fprintf(outfile, "%s=%d\t", optname->name, *optval.i);
999 } else {
1000 char outbuf[FILAN_OPTLEN*9+128], *cp = outbuf;
1001 int i;
1002 for (i = 0; i < optlen/sizeof(unsigned int); ++i) {
1003 cp += sprintf(cp, "%08x ", (unsigned int)optval.i[i]);
1005 *--cp = '\0'; /* delete trailing space */
1006 Debug2("getsockopt(,,, {%s}, %d)", outbuf, optlen);
1007 fflush(outfile);
1008 fprintf(outfile, "%s={%s}\t", optname->name, outbuf);
1010 return 0;
1011 #undef FILAN_OPTLEN
1013 #endif /* _WITH_SOCKET */
1016 #if _WITH_SOCKET
1017 int isasocket(int fd) {
1018 int retval;
1019 #if HAVE_STAT64
1020 struct stat64 props;
1021 #else
1022 struct stat props;
1023 #endif /* HAVE_STAT64 */
1024 retval =
1025 #if HAVE_STAT64
1026 Fstat64(fd, &props);
1027 #else
1028 Fstat(fd, &props);
1029 #endif
1030 if (retval < 0) {
1031 Info3("fstat(%d, %p): %s", fd, &props, strerror(errno));
1032 return 0;
1034 /* note: when S_ISSOCK was undefined, it always gives 0 */
1035 return S_ISSOCK(props.st_mode);
1037 #endif /* _WITH_SOCKET */
1040 const char *getfiletypestring(int st_mode) {
1041 const char *s;
1043 switch (st_mode&S_IFMT) {
1044 case S_IFIFO: s = "pipe"; break;
1045 case S_IFCHR: s = "chrdev"; break;
1046 case S_IFDIR: s = "dir"; break;
1047 case S_IFBLK: s = "blkdev"; break;
1048 case S_IFREG: s = "file"; break;
1049 case S_IFLNK: s = "symlink"; break;
1050 case S_IFSOCK: s = "socket"; break;
1051 /*! AIX: MT? */
1052 default: s = "undef"; break;
1054 return s;
1057 static int printtime(FILE *outfile, time_t time) {
1058 const char *s;
1060 if (filan_rawoutput) {
1061 fprintf(outfile, "\t"F_time, time);
1062 } else {
1063 fputc('\t', outfile);
1064 s = asctime(localtime(&time));
1065 if (strchr(s, '\n')) *strchr(s, '\n') = '\0';
1066 fputs(s, outfile);
1068 return 0;