2006-11-06 03:05:06 +00:00
|
|
|
/*
|
|
|
|
* Pcap.c
|
|
|
|
*
|
|
|
|
* $Id: Pcap.c,v 1.10 2000/08/13 05:56:31 fukusima Exp $
|
|
|
|
*
|
|
|
|
* Copyright (C) 1998-2000 Masaki Fukushima
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ruby_pcap.h"
|
|
|
|
#include "rubysig.h"
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#define DEFAULT_DATALINK DLT_EN10MB
|
|
|
|
#define DEFAULT_SNAPLEN 68
|
|
|
|
#define DEFAULT_PROMISC 1
|
|
|
|
#define DEFAULT_TO_MS 1000
|
|
|
|
static char pcap_errbuf[PCAP_ERRBUF_SIZE];
|
|
|
|
|
|
|
|
VALUE mPcap, rbpcap_convert = Qnil;
|
|
|
|
VALUE ePcapError;
|
|
|
|
VALUE eTruncatedPacket;
|
|
|
|
VALUE cFilter;
|
|
|
|
static VALUE cCapture;
|
|
|
|
static VALUE cPcapStat;
|
|
|
|
static VALUE cDumper;
|
|
|
|
|
|
|
|
struct filter_object {
|
|
|
|
char *expr;
|
|
|
|
struct bpf_program program;
|
|
|
|
int datalink;
|
|
|
|
int snaplen;
|
|
|
|
VALUE param;
|
|
|
|
VALUE optimize;
|
|
|
|
VALUE netmask;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define GetFilter(obj, filter) \
|
|
|
|
Data_Get_Struct(obj, struct filter_object, filter)
|
|
|
|
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
pcap_s_lookupdev(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
char *dev;
|
|
|
|
|
|
|
|
dev = pcap_lookupdev(pcap_errbuf);
|
|
|
|
if (dev == NULL) {
|
|
|
|
rb_raise(ePcapError, "%s", pcap_errbuf);
|
|
|
|
}
|
|
|
|
return rb_str_new2(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
pcap_s_lookupnet(self, dev)
|
|
|
|
VALUE self;
|
|
|
|
VALUE dev;
|
|
|
|
{
|
|
|
|
bpf_u_int32 net, mask, m;
|
|
|
|
struct in_addr addr;
|
|
|
|
|
|
|
|
Check_Type(dev, T_STRING);
|
|
|
|
if (pcap_lookupnet(STR2CSTR(dev), &net, &mask, pcap_errbuf) == -1) {
|
|
|
|
rb_raise(ePcapError, "%s", pcap_errbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
addr.s_addr = net;
|
|
|
|
m = ntohl(mask);
|
|
|
|
return rb_ary_new3(2, new_ipaddr(&addr), UINT32_2_NUM(m));
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
pcap_s_convert(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
return rbpcap_convert;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
pcap_s_convert_set(self, val)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
rbpcap_convert = val;
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Capture object
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct capture_object {
|
|
|
|
pcap_t *pcap;
|
|
|
|
bpf_u_int32 netmask;
|
|
|
|
int dl_type; /* data-link type (DLT_*) */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
closed_capture()
|
|
|
|
{
|
|
|
|
rb_raise(rb_eRuntimeError, "device is already closed");
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GetCapture(obj, cap) {\
|
|
|
|
Data_Get_Struct(obj, struct capture_object, cap);\
|
|
|
|
if (cap->pcap == NULL) closed_capture();\
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called from GC */
|
|
|
|
static void
|
|
|
|
free_capture(cap)
|
|
|
|
struct capture_object *cap;
|
|
|
|
{
|
|
|
|
DEBUG_PRINT("free_capture");
|
|
|
|
if (cap->pcap != NULL) {
|
|
|
|
DEBUG_PRINT("closing capture");
|
|
|
|
rb_thread_fd_close(pcap_fileno(cap->pcap));
|
|
|
|
pcap_close(cap->pcap);
|
|
|
|
cap->pcap = NULL;
|
|
|
|
}
|
|
|
|
free(cap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_open_live(argc, argv, class)
|
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
|
|
|
VALUE class;
|
|
|
|
{
|
|
|
|
VALUE v_device, v_snaplen, v_promisc, v_to_ms;
|
|
|
|
char *device;
|
|
|
|
int snaplen, promisc, to_ms;
|
|
|
|
int rs;
|
|
|
|
VALUE self;
|
|
|
|
struct capture_object *cap;
|
|
|
|
pcap_t *pcap;
|
|
|
|
bpf_u_int32 net, netmask;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_open_live");
|
|
|
|
|
|
|
|
/* scan arg */
|
|
|
|
rs = rb_scan_args(argc, argv, "13", &v_device, &v_snaplen,
|
|
|
|
&v_promisc, &v_to_ms);
|
|
|
|
|
|
|
|
/* device */
|
|
|
|
Check_SafeStr(v_device);
|
|
|
|
device = RSTRING(v_device)->ptr;
|
|
|
|
/* snaplen */
|
|
|
|
if (rs >= 2) {
|
|
|
|
Check_Type(v_snaplen, T_FIXNUM);
|
|
|
|
snaplen = FIX2INT(v_snaplen);
|
|
|
|
} else {
|
|
|
|
snaplen = DEFAULT_SNAPLEN;
|
|
|
|
}
|
|
|
|
if (snaplen < 0)
|
|
|
|
rb_raise(rb_eArgError, "invalid snaplen");
|
|
|
|
/* promisc */
|
|
|
|
if (rs >= 3) {
|
|
|
|
promisc = RTEST(v_promisc);
|
|
|
|
} else {
|
|
|
|
promisc = DEFAULT_PROMISC;
|
|
|
|
}
|
|
|
|
/* to_ms */
|
|
|
|
if (rs >= 4) {
|
|
|
|
Check_Type(v_to_ms, T_FIXNUM);
|
|
|
|
to_ms = FIX2INT(v_to_ms);
|
|
|
|
} else
|
|
|
|
to_ms = DEFAULT_TO_MS;
|
|
|
|
|
|
|
|
/* open */
|
|
|
|
pcap = pcap_open_live(device, snaplen, promisc, to_ms, pcap_errbuf);
|
|
|
|
if (pcap == NULL) {
|
|
|
|
rb_raise(ePcapError, "%s", pcap_errbuf);
|
|
|
|
}
|
|
|
|
if (pcap_lookupnet(device, &net, &netmask, pcap_errbuf) == -1) {
|
|
|
|
netmask = 0;
|
|
|
|
rb_warning("cannot lookup net: %s\n", pcap_errbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup instance */
|
|
|
|
self = Data_Make_Struct(class, struct capture_object,
|
|
|
|
0, free_capture, cap);
|
|
|
|
cap->pcap = pcap;
|
|
|
|
cap->netmask = netmask;
|
|
|
|
cap->dl_type = pcap_datalink(pcap);
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_open_offline(class, fname)
|
|
|
|
VALUE class;
|
|
|
|
VALUE fname;
|
|
|
|
{
|
|
|
|
VALUE self;
|
|
|
|
struct capture_object *cap;
|
|
|
|
pcap_t *pcap;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_open_offline");
|
|
|
|
|
|
|
|
/* open offline */
|
|
|
|
Check_SafeStr(fname);
|
|
|
|
pcap = pcap_open_offline(RSTRING(fname)->ptr, pcap_errbuf);
|
|
|
|
if (pcap == NULL) {
|
|
|
|
rb_raise(ePcapError, "%s", pcap_errbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup instance */
|
|
|
|
self = Data_Make_Struct(class, struct capture_object,
|
|
|
|
0, free_capture, cap);
|
|
|
|
cap->pcap = pcap;
|
|
|
|
cap->netmask = 0;
|
|
|
|
cap->dl_type = pcap_datalink(pcap);
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_close(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct capture_object *cap;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_close");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
rb_thread_fd_close(pcap_fileno(cap->pcap));
|
|
|
|
pcap_close(cap->pcap);
|
|
|
|
cap->pcap = NULL;
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handler(cap, pkthdr, data)
|
|
|
|
struct capture_object *cap;
|
|
|
|
const struct pcap_pkthdr *pkthdr;
|
|
|
|
const u_char *data;
|
|
|
|
{
|
|
|
|
rb_yield(new_packet(data, pkthdr, cap->dl_type));
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_dispatch(argc, argv, self)
|
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
VALUE v_cnt;
|
|
|
|
int cnt;
|
|
|
|
struct capture_object *cap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_dispatch");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
|
|
|
|
/* scan arg */
|
|
|
|
if (rb_scan_args(argc, argv, "01", &v_cnt) >= 1) {
|
|
|
|
FIXNUM_P(v_cnt);
|
|
|
|
cnt = FIX2INT(v_cnt);
|
|
|
|
} else
|
|
|
|
cnt = -1;
|
|
|
|
|
|
|
|
TRAP_BEG;
|
|
|
|
ret = pcap_dispatch(cap->pcap, cnt, handler, (u_char *)cap);
|
|
|
|
TRAP_END;
|
|
|
|
if (ret == -1)
|
|
|
|
rb_raise(ePcapError, "dispatch: %s", pcap_geterr(cap->pcap));
|
|
|
|
|
|
|
|
return INT2FIX(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_read(pcap_t *, int cnt, pcap_handler, u_char *); /* pcap-int.h */
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_loop(argc, argv, self)
|
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
VALUE v_cnt;
|
|
|
|
int cnt;
|
|
|
|
struct capture_object *cap;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_loop");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
|
|
|
|
/* scan arg */
|
|
|
|
if (rb_scan_args(argc, argv, "01", &v_cnt) >= 1) {
|
|
|
|
FIXNUM_P(v_cnt);
|
|
|
|
cnt = FIX2INT(v_cnt);
|
|
|
|
} else
|
|
|
|
cnt = -1;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
TRAP_BEG;
|
|
|
|
ret = pcap_loop(cap->pcap, cnt, handler, (u_char *)cap);
|
|
|
|
TRAP_END;
|
|
|
|
#else
|
|
|
|
if (pcap_file(cap->pcap) != NULL) {
|
|
|
|
TRAP_BEG;
|
|
|
|
ret = pcap_loop(cap->pcap, cnt, handler, (u_char *)cap);
|
|
|
|
TRAP_END;
|
|
|
|
} else {
|
|
|
|
int fd = pcap_fileno(cap->pcap);
|
|
|
|
fd_set rset;
|
|
|
|
struct timeval tm;
|
|
|
|
|
|
|
|
FD_ZERO(&rset);
|
|
|
|
tm.tv_sec = 0;
|
|
|
|
tm.tv_usec = 0;
|
|
|
|
for (;;) {
|
|
|
|
do {
|
|
|
|
FD_SET(fd, &rset);
|
|
|
|
if (select(fd+1, &rset, NULL, NULL, &tm) == 0) {
|
|
|
|
rb_thread_wait_fd(fd);
|
|
|
|
}
|
|
|
|
TRAP_BEG;
|
|
|
|
ret = pcap_read(cap->pcap, 1, handler, (u_char *)cap);
|
|
|
|
TRAP_END;
|
|
|
|
} while (ret == 0);
|
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
if (cnt > 0) {
|
|
|
|
cnt -= ret;
|
|
|
|
if (cnt <= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return INT2FIX(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_setfilter(argc, argv, self)
|
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct capture_object *cap;
|
|
|
|
VALUE vfilter, optimize;
|
|
|
|
char *filter;
|
|
|
|
int opt;
|
|
|
|
struct bpf_program program;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_setfilter");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
/* scan arg */
|
|
|
|
if (rb_scan_args(argc, argv, "11", &vfilter, &optimize) == 1) {
|
|
|
|
optimize = Qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check arg */
|
|
|
|
if (IsKindOf(vfilter, cFilter)) {
|
|
|
|
struct filter_object *f;
|
|
|
|
GetFilter(vfilter, f);
|
|
|
|
filter = f->expr;
|
|
|
|
} else {
|
|
|
|
Check_Type(vfilter, T_STRING);
|
|
|
|
filter = RSTRING(vfilter)->ptr;
|
|
|
|
}
|
|
|
|
opt = RTEST(optimize);
|
|
|
|
|
|
|
|
/* operation */
|
|
|
|
if (pcap_compile(cap->pcap, &program, filter,
|
|
|
|
opt, cap->netmask) < 0)
|
|
|
|
rb_raise(ePcapError, "setfilter: %s", pcap_geterr(cap->pcap));
|
|
|
|
if (pcap_setfilter(cap->pcap, &program) < 0)
|
|
|
|
rb_raise(ePcapError, "setfilter: %s", pcap_geterr(cap->pcap));
|
|
|
|
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_datalink(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct capture_object *cap;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_datalink");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
return INT2NUM(pcap_datalink(cap->pcap));
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_snapshot(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct capture_object *cap;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_snapshot");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
return INT2NUM(pcap_snapshot(cap->pcap));
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
capture_stats(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct capture_object *cap;
|
|
|
|
struct pcap_stat stat;
|
|
|
|
VALUE v_stat;
|
|
|
|
|
|
|
|
DEBUG_PRINT("capture_stats");
|
|
|
|
GetCapture(self, cap);
|
|
|
|
|
|
|
|
if (pcap_stats(cap->pcap, &stat) == -1)
|
|
|
|
return Qnil;
|
|
|
|
|
|
|
|
v_stat = rb_funcall(cPcapStat, rb_intern("new"), 3,
|
|
|
|
UINT2NUM(stat.ps_recv),
|
|
|
|
UINT2NUM(stat.ps_drop),
|
|
|
|
UINT2NUM(stat.ps_ifdrop));
|
|
|
|
|
|
|
|
return v_stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dumper object
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct dumper_object {
|
|
|
|
pcap_dumper_t *pcap_dumper;
|
|
|
|
int dl_type;
|
|
|
|
bpf_u_int32 snaplen;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
closed_dumper()
|
|
|
|
{
|
|
|
|
rb_raise(rb_eRuntimeError, "dumper is already closed");
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GetDumper(obj, dumper) {\
|
|
|
|
Data_Get_Struct(obj, struct dumper_object, dumper);\
|
|
|
|
if (dumper->pcap_dumper == NULL) closed_dumper();\
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called from GC */
|
|
|
|
static void
|
|
|
|
free_dumper(dumper)
|
|
|
|
struct dumper_object *dumper;
|
|
|
|
{
|
|
|
|
DEBUG_PRINT("free_dumper");
|
|
|
|
if (dumper->pcap_dumper != NULL) {
|
|
|
|
DEBUG_PRINT("closing dumper");
|
|
|
|
pcap_dump_close(dumper->pcap_dumper);
|
|
|
|
dumper->pcap_dumper = NULL;
|
|
|
|
}
|
|
|
|
free(dumper);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
dumper_open(class, v_cap, v_fname)
|
|
|
|
VALUE class;
|
|
|
|
VALUE v_cap;
|
|
|
|
VALUE v_fname;
|
|
|
|
{
|
|
|
|
struct dumper_object *dumper;
|
|
|
|
struct capture_object *cap;
|
|
|
|
pcap_dumper_t *pcap_dumper;
|
|
|
|
VALUE self;
|
|
|
|
|
|
|
|
DEBUG_PRINT("dumper_open");
|
|
|
|
|
|
|
|
CheckClass(v_cap, cCapture);
|
|
|
|
GetCapture(v_cap, cap);
|
|
|
|
Check_SafeStr(v_fname);
|
|
|
|
|
|
|
|
pcap_dumper = pcap_dump_open(cap->pcap, RSTRING(v_fname)->ptr);
|
|
|
|
if (pcap_dumper == NULL) {
|
|
|
|
rb_raise(ePcapError, "dumper_open: %s", pcap_geterr(cap->pcap));
|
|
|
|
}
|
|
|
|
|
|
|
|
self = Data_Make_Struct(class, struct dumper_object, 0,
|
|
|
|
free_dumper, dumper);
|
|
|
|
dumper->pcap_dumper = pcap_dumper;
|
|
|
|
dumper->dl_type = cap->dl_type;
|
|
|
|
dumper->snaplen = pcap_snapshot(cap->pcap);
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
dumper_close(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct dumper_object *dumper;
|
|
|
|
|
|
|
|
DEBUG_PRINT("dumper_close");
|
|
|
|
GetDumper(self, dumper);
|
|
|
|
|
|
|
|
pcap_dump_close(dumper->pcap_dumper);
|
|
|
|
dumper->pcap_dumper = NULL;
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
dumper_dump(self, v_pkt)
|
|
|
|
VALUE self;
|
|
|
|
VALUE v_pkt;
|
|
|
|
{
|
|
|
|
struct dumper_object *dumper;
|
|
|
|
struct packet_object *pkt;
|
|
|
|
|
|
|
|
DEBUG_PRINT("dumper_dump");
|
|
|
|
GetDumper(self, dumper);
|
|
|
|
|
|
|
|
CheckClass(v_pkt, cPacket);
|
|
|
|
GetPacket(v_pkt, pkt);
|
|
|
|
if (pkt->hdr.dl_type != dumper->dl_type)
|
|
|
|
rb_raise(rb_eArgError, "Cannot dump this packet: data-link type mismatch");
|
|
|
|
if (pkt->hdr.pkthdr.caplen > dumper->snaplen)
|
|
|
|
rb_raise(rb_eArgError, "Cannot dump this packet: too large caplen");
|
|
|
|
|
|
|
|
pcap_dump((u_char *)dumper->pcap_dumper, &pkt->hdr.pkthdr, pkt->data);
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Filter object
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* called from GC */
|
|
|
|
static void
|
|
|
|
mark_filter(filter)
|
|
|
|
struct filter_object *filter;
|
|
|
|
{
|
|
|
|
rb_gc_mark(filter->param);
|
|
|
|
rb_gc_mark(filter->optimize);
|
|
|
|
rb_gc_mark(filter->netmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_filter(filter)
|
|
|
|
struct filter_object *filter;
|
|
|
|
{
|
|
|
|
free(filter->expr);
|
|
|
|
free(filter);
|
|
|
|
/*
|
|
|
|
* This cause memory leak because filter->program hold some memory.
|
|
|
|
* We overlook it because libpcap does not implement pcap_freecode().
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
filter_new(argc, argv, class)
|
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
|
|
|
VALUE class;
|
|
|
|
{
|
|
|
|
VALUE self, v_expr, v_optimize, v_capture, v_netmask;
|
|
|
|
struct filter_object *filter;
|
|
|
|
struct capture_object *capture;
|
|
|
|
pcap_t *pcap;
|
|
|
|
char *expr;
|
|
|
|
int n, optimize, snaplen, linktype;
|
|
|
|
bpf_u_int32 netmask;
|
|
|
|
|
|
|
|
n = rb_scan_args(argc, argv, "13", &v_expr, &v_capture,
|
|
|
|
&v_optimize, &v_netmask);
|
|
|
|
/* filter expression */
|
|
|
|
Check_Type(v_expr, T_STRING);
|
|
|
|
expr = STR2CSTR(v_expr);
|
|
|
|
/* capture object */
|
|
|
|
if (IsKindOf(v_capture, cCapture)) {
|
|
|
|
CheckClass(v_capture, cCapture);
|
|
|
|
GetCapture(v_capture, capture);
|
|
|
|
pcap = capture->pcap;
|
|
|
|
} else if (NIL_P(v_capture)) {
|
|
|
|
/* assume most common case */
|
|
|
|
snaplen = DEFAULT_SNAPLEN;
|
|
|
|
linktype = DEFAULT_DATALINK;
|
|
|
|
pcap = 0;
|
|
|
|
} else {
|
|
|
|
snaplen = NUM2INT(rb_funcall(v_capture, rb_intern("[]"), 1, INT2FIX(0)));
|
|
|
|
linktype = NUM2INT(rb_funcall(v_capture, rb_intern("[]"), 1, INT2FIX(1)));
|
|
|
|
pcap = 0;
|
|
|
|
}
|
|
|
|
/* optimize flag */
|
|
|
|
optimize = 1;
|
|
|
|
if (n >= 3) {
|
|
|
|
optimize = RTEST(v_optimize);
|
|
|
|
}
|
|
|
|
/* netmask */
|
|
|
|
netmask = 0;
|
|
|
|
if (n >= 4) {
|
|
|
|
bpf_u_int32 mask = NUM2UINT(v_netmask);
|
|
|
|
netmask = htonl(mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
filter = (struct filter_object *)xmalloc(sizeof(struct filter_object));
|
|
|
|
if (pcap) {
|
|
|
|
if (pcap_compile(pcap, &filter->program, expr, optimize, netmask) < 0)
|
|
|
|
rb_raise(ePcapError, "%s", pcap_geterr(pcap));
|
|
|
|
filter->datalink = pcap_datalink(pcap);
|
|
|
|
filter->snaplen = pcap_snapshot(pcap);
|
|
|
|
} else {
|
|
|
|
#ifdef HAVE_PCAP_COMPILE_NOPCAP
|
|
|
|
if (pcap_compile_nopcap(snaplen, linktype, &filter->program, expr, optimize, netmask) < 0)
|
|
|
|
/* libpcap-0.5 provides no error report for pcap_compile_nopcap */
|
|
|
|
rb_raise(ePcapError, "pcap_compile_nopcap error");
|
|
|
|
filter->datalink = linktype;
|
|
|
|
filter->snaplen = snaplen;
|
|
|
|
#else
|
|
|
|
rb_raise(rb_eArgError, "pcap_compile_nopcap needs libpcap-0.5 or later");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
self = Data_Wrap_Struct(class, mark_filter, free_filter, filter);
|
|
|
|
filter->expr = strdup(expr);
|
|
|
|
filter->param = v_capture;
|
|
|
|
filter->optimize = optimize ? Qtrue : Qfalse;
|
|
|
|
filter->netmask = INT2NUM(ntohl(netmask));
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
filter_match(self, v_pkt)
|
|
|
|
VALUE self, v_pkt;
|
|
|
|
{
|
|
|
|
struct filter_object *filter;
|
|
|
|
struct packet_object *pkt;
|
|
|
|
struct pcap_pkthdr *h;
|
|
|
|
|
|
|
|
GetFilter(self, filter);
|
|
|
|
CheckClass(v_pkt, cPacket);
|
|
|
|
GetPacket(v_pkt, pkt);
|
|
|
|
h = &pkt->hdr.pkthdr;
|
|
|
|
|
|
|
|
if (filter->datalink != pkt->hdr.dl_type)
|
|
|
|
rb_raise(rb_eRuntimeError, "Incompatible datalink type");
|
|
|
|
if (filter->snaplen < h->caplen)
|
|
|
|
rb_raise(rb_eRuntimeError, "Incompatible snaplen");
|
|
|
|
|
|
|
|
if (bpf_filter(filter->program.bf_insns, pkt->data, h->len, h->caplen))
|
|
|
|
return Qtrue;
|
|
|
|
else
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
filter_source(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct filter_object *filter;
|
|
|
|
|
|
|
|
GetFilter(self, filter);
|
|
|
|
return rb_str_new2(filter->expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
new_filter(expr, param, optimize, netmask)
|
|
|
|
char *expr;
|
|
|
|
VALUE param, optimize, netmask;
|
|
|
|
{
|
|
|
|
return rb_funcall(cFilter,
|
|
|
|
rb_intern("new"), 4,
|
|
|
|
rb_str_new2(expr), param, optimize, netmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
filter_or(self, other)
|
|
|
|
VALUE self, other;
|
|
|
|
{
|
|
|
|
struct filter_object *filter, *filter2;
|
|
|
|
char *expr;
|
|
|
|
|
|
|
|
CheckClass(other, cFilter);
|
|
|
|
GetFilter(self, filter);
|
|
|
|
GetFilter(other, filter2);
|
|
|
|
|
|
|
|
expr = ALLOCA_N(char, strlen(filter->expr) + strlen(filter2->expr) + 16);
|
|
|
|
sprintf(expr, "( %s ) or ( %s )", filter->expr, filter2->expr);
|
|
|
|
return new_filter(expr, filter->param, filter->optimize, filter->netmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
filter_and(self, other)
|
|
|
|
VALUE self, other;
|
|
|
|
{
|
|
|
|
struct filter_object *filter, *filter2;
|
|
|
|
char *expr;
|
|
|
|
|
|
|
|
CheckClass(other, cFilter);
|
|
|
|
GetFilter(self, filter);
|
|
|
|
GetFilter(other, filter2);
|
|
|
|
|
|
|
|
expr = ALLOCA_N(char, strlen(filter->expr) + strlen(filter2->expr) + 16);
|
|
|
|
sprintf(expr, "( %s ) and ( %s )", filter->expr, filter2->expr);
|
|
|
|
return new_filter(expr, filter->param, filter->optimize, filter->netmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
filter_not(self)
|
|
|
|
VALUE self;
|
|
|
|
{
|
|
|
|
struct filter_object *filter;
|
|
|
|
char *expr;
|
|
|
|
|
|
|
|
GetFilter(self, filter);
|
|
|
|
expr = ALLOCA_N(char, strlen(filter->expr) + 16);
|
|
|
|
sprintf(expr, "not ( %s )", filter->expr);
|
|
|
|
return new_filter(expr, filter->param, filter->optimize, filter->netmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Class definition
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2006-11-06 04:14:18 +00:00
|
|
|
Init_PcapX(void)
|
2006-11-06 03:05:06 +00:00
|
|
|
{
|
2006-11-06 03:43:33 +00:00
|
|
|
DEBUG_PRINT("Init_PcapX");
|
2006-11-06 03:05:06 +00:00
|
|
|
|
|
|
|
/* define module Pcap */
|
2006-11-06 03:43:33 +00:00
|
|
|
mPcap = rb_define_module("PcapX");
|
2006-11-06 03:05:06 +00:00
|
|
|
rb_define_module_function(mPcap, "lookupdev", pcap_s_lookupdev, 0);
|
|
|
|
rb_define_module_function(mPcap, "lookupnet", pcap_s_lookupnet, 1);
|
|
|
|
rb_global_variable(&rbpcap_convert);
|
|
|
|
rb_define_singleton_method(mPcap, "convert?", pcap_s_convert, 0);
|
|
|
|
rb_define_singleton_method(mPcap, "convert=", pcap_s_convert_set, 1);
|
2006-11-06 03:43:33 +00:00
|
|
|
|
2006-11-06 03:05:06 +00:00
|
|
|
rb_define_const(mPcap, "DLT_NULL", INT2NUM(DLT_NULL));
|
|
|
|
rb_define_const(mPcap, "DLT_EN10MB", INT2NUM(DLT_EN10MB));
|
|
|
|
rb_define_const(mPcap, "DLT_EN3MB", INT2NUM(DLT_EN3MB));
|
|
|
|
rb_define_const(mPcap, "DLT_AX25", INT2NUM(DLT_AX25));
|
|
|
|
rb_define_const(mPcap, "DLT_PRONET", INT2NUM(DLT_PRONET));
|
|
|
|
rb_define_const(mPcap, "DLT_CHAOS", INT2NUM(DLT_CHAOS));
|
|
|
|
rb_define_const(mPcap, "DLT_IEEE802", INT2NUM(DLT_IEEE802));
|
|
|
|
rb_define_const(mPcap, "DLT_ARCNET", INT2NUM(DLT_ARCNET));
|
|
|
|
rb_define_const(mPcap, "DLT_SLIP", INT2NUM(DLT_SLIP));
|
|
|
|
rb_define_const(mPcap, "DLT_PPP", INT2NUM(DLT_PPP));
|
|
|
|
rb_define_const(mPcap, "DLT_FDDI", INT2NUM(DLT_FDDI));
|
|
|
|
rb_define_const(mPcap, "DLT_ATM_RFC1483", INT2NUM(DLT_ATM_RFC1483));
|
|
|
|
#ifdef DLT_RAW
|
|
|
|
rb_define_const(mPcap, "DLT_RAW", INT2NUM(DLT_RAW));
|
|
|
|
rb_define_const(mPcap, "DLT_SLIP_BSDOS", INT2NUM(DLT_SLIP_BSDOS));
|
|
|
|
rb_define_const(mPcap, "DLT_PPP_BSDOS", INT2NUM(DLT_PPP_BSDOS));
|
|
|
|
#endif
|
2006-11-06 03:43:33 +00:00
|
|
|
rb_define_const(mPcap, "DLT_IEEE802_11", INT2NUM(DLT_IEEE802_11));
|
|
|
|
rb_define_const(mPcap, "DLT_IEEE802_11_RADIO", INT2NUM(DLT_IEEE802_11_RADIO));
|
|
|
|
rb_define_const(mPcap, "DLT_IEEE802_11_RADIO_AVS", INT2NUM(DLT_IEEE802_11_RADIO_AVS));
|
|
|
|
rb_define_const(mPcap, "DLT_LINUX_SLL", INT2NUM(DLT_LINUX_SLL));
|
|
|
|
rb_define_const(mPcap, "DLT_PRISM_HEADER", INT2NUM(DLT_PRISM_HEADER));
|
|
|
|
rb_define_const(mPcap, "DLT_AIRONET_HEADER", INT2NUM(DLT_AIRONET_HEADER));
|
|
|
|
|
2006-11-06 03:05:06 +00:00
|
|
|
/* define class Capture */
|
|
|
|
cCapture = rb_define_class_under(mPcap, "Capture", rb_cObject);
|
|
|
|
rb_include_module(cCapture, rb_mEnumerable);
|
|
|
|
rb_define_singleton_method(cCapture, "open_live", capture_open_live, -1);
|
|
|
|
rb_define_singleton_method(cCapture, "open_offline", capture_open_offline, 1);
|
|
|
|
rb_define_method(cCapture, "close", capture_close, 0);
|
|
|
|
rb_define_method(cCapture, "dispatch", capture_dispatch, -1);
|
|
|
|
rb_define_method(cCapture, "loop", capture_loop, -1);
|
|
|
|
rb_define_method(cCapture, "each_packet", capture_loop, -1);
|
|
|
|
rb_define_method(cCapture, "each", capture_loop, -1);
|
|
|
|
rb_define_method(cCapture, "setfilter", capture_setfilter, -1);
|
|
|
|
rb_define_method(cCapture, "datalink", capture_datalink, 0);
|
|
|
|
rb_define_method(cCapture, "snapshot", capture_snapshot, 0);
|
|
|
|
rb_define_method(cCapture, "snaplen", capture_snapshot, 0);
|
|
|
|
rb_define_method(cCapture, "stats", capture_stats, 0);
|
|
|
|
|
|
|
|
/* define class Dumper */
|
|
|
|
cDumper = rb_define_class_under(mPcap, "Dumper", rb_cObject);
|
|
|
|
rb_define_singleton_method(cDumper, "open", dumper_open, 2);
|
|
|
|
rb_define_method(cDumper, "close", dumper_close, 0);
|
|
|
|
rb_define_method(cDumper, "dump", dumper_dump, 1);
|
|
|
|
|
|
|
|
/* define class Filter */
|
|
|
|
cFilter = rb_define_class_under(mPcap, "Filter", rb_cObject);
|
|
|
|
rb_define_singleton_method(cFilter, "new", filter_new, -1);
|
|
|
|
rb_define_singleton_method(cFilter, "compile", filter_new, -1);
|
|
|
|
rb_define_method(cFilter, "=~", filter_match, 1);
|
|
|
|
rb_define_method(cFilter, "===", filter_match, 1);
|
|
|
|
rb_define_method(cFilter, "source", filter_source, 0);
|
|
|
|
rb_define_method(cFilter, "|", filter_or, 1);
|
|
|
|
rb_define_method(cFilter, "&", filter_and, 1);
|
|
|
|
rb_define_method(cFilter, "~@", filter_not, 0);
|
|
|
|
/*rb_define_method(cFilter, "&", filter_and, 1);*/
|
|
|
|
|
|
|
|
/* define class PcapStat */
|
|
|
|
cPcapStat = rb_funcall(rb_cStruct, rb_intern("new"), 4,
|
|
|
|
Qnil,
|
2006-11-06 03:14:43 +00:00
|
|
|
ID2SYM(rb_intern("recv")),
|
|
|
|
ID2SYM(rb_intern("drop")),
|
|
|
|
ID2SYM(rb_intern("ifdrop")));
|
2006-11-06 03:05:06 +00:00
|
|
|
rb_define_const(mPcap, "Stat", cPcapStat);
|
|
|
|
|
|
|
|
/* define exception classes */
|
|
|
|
ePcapError = rb_define_class_under(mPcap, "PcapError", rb_eStandardError);
|
|
|
|
eTruncatedPacket = rb_define_class_under(mPcap, "TruncatedPacket", ePcapError);
|
|
|
|
|
|
|
|
Init_packet();
|
2006-11-06 03:43:33 +00:00
|
|
|
rb_f_require(Qnil, rb_str_new2("pcapx_misc"));
|
2006-11-06 03:05:06 +00:00
|
|
|
}
|