The following two source files comprise the Lilt codebase. To preserve readability, we richly comment the code but do not display book-text inside the code. You can download the full source files from this book's companion Web site at http://www.wiley.com/compbooks/schiffman.
/* * $Id: lilt.h,v 1.7 2002/01/02 02:43:02 route Exp $ * * Building Open Source Network Security Tools * lilt.h - libnids example code * * Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com> * All rights reserved. * */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <unistd.h> #include <termios.h> #include <ctype.h> #include <time.h> #include <nids.h> /* * The following two structures taken from libnids sources to be used * in the reporting function. */ struct scan { u_int addr; unsigned short port; u_char flags; }; struct host { struct host *next; struct host *prev; u_int addr; int modtime; int n_packets; struct scan *packets; }; struct lilt_pack { #define M_LEN 128 /* this should be more than enough */ u_short mon[M_LEN]; /* list of TCP WKP to monitor */ u_char flags; /* control flags */ #define LP_CONN Ox1 /* there is a connection to watch */ #define LP_WATCH 0x2 /* watch this connection */ #define LP_KILL 0x4 /* kill this connection */ #define LP_DISCARD 0x8 /* discard this connection */ struct tuple4 t; /* four tuple of the connection in question */ int tcp_count; /* number of TCP connections seen */ int tcp_killed; /* number of TCP connections killed */ int ps_count; /* number of port scans seen */ }; char *cull_address (struct tuple4); char *get_time (); int set_ports (char *); void monitor_tcp (struct tcp_stream *, void *); void report (int, int, void *, void *); void command_summary (); void usage (char *); int interesting (u_short); void lock_tuple (struct tuple4); int our_tuple (struct tuple4); void process_command (); /* EOF */
/* * $Id: lilt.c,v 1.10 2002/01/02 03:21:27 route Exp $ * * Building Open Source Network Security Tools * lilt.c - libnids example code * * Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com> * All rights reserved. * */ #include "./lilt.h" struct lilt_pack lp; int main(int argc, char **argv) { int c, fd; fd_set read_set; struct termios term; memset (&1p, 0, sizeof (lp)); while ((c = getopt (argc, argv, "m:")) != EOF) { switch (c) { case 'm': /* * Set the ports to be monitored. We want them to be * of the format x,y,z. If we wanted, we could use * libnet's port list chaining functionality here to * be more robust. */ if (set_ports (optarg) == -1) { fprintf (stderr, "set_ports (): bad port list\n"); exit (EXIT_FAILURE); } break; default: usage (argv[0]); exit (EXIT_FAILURE); } } printf ("Lilt 1.0 [the littlest network watcher]\n"); if (lp.mon[0] == 0) { /* if the user specified no ports to look for, use these */ lp.mon[0] = 23; lp.mon[1] = 6667; } /* * Change the following libnids defaults: * scan_num_ports: 7 * Slightly more sensitive than the default of 10. * syslog: report * Use our own function rather than syslog to report portscans. * pcap_filter: "tcp" * Limit libnids to capturing TCP packets only. */ nids_params.scan_num_ports = 7; nids_params.syslog = report; nids_params.pcap_filter = "tcp"; /* initialize the library */ if (nids_init () == 0) { fprintf (stderr, "nids_init() failed: %s\n", nids_errbuf); exit (EXIT_FAILURE); } /* * Register the TCP callback. We could stack more TCP callback * functions here but in this sample program we only have one. */ nids_register_tcp(monitor_tcp); printf("TCP monitoring callback registered\n"); printf("Monitoring connections to the following ports: "); for (c = 0; 1p.mon[c]; C++) { printf("%d", 1p.mon[c]); } printf("\nLibnids engine initialized, waiting for events...\n"); /* * We want to change the behavior of stdin to not echo characters * typed and more importantly we want each character to be handed * off as soon as it is pressed (not waiting for \r). To do this * we have to manipulate the termios structure and change the * normal behavior of stdin. First we get the current terminal * state of stdin. If any of this fails, we'll warn, but not quit. */ c = tcgetattr(STDIN_FILENO, &term); if (c == -1) { perror("main(): tcgetattr():"); /* nonfatal */ } else { /* disable canonical mode and terminal echo */ term.c_lflag ~= $ICANON; term.c_lflag ~= $ECHO; /* set our changed state "NOW" */ c = tcsetattr (STDIN_FILENO, TCSANOW, &term); if (c == -1) { perror("main(): tcsetattr():"); /* nonfatal */ } } /* * Lilt is driven by commands from the user and input from the * network. Since we want to monitor for both at the "same time" * we need to do synchronous I/O multiplexing across these two * input streams. We'll watch the libnids descriptor to see if * there is any network traffic we need to pay attention to and * also we monitor stdin to see if the user hits a key we need to * process. To do this, we call nids_getfd() to get the underlying * network file descriptor (which is really a wrapper to * pcap_fileno()). Then we call nids_next () in conjunction with * select (). */ for (fd = nids_getfd();;) { FD_ZERO(&read_set); FD_SET(fd, &read_set); FD_SET(STDIN_FILENO, &read_set); /* check the status of our file descriptors */ c = select (fd + 1, &read_set, 0, 0, NULL); if (c > 0) { /* input from libnids? */ if (FD_ISSET(fd, &read_set)) { /* * nids_next() handles the calling of our callback * function. */ if (nids_next() == 0) { /* non-fatal, pcap_next() probably returned NULL */ continue; } } /* input from the user? */ if (FD_ISSET(STDIN_FILENO, &read_set)) { /* hand the keypress off be processed */ process_command(argv[0]); } } if (c == -1) { perror("select:"); } } /* NOT REACHED */ return (EXIT_SUCCESS); } int set_ports(char *list) { u_short p; u_char *q; int i; q = list; /* pull out ports and stick them in our port list array */ for (i = 0; q; i++) { if (i > M_LEN) { /* list too long */ return (-1); } p = atoi(q); if (p == 0) { return (-1); } else { lp.mon[i] = p; } if ((q = strchr(q, (char)','))) { *q = NULL; q++; } } return (1); } void report (int type, int err, void *unused, void *data) { int i; char buf[BUFSIZ]; struct host *offender; /* port scan warning? */ if (type == NIDS_WARN_SCAN) { lp.ps_count++; offender = (struct host *)data; fprintf(stderr, "\n-[%s: portscan detected from %s]-\n", get_time(), inet_ntoa(*((struct in_addr *)&offender->addr))); /* pull out IPs and ports scanned */ for (memset (buf, 0, BUFSIZ), i = 0; i < offender->n_packets; i++) { sprintf(buf + strlen(buf), "%s", inet_ntoa(*((struct in_addr *) &offender->packets[i].addr))); sprintf(buf + strlen(buf), ":%hi", offender->packets[i].port); strcat(buf, "\n"); } fprintf(stderr, "%s", buf); } } void monitor_tcp(struct tcp_stream *stream, void *unused) { int i; struct half_stream *half; /* * First check to see if we have a connection we're watching * and the user presses 'D' to discard it. */ if (lp.flags & LP_DISCARD) { /* clear out all the state for this connection */ lp.flags &= ~LP_DISCARD; lp.flags &= ~LP_WATCH; lp.flags &= ~LP_KILL; memset (&lp.t, 0, sizeof(lp.t)); } /* TCP SYN packet */ if (stream->nids_state == NIDS_JUST_EST) { /* if we already have a connection in scope, ignore this one */ if (lp.flags & LP_CONN) { return; } /* see if this connection is to a port we're monitoring */ if (!interesting(stream->addr.dest)) { return; } /* lock this connection in scope */ lock_tuple(stream->addr); lp.flags |= LP_CONN; lp.tcp_count++; fprintf(stderr, "\n-[%s: TCP connection: %s]-\n", get_time(), cull_address(stream->addr)); /* we want data from both ends of the connection */ stream->client.collect++; stream->server.collect++; return; } /* TCP FIN or RST packet */ if (stream->nids_state == NIDS_CLOSE || stream->nids_state == NIDS_RESET) { /* if this isn't data from our locked connection return */ if (!our_tuple(stream->addr)) { return; } fprintf(stderr, "\n-[%s: TCP connection terminated]-\n", get_time()); if (lp.flags & LP_KILL) { /* we were set to kill this connection, increment counter */ lp.tcp_killed++; } /* clear out all the state for this connection */ lp.flags &= ~LP_CONN; lp.flags &= ~LP_WATCH; lp.flags &= ~LP_KILL; memset(&lp.t, 0, sizeof(lp.t)); return; } /* TCP data packet */ if (stream->nids_state == NIDS_DATA) { /* if this isn't data from our locked connection return */ if (!our_tuple(stream->addr)) { return; } if (stream->client.count_new) { half = &stream->client; } else { half = &stream->server; } /* if we're not set to watch the connection, return */ if (!(lp.flags & LP_WATCH)) { return; } if (lp.flags & LP_KILL) { /* kill the connection */ nids_killtcp(stream); /* dump the rest of the data */ nids_discard(stream, half->count_new); return; } for (i = 0; i < half->count_new; i++) { /* we only want to print characters that are printable! */ if (isascii(half->data[i])) { fprintf(stderr, "%c", half->data[i]); } } } } /* peel off the character and process it */ void process_command() { int i; char buf[1]; if (read(STDIN_FILENO, buf, 1) == -1) { perror("read error:"); return; } switch (toupper(buf[0])) { case '?': /* help */ command_summary(); break; case 'D': /* if we have a connection, discard it */ if (lp.flags & LP_DISCARD) { /* got it the first time you typed it dorkus! */ return; } if (lp.flags & LP_CONN) { lp.flags |= LP_DISCARD; lp.flags &= ~LP_CONN; fprintf(stderr, "\n-[discarded connection]-\n"); } break; case 'K': /* if we have a connection, kill it */ if (lp.flags & LP_KILL) { /* got it the first time you typed it dorkus! */ return; } if (lp.flags & LP_CONN) { lp.flags |= LP_KILL; fprintf(stderr, "\n-[killing connection]-\n"); } break; case 'p': /* ports we're watching */ fprintf(stderr, "\n-[lilt monitor ports]-\n"); for (i = 0; lp.mon[i]; i++) { fprintf(stderr, "%d", lp.mon[i]); } fprintf(stderr, "\n"); break; case 'Q': /* quit */ fprintf(stderr, "\n-[later dorkus!]-\n"); exit (EXIT_SUCCESS); case 'S': /* statistics */ fprintf(stderr, "\n-[lilt statistics]-\n"); fprintf(stderr, "TCP connections:\t%d\n", lp.tcp_count); fprintf(stderr, "TCP connections killed:\t%d\n", lp.tcp_killed); fprintf(stderr, "port scans detected:\t%d\n", lp.ps_count); break; case 'W': /* if we have a connection, watch it */ if (lp.flags & LP_WATCH) { /* got it the first time you typed it dorkus! */ return; } if (lp.flags & LP_CONN) { lp.flags |= LP_WATCH; fprintf(stderr, "\n-[watching connection]-\n"); } break; default: break; } } /* basically pulled from libnids sample code */ char * cull_address(struct tuple4 addr) { static char buf[256]; strcpy(buf, inet_ntoa(*((struct in_addr *)&addr.saddr))); sprintf(buf + strlen(buf), ".%d ->", addr.source); strcat(buf, inet_ntoa(*((struct in_addr *)&addr.daddr))); sprintf(buf + strlen(buf),".%d", addr.dest); return (buf); } int interesting(u_short port) { int i; /* check our TCP WKP list for the port in question */ for (i = 0; lp.mon[i]; i++) { if (lp.mon[i] == port) { return (1); } } return (0); } int our_tuple(struct tuple4 addr) { /* check to see if this packet belongs to us */ if (addr.source == lp.t.source && addr.dest == lp.t.dest && addr.saddr == lp.t.saddr && addr.daddr == lp.t.daddr) { return (1); } else if (addr.source == lp.t.dest && addr.dest == lp.t.source && addr.daddr == lp.t.saddr && addr.saddr == lp.t.daddr) { return (2); } else { return (0); } } void lock_tuple(struct tuple4 addr) { /* lock this tuple in to our radar */ lp.t.source = addr.source; lp.t.dest = addr.dest; lp.t.saddr = addr.saddr; lp.t.daddr = addr.daddr; } char * get_time() { int i; time_t t; static char buf[26]; t = time((time_t *)NULL); strcpy(buf, ctime(&t)); /* cut out the day, year and \n */ for (i = 0; i < 20; i++) { buf[i] = buf[i + 4]; } buf[15] = 0; return (buf); } void usage(char *name) { fprintf(stderr, "usage: %s:\n" "-m ports\tList of TCP ports to monitor (x,y,z)\n", name); } void command_summary () { /* print the commands that are available to the user in brackets */ fprintf(stderr,"\n-[lilt command summary]-\n[?] - this blurb\n"); if (lp.flags & LP_CONN) { fprintf(stderr,"[d] - discard connection from scope\n"); fprintf(stderr,"[k] - kill connection in scope\n"); } else {' fprintf(stderr," d - discard connection from scope\n"); fprintf(stderr," k - kill connection in scope\n"); } fprintf(stderr,"[p] - display ports being monitored\n"); fprintf(stderr,"[q] - quit lilt\n"); fprintf(stderr,"[s] - statistics\n"); if (lp.flags & LP_CONN) { fprintf(stderr,"[w] - watch connection in scope\n"); } else { fprintf(stderr," w - watch connection in scope\n"); } } /* EOF */