#include "Lorcon2.h" #include "ruby.h" #ifndef RUBY_19 #include "rubysig.h" #endif /* self.license = GPLv2; */ /* This is a derivative of the tx.c sample included with lorcon: http://802.11ninja.net/lorcon/ lorcon 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. lorcon 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 lorcon; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Copyright (c) 2005 dragorn and Joshua Wright */ /* Lots of code borrowed from Tom Wambold's pylorcon: http://pylorcon.googlecode.com/ - tom5760[at]gmail.com */ /* All ruby-lorcon/rubyisms are by Metasploit LLC (C) 2006-2007 http://metasploit.com/ - msfdev[at]metasploit.com */ VALUE mLorcon; VALUE cDevice; VALUE cPacket; VALUE Lorcon_get_version(VALUE self) { return INT2NUM(lorcon_get_version()); } static VALUE Lorcon_list_drivers(VALUE self) { VALUE list; VALUE hash; lorcon_driver_t *drvlist, *dri; list = rb_hash_new(); dri = drvlist = lorcon_list_drivers(); if (dri == NULL) return Qnil; while (dri) { hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name)); rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details)); rb_hash_aset(list, rb_str_new2(dri->name),hash); dri = dri->next; } lorcon_free_driver_list(drvlist); return(list); } static VALUE Lorcon_find_driver(VALUE self, VALUE driver) { VALUE hash; lorcon_driver_t *dri; char *drivert = RSTRING_PTR(driver); dri = lorcon_find_driver(drivert); if (dri == NULL) return Qnil; hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name)); rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details)); lorcon_free_driver_list(dri); return(hash); } static VALUE Lorcon_auto_driver(VALUE self, VALUE interface) { VALUE hash; lorcon_driver_t *dri; char *intf = RSTRING_PTR(interface); dri = lorcon_auto_driver(intf); if (dri == NULL) return Qnil; hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name)); rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details)); lorcon_free_driver_list(dri); return hash; } void Lorcon_free(struct rldev *rld) { if (rld->context != NULL) lorcon_free(rld->context); } static VALUE Lorcon_create(int argc, VALUE *argv, VALUE self) { struct rldev *rld; char *intf = NULL, *driver = NULL; VALUE rbdriver, rbintf, obj; lorcon_driver_t *dri; if (argc == 2) { rb_scan_args(argc, argv, "2", &rbintf, &rbdriver); intf = StringValuePtr(rbintf); driver = StringValuePtr(rbdriver); } else { rb_scan_args(argc, argv, "1", &rbintf); intf = StringValuePtr(rbintf); } if (driver == NULL) { if ((dri = lorcon_auto_driver(intf)) == NULL) { rb_raise(rb_eRuntimeError, "LORCON could not detect a driver and none specified"); return (Qnil); } } else { if ((dri = lorcon_find_driver(driver)) == NULL) { rb_raise(rb_eArgError, "LORCON could not recognize the specified driver"); return (Qnil); } } obj = Data_Make_Struct(cDevice, struct rldev, 0, Lorcon_free, rld); rld->context = lorcon_create(intf, dri); // Obsolete: XXX // lorcon_set_timeout(rld->context, 100); if (rld->context == NULL) { rb_raise(rb_eRuntimeError, "LORCON could not create context"); return (Qnil); } lorcon_free_driver_list(dri); rb_obj_call_init(obj, 0, 0); return(obj); } static VALUE Lorcon_open_inject(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); if (lorcon_open_inject(rld->context) < 0) return Qfalse; return Qtrue; } static VALUE Lorcon_open_monitor(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); if (lorcon_open_monitor(rld->context) < 0) return Qfalse; return Qtrue; } static VALUE Lorcon_open_injmon(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); if (lorcon_open_injmon(rld->context) < 0) return Qfalse; return Qtrue; } static VALUE Lorcon_get_error(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); return rb_str_new2(lorcon_get_error(rld->context)); } static VALUE Lorcon_get_capiface(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); return rb_str_new2(lorcon_get_capiface(rld->context)); } void Lorcon_packet_free(struct rlpack *rlp) { if (rlp->packet != NULL) { lorcon_packet_free(rlp->packet); rlp->packet = NULL; free(rlp); } } static VALUE Lorcon_packet_create(int argc, VALUE *argv, VALUE self) { struct rlpack *rlp; VALUE obj; obj = Data_Make_Struct(cPacket, struct rlpack, 0, Lorcon_packet_free, rlp); rlp->packet = (struct lorcon_packet *) malloc(sizeof(struct lorcon_packet)); memset(rlp->packet, 0, sizeof(struct lorcon_packet)); rlp->bssid = NULL; rlp->dot3 = NULL; rlp->len = 0; rlp->dir = 0; rb_obj_call_init(obj, 0, 0); return(obj); } static VALUE Lorcon_packet_get_channel(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); return INT2FIX(rlp->packet->channel); } static VALUE Lorcon_packet_set_channel(VALUE self, VALUE channel) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); lorcon_packet_set_channel(rlp->packet, NUM2INT(channel)); return channel; } static VALUE Lorcon_packet_get_dlt(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); return INT2FIX(rlp->packet->dlt); } static VALUE Lorcon_packet_get_bssid(VALUE self) { struct rlpack *rlp; struct lorcon_dot11_extra *extra; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->extra_info == NULL || rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211) return Qnil; extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info; if (extra->bssid_mac == NULL) return Qnil; return rb_str_new((char *)extra->bssid_mac, 6); } static VALUE Lorcon_packet_get_source(VALUE self) { struct rlpack *rlp; struct lorcon_dot11_extra *extra; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->extra_info == NULL || rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211) return Qnil; extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info; if (extra->source_mac == NULL) return Qnil; return rb_str_new((char *)extra->source_mac, 6); } static VALUE Lorcon_packet_get_dest(VALUE self) { struct rlpack *rlp; struct lorcon_dot11_extra *extra; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->extra_info == NULL || rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211) return Qnil; extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info; if (extra->dest_mac == NULL) return Qnil; return rb_str_new((char *)extra->dest_mac, 6); } static VALUE Lorcon_packet_get_rawdata(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->packet_raw == NULL) return Qnil; return rb_str_new((char *)rlp->packet->packet_raw, rlp->packet->length); } static VALUE Lorcon_packet_get_headerdata(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->packet_header == NULL) return Qnil; return rb_str_new((char *)rlp->packet->packet_header, rlp->packet->length_header); } static VALUE Lorcon_packet_get_data(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->packet_data == NULL) return Qnil; return rb_str_new((char *)rlp->packet->packet_data, rlp->packet->length_data); } static VALUE Lorcon_packet_getdot3(VALUE self) { struct rlpack *rlp; u_char *pdata; int len; VALUE ret; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->packet->packet_data == NULL) return Qnil; len = lorcon_packet_to_dot3(rlp->packet, &pdata); ret = rb_str_new((char *)pdata, len); free(pdata); return ret; } static VALUE Lorcon_packet_prepdot3(VALUE self, VALUE dot3) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); rlp->dot3 = (unsigned char *) RSTRING_PTR(dot3); rlp->len = RSTRING_LEN(dot3); return dot3; } static VALUE Lorcon_packet_prepbssid(VALUE self, VALUE bssid) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); rlp->bssid = (unsigned char *)RSTRING_PTR(bssid); return bssid; } static VALUE Lorcon_packet_prepdir(VALUE self, VALUE dir) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); rlp->dir = NUM2INT(dir); return dir; } static VALUE Lorcon_packet_getdir(VALUE self) { struct rlpack *rlp; struct lorcon_dot11_extra *extra; Data_Get_Struct(self, struct rlpack, rlp); if (rlp->dir != 0) return INT2FIX(rlp->dir); if (rlp->packet == NULL) return Qnil; if (rlp->packet->extra_info == NULL || rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211) return Qnil; extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info; if (extra->from_ds && !extra->to_ds) return INT2FIX(LORCON_DOT11_DIR_FROMDS); else if (!extra->from_ds && extra->to_ds) return INT2FIX(LORCON_DOT11_DIR_TODS); else if (!extra->from_ds && !extra->to_ds) return INT2FIX(LORCON_DOT11_DIR_ADHOCDS); else if (extra->from_ds && extra->to_ds) return INT2FIX(LORCON_DOT11_DIR_INTRADS); return Qnil; } static VALUE Lorcon_packet_get_rawlength(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); return INT2FIX(rlp->packet->length); } static VALUE Lorcon_packet_get_headerlength(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); return INT2FIX(rlp->packet->length_header); } static VALUE Lorcon_packet_get_datalength(VALUE self) { struct rlpack *rlp; Data_Get_Struct(self, struct rlpack, rlp); return INT2FIX(rlp->packet->length_data); } VALUE new_lorcon_packet(struct lorcon_packet **packet) { struct rlpack *rlp; VALUE obj; obj = Data_Make_Struct(cPacket, struct rlpack, 0, Lorcon_packet_free, rlp); rlp->packet = *packet; rb_obj_call_init(obj, 0, 0); return(obj); } static VALUE Lorcon_inject_packet(VALUE self, VALUE packet) { struct rldev *rld; struct rlpack *rlp; lorcon_packet_t *pack = NULL; int ret; if (rb_obj_is_kind_of(packet, cPacket) == 0) { rb_raise(rb_eTypeError, "wrong type expected %s", rb_class2name(cPacket)); return Qnil; } Data_Get_Struct(self, struct rldev, rld); Data_Get_Struct(packet, struct rlpack, rlp); if (rlp->bssid != NULL && rlp->dot3 != NULL) { pack = lorcon_packet_from_dot3(rlp->bssid, rlp->dir, rlp->dot3, rlp->len); ret = lorcon_inject(rld->context, pack); lorcon_packet_free(pack); } else { ret = lorcon_inject(rld->context, rlp->packet); } return INT2FIX(ret); } static VALUE Lorcon_write_raw(VALUE self, VALUE rpacket) { struct rldev *rld; int ret; Data_Get_Struct(self, struct rldev, rld); if(TYPE(rpacket) != T_STRING) { rb_raise(rb_eArgError, "packet data must be a string"); return Qnil; } ret = lorcon_send_bytes(rld->context, RSTRING_LEN(rpacket), (unsigned char *)RSTRING_PTR(rpacket)); return INT2FIX(ret); } static VALUE Lorcon_set_filter(VALUE self, VALUE filter) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); return INT2FIX(lorcon_set_filter(rld->context, RSTRING_PTR(filter))); } static VALUE Lorcon_set_channel(VALUE self, VALUE channel) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); return INT2FIX(lorcon_set_channel(rld->context, NUM2INT(channel))); } static VALUE Lorcon_get_channel(VALUE self) { struct rldev *rld; Data_Get_Struct(self, struct rldev, rld); return INT2FIX(lorcon_get_channel(rld->context)); } static void rblorcon_pcap_handler(rblorconjob_t *job, struct pcap_pkthdr *hdr, u_char *pkt){ job->pkt = (unsigned char *)pkt; job->hdr = *hdr; } static VALUE Lorcon_capture_next(VALUE self) { struct rldev *rld; int ret = 0; struct lorcon_packet *packet; unsigned char *raw; pcap_t *pd; rblorconjob_t job; Data_Get_Struct(self, struct rldev, rld); pd = lorcon_get_pcap(rld->context); #ifndef RUBY_19 TRAP_BEG; #endif ret = pcap_dispatch(pd, 1, (pcap_handler) rblorcon_pcap_handler, (u_char *)&job); #ifndef RUBY_19 TRAP_END; #endif if (ret == 0) return(Qnil); if (ret < 0 || job.hdr.caplen <= 0) return INT2FIX(ret); raw = malloc(job.hdr.caplen); if(! raw) return Qnil; memcpy(raw, job.pkt, job.hdr.caplen); packet = lorcon_packet_from_pcap(rld->context, &job.hdr, raw); lorcon_packet_set_freedata(packet, 1); return new_lorcon_packet(&packet); } static VALUE Lorcon_capture_loop(int argc, VALUE *argv, VALUE self) { struct rldev *rld; int count = 0; int p = 0; VALUE v_cnt; VALUE ret; int fd; Data_Get_Struct(self, struct rldev, rld); if (rb_scan_args(argc, argv, "01", &v_cnt) >= 1) { count = FIX2INT(v_cnt); } else { count = -1; } fd = lorcon_get_selectable_fd(rld->context); if(fd < 0 ) { rb_raise(rb_eRuntimeError, "LORCON context could not provide a pollable descriptor " "and we need one for the threaded dispatch loop"); } while (p < count || count <= 0) { ret = Lorcon_capture_next(self); if(TYPE(ret) == T_FIXNUM) return(ret); if(ret == Qnil) { rb_thread_wait_fd(fd); } else { rb_yield(ret); p++; } } return INT2FIX(p); } void Init_Lorcon2() { mLorcon = rb_define_module("Lorcon"); cPacket = rb_define_class_under(mLorcon, "Packet", rb_cObject); rb_define_const(cPacket, "LORCON_FROM_DS", INT2NUM(LORCON_DOT11_DIR_FROMDS)); rb_define_const(cPacket, "LORCON_TO_DS", INT2NUM(LORCON_DOT11_DIR_TODS)); rb_define_const(cPacket, "LORCON_INTRA_DS", INT2NUM(LORCON_DOT11_DIR_INTRADS)); rb_define_const(cPacket, "LORCON_ADHOC_DS", INT2NUM(LORCON_DOT11_DIR_ADHOCDS)); rb_define_singleton_method(cPacket, "new", Lorcon_packet_create, -1); rb_define_method(cPacket, "bssid", Lorcon_packet_get_bssid, 0); rb_define_method(cPacket, "source", Lorcon_packet_get_source, 0); rb_define_method(cPacket, "dest", Lorcon_packet_get_dest, 0); rb_define_method(cPacket, "channel", Lorcon_packet_get_channel, 0); rb_define_method(cPacket, "channel=", Lorcon_packet_set_channel, 1); rb_define_method(cPacket, "dlt", Lorcon_packet_get_dlt, 0); rb_define_method(cPacket, "rawdata", Lorcon_packet_get_rawdata, 0); rb_define_method(cPacket, "headerdata", Lorcon_packet_get_headerdata, 0); rb_define_method(cPacket, "data", Lorcon_packet_get_data, 0); rb_define_method(cPacket, "dot3", Lorcon_packet_getdot3, 0); rb_define_method(cPacket, "dot3=", Lorcon_packet_prepdot3, 1); rb_define_method(cPacket, "bssid=", Lorcon_packet_prepbssid, 1); rb_define_method(cPacket, "direction=", Lorcon_packet_prepdir, 1); rb_define_method(cPacket, "direction", Lorcon_packet_getdir, 0); rb_define_method(cPacket, "size", Lorcon_packet_get_rawlength, 0); rb_define_method(cPacket, "linesize", Lorcon_packet_get_rawlength, 0); rb_define_method(cPacket, "headersize", Lorcon_packet_get_headerlength, 0); rb_define_method(cPacket, "datasize", Lorcon_packet_get_datalength, 0); cDevice = rb_define_class_under(mLorcon, "Device", rb_cObject); rb_define_singleton_method(cDevice, "new", Lorcon_create, -1); rb_define_method(cDevice, "openinject", Lorcon_open_inject, 0); rb_define_method(cDevice, "openmonitor", Lorcon_open_monitor, 0); rb_define_method(cDevice, "openinjmon", Lorcon_open_injmon, 0); rb_define_method(cDevice, "error", Lorcon_get_error, 0); rb_define_method(cDevice, "capiface", Lorcon_get_capiface, 0); rb_define_method(cDevice, "filter=", Lorcon_set_filter, 1); rb_define_method(cDevice, "channel=", Lorcon_set_channel, 1); rb_define_method(cDevice, "channel", Lorcon_get_channel, 0); rb_define_method(cDevice, "loop", Lorcon_capture_loop, -1); rb_define_method(cDevice, "each", Lorcon_capture_loop, -1); rb_define_method(cDevice, "each_packet", Lorcon_capture_loop, -1); rb_define_method(cDevice, "write", Lorcon_write_raw, 1); rb_define_method(cDevice, "inject", Lorcon_inject_packet, 1); rb_define_module_function(mLorcon, "drivers", Lorcon_list_drivers, 0); rb_define_module_function(mLorcon, "version", Lorcon_get_version, 0); rb_define_module_function(mLorcon, "find_driver", Lorcon_find_driver, 1); rb_define_module_function(mLorcon, "auto_driver", Lorcon_auto_driver, 1); }