├── .gitignore ├── pcap-recode ├── FreeBSD.mak ├── Darwin.mak ├── Linux.mak ├── pcap-split ├── msvcbuild.bat ├── README.txt.in ├── fuzzed-pcap-decoding-test ├── pcapx.lua ├── pcap-dump ├── Makefile ├── pcap-test ├── netutil.lua ├── README.txt ├── tostring.lua └── pcap.c /.gitignore: -------------------------------------------------------------------------------- 1 | _* 2 | cap*.pcap 3 | *.so 4 | *.test 5 | tstpcap-* 6 | debian 7 | -------------------------------------------------------------------------------- /pcap-recode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local pcap = require"pcapx" 4 | 5 | pcap.recode(arg[1], arg[2]) 6 | 7 | -------------------------------------------------------------------------------- /FreeBSD.mak: -------------------------------------------------------------------------------- 1 | # FreeBSD 2 | CC = clang 3 | LDFLAGS = -fPIC -fno-common -shared 4 | LUA = lua51 5 | CLUA=-I/usr/local/include -I/usr/local/include/${LUA} 6 | LLUA=-llua-5.1 7 | -------------------------------------------------------------------------------- /Darwin.mak: -------------------------------------------------------------------------------- 1 | # OS X 2 | CC = MACOSX_DEPLOYMENT_TARGET="10.3" gcc 3 | LUA = lua 4 | LDFLAGS = -fno-common -bundle -undefined dynamic_lookup 5 | CLUA=-I/usr/local/include 6 | LLUA=-l${LUA} 7 | -------------------------------------------------------------------------------- /Linux.mak: -------------------------------------------------------------------------------- 1 | # Linux 2 | CC = gcc 3 | LDFLAGS = -fPIC -fno-common -shared 4 | LUA = lua5.1 5 | CLUA=$(shell pkg-config --cflags ${LUA}) 6 | LLUA=$(shell pkg-config --libs ${LUA}) 7 | 8 | -------------------------------------------------------------------------------- /pcap-split: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua5.1 2 | 3 | local pcap = require"pcap" 4 | 5 | file = assert(arg[1]) 6 | cap = assert(pcap.open_offline(file)) 7 | 8 | for pkt, time, len in cap.next, cap do 9 | i = (i or 0) + 1 10 | print("packet", i, "wirelen", len, "timestamp", time, os.date("!%c", time)) 11 | dumper = assert(cap:dump_open(i..".pcap")) 12 | assert(dumper:dump(pkt, time, len)) 13 | dumper:close() 14 | end 15 | 16 | -------------------------------------------------------------------------------- /msvcbuild.bat: -------------------------------------------------------------------------------- 1 | @rem Script to build luapcap 2 | 3 | @setlocal 4 | @set MYCOMPILE=cl /nologo /MD /O2 /W3 /TC /c /D_CRT_SECURE_NO_DEPRECATE /D "WIN32" /I"..\wpdpack\Include" /I"..\lua\src" /I"..\wt-win-common" /D "LUA_BUILD_AS_DLL" 5 | @set MYLINK=link /nologo /DLL /LIBPATH:"..\wpdpack\Lib" /LIBPATH:"..\wt-win-common" /LIBPATH:"..\lua\src" /TLBID:1 /DLL "lua51.lib" "wpcap.lib" "wt-win-common.lib" "Packet.lib" "ws2_32.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" 6 | @set MYMT=mt /nologo 7 | 8 | if "%PLATFORM%"=="X64" ( 9 | @set MACHINE=X64 10 | ) else ( 11 | @set MACHINE=X86 12 | ) 13 | 14 | del pcap.lib pcap.dll 15 | %MYCOMPILE% /D "_WINDLL" *.c 16 | %MYLINK% /export:luaopen_pcap /out:pcap.dll /implib:pcap.lib /MACHINE:%MACHINE% *.obj 17 | del /Q *.obj *.exp 18 | -------------------------------------------------------------------------------- /README.txt.in: -------------------------------------------------------------------------------- 1 | = pcap - a binding to libpcap 2 | 3 | libpcap is the library behind the commonly use tcpdump utility. It allows 4 | reading packet captures live from a network, as well as reading and writing 5 | saved packet captures in "pcap" format. It has been ported to many operating 6 | systems. 7 | 8 | The binding doesn't implement the full libpcap API, just what we've needed so 9 | far. 10 | 11 | To build, see Makefile, it supports FreeBSD, Linux and OS X. 12 | 13 | To decode the packets, you might want to use libnet's lua bindings, see the 14 | lua/ subdirectory of . 15 | 16 | Homepage: 17 | Author: 18 | 19 | If this doesn't do what you need, 20 | is a binding to a different 21 | subset of libpcap's API. Also, it has tcp/ip parsing functions, whereas we use 22 | libnet for that. 23 | 24 | 25 | Documentation: 26 | 27 | See below, extracted from in-source comments. 28 | -------------------------------------------------------------------------------- /fuzzed-pcap-decoding-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- binary to hex 4 | function h(s) 5 | local function hex(s) 6 | return string.format("%02x", string.byte(s)) 7 | end 8 | return "["..#s.."] "..string.gsub(s, ".", hex) 9 | end 10 | 11 | local function debug(...) 12 | --print(...) 13 | end 14 | 15 | require"pcap" 16 | require"net" 17 | require"tostring" 18 | 19 | cap = assert(pcap.open_offline(arg[1])) 20 | n = net.init() 21 | 22 | local i = 0; 23 | for pkt, time, len in cap.next, cap do 24 | if pkt then 25 | i = i + 1 26 | print("packet", i, "wirelen", len, "timestamp", time, os.date("!%c", time)) 27 | debug("snap", h(pkt)) 28 | 29 | assert(n:clear()) 30 | assert(n:decode_eth(pkt)) 31 | 32 | local block = assert(n:block()) 33 | if block ~= pkt then 34 | print("=> reencoding failed!") 35 | local tag = n:tag_above() 36 | while tag do 37 | print(n:pblock(tag)) 38 | tag = n:tag_below(tag) 39 | end 40 | print("pkt", h(pkt)) 41 | print("out", h(block)) 42 | print("udp", n:get_udp()) 43 | print("eth", n:get_eth()) 44 | os.exit(1) 45 | end 46 | else 47 | print(pkt, time) 48 | end 49 | end 50 | 51 | -------------------------------------------------------------------------------- /pcapx.lua: -------------------------------------------------------------------------------- 1 | --[[- 2 | pcapx - extensions to pcap 3 | 4 | ]] 5 | 6 | local pcap = require"pcap" 7 | require"net" 8 | 9 | local function NOP() 10 | end 11 | 12 | --[[- 13 | - pcap.recode(incap, outcap, progress, debug) 14 | 15 | - incap, name of input pcap 16 | - outcap, name of output pcap, default to "recoded-"..incap 17 | - progress, pass print-like function to receive progress messages, 18 | defaults to no progress 19 | - debug, as above, but for debug output 20 | 21 | Re-encode file.pcap as recoded-file.pcap, using print() 22 | to report progress: 23 | 24 | pcap.recode("file.pcap", nil, print) 25 | ]] 26 | function pcap.recode(incap, outcap, progress, debug) 27 | progress = progress or NOP 28 | debug = debug or NOP 29 | 30 | if not outcap then 31 | outcap = "recoded-"..incap 32 | end 33 | os.remove(outcap) 34 | 35 | local cap = assert(pcap.open_offline(incap)) 36 | local dmp = assert(cap:dump_open(outcap)) 37 | local n = assert(net.init()) 38 | local i = 0 39 | for pkt, time, len in cap.next, cap do 40 | i = i + 1 41 | progress("packet", i, "wirelen", len, "timestamp", time, os.date("!%c", time)) 42 | assert(n:clear()) 43 | assert(n:decode_eth(pkt)) 44 | assert(dmp:dump(n:block(), time, len)) 45 | debug(n:dump()) 46 | end 47 | dmp:close() 48 | cap:close() 49 | n:destroy() 50 | return outcap 51 | end 52 | 53 | return pcap 54 | -------------------------------------------------------------------------------- /pcap-dump: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua5.1 2 | 3 | local pcap = require"pcap" 4 | 5 | arg.device = "any" 6 | arg.snaplen = 0 7 | arg.promisc = false 8 | arg.filter = nil 9 | arg.save = nil 10 | arg.timeout = 1 11 | 12 | for i,a in ipairs(arg) do 13 | local s,e,k,v = a:find("^([^=]+)=(.*)$") 14 | arg[k] = v 15 | end 16 | 17 | for k,v in pairs(arg) do 18 | if type(k) == "string" then 19 | print("arg", k, v) 20 | end 21 | end 22 | 23 | cap = assert(pcap.open_live(arg.device, arg.snaplen, arg.promisc, arg.timeout)) 24 | 25 | print("cap", cap) 26 | 27 | if arg.save then 28 | out = assert(cap:dump_open(arg.save)) 29 | end 30 | 31 | if arg.filter then 32 | assert(cap:set_filter(arg.filter)) 33 | end 34 | 35 | function loop(cap) 36 | local n 37 | n = function(cap) 38 | local capdata, timestamp, wirelen = cap:next() 39 | if capdata then 40 | return capdata, timestamp, wirelen 41 | end 42 | local emsg = timestamp 43 | if emsg == "closed" then 44 | return nil 45 | end 46 | if emsg == "timeout" then 47 | return n(cap) 48 | end 49 | assert(nil, timestamp) 50 | end 51 | 52 | return n, cap 53 | end 54 | 55 | for capdata, timestamp, wirelen in loop(cap) do 56 | print("#", timestamp, wirelen, #capdata) 57 | 58 | if out then 59 | assert(out:dump(capdata, timestamp, wirelen)) 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default build test 2 | 3 | -include local.mak 4 | 5 | default: build 6 | 7 | UNAME=$(shell uname) 8 | 9 | include $(UNAME).mak 10 | 11 | BINDING=pcap.so 12 | 13 | build: $(BINDING) 14 | 15 | prefix=/usr/local 16 | 17 | SODIR = $(DESTDIR)/$(prefix)/lib/lua/5.1/ 18 | 19 | LIBDIR = $(DESTDIR)/$(prefix)/share/lua/5.1/ 20 | BINDIR = $(DESTDIR)/$(prefix)/bin/ 21 | 22 | .PHONY: install 23 | install: $(BINDING) 24 | mkdir -p $(SODIR) 25 | install -t $(SODIR) $(BINDING) 26 | 27 | .PHONY: install-all 28 | install-all: install 29 | mkdir -p $(LIBDIR) 30 | mkdir -p $(BINDIR) 31 | install -t $(LIBDIR) pcapx.lua 32 | install -t $(BINDIR) pcap-recode pcap-dump pcap-split 33 | 34 | CWARNS = -Wall \ 35 | -pedantic \ 36 | -Wcast-align \ 37 | -Wnested-externs \ 38 | -Wpointer-arith \ 39 | -Wshadow \ 40 | -Wwrite-strings 41 | 42 | COPT=-O2 -DNDEBUG 43 | CFLAGS=$(CWARNS) $(CDEFS) $(CLUA) $(LDFLAGS) $(shell pcap-config --cflags) 44 | LDLIBS=$(LLUA) $(shell pcap-config --libs) 45 | 46 | CC.SO := $(CC) $(COPT) $(CFLAGS) 47 | 48 | %.so: %.c 49 | $(CC.SO) -o $@ $^ $(LDLIBS) 50 | 51 | pcap.so: pcap.c 52 | 53 | TNET=$(wildcard test-*.lua) 54 | TOUT=$(TNET:.lua=.test) 55 | 56 | echo: 57 | echo $(TOUT) 58 | 59 | test: pcap.test $(TOUT) 60 | 61 | %.test: %.lua net.so 62 | $(LUA) $< 63 | touch $@ 64 | 65 | %.test: %-test %.so 66 | $(LUA) $< 67 | touch $@ 68 | 69 | %.test: %-test net.so 70 | $(LUA) $< 71 | touch $@ 72 | 73 | doc: README.txt 74 | 75 | .PHONY: README.txt 76 | README.txt: README.txt.in pcap.c 77 | cp README.txt.in $@ 78 | luadoc pcap.c >> $@ 79 | 80 | -------------------------------------------------------------------------------- /pcap-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua5.1 2 | 3 | local pcap = require "pcap" 4 | require "netutil" 5 | 6 | local function gc() 7 | for _=1,5 do 8 | collectgarbage() 9 | end 10 | end 11 | 12 | function assertfail(...) 13 | --print("assertfail", ...) 14 | local ok, emsg = pcall(...) 15 | --print("pcall", ok, emsg) 16 | assert(ok == false, tostring(ok)..","..tostring(emsg)) 17 | assert(emsg) 18 | return emsg 19 | end 20 | 21 | function assertnext(cap, _pkt, _stamp, _wirelen) 22 | local pkt, stamp, wirelen = cap:next() 23 | 24 | print("want", _pkt and h(_pkt) or "nil", _stamp, _wirelen) 25 | print("next", pkt and h(pkt) or "nil", stamp, wirelen) 26 | 27 | if _pkt then assert(_pkt == pkt) end 28 | if _stamp then assert(_stamp == stamp) end 29 | if _wirelen then assert(_wirelen == wirelen) end 30 | end 31 | 32 | 33 | assert(pcap) 34 | 35 | print"test: tv to secs conversions" 36 | 37 | for _, tvsecs in ipairs{0, 1, 2, 3, 4, 5, 100, 788964454} do 38 | for tvusecs=0,1000 do 39 | local secs = pcap.tv2secs(tvsecs, tvusecs) 40 | local _tvsecs, _tvusecs = pcap.secs2tv(secs) 41 | if tvusecs ~= _tvusecs or tvsecs ~= _tvsecs then 42 | if tvsecs == 0 then 43 | print("tv in", tvsecs, tvusecs, "secs", string.format("%.8f", secs), "tv out", _tvsecs, _tvusecs) 44 | end 45 | end 46 | assert(tvsecs == _tvsecs) 47 | assert(tvusecs == _tvusecs, "diff is "..(tvusecs - _tvusecs)) 48 | end 49 | end 50 | 51 | print"+ok" 52 | 53 | print"pcap test" 54 | 55 | print("pcap lib version", assert(pcap._LIB_VERSION)) 56 | 57 | cap = assert(pcap.open_dead()) 58 | dmp = assert(cap:dump_open("cap0.pcap")) 59 | 60 | eth = b"112233445566aabbccddeeff080045" -- eth header + 1 byte of ipv4 61 | 62 | assert(dmp:dump(eth, 1.000010, #eth - 1 + 20)) 63 | assert(dmp:dump(eth, 2.000020)) 64 | assert(dmp:dump(eth)) 65 | 66 | assertfail(dmp.dump, dmp) 67 | assertfail(dmp.dump, dmp, nil) 68 | 69 | _, emsg = dmp:dump(nil, "msg") 70 | 71 | assert(_ == nil) 72 | assert(emsg == "msg") 73 | 74 | cap:close() 75 | 76 | assert(dmp:dump("pcap closed")) 77 | 78 | dmp:close() 79 | 80 | cap = assert(pcap.open_offline("cap0.pcap")) 81 | 82 | assertnext(cap, eth, 1.000010, #eth - 1 + 20) 83 | assertnext(cap, eth, 2.000020, #eth) 84 | assertnext(cap, eth, nil, #eth) 85 | assertnext(cap, "pcap closed", nil, #"pcap closed") 86 | assertnext(cap, nil, nil, nil) 87 | 88 | print"dlt table test" 89 | 90 | assert(pcap.DLT) 91 | assert(pcap.DLT.EN10MB == 1) 92 | assert(pcap.DLT.LINUX_SLL == 113) 93 | assert(pcap.DLT[1] == "EN10MB") 94 | assert(pcap.DLT[113] == "LINUX_SLL") 95 | 96 | print"+ok" 97 | 98 | 99 | -------------------------------------------------------------------------------- /netutil.lua: -------------------------------------------------------------------------------- 1 | local pcap = require"pcap" 2 | require"net" 3 | require"tostring" 4 | 5 | -- Quote a string into lua form (including the non-printable characters from 6 | -- 0-31, and from 127-255). 7 | function quote(_) 8 | local fmt = string.format 9 | local _ = fmt("%q", _) 10 | 11 | _ = string.gsub(_, "\\\n", "\\n") 12 | _ = string.gsub(_, "[%z\1-\31,\127-\255]", function (x) 13 | --print("x=", x) 14 | return fmt("\\%03d",string.byte(x)) 15 | end) 16 | 17 | return _ 18 | end 19 | 20 | q = quote 21 | 22 | -- binary to hex 23 | -- binary to hex 24 | function h(s) 25 | if s == nil then 26 | return "nil" 27 | end 28 | local function hex(s) 29 | return string.format("%02x", string.byte(s)) 30 | end 31 | return "["..#s.."] "..string.gsub(s, ".", hex) 32 | end 33 | 34 | -- hex to binary 35 | function b(s) 36 | if not s then 37 | return s 38 | end 39 | 40 | local function cvt (hexpair) 41 | n = string.char(tonumber(hexpair, 16)) 42 | return n 43 | end 44 | 45 | local s = s:gsub("(..)", cvt) 46 | return s 47 | end 48 | 49 | function countdiff(s0, s1) 50 | assert(#s0 == #s1) 51 | local count = 0 52 | for i=1,#s0 do 53 | if string.byte(s0, i) ~= string.byte(s1, i) then 54 | count = count + 1 55 | end 56 | end 57 | return count 58 | end 59 | 60 | function assertmostlyeql(threshold, s0, s1) 61 | if #s0 ~= #s1 then 62 | print("s0", h(s0)) 63 | print("s1", h(s1)) 64 | end 65 | assert(#s0 == #s1) 66 | 67 | local diff = countdiff(s0, s1) 68 | if diff > threshold then 69 | print("s0", h(s0)) 70 | print("s1", h(s1)) 71 | end 72 | assert(diff <= threshold, diff.." is less than threshold "..threshold) 73 | end 74 | 75 | function assertpcapsimilar(threshold, file0, file1) 76 | local n0 = assert(net.init()) 77 | local n1 = assert(net.init()) 78 | local cap0 = assert(pcap.open_offline(file0)) 79 | local cap1 = assert(pcap.open_offline(file1)) 80 | local i = 0 81 | for pkt0, time0, len0 in cap0.next, cap0 do 82 | local pkt1, time1, len1 = assert(cap1:next()) 83 | i = i + 1 84 | 85 | print(" "..file0, i, "wirelen", len0, "timestamp", time0, os.date("!%c", time0)) 86 | print(file1, i, "wirelen", len1, "timestamp", time1, os.date("!%c", time1)) 87 | 88 | assert(len0 == len1) 89 | assert(time0 == time1, string.format("%.7f ~= %.7f", time0, time1)) 90 | 91 | local _threshold = threshold 92 | if type(threshold) == "table" then 93 | if threshold[file0] then 94 | if type(threshold[file0]) == "table" then 95 | if threshold[file0][i] then 96 | _threshold = threshold[file0][i] 97 | end 98 | else 99 | _threshold = threshold[file0] 100 | end 101 | else 102 | _threshold = 0 103 | end 104 | end 105 | 106 | assertmostlyeql(_threshold, pkt0, pkt1) 107 | end 108 | assert(cap1:next() == nil) 109 | n0:destroy() 110 | n1:destroy() 111 | end 112 | 113 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | = pcap - a binding to libpcap 2 | 3 | libpcap is the library behind the commonly use tcpdump utility. It allows 4 | reading packet captures live from a network, as well as reading and writing 5 | saved packet captures in "pcap" format. It has been ported to many operating 6 | systems. 7 | 8 | The binding doesn't implement the full libpcap API, just what we've needed so 9 | far. 10 | 11 | To build, see Makefile, it supports FreeBSD, Linux and OS X. 12 | 13 | To decode the packets, you might want to use libnet's lua bindings, see the 14 | lua/ subdirectory of . 15 | 16 | Homepage: 17 | Author: 18 | 19 | If this doesn't do what you need, 20 | is a binding to a different 21 | subset of libpcap's API. Also, it has tcp/ip parsing functions, whereas we use 22 | libnet for that. 23 | 24 | 25 | Documentation: 26 | 27 | See below, extracted from in-source comments. 28 | 29 | 30 | 31 | 32 | ** pcap - a binding to libpcap 33 | 34 | 35 | -- pcap.DLT = { EN10MB=DLT_EN10MB, [DLT_EN10MB] = "EN10MB", ... } 36 | 37 | DLT is a table of common DLT types. The DLT number and name are mapped to each other. 38 | 39 | DLT.EN10MB is Ethernet (of all speeds, the name is historical). 40 | DLT.LINUX_SLL can occur when capturing on Linux with a device of "any". 41 | 42 | See for more information. 43 | 44 | The numeric values are returned by cap:datalink() and accepted as linktype values 45 | in pcap.open_dead(). 46 | 47 | 48 | -- cap = pcap.open_live(device, snaplen, promisc, timeout) 49 | 50 | Open a source device to read packets from. 51 | 52 | 53 | - device is the physical device (defaults to "any") 54 | 55 | - snaplen is the size to capture, where 0 means max possible (defaults to 0) 56 | 57 | - promisc is whether to set the device into promiscuous mode (default is false) 58 | 59 | - timeout is the timeout for reads in seconds (default is 0, return if no packets available) 60 | 61 | 62 | 63 | -- cap = pcap.open_dead([linktype, [snaplen]]) 64 | 65 | 66 | - linktype is one of the DLT numbers, and defaults to pcap.DLT.EN10MB. 67 | 68 | - snaplen is the maximum size of packet, and defaults to 65535 (also, 69 | a value of 0 is changed into 65535 internally, as tcpdump does). 70 | 71 | Open a pcap that doesn't read from either a live interface, or an offline pcap 72 | file. It can be used with cap:dump_open() to write a pcap file, or to compile a 73 | BPF program. 74 | 75 | 76 | -- cap = pcap.open_offline(fname) 77 | 78 | Open a savefile to read packets from. 79 | 80 | An fname of "-" is a synonym for stdin. 81 | 82 | 83 | -- cap:close() 84 | 85 | Manually close a cap object, freeing it's resources (this will happen on 86 | garbage collection if not done explicitly). 87 | 88 | 89 | -- cap = cap:set_filter(filter, nooptimize) 90 | 91 | 92 | - filter is the filter string, see tcpdump or pcap-filter man page. 93 | 94 | - nooptimize can be true if you don't want the filter optimized during compile 95 | (the default is to optimize). 96 | 97 | 98 | -- num = cap:datalink() 99 | 100 | Interpretation of the packet data requires knowing it's datalink type. This 101 | function returns that as a number. 102 | 103 | See pcap.DLT for more information. 104 | 105 | 106 | -- snaplen = cap:snapshot() 107 | 108 | The snapshot length. 109 | 110 | For a live capture, snapshot is the maximum amount of the packet that will be 111 | captured, for writing of captures, it is the maximum size of a packet that can 112 | be written. 113 | 114 | 115 | -- fd = cap:getfd() 116 | 117 | Get a selectable file descriptor number which can be used to wait for packets. 118 | 119 | Returns the descriptor number on success, or nil if no such descriptor is 120 | available (see pcap_get_selectable_fd). 121 | 122 | 123 | -- capdata, timestamp, wirelen = cap:next() 124 | 125 | Example: 126 | 127 | for capdata, timestamp, wirelen in cap.next, cap do 128 | print(timestamp, wirelen, #capdata) 129 | end 130 | 131 | Returns capdata, timestamp, wirelen on sucess: 132 | 133 | 134 | - capdata is the captured data 135 | 136 | - timestamp is in seconds, theoretically to microsecond accuracy 137 | 138 | - wirelen is the packets original length, the capdata may be shorter 139 | 140 | Returns nil,emsg on failure, where emsg is: 141 | 142 | 143 | - "timeout", timeout on a live capture 144 | 145 | - "closed", no more packets to be read from a file 146 | 147 | - ... some other string returned from pcap_geterr() describing the error 148 | 149 | 150 | -- sent = cap:inject(packet) 151 | 152 | Injects packet. 153 | 154 | Return is bytes sent on success, or nil,emsg on failure. 155 | 156 | 157 | -- dumper = cap:dump_open(fname) 158 | 159 | Open a dump file to write packets to. 160 | 161 | An fname of "-" is a synonym for stdout. 162 | 163 | Note that the dumper object is independent of the cap object, once 164 | it's created (so the cap object can be closed if its not going to 165 | be used). 166 | 167 | 168 | -- dumper:close() 169 | 170 | Manually close a dumper object, freeing it's resources (this will happen on 171 | garbage collection if not done explicitly). 172 | 173 | 174 | -- dumper = dumper:dump(pkt, [timestamp, [wirelen]]) 175 | 176 | pkt is the packet to write to the dumpfile. 177 | 178 | timestamp of packet, defaults to 0, meaning the current time. 179 | 180 | wirelen was the original length of the packet before being truncated to header 181 | (defaults to length of header, the correct value if it was not truncated). 182 | 183 | If only the header of the packet is available, wirelen should be set to the 184 | original packet length before it was truncated. Also, be very careful to not 185 | write a header that is longer than the caplen (which will 65535 unless a 186 | different value was specified in open_live or open_dead), the pcap file 187 | will not be valid. 188 | 189 | Returns self on sucess. 190 | Returns nil and an error msg on failure. 191 | 192 | Note that arguments are compatible with cap:next(), and that since 193 | pcap_dump() doesn't return error indicators only the failure 194 | values from cap:next() will ever be returned. 195 | 196 | 197 | -- dumper = dumper:flush() 198 | 199 | Flush all dumped packets to disk. 200 | 201 | Returns self on sucess. 202 | Returns nil and an error msg on failure. 203 | 204 | 205 | -- secs = pcap.tv2secs(seci, useci) 206 | 207 | Combine seperate seconds and microseconds into one numeric seconds. 208 | 209 | 210 | -- seci, useci = pcap.secs2tv(secs) 211 | 212 | Split one numeric seconds into seperate seconds and microseconds. 213 | 214 | 215 | -- pcap._LIB_VERSION = ... 216 | 217 | The libpcap version string, as returned from pcap_lib_version(). 218 | -------------------------------------------------------------------------------- /tostring.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Matias Guijarro wrote: 3 | 4 | > I would like to change the default behaviour of Lua when 5 | > representing a table through the tostring() function ; I 6 | > would prefer to have something more like in Python for 7 | > example (instead of having "table: 0x8062ac0", I would 8 | > like to see the contents, for example { 1, 2, 3 }). 9 | > 10 | > So first of all I tried to set a new metatable for tables, 11 | 12 | As Shmuel pointed out, table metatables are per-table, so 13 | they don't really work for what you want to do. 14 | 15 | Lua 5.1.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio 16 | > Tbl = setmetatable({}, {__tostring = function(t) 17 | >> return "Hello!" 18 | >> end}) 19 | > print(Tbl) -- Works for this table, 20 | Hello! 21 | > print({}) -- but doesn't work for other tables. 22 | table: 0x493378 23 | 24 | A way that will work is to simply replace the tostring 25 | function itself. Replace it with a function that calls your 26 | table-to-string function for tables, and calls the original 27 | tostring for everything else. 28 | 29 | Roberto's Programming in Lua book explains how to write the 30 | table-to-string function. (The first edition is available 31 | online: .) 32 | 33 | As it happens, I happened to write an implementation of just 34 | the functionality you're looking for a few days ago. It is 35 | below, but I encourage you to try whipping together at least 36 | a simple version of your own before looking at mine. 37 | 38 | - Aaron 39 | ]] 40 | 41 | -- This script makes tostring convert tables to a 42 | -- representation of their contents. 43 | 44 | -- The real tostring: 45 | local _tostring = tostring 46 | 47 | -- Characters that have non-numeric backslash-escaped versions: 48 | local BsChars = { 49 | ["\a"] = "\\a", 50 | ["\b"] = "\\b", 51 | ["\f"] = "\\f", 52 | ["\n"] = "\\n", 53 | ["\r"] = "\\r", 54 | ["\t"] = "\\t", 55 | ["\v"] = "\\v", 56 | ["\""] = "\\\"", 57 | ["\\"] = "\\\\"} 58 | 59 | -- Is Str an "escapeable" character (a non-printing character other than 60 | -- space, a backslash, or a double quote)? 61 | local function IsEscapeable(Char) 62 | return string.find(Char, "[^%w%p]") -- Non-alphanumeric, non-punct. 63 | and Char ~= " " -- Don't count spaces. 64 | or string.find(Char, '[\\"]') -- A backslash or quote. 65 | end 66 | 67 | -- Converts an "escapeable" character (a non-printing character, 68 | -- backslash, or double quote) to its backslash-escaped version; the 69 | -- second argument is used so that numeric character codes can have one 70 | -- or two digits unless three are necessary, which means that the 71 | -- returned value may represent both the character in question and the 72 | -- digit after it: 73 | local function EscapeableToEscaped(Char, FollowingDigit) 74 | if IsEscapeable(Char) then 75 | local Format = FollowingDigit == "" 76 | and "\\%d" 77 | or "\\%03d" .. FollowingDigit 78 | return BsChars[Char] 79 | or string.format(Format, string.byte(Char)) 80 | else 81 | return Char .. FollowingDigit 82 | end 83 | end 84 | 85 | -- Quotes a string in a Lua- and human-readable way. (This is a 86 | -- replacement for string.format's %q placeholder, whose result 87 | -- isn't always human readable.) 88 | local function StrToStr(Str) 89 | return '"' .. string.gsub(Str, "(.)(%d?)", 90 | EscapeableToEscaped) .. '"' 91 | end 92 | 93 | -- Quote a string into lua form (including the non-printable characters from 94 | -- 0-31, and from 127-255). 95 | local function quote(_) 96 | local fmt = string.format 97 | local _ = fmt("%q", _) 98 | 99 | _ = string.gsub(_, "\\\n", "\\n") 100 | _ = string.gsub(_, "[%z\1-\31,\127-\255]", function (x) 101 | --print("x=", x) 102 | return fmt("\\%03d",string.byte(x)) 103 | end) 104 | 105 | return _ 106 | end 107 | StrToStr = quote 108 | 109 | -- Lua keywords: 110 | local Keywords = {["and"] = true, ["break"] = true, ["do"] = true, 111 | ["else"] = true, ["elseif"] = true, ["end"] = true, ["false"] = true, 112 | ["for"] = true, ["function"] = true, ["if"] = true, ["in"] = true, 113 | ["local"] = true, ["nil"] = true, ["not"] = true, ["or"] = true, 114 | ["repeat"] = true, ["return"] = true, ["then"] = true, 115 | ["true"] = true, ["until"] = true, ["while"] = true} 116 | 117 | -- Is Str an identifier? 118 | local function IsIdent(Str) 119 | return not Keywords[Str] and string.find(Str, "^[%a_][%w_]*$") 120 | end 121 | 122 | -- Converts a non-table to a Lua- and human-readable string: 123 | local function ScalarToStr(Val) 124 | local Ret 125 | local Type = type(Val) 126 | if Type == "string" then 127 | Ret = StrToStr(Val) 128 | elseif Type == "function" or Type == "userdata" or Type == "thread" then 129 | -- Punt: 130 | Ret = "<" .. _tostring(Val) .. ">" 131 | else 132 | Ret = _tostring(Val) 133 | end -- if 134 | return Ret 135 | end 136 | 137 | -- Converts a table to a Lua- and human-readable string. 138 | local function TblToStr(Tbl, Seen) 139 | Seen = Seen or {} 140 | local Ret = {} 141 | if not Seen[Tbl] then 142 | Seen[Tbl] = true 143 | local LastArrayKey = 0 144 | for Key, Val in pairs(Tbl) do 145 | if type(Key) == "table" then 146 | Key = "[" .. TblToStr(Key, Seen) .. "]" 147 | elseif not IsIdent(Key) then 148 | if type(Key) == "number" and Key == LastArrayKey + 1 then 149 | -- Don't mess with Key if it's an array key. 150 | LastArrayKey = Key 151 | else 152 | Key = "[" .. ScalarToStr(Key) .. "]" 153 | end 154 | end 155 | if type(Val) == "table" then 156 | Val = TblToStr(Val, Seen) 157 | else 158 | Val = ScalarToStr(Val) 159 | end 160 | Ret[#Ret + 1] = 161 | (type(Key) == "string" 162 | and (Key .. " = ") -- Explicit key. 163 | or "") -- Implicit array key. 164 | .. Val 165 | end 166 | Ret = "{\n" .. table.concat(Ret, ", ") .. "\n}" 167 | else 168 | Ret = "" 169 | end 170 | return Ret 171 | end 172 | 173 | -- A replacement for tostring that prints tables in Lua- and 174 | -- human-readable format: 175 | function tostring(Val) 176 | return type(Val) == "table" 177 | and TblToStr(Val) 178 | or _tostring(Val) 179 | end 180 | 181 | --[=[ 182 | 183 | -- A test function: 184 | local function TblToStrTest() 185 | local Fnc = function() end 186 | local Tbl = {} 187 | Tbl[Tbl] = Tbl 188 | local Tbls = { 189 | {}, 190 | {1, true, 3}, 191 | {Foo = "Bar"}, 192 | {["*Foo*"] = "Bar"}, 193 | {Fnc}, 194 | {"\0\1\0011\a\b\f\n\r\t\v\"\\"}, 195 | {[{}] = {}}, 196 | Tbl, 197 | } 198 | local Strs = { 199 | "{}", 200 | "{1, true, 3}", 201 | '{Foo = "Bar"}', 202 | '{["*Foo*"] = "Bar"}', 203 | '{<' .. _tostring(Fnc) .. '>}', 204 | [[{"\0\1\0011\a\b\f\n\r\t\v\"\\"}]], 205 | "{[{}] = {}}", 206 | "{[] = }", 208 | } 209 | for I, Tbl in ipairs(Tbls) do 210 | assert(tostring(Tbl) == Strs[I], 211 | "Assertion failed on " .. _tostring(tostring(Tbl))) 212 | end 213 | end 214 | 215 | TblToStrTest() 216 | ]=] 217 | 218 | -------------------------------------------------------------------------------- /pcap.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2010 Wurldtech Security Technologies All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 | PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | 29 | /*- 30 | ** pcap - a binding to libpcap 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "lua.h" 43 | #include "lauxlib.h" 44 | #include "lualib.h" 45 | 46 | #ifdef WIN32 47 | #include 48 | #endif 49 | 50 | #if LUA_VERSION_NUM > 501 51 | #define luaL_reg luaL_Reg 52 | #endif 53 | 54 | static double tv2secs(struct timeval* tv) 55 | { 56 | double secs = tv->tv_sec; 57 | secs += (double)tv->tv_usec / 1000000.0; 58 | return secs; 59 | } 60 | 61 | static struct timeval* secs2tv(double secs, struct timeval* tv) 62 | { 63 | double ipart = 0.0; 64 | double fpart = 0.0; 65 | 66 | fpart = modf(secs, &ipart); 67 | 68 | tv->tv_sec = (long) ipart; 69 | 70 | fpart = modf(fpart * 1000000.0, &ipart); 71 | 72 | tv->tv_usec = (long) ipart; 73 | 74 | if(fpart > 0.5) 75 | tv->tv_usec += 1; 76 | 77 | return tv; 78 | } 79 | 80 | static struct timeval* opttimeval(lua_State* L, int argi, struct timeval* tv) 81 | { 82 | if(lua_isnoneornil(L, argi)) { 83 | #ifndef NDEBUG 84 | int e = 85 | #endif 86 | gettimeofday(tv, NULL); 87 | #ifndef NDEBUG 88 | assert(e == 0); /* can only fail due to argument errors */ 89 | #endif 90 | } else { 91 | double secs = luaL_checknumber(L, argi); 92 | secs2tv(secs, tv); 93 | } 94 | return tv; 95 | } 96 | 97 | static void v_obj_metatable(lua_State* L, const char* regid, const struct luaL_reg methods[]) 98 | { 99 | /* metatable = { ... methods ... } */ 100 | luaL_newmetatable(L, regid); 101 | 102 | #if LUA_VERSION_NUM > 501 103 | luaL_setfuncs(L, methods, 0); 104 | #else 105 | luaL_register(L, NULL, methods); 106 | #endif 107 | 108 | /* metatable["__index"] = metatable */ 109 | lua_pushvalue(L, -1); 110 | lua_setfield(L, -2, "__index"); 111 | lua_pop(L, 1); 112 | } 113 | 114 | 115 | /* Registry IDs and helper functions */ 116 | 117 | #define L_PCAP_REGID "wt.pcap" 118 | #define L_PCAP_DUMPER_REGID "wt.pcap_dumper" 119 | 120 | static int pusherr(lua_State* L, pcap_t* cap) 121 | { 122 | lua_pushnil(L); 123 | lua_pushstring(L, pcap_geterr(cap)); 124 | return 2; 125 | } 126 | 127 | static pcap_t* checkpcap(lua_State* L) 128 | { 129 | pcap_t** cap = luaL_checkudata(L, 1, L_PCAP_REGID); 130 | 131 | luaL_argcheck(L, *cap, 1, "pcap has been closed"); 132 | 133 | return *cap; 134 | } 135 | 136 | 137 | static pcap_t** pushpcapopen(lua_State* L) 138 | { 139 | pcap_t** cap = lua_newuserdata(L, sizeof(*cap)); 140 | *cap = NULL; 141 | luaL_getmetatable(L, L_PCAP_REGID); 142 | lua_setmetatable(L, -2); 143 | return cap; 144 | } 145 | 146 | static int checkpcapopen(lua_State* L, pcap_t** cap, const char* errbuf) 147 | { 148 | if (!*cap) { 149 | lua_pushnil(L); 150 | lua_pushstring(L, errbuf); 151 | return 2; 152 | } 153 | return 1; 154 | } 155 | 156 | 157 | /* Wrap pcap_t */ 158 | 159 | /*- 160 | -- pcap.DLT = { EN10MB=DLT_EN10MB, [DLT_EN10MB] = "EN10MB", ... } 161 | 162 | DLT is a table of common DLT types. The DLT number and name are mapped to each other. 163 | 164 | DLT.EN10MB is Ethernet (of all speeds, the name is historical). 165 | DLT.LINUX_SLL can occur when capturing on Linux with a device of "any". 166 | 167 | See for more information. 168 | 169 | The numeric values are returned by cap:datalink() and accepted as linktype values 170 | in pcap.open_dead(). 171 | */ 172 | /* In the table at the top of the stack, dlt, do: 173 | * dlt[name] = number 174 | * dlt[number] = name 175 | */ 176 | static void pcap_dlt_set(lua_State* L, const char* name, int number) 177 | { 178 | lua_pushstring(L, name); 179 | lua_pushinteger(L, number); 180 | lua_settable(L, -3); 181 | 182 | lua_pushinteger(L, number); 183 | lua_pushstring(L, name); 184 | lua_settable(L, -3); 185 | } 186 | 187 | /* TODO - add all the DLT values... */ 188 | static void pcap_make_dlt(lua_State* L) 189 | { 190 | lua_newtable(L); 191 | #ifdef DLT_EN10MB 192 | pcap_dlt_set(L, "EN10MB", DLT_EN10MB); 193 | #endif 194 | #ifdef DLT_RAW 195 | pcap_dlt_set(L, "RAW", DLT_RAW); 196 | #endif 197 | #ifdef DLT_LINUX_SLL 198 | pcap_dlt_set(L, "LINUX_SLL", DLT_LINUX_SLL); 199 | #endif 200 | #ifdef DLT_USER0 201 | pcap_dlt_set(L, "USER0", DLT_USER0); 202 | #endif 203 | #ifdef DLT_USER1 204 | pcap_dlt_set(L, "USER1", DLT_USER1); 205 | #endif 206 | #ifdef DLT_USER2 207 | pcap_dlt_set(L, "USER2", DLT_USER2); 208 | #endif 209 | #ifdef DLT_USER3 210 | pcap_dlt_set(L, "USER3", DLT_USER3); 211 | #endif 212 | } 213 | 214 | 215 | /*- 216 | -- cap = pcap.open_live(device, snaplen, promisc, timeout) 217 | 218 | Open a source device to read packets from. 219 | 220 | - device is the physical device (defaults to "any") 221 | - snaplen is the size to capture, where 0 means max possible (defaults to 0) 222 | - promisc is whether to set the device into promiscuous mode (default is false) 223 | - timeout is the timeout for reads in seconds (default is 0, return if no packets available) 224 | 225 | */ 226 | static int lpcap_open_live(lua_State *L) 227 | { 228 | const char *device = luaL_optstring(L, 1, "any"); 229 | int snaplen = luaL_optint(L, 2, 0); 230 | int promisc = lua_toboolean(L, 3); 231 | int to_ms = 1000 * luaL_optint(L, 4, 0); /* convert to milliseconds */ 232 | pcap_t** cap = pushpcapopen(L); 233 | char errbuf[PCAP_ERRBUF_SIZE]; 234 | if(snaplen == 0) 235 | snaplen = 0xffff; 236 | *cap = pcap_open_live(device, snaplen, promisc, to_ms, errbuf); 237 | return checkpcapopen(L, cap, errbuf); 238 | } 239 | 240 | 241 | /*- 242 | -- cap = pcap.open_dead([linktype, [snaplen]]) 243 | 244 | - linktype is one of the DLT numbers, and defaults to pcap.DLT.EN10MB. 245 | - snaplen is the maximum size of packet, and defaults to 65535 (also, 246 | a value of 0 is changed into 65535 internally, as tcpdump does). 247 | 248 | Open a pcap that doesn't read from either a live interface, or an offline pcap 249 | file. It can be used with cap:dump_open() to write a pcap file, or to compile a 250 | BPF program. 251 | */ 252 | static int lpcap_open_dead(lua_State *L) 253 | { 254 | int linktype = luaL_optint(L, 1, DLT_EN10MB); 255 | int snaplen = luaL_optint(L, 2, 0); 256 | pcap_t** cap = pushpcapopen(L); 257 | 258 | /* this is the value tcpdump uses, its way bigger than any known link size */ 259 | if(!snaplen) 260 | snaplen = 0xffff; 261 | 262 | *cap = pcap_open_dead(linktype, snaplen); 263 | 264 | return checkpcapopen(L, cap, "open dead failed for unknown reason"); 265 | } 266 | 267 | 268 | /*- 269 | -- cap = pcap.open_offline(fname) 270 | 271 | Open a savefile to read packets from. 272 | 273 | An fname of "-" is a synonym for stdin. 274 | */ 275 | static int lpcap_open_offline(lua_State *L) 276 | { 277 | const char *fname = luaL_checkstring(L, 1); 278 | pcap_t** cap = pushpcapopen(L); 279 | char errbuf[PCAP_ERRBUF_SIZE]; 280 | *cap = pcap_open_offline(fname, errbuf); 281 | return checkpcapopen(L, cap, errbuf); 282 | } 283 | 284 | 285 | /*- 286 | -- cap:close() 287 | 288 | Manually close a cap object, freeing it's resources (this will happen on 289 | garbage collection if not done explicitly). 290 | */ 291 | static int lpcap_close (lua_State *L) 292 | { 293 | pcap_t** cap = luaL_checkudata(L, 1, L_PCAP_REGID); 294 | 295 | if(*cap) 296 | pcap_close(*cap); 297 | 298 | *cap = NULL; 299 | 300 | return 0; 301 | } 302 | 303 | 304 | /* Current libpcap says to use PCAP_NETMASK_UNKNOWN if you don't know the 305 | netmask, older libpcaps says to use 0, so we do one or the other 306 | depending on whether the macro exists. 307 | */ 308 | #ifndef PCAP_NETMASK_UNKNOWN 309 | # define PCAP_NETMASK_UNKNOWN 0 310 | #endif 311 | /*- 312 | -- cap = cap:set_filter(filter, nooptimize) 313 | 314 | - filter is the filter string, see tcpdump or pcap-filter man page. 315 | - nooptimize can be true if you don't want the filter optimized during compile 316 | (the default is to optimize). 317 | */ 318 | static int lpcap_set_filter(lua_State* L) 319 | { 320 | pcap_t* cap = checkpcap(L); 321 | const char* filter = luaL_checkstring(L, 2); 322 | int optimize = !lua_toboolean(L, 3); 323 | bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN; /* TODO get device from registry, and call pcap_lookup_net()*/ 324 | int ret = 0; 325 | struct bpf_program program = { 0 }; 326 | 327 | ret = pcap_compile(cap, &program, filter, optimize, netmask); 328 | 329 | if(ret < 0) { 330 | return pusherr(L, cap); 331 | } 332 | 333 | ret = pcap_setfilter(cap, &program); 334 | 335 | pcap_freecode(&program); 336 | 337 | if(ret < 0) { 338 | return pusherr(L, cap); 339 | } 340 | 341 | lua_settop(L, 1); 342 | 343 | return 1; 344 | } 345 | 346 | /*- 347 | -- num = cap:datalink() 348 | 349 | Interpretation of the packet data requires knowing it's datalink type. This 350 | function returns that as a number. 351 | 352 | See pcap.DLT for more information. 353 | */ 354 | static int lpcap_datalink(lua_State* L) 355 | { 356 | pcap_t* cap = checkpcap(L); 357 | lua_pushnumber(L, pcap_datalink(cap)); 358 | return 1; 359 | } 360 | 361 | /*- 362 | -- snaplen = cap:snapshot() 363 | 364 | The snapshot length. 365 | 366 | For a live capture, snapshot is the maximum amount of the packet that will be 367 | captured, for writing of captures, it is the maximum size of a packet that can 368 | be written. 369 | */ 370 | static int lpcap_snapshot(lua_State* L) 371 | { 372 | pcap_t* cap = checkpcap(L); 373 | lua_pushnumber(L, pcap_snapshot(cap)); 374 | return 1; 375 | } 376 | 377 | /*- 378 | -- fd = cap:getfd() 379 | 380 | Get a selectable file descriptor number which can be used to wait for packets. 381 | 382 | Returns the descriptor number on success, or nil if no such descriptor is 383 | available (see pcap_get_selectable_fd). 384 | */ 385 | #ifndef WIN32 386 | static int lpcap_getfd(lua_State* L) 387 | { 388 | pcap_t* cap = checkpcap(L); 389 | int fd = pcap_get_selectable_fd(cap); 390 | if(fd < 0) { 391 | lua_pushnil(L); 392 | lua_pushstring(L, "not selectable"); 393 | return 2; 394 | } 395 | lua_pushnumber(L, fd); 396 | return 1; 397 | } 398 | #endif 399 | 400 | /*- 401 | -- capdata, timestamp, wirelen = cap:next() 402 | 403 | Example: 404 | 405 | for capdata, timestamp, wirelen in cap.next, cap do 406 | print(timestamp, wirelen, #capdata) 407 | end 408 | 409 | Returns capdata, timestamp, wirelen on sucess: 410 | 411 | - capdata is the captured data 412 | - timestamp is in seconds, theoretically to microsecond accuracy 413 | - wirelen is the packets original length, the capdata may be shorter 414 | 415 | Returns nil,emsg on failure, where emsg is: 416 | 417 | - "timeout", timeout on a live capture 418 | - "closed", no more packets to be read from a file 419 | - ... some other string returned from pcap_geterr() describing the error 420 | */ 421 | /* TODO cap:loop() -> function(cap) return cap.next, cap end */ 422 | static int pushpkt(lua_State* L, struct pcap_pkthdr* pkt_header, const u_char* pkt_data) 423 | { 424 | lua_pushlstring(L, (const char*)pkt_data, pkt_header->caplen); 425 | lua_pushnumber(L, tv2secs(&pkt_header->ts)); 426 | lua_pushinteger(L, pkt_header->len); 427 | 428 | return 3; 429 | } 430 | 431 | static int lpcap_next(lua_State* L) 432 | { 433 | pcap_t* cap = checkpcap(L); 434 | struct pcap_pkthdr* pkt_header = NULL; 435 | const u_char* pkt_data = NULL; 436 | int e = pcap_next_ex(cap, &pkt_header, &pkt_data); 437 | 438 | /* Note: return values don't have names, they are documented numerically 439 | in the man page. */ 440 | switch(e) { 441 | case 1: /* success */ 442 | return pushpkt(L, pkt_header, pkt_data); 443 | case 0: /* read live, and timeout occurred */ 444 | lua_pushnil(L); 445 | lua_pushstring(L, "timeout"); 446 | return 2; 447 | case -2: /* read from a savefile, and no more packets */ 448 | lua_pushnil(L); 449 | lua_pushstring(L, "closed"); 450 | return 2; 451 | case -1: /* an error occurred */ 452 | return pusherr(L, cap); 453 | } 454 | return luaL_error(L, "unreachable"); 455 | } 456 | 457 | 458 | /*- 459 | -- sent = cap:inject(packet) 460 | 461 | Injects packet. 462 | 463 | Return is bytes sent on success, or nil,emsg on failure. 464 | */ 465 | #ifndef WIN32 466 | static int lpcap_inject(lua_State* L) 467 | { 468 | pcap_t* cap = checkpcap(L); 469 | size_t datasz = 0; 470 | const char* data = luaL_checklstring(L, 2, &datasz); 471 | 472 | int sent = pcap_inject(cap, data, datasz); 473 | 474 | if (sent < 0) { 475 | return pusherr(L, cap); 476 | } 477 | 478 | lua_pushinteger(L, sent); 479 | 480 | return 1; 481 | } 482 | 483 | #endif 484 | 485 | /* Wrap pcap_dumper_t */ 486 | 487 | static pcap_dumper_t* checkdumper(lua_State* L) 488 | { 489 | pcap_dumper_t** dumper = luaL_checkudata(L, 1, L_PCAP_DUMPER_REGID); 490 | 491 | luaL_argcheck(L, *dumper, 1, "pcap dumper has been closed"); 492 | 493 | return *dumper; 494 | } 495 | 496 | /*- 497 | -- dumper = cap:dump_open(fname) 498 | 499 | Open a dump file to write packets to. 500 | 501 | An fname of "-" is a synonym for stdout. 502 | 503 | Note that the dumper object is independent of the cap object, once 504 | it's created (so the cap object can be closed if its not going to 505 | be used). 506 | */ 507 | static int lpcap_dump_open(lua_State *L) 508 | { 509 | pcap_t* cap = checkpcap(L); 510 | const char* fname = luaL_checkstring(L, 2); 511 | pcap_dumper_t** dumper = lua_newuserdata(L, sizeof(*dumper)); 512 | 513 | *dumper = NULL; 514 | 515 | luaL_getmetatable(L, L_PCAP_DUMPER_REGID); 516 | lua_setmetatable(L, -2); 517 | 518 | *dumper = pcap_dump_open(cap, fname); 519 | 520 | if (!*dumper) { 521 | return pusherr(L, cap); 522 | } 523 | 524 | return 1; 525 | } 526 | 527 | 528 | /*- 529 | -- dumper:close() 530 | 531 | Manually close a dumper object, freeing it's resources (this will happen on 532 | garbage collection if not done explicitly). 533 | */ 534 | static int lpcap_dump_close (lua_State *L) 535 | { 536 | pcap_dumper_t** dumper = luaL_checkudata(L, 1, L_PCAP_DUMPER_REGID); 537 | 538 | if(*dumper) 539 | pcap_dump_close(*dumper); 540 | 541 | *dumper = NULL; 542 | 543 | return 0; 544 | } 545 | 546 | 547 | /*- 548 | -- dumper = dumper:dump(pkt, [timestamp, [wirelen]]) 549 | 550 | pkt is the packet to write to the dumpfile. 551 | 552 | timestamp of packet, defaults to 0, meaning the current time. 553 | 554 | wirelen was the original length of the packet before being truncated to header 555 | (defaults to length of header, the correct value if it was not truncated). 556 | 557 | If only the header of the packet is available, wirelen should be set to the 558 | original packet length before it was truncated. Also, be very careful to not 559 | write a header that is longer than the caplen (which will 65535 unless a 560 | different value was specified in open_live or open_dead), the pcap file 561 | will not be valid. 562 | 563 | Returns self on sucess. 564 | Returns nil and an error msg on failure. 565 | 566 | Note that arguments are compatible with cap:next(), and that since 567 | pcap_dump() doesn't return error indicators only the failure 568 | values from cap:next() will ever be returned. 569 | */ 570 | /* TODO store the snaplen in dumper's environment, so we can check it here */ 571 | static int lpcap_dump(lua_State* L) 572 | { 573 | pcap_dumper_t* dumper = checkdumper(L); 574 | const char* pkt; 575 | size_t caplen; 576 | size_t wirelen; 577 | struct pcap_pkthdr hdr; 578 | 579 | /* first check if we are echoing the nil,emsg from cap:next() 580 | * before checking our argument types 581 | */ 582 | if(lua_isnil(L, 2) && lua_type(L, 3) == LUA_TSTRING) { 583 | return 2; 584 | } 585 | 586 | pkt = luaL_checklstring(L, 2, &caplen); 587 | opttimeval(L, 3, &hdr.ts); 588 | wirelen = luaL_optint(L, 4, caplen); 589 | 590 | luaL_argcheck(L, wirelen >= caplen, 4, "original wirelen cannot be less than current pkt length"); 591 | 592 | hdr.caplen = caplen; 593 | hdr.len = wirelen; 594 | 595 | /* Note odd type signature for dumper, its because pcap_dump() is 596 | * designed to be called from a pcap_handler, where the dumper 597 | * is received as the user data. 598 | */ 599 | pcap_dump((u_char*) dumper, &hdr, (u_char*)pkt); 600 | 601 | /* clear the stack above self, and return self */ 602 | lua_settop(L, 1); 603 | 604 | return 1; 605 | } 606 | 607 | /*- 608 | -- dumper = dumper:flush() 609 | 610 | Flush all dumped packets to disk. 611 | 612 | Returns self on sucess. 613 | Returns nil and an error msg on failure. 614 | */ 615 | static int lpcap_flush(lua_State* L) 616 | { 617 | pcap_dumper_t* dumper = checkdumper(L); 618 | int e = pcap_dump_flush(dumper); 619 | 620 | if(e == 0) { 621 | return 1; 622 | } 623 | 624 | lua_pushnil(L); 625 | lua_pushstring(L, strerror(errno)); 626 | 627 | return 2; 628 | } 629 | 630 | /* timeval to second conversions */ 631 | /* These don't need to be external, but are useful to test timeval conversion from lua. */ 632 | /*- 633 | -- secs = pcap.tv2secs(seci, useci) 634 | 635 | Combine seperate seconds and microseconds into one numeric seconds. 636 | */ 637 | static int lpcap_tv2secs(lua_State* L) 638 | { 639 | struct timeval tv; 640 | tv.tv_sec = (long)luaL_checknumber(L, 1); 641 | tv.tv_usec = (long)luaL_checknumber(L, 2); 642 | 643 | lua_pushnumber(L, tv2secs(&tv)); 644 | return 1; 645 | } 646 | 647 | /*- 648 | -- seci, useci = pcap.secs2tv(secs) 649 | 650 | Split one numeric seconds into seperate seconds and microseconds. 651 | */ 652 | static int lpcap_secs2tv(lua_State* L) 653 | { 654 | struct timeval tv; 655 | double secs = luaL_checknumber(L, 1); 656 | 657 | secs2tv(secs, &tv); 658 | lua_pushnumber(L, tv.tv_sec); 659 | lua_pushnumber(L, tv.tv_usec); 660 | return 2; 661 | } 662 | 663 | /*- 664 | -- pcap._LIB_VERSION = ... 665 | 666 | The libpcap version string, as returned from pcap_lib_version(). 667 | */ 668 | static const luaL_reg pcap_module[] = 669 | { 670 | {"open_live", lpcap_open_live}, 671 | {"open_offline", lpcap_open_offline}, 672 | {"open_dead", lpcap_open_dead}, 673 | {"tv2secs", lpcap_tv2secs}, 674 | {"secs2tv", lpcap_secs2tv}, 675 | {NULL, NULL} 676 | }; 677 | 678 | static const luaL_reg pcap_methods[] = 679 | { 680 | {"__gc", lpcap_close}, 681 | {"close", lpcap_close}, 682 | {"dump_open", lpcap_dump_open}, 683 | {"set_filter", lpcap_set_filter}, 684 | {"datalink", lpcap_datalink}, 685 | {"snapshot", lpcap_snapshot}, 686 | #ifndef WIN32 687 | {"getfd", lpcap_getfd}, 688 | #endif 689 | {"next", lpcap_next}, 690 | /* TODO - wt_pcap.c also had a next_nonblocking(), I'm not sure why a setnonblocking() wasn't sufficient */ 691 | #ifndef WIN32 692 | {"inject", lpcap_inject}, 693 | #endif 694 | {NULL, NULL} 695 | }; 696 | 697 | static const luaL_reg dumper_methods[] = 698 | { 699 | {"__gc", lpcap_dump_close}, 700 | {"close", lpcap_dump_close}, 701 | {"dump", lpcap_dump}, 702 | {"flush", lpcap_flush}, 703 | {NULL, NULL} 704 | }; 705 | 706 | 707 | int luaopen_pcap (lua_State *L) 708 | { 709 | v_obj_metatable(L, L_PCAP_DUMPER_REGID, dumper_methods); 710 | v_obj_metatable(L, L_PCAP_REGID, pcap_methods); 711 | 712 | #if LUA_VERSION_NUM > 501 713 | lua_newtable(L); 714 | luaL_setfuncs (L,pcap_module,0); //leaving global namespace clean in 5.2 715 | #else 716 | luaL_register(L, "pcap", pcap_module); 717 | #endif 718 | 719 | lua_pushstring(L, pcap_lib_version()); 720 | lua_setfield(L, -2, "_LIB_VERSION"); 721 | 722 | pcap_make_dlt(L); 723 | lua_setfield(L, -2, "DLT"); 724 | 725 | return 1; 726 | } 727 | 728 | --------------------------------------------------------------------------------