/* ipstrings.c * * ipstrings - network traffic data gathering * By Jon Rifkin * Copyright 1999,2000 Jonathan Rifkin * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* ------------------------------------------------------------------------ Compile Switches ------------------------------------------------------------------------ */ #define DEBUG #define DUMP #undef DUMP #define HASH #undef HASH /* ------------------------------------------------------------------------ Include Files ------------------------------------------------------------------------ */ #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ Defines ------------------------------------------------------------------------ */ #define VERSION_STR "ipstrings 0.94b2" #define TRUE 1 #define FALSE 0 #define U_CHAR unsigned char /* TCP ports to dump */ #define NPORT 6 #define P_FTP 21 #define P_TELNET 23 #define P_SUNRPC 111 #define P_LOGIN 513 #define P_INGRESLOCK 1524 #define P_IRC 6667 /* Misc protocols to dump */ #define PROT_ICMP 1 /* Protocols with port info */ #define PROT_TCP 6 #define PROT_UDP 17 /* Flags for udp/tcp accepting all/some ports */ #define PROT_ACC_ALL 1 #define PROT_ACC_SOME 2 /* Length of saved packets */ #define PLEN_DEF 96 /* default */ #define PLEN_MIN 68 /* min allowed */ /* Length of packet headers (culled this info from tcpdump source code) */ #define POFF_ETH 14 #define POFF_NULL 4 /* Used by loopback ? */ #define POFF_PPP 4 #define POFF_RAW 0 /* Number of 1/10,000 of second in second */ #define M0SEC 10000 /* DEFAULT NUMBER OF CHARS IN A STRING */ #define DEF_MINSTRING 4 /* ------------------------------------------------------------------------ DEBUGGING MACROS ------------------------------------------------------------------------ */ #ifdef DEBUG #define WRITEMSG \ if (debug_m) { \ printf ("File %s line %d: ", __FILE__, __LINE__); \ printf ("errmsg <%s>\n", strerror(errno)); fflush(stdout); \ } #define WRITEVAR(VAL,FMT) \ if (debug_m) { \ printf ("File %s line %d: ", __FILE__, __LINE__); \ printf ("%s=",#VAL); printf (#FMT, VAL); printf ("\n"); \ fflush(stdout); \ } #else #define WRITEMSG #define WRITEVAR(VAL,FMT) #endif /* ------------------------------------------------------------------------ MACROS ------------------------------------------------------------------------ */ /* Convert time in 1/M0SEC to hours, min, seconds, 1/M0SEC */ #define HMS(hour,min,sec,sec4) \ sec = sec4/M0SEC; \ sec4 -= M0SEC*sec; \ min = sec/60; \ sec -= 60*min; \ hour = min/60; \ min -= 60*hour; #define ISPRINT(c) \ ((c)>31 && (c)<127) /* ------------------------------------------------------------------------ Type Definitions ------------------------------------------------------------------------ */ /* Packet structure used by pcap library */ typedef struct { U_CHAR src[6]; U_CHAR dst[6]; U_CHAR ptype[2]; /* ==0x800 if ip */ U_CHAR version[1]; U_CHAR service[1]; U_CHAR length[2]; U_CHAR id[2]; U_CHAR flag[2]; U_CHAR ttl[1]; U_CHAR prot[1]; U_CHAR chksum[2]; U_CHAR srcip[4]; U_CHAR dstip[4]; U_CHAR srcpt[2]; U_CHAR dstpt[2]; U_CHAR seq[4]; U_CHAR ack[4]; U_CHAR stuff[8]; U_CHAR data; } pkt_struct_t; typedef struct { U_CHAR src[6]; U_CHAR dst[6]; U_CHAR ptype[2]; /* ==0x800 if ip */ U_CHAR ip; } eth_struct_t; typedef struct { U_CHAR version[1]; U_CHAR service[1]; U_CHAR length[2]; U_CHAR id[2]; U_CHAR flag[2]; U_CHAR ttl[1]; U_CHAR prot[1]; U_CHAR chksum[2]; U_CHAR srcip[4]; U_CHAR dstip[4]; } ip_struct_t; typedef struct { U_CHAR srcpt[2]; U_CHAR dstpt[2]; U_CHAR seq[4]; U_CHAR ack[4]; U_CHAR length[1]; U_CHAR reserved[1]; U_CHAR window[2]; U_CHAR checksum[2]; U_CHAR urgentptr[2]; } tcp_struct_t; typedef struct { U_CHAR srcpt[2]; U_CHAR dstpt[2]; U_CHAR length[2]; U_CHAR checksum[2]; } udp_struct_t; /* Start and stop time of each connection */ typedef struct { /* Time (in sec/10,000) of first and last packet */ int first_time, last_time; /* Indentity of machine source for first, last packet */ unsigned char first_mach, last_mach; } datatime_t; /* All data for connection */ typedef struct { long nbyte1, nbyte2; int npkt1, npkt2; datatime_t time; } data_t; /* ------------------------------------------------------------------------ Global variables ------------------------------------------------------------------------ */ extern int errno; extern char *pcap_version[]; /* ------------------------------------------------------------------------ Module variables ------------------------------------------------------------------------ */ int isig_m=0; U_CHAR *prots_m = NULL; U_CHAR *tcp_ports_m = NULL; U_CHAR *udp_ports_m = NULL; /* Flag for writing connection time in output */ int write_time_m = FALSE; int write_eth_m = FALSE; int write_ip_m = FALSE; int write_port_m = FALSE; int write_size_m = FALSE; int debug_m = FALSE; /* Pcap input file */ pcap_t *pcapfile_m = NULL; int npkt_m = 0; /* Number of packets */ int nippkt_m = 0; /* Number of ip packets */ int nconn_m = 0; /* Number of connections */ int inport_m = P_FTP; /* TCP port to monitor */ int minstring_m = DEF_MINSTRING; /* Minimum number of chars in string */ /* ------------------------------------------------------------------------ Local Function Prototypes ------------------------------------------------------------------------ */ void ihandler (int); void writepkt (struct pcap_pkthdr *, eth_struct_t *, int); void PrintUsage(); void parse_portstr(char *str); void add_protocol(int val); void add_port (int prot, int port); void *alloc_err (int, int); char *get_localstr (char *); int get_pkttime (struct pcap_pkthdr *); int cmptime (const void *, const void *); int cmpip (const void *, const void *); /* ------------------------------------------------------------------------ Main Function ------------------------------------------------------------------------ */ int main (int argc, char *argv[]) { char ebuf[PCAP_ERRBUF_SIZE]; struct pcap_pkthdr pkthdr; eth_struct_t *pkt=NULL; eth_struct_t *pkt_orig = NULL; struct bpf_program fcode; pcap_dumper_t *df = NULL; int dump_this = FALSE; int ival; int length; int i; U_CHAR *dumptable = NULL; int optchar; int outputbin = FALSE; int promisc = 1; /* Default, set promiscuius mode */ char *progarg = NULL; char *writefile = NULL; char *readfile = NULL; int maxpkt = 0; int nlen = PLEN_DEF; /* Packet length to dump */ char *localstr = NULL; /* Sorting order for ip address */ /* Hash table for ip connections */ int hostonly = FALSE; int uselimit = FALSE; int hostportlimit = 0; int hostlimit = 0; U_CHAR nullip[4] = {0,0,0,0}; int DataLinkType; int PacketOffset; char *filtercmd = ""; /* Read options */ while (-1 != (optchar=getopt(argc,argv,"cdef:imn:pr:s:tw:vz"))) { switch (optchar) { case '?': PrintUsage(); return 1; case 'v': printf ("%s (compiled %s)\n", VERSION_STR, __DATE__); printf ("libpcap version % s\n", pcap_version); return 0; /* Debugging option */ case 'd': debug_m = TRUE; break; /* Print ethernet addresses if present */ case 'e': write_eth_m = TRUE; break; /* Get pcap filter string */ case 'f': filtercmd = strdup (optarg); break; /* Write write ip address file */ case 'i': write_ip_m = TRUE; break; case 'r': readfile = strdup(optarg); break; case 'w': writefile = strdup(optarg); break; case 'c': maxpkt = atoi(optarg); break; case 'm': promisc = 0; break; case 'p': write_port_m = TRUE; break; case 's': nlen = atoi(optarg); if (nlen */ } /* Counters */ npkt_m = 0; nippkt_m = 0; /* Open dump file(s) */ if (writefile ) df = pcap_dump_open(pcapfile_m, writefile); /* Read inteface until interupt signal */ while (isig_m==0) { npkt_m++; pkt_orig = (eth_struct_t *) pcap_next (pcapfile_m, &pkthdr); /* If pkt is null and we're reading file then we're done */ /* Otherwise if reading live try again */ if (pkt_orig==NULL) { if (readfile) break; else continue; } /* Adjust pkt pointer to compenstate for non-ethernet packet Only need pkt_orig for pcap file dump */ pkt = (eth_struct_t *) ( (U_CHAR *) pkt_orig + PacketOffset ); /* Read next packet unless ip */ if ( ! (pkt->ptype[0]==8 && pkt->ptype[1]==0) ) continue; /* Don't exceed limit of ip packets */ nippkt_m++; if (maxpkt && nippkt_m>maxpkt) break; /* Store packets */ writepkt (&pkthdr, pkt, -PacketOffset); /* Using dump */ if (writefile) { pcap_dump ((U_CHAR *) df, &pkthdr, (U_CHAR *) pkt_orig); } } /* Read tcpdump data */ if (NULL!=pcapfile_m) pcap_close(pcapfile_m); /* Clear error if breaking during pcap call */ errno = 0; /* Close dump file */ if (writefile ) pcap_dump_close (df); } /* Interupt handler (called when program recieves operating system signal */ void ihandler (int cursig) { /* Set signal flag */ isig_m = 1; /* Flush buffers */ fflush (stdout); /* Close pcap file */ pcap_close(pcapfile_m); pcapfile_m = NULL; /* RE-INSTALL SIGNAL HANDLER */ #if 0 signal (cursig, ihandler); #endif signal (cursig, SIG_DFL); #ifdef DEBUG if (debug_m) { struct tm *tm; time_t seconds; fprintf (stderr, "ipstrings received signal number <%i>\n", cursig); time (&seconds); tm = localtime (&seconds); fprintf (stderr, "date is <%04d-%02d-%02d-%02d:%02d:%02d>\n", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); fprintf (stderr, "number of packets read <%d>\n", npkt_m); fprintf (stderr, "number of ip packets read <%d>\n", nippkt_m); } #endif } /* Store packet info in hash table, keyed by ip1,ip2,port1,por2,protocol data is number of incoming/outgoing bytes, packets */ void writepkt (struct pcap_pkthdr *pkthdr, eth_struct_t *ep, int PacketOffset) { U_CHAR *dptr = NULL; U_CHAR *datastart = NULL; U_CHAR *pktend = NULL; U_CHAR *strstart = NULL; U_CHAR *strend = NULL; int dstpt = 0; int srcpt = 0; int firsttime = 0; int nstring=0; int hour, min, sec, msec; ip_struct_t *ip = NULL; udp_struct_t *up = NULL; tcp_struct_t *tp = NULL; /* Find ip packet start */ ip = (ip_struct_t *) &(ep->ip); /* Find end of captured packet */ pktend = (U_CHAR *) ep + pkthdr->caplen + PacketOffset; /* Find ip data start */ datastart = (U_CHAR *) ip + 4 * (ip->version[0] & 0x0f); /* Increment start of data by tcp header length */ if (ip->prot[0]==PROT_TCP) { tp = (tcp_struct_t *) datastart; datastart += 4 * (tp->length[0] >> 4); /* Increment start of data by tcp header length */ } else if (ip->prot[0]==PROT_UDP) { up = (udp_struct_t *) datastart; datastart += 8; } /* Scan data portion of packet for printable characters */ strstart = datastart; firsttime = 1; while (strstartsrcip[0], ip->srcip[1], ip->srcip[2], ip->srcip[3], ip->dstip[0], ip->dstip[1], ip->dstip[2], ip->dstip[3]); } /* Write protocol number and ports */ if (write_port_m) { /* Print udp protocol and port values */ if (ip->prot[0]==PROT_UDP) { printf (" 17 %6d %6d ", up->srcpt[0]*256 + up->srcpt[1], up->dstpt[0]*256 + up->dstpt[1]); /* Print tcp protocol and port values */ } else if (ip->prot[0]==PROT_TCP) { printf (" 6 %6d %6d ", tp->srcpt[0]*256 + tp->srcpt[1], tp->dstpt[0]*256 + tp->dstpt[1]); /* Print protocol and dummy port values */ } else { printf ("%3d %6d %6d ", ip->prot[0], 0, 0); } } /* Write ethernet addresses */ if (write_eth_m) { printf ("%02x%02x%02x%02x%02x%02x ", ep->src[0], ep->src[1], ep->src[2], ep->src[3], ep->src[4], ep->src[5]); printf ("%02x%02x%02x%02x%02x%02x ", ep->dst[0], ep->dst[1], ep->dst[2], ep->dst[3], ep->dst[4], ep->dst[5]); } /* Convert seconds from midnight to 24 hour time */ if (write_time_m) { msec = get_pkttime(pkthdr); HMS(hour,min,sec,msec) printf ("%02d:%02d:%02d.%04d ", hour,min,sec,msec); } /* Write ip packet size */ if (write_size_m) { printf ("%d ", ip->length[1] + 256*(int) ip->length[0] + 14); } /* Print extra separating space */ if (write_ip_m || write_port_m || write_eth_m || write_time_m || write_size_m) printf (" "); } /* Print this string, change newlines to '\n' */ while (*strstart && nstring--) { switch (*strstart) { case '\n': printf ("\\n"); break; case '\r': printf ("\\r"); break; default: printf ("%c", *strstart); break; } strstart++; } #if 0 printf ("%s\n", strstart); #endif strstart = strend+1; } /* Print linefeed only if packet info printed */ if (!firsttime) printf ("\n"); } void PrintUsage(void) { printf ("\nUsage: ipstrings -defimnprtwvz [interface]\n"); printf (" Read tcp packets destined for specific port (default 21, FTP) live\n"); printf (" from interface or from dump file and print ascii strings.\n"); printf ("\n"); printf (" -n nchar - Minimum length of printed strings, 0 means print only NULL\n"); printf (" terminated strings, default is %d\n", DEF_MINSTRING); printf (" -r readfile - Read packets from pcap format file, use -r- for standard in\n"); printf (" Don't need interface with this option\n"); printf (" -f filterstr - Use pcap filters (see tcpdump)\n"); printf (" -w writefile - Dump selected packets to pcap format file,\n"); printf (" use -w- for standard out\n"); printf (" -s nlen - Read first bytes of each packet live\n"); printf (" (default %d, min %d)\n", PLEN_DEF, PLEN_MIN); printf (" -c npacket - Only read in specific number of ip packets\n"); printf (" -m - Do not enter promiscuous mode\n"); printf (" -e - Print source, destination ethernet address for each packet\n"); printf (" -i - Print source, destination ip address for each packet\n"); printf (" -p - Print source, destination port addresses for each packet\n"); printf (" -t - Print time of day for each packet\n"); printf (" -z - Print size of each ip packet\n"); printf (" -v - Print version info\n"); printf (" -d - Print debug info\n"); printf ("Examples:\n"); printf (" ipstrings -r dump.fil\n"); printf (" ipstrings eth0\n"); } /* Determine protocols and ports to accept from string of format "1:6,21,23,6667:17" means protocols 1 (icmp), 6 (tcp) and 17 (udp) For tcp only ports 21,23,6667. For udp all ports (default) */ void parse_portstr(char *str) { char *p; char delim; int lastprot = -1; int is_prot; int val; is_prot = TRUE; delim=' '; while (delim) { /* Find : or '\0' */ p=str; /* Find next : , '\0' */ while (*p!=':' && *p!=',' && *p!='\0') p++; delim = *p; *p = '\0'; val = atoi(str); str = p+1; if (is_prot) { lastprot = val; add_protocol(val); } else { add_port (lastprot,val); } is_prot = (delim==':'); } } void add_protocol(int val) { /* Check for valid protocol */ if (val<0 || val>255) return; /* ALlocate if not done already */ if (NULL==prots_m) prots_m = (U_CHAR *) alloc_err(256, sizeof(U_CHAR)); if (NULL==prots_m) { fprintf (stderr, "ERROR: Out of memory\n"); exit (1); } /* Accept all ports */ prots_m[val] = PROT_ACC_ALL; } void add_port (int prot, int port) { /* Check for valid port */ if (prot<0 || prot>0x0ffff) return; /* Check for valid protocol */ if (prot==PROT_TCP) { prots_m[prot] = PROT_ACC_SOME; if (NULL==tcp_ports_m) tcp_ports_m = (U_CHAR *) alloc_err(0x010000, sizeof(U_CHAR)); tcp_ports_m[port] = 1; } else if (prot==PROT_UDP) { prots_m[prot] = PROT_ACC_SOME; if (NULL==udp_ports_m) udp_ports_m = (U_CHAR *) alloc_err(0x010000, sizeof(U_CHAR)); tcp_ports_m[port] = 1; } } void *alloc_err (int n, int size) { void *p; p = calloc (n, size); if (NULL==p) { fprintf (stderr, "ERROR: Out of memory.\n"); exit(1); } return p; } /* Read and format ip address : for example, "112.51.1" -> "112.051.001" */ char *get_localstr(char *instr) { /* Room for string containing formated ip address */ char *buffer = (char *) calloc(16,sizeof(char)); char *b = buffer; char triplet[3]; char *p = instr; int i,n; /* Search over all bytes (things between ..) */ while (1) { n = 0; while ((*p>='0' && *p<='9') && n<3) triplet[n++] = *p++; /* Either non-digit or two many digits - return NULL */ if (*p!='.' && *p!=0) return NULL; for (i=n;i<3;i++) *b++ = '0'; for (i=0;its.tv_sec); return (int) pkthdr->ts.tv_usec/100 + /* Convert microseconds */ M0SEC * (time->tm_sec + 60*(time->tm_min + 60*time->tm_hour)); }