Plogd v2 (Revision 1.5) is a syn/udp/icmp packet logger for freebsd.
c8063e2434da5fb556ad35fc5af1b0a42d30521cf23bede1da4f7da952df83df
/*
*
* (c) 2000 venglin / buffer0verfl0w security (www.b0f.com)
*
* plogd v2 -- syn/udp/icmp packet logger (freebsd version)
*
* [ compile with -lpcap ]
*
* bug reports: babunia@freebsd.lublin.pl
*
* $Log: plogd2.c,v $
* Revision 1.5 2000/04/18 09:11:09 venglin
* fd leak
*
* Revision 1.4 2000/04/16 11:12:31 venglin
* ctime() added
*
* Revision 1.3 2000/04/14 20:13:41 venglin
* some fflush() added
*
* Revision 1.2 2000/02/28 00:07:51 venglin
* SIGSEGV in rfc931_lookup()
*
* Revision 1.1 2000/02/27 23:29:18 venglin
* Initial revision
*
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#define IDENT 113
#define BUFSIZE 1024
#define IP_SIZE 20
#define TCP_SIZE 20
#define DLT_LEN 14
/* static */
FILE *log;
pcap_t *ip_socket;
struct {
unsigned int noresolve : 1;
unsigned int noident : 1;
unsigned int notcp : 1;
unsigned int noudp : 1;
unsigned int noicmp : 1;
} flags;
char rcsid[]="$Id: plogd2.c,v 1.5 2000/04/18 09:11:09 venglin Exp $";
/* prototypes */
static char *hostlookup __P((unsigned));
void filter_packet __P((u_char *, struct pcap_pkthdr *, u_char *));
void log_syn __P((struct ip *, struct tcphdr *));
void log_icmp __P((struct ip *, struct icmp *));
void log_udp __P((struct ip *, struct udphdr *));
void usage __P((char *));
int rfc931_lookup __P((long, unsigned, unsigned, char *));
void loginit __P((char *, char *));
void debug __P((const char *, ...));
void fatal __P((const char *, ...));
/* log routines */
void loginit(ofile, av0)
char *ofile, *av0;
{
if (ofile)
{
if (!(log = fopen(ofile, "w")))
fatal("fopen(): %s", strerror(errno));
}
else
{
closelog();
openlog(av0, LOG_PID, LOG_DAEMON);
}
}
void fatal(const char *fmt, ...)
{
char buf[BUFSIZE];
va_list args;
time_t now;
char *timestamp;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (log)
{
(void)time(&now);
timestamp = ctime(&now) + 4;
fprintf(log, "%.20s: fatal: %s\n", timestamp, buf);
fflush(log);
}
else
syslog(LOG_ERR, "fatal: %.500s", buf);
closelog();
if (log)
fclose(log);
if (ip_socket)
pcap_close(ip_socket);
exit(1);
}
void debug(const char *fmt, ...)
{
char buf[BUFSIZE];
va_list args;
time_t now;
char *timestamp;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (log)
{
(void)time(&now);
timestamp = ctime(&now) + 4;
fprintf(log, "%.20s: status: %s\n", timestamp, buf);
fflush(log);
}
else
syslog(LOG_NOTICE, "status: %.500s", buf);
return;
}
/* signal handler */
void sigcatch(sig)
int sig;
{
fatal("Received signal %d; terminating.", sig);
return;
}
/* reverse host lookup routine */
char *hostlookup(unsigned int in)
{
static char tmp[BUFSIZE];
struct in_addr i;
struct hostent *he = NULL;
i.s_addr = in;
if(!flags.noresolve)
he = gethostbyaddr((char *)&i, sizeof(struct in_addr), AF_INET);
if (he == NULL || flags.noresolve)
strncpy(tmp, inet_ntoa(i), BUFSIZE-1);
else
strncpy(tmp, he->h_name, BUFSIZE-1);
tmp[BUFSIZE]='\0';
return (char *)tmp;
}
/* ident/tap (rfc931) lookup routine */
int rfc931_lookup(host, sport, dport, ident)
long host;
unsigned int sport, dport;
char *ident;
{
char buf[BUFSIZE], tmp[BUFSIZE];
char *cp;
int sockfd, dump;
struct sockaddr_in cli;
bzero(&cli, sizeof(cli));
cli.sin_family = AF_INET;
cli.sin_addr.s_addr=host;
cli.sin_port = htons(IDENT);
sprintf(buf, "%u,%u\r\n", sport, dport);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
if(connect(sockfd, (struct sockaddr *)&cli, sizeof(cli)) < 0)
{
close(sockfd);
return -1;
}
if (write(sockfd, buf, strlen(buf)) < 0)
{
close(sockfd);
return -1;
}
bzero(buf, BUFSIZE);
if (read(sockfd, buf, BUFSIZE-1) < 0)
{
close(sockfd);
return -1;
}
close(sockfd);
if (sscanf(buf, "%u , %u : USERID :%*[^:]:%s", &dump, &dump, tmp) != 3)
return -1;
if ((cp = strchr(tmp, '\r')))
*cp = '\0';
strncpy(ident, tmp, BUFSIZE);
return 0;
}
/* tcp syn log routine */
void log_syn(ip, tcp)
struct ip *ip;
struct tcphdr *tcp;
{
char ident[BUFSIZE];
struct servent *service;
if (flags.notcp)
return;
if (!flags.noident)
{
if (ntohs(tcp->th_dport) != IDENT)
{
if (rfc931_lookup((long)ip->ip_src.s_addr,
(unsigned)ntohs(tcp->th_sport),
(unsigned)ntohs(tcp->th_dport),
ident) < 0)
strcpy(ident, "unknown");
}
else
strcpy(ident, "unknown");
}
else
strcpy(ident, "unknown");
if ((service = getservbyport(tcp->th_dport, NULL)) != NULL)
debug("connection from %s@%s (port %u) to service %s (port %u)",
ident, hostlookup(ip->ip_src.s_addr),
ntohs(tcp->th_sport), service->s_name,
ntohs(tcp->th_dport));
else
debug("connection from %s@%s (port %u) to port %u", ident,
hostlookup(ip->ip_src.s_addr),
ntohs(tcp->th_sport),
ntohs(tcp->th_dport));
return;
}
/* icmp log routine */
void log_icmp(ip, icmp)
struct ip *ip;
struct icmp *icmp;
{
static char *icmptab[] =
{
"echo reply",
NULL,
NULL,
"unreachable",
"source quench",
"redirect",
NULL,
NULL,
"echo request",
"router advertisement",
"router solicitation",
"time exceed",
"parameter problem",
"timestamp request",
"timestamp reply",
"information request",
"information reply",
"address mask request",
"address mask reply",
NULL
};
if (flags.noicmp)
return;
debug("icmp %s from %s", icmptab[icmp->icmp_type],
hostlookup(ip->ip_src.s_addr));
return;
}
/* udp log routine */
void log_udp(ip, udp)
struct ip *ip;
struct udphdr *udp;
{
struct servent *service;
if (flags.noudp)
return;
if ((service = getservbyport(udp->uh_dport, NULL)) != NULL)
debug("udp packet from %s (port %u) to service %s (port %u)",
hostlookup(ip->ip_src.s_addr),
ntohs(udp->uh_sport), service->s_name,
ntohs(udp->uh_dport));
else
debug("udp packet from %s (port %u) to port %u",
hostlookup(ip->ip_src.s_addr),
ntohs(udp->uh_sport), ntohs(udp->uh_dport));
return;
}
/* filter routine */
void filter_packet(u, p, packet)
u_char *u, *packet;
struct pcap_pkthdr *p;
{
unsigned short ip_options = 0;
struct ip *ip;
struct tcphdr *tcp;
struct icmp *icmp;
struct udphdr *udp;
static u_char align_buf[4096];
ip = (struct ip *) (packet + DLT_LEN);
bcopy((char *)ip, (char *)align_buf, p->len);
packet = align_buf;
ip = (struct ip *)align_buf;
switch(ip->ip_p)
{
case IPPROTO_TCP:
ip_options = ip->ip_hl;
ip_options -= 5;
ip_options *= 4;
tcp = (struct tcphdr *)(packet + IP_SIZE + ip_options);
ip->ip_off &= 0xFF9F;
if(ip->ip_off != 0) return;
if((tcp->th_flags & TH_SYN) && !(tcp->th_flags & TH_ACK))
log_syn(ip, tcp);
break;
case IPPROTO_ICMP:
ip_options = ip->ip_hl;
ip_options -= 5;
ip_options *= 4;
icmp = (struct icmp *)(packet + IP_SIZE + ip_options);
log_icmp(ip, icmp);
break;
case IPPROTO_UDP:
ip_options = ip->ip_hl;
ip_options -= 5;
ip_options *= 4;
udp = (struct udphdr *)(packet + IP_SIZE + ip_options);
log_udp(ip, udp);
break;
}
}
/* nice usage routine */
void usage(av0)
char *av0;
{
(void)fprintf(stderr,
"\nusage: %s [-r] [-l] [-t] [-u] [-c] [-o file] [-i interface] \"filt_expr\"\n\n"
"\t[-r]\t\tdon't resolve hostnames\n"
"\t[-l]\t\tdon't perform rfc931 lookup\n"
"\t[-t]\t\tdon't log tcp connections\n"
"\t[-u]\t\tdon't log udp packets\n"
"\t[-c]\t\tdon't log icmp packets\n"
"\t[-o]\t\toutput file (default: syslogd)\n"
"\t[-i]\t\tnetwork interface\n"
"\t\"filt_expr\"\ttcpdump compatibile filter expression\n\n", av0);
exit(0);
}
/* ...and main! */
int main(argc, argv)
int argc;
char **argv;
{
extern char *optarg;
extern int optind;
int ch, f;
char *iface, *ofile, *av0, errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 network, netmask;
struct bpf_program prog;
if (strchr(argv[0], '/'))
av0 = strrchr(argv[0], '/') + 1;
else
av0 = argv[0];
opterr = flags.noresolve = flags.noident = flags.notcp = flags.noudp =
flags.noicmp = 0;
iface = ofile = NULL;
log = NULL;
ip_socket = NULL;
while ((ch = getopt(argc, argv, "rltuci:a:o:e:")) != -1)
switch((char)ch)
{
case 'r':
flags.noresolve = 1;
break;
case 'l':
flags.noident = 1;
break;
case 't':
flags.notcp = 1;
break;
case 'u':
flags.noudp = 1;
break;
case 'c':
flags.noicmp = 1;
break;
case 'i':
iface = strdup(optarg);
if (!iface)
fatal("malloc(): %s", strerror(errno));
break;
case 'o':
ofile = strdup(optarg);
if (!ofile)
fatal("malloc(): %s", strerror(errno));
break;
case '?':
default:
(void)usage(av0);
}
argc -= optind;
argv += optind;
if (argc < 1)
(void)usage(av0);
signal(SIGHUP, SIG_IGN);
signal(SIGINT, sigcatch);
signal(SIGQUIT, sigcatch);
signal(SIGTERM, sigcatch);
signal(SIGSEGV, sigcatch);
signal(SIGBUS, sigcatch);
loginit(ofile, av0);
if (!iface)
{
iface = strdup(pcap_lookupdev(errbuf));
if (!iface)
fatal("pcap_lookupdev(): %s", errbuf);
}
if (pcap_lookupnet(iface, &network, &netmask, errbuf) < 0)
fatal("pcap_lookupnet(): %s", errbuf);
if (!(ip_socket = pcap_open_live(iface, 1024, 0, 1024, errbuf)))
fatal("pcap_open_live(): %s", errbuf);
if (pcap_datalink(ip_socket) != DLT_EN10MB)
fatal("pcap_datalink(): only ethernet hardware supported");
if (pcap_compile(ip_socket, &prog, *argv, 1, netmask) < 0)
fatal("pcap_compile(): %s", errbuf);
if (pcap_setfilter(ip_socket, &prog) < 0)
fatal("pcap_setfilter(): %s", errbuf);
debug("ready to rock on %s (%s)", iface, *argv);
f = fork();
if (f < 0)
debug("fork(): %s (continuing in foreground)", strerror(errno));
if (f > 0)
{
debug("fork ok (pid %d)", f);
return 0;
}
setsid();
free(iface);
free(ofile);
while(1)
pcap_loop(ip_socket, -1, (pcap_handler)filter_packet, NULL);
}