(module libpcap mzscheme
(require "define-utils.ss"
"netutils.ss"
"ffi-utils.ss"
"security-guard.ss"
(lib "list.ss")
(lib "etc.ss")
(all-except (lib "contract.ss") ->)
(rename (lib "contract.ss") => ->)
(lib "foreign.ss")) (unsafe!)
(provide
raise-pcap-exn
(rename open-live-secure open-live)
open-dead
(rename open-offline-secure open-offline)
(rename dump-open-secure dump-open)
dump-fopen
dump
dump-flush
dump-file
dump-close
(rename setnonblock set-non-block!)
(rename getnonblock get-non-block)
(rename setfilter set-filter!)
(rename findalldevs find-all-devs)
(rename lookupdev lookup-dev)
(rename lookupnet lookup-net)
dispatch
loop
compile-filter
compile-nopcap
next
next-ex
datalink
list-datalinks
set-datalink!
datalink-name-to-val
datalink-val-to-name
datalink-val-to-description
snapshot
(rename is-swapped swapped?)
major-version
minor-version
stats
file
fileno
get-selectable-fd
perror
(rename geterr get-error)
strerror
lib-version
(rename close pcap-close))
(provide* (unsafe next*)
(unsafe next-ex*)
(unsafe loop*)
(unsafe dispatch*)
(unsafe dump*))
(define-unsafer unsafe-pcap!)
(define (get-dll-path file)
(case (system-type)
[(unix) file]
[(windows) (build-path (this-expression-source-directory) "..\\lib\\" file)]))
(if (eq? 'windows (system-type))
(begin
(ffi-lib (get-dll-path "WanPacket.dll"))
(ffi-lib (get-dll-path "Packet.dll"))))
(define libpcap (ffi-lib
(case (system-type)
[(unix) "libpcap"]
[(windows) (get-dll-path "wpcap")])))
(define/provide SNAPLEN 65535)
(define PCAP-ERRORBUF-SIZE 256)
(define err-buf (make-parameter (make-bytes PCAP-ERRORBUF-SIZE)))
(define pointer/c (union cpointer? false/c))
(default-_string-type _string*/utf-8)
(define-handle pcap)
(define-handle dumper)
(define _datalink (_enum
'(DLT-INVALID = -1
DLT-NULL = 0
DLT-EN10MB = 1
DLT-EN3MB = 2
DLT-AX25 = 3
DLT-PRONET = 4
DLT-CHAOS = 5
DLT-IEEE802 = 6
DLT-ARCNET = 7
DLT-SLIP = 8
DLT-PPP = 9
DLT-FDDI = 10
DLT-ATM-RFC1483 = 11
DLT-RAW = 12
DLT-SLIP-BSDOS = 15
DLT-PPP-BSDOS = 16
DLT-ATM-CLIP = 19
DLT-PPP-SERIAL = 50
DLT-PPP-ETHER = 51
DLT-SYMANTEC-FIREWALL = 99
DLT-C-HDLC = 104
DLT-IEEE802-11 = 105
DLT-FRELAY = 107
DLT-LOOP = 108
DLT-ENC = 109
DLT-LINUX-SLL = 113
DLT-LTALK = 114
DLT-ECONET = 115
DLT-IPFILTER = 116
DLT-PFLOG = 117
DLT-CISCO-IOS = 118
DLT-PRISM-HEADER = 119
DLT-AIRONET-HEADER = 120
DLT-PFSYNC = 121
DLT-IP-OVER-FC = 122
DLT-SUNATM = 123
DLT-RIO = 124
DLT-PCI-EXP = 125
DLT-AURORA = 126
DLT-IEEE802-11-RADIO = 127
DLT-TZSP = 128
DLT-ARCNET-LINUX = 129
DLT-JUNIPER-MLPPP = 130
DLT-APPLE-IP-OVER-IEEE1394 = 138
DLT-JUNIPER-MLFR = 131
DLT-JUNIPER-ES = 132
DLT-JUNIPER-GGSN = 133
DLT-JUNIPER-MFR = 134
DLT-JUNIPER-ATM2 = 135
DLT-JUNIPER-SERVICES = 136
DLT-JUNIPER-ATM1 = 137
DLT-MTP2-WITH-PHDR = 139
DLT-MTP2 = 140
DLT-MTP3 = 141
DLT-SCCP = 142
DLT-DOCSIS = 143
DLT-LINUX-IRDA = 144
DLT-IBM-SP = 145
DLT-IBM-SN = 146
DLT-USER0 = 147
DLT-USER1 = 148
DLT-USER2 = 149
DLT-USER3 = 150
DLT-USER4 = 151
DLT-USER5 = 152
DLT-USER6 = 153
DLT-USER7 = 154
DLT-USER8 = 155
DLT-USER9 = 156
DLT-USER10 = 157
DLT-USER11 = 158
DLT-USER12 = 159
DLT-USER13 = 160
DLT-USER14 = 161
DLT-USER15 = 162
DLT-IEEE802-11-RADIO-AVS = 163
DLT-JUNIPER-MONITOR = 164
DLT-BACNET-MS-TP = 165
DLT-PPP-PPPD = 166
DLT-JUNIPER-PPPOE = 167
DLT-JUNIPER-PPPOE-ATM = 168
DLT-GPRS-LLC = 169
DLT-GPF-T = 170
DLT-GPF-F = 171
DLT-GCOM-T1E1 = 172
DLT-GCOM-SERIAL = 173
DLT-JUNIPER-PIC-PEER = 174
DLT-ERF-ETH = 175
DLT-ERF-POS = 176
DLT-LINUX-LAPD = 177)))
(define-struct/provide/contract net ([ip string?]
[mask string?])
(make-inspector))
(define-clist-struct _addr-list
addr
([next _addr-list]
[addr _sockaddr]
[netmask _sockaddr]
[broadaddr _sockaddr]
[dstaddr _sockaddr]))
(define _iface-flags (_enum
'(PCAP-IF-FLAG-NONE = 0
PCAP-IF-FLAG-LOOPBACK = 1)))
(define-clist-struct _iface-list
iface
([next _iface-list]
[name _string]
[description _string]
[addresses _addr-list]
[flags _iface-flags])
freealldevs)
(define/provide-ctype-struct stat
_stat
([ps-recv _uint]
[ps-drop _uint]
[ps-ifdrop _uint]
[ps-bs-capt _uint])
(make-inspector))
(define/provide-ctype-struct timeval
_timeval
([tv-sec _ulong]
[tv-usec _ulong])
(make-inspector))
(define/provide-ctype-struct pkthdr
_pkthdr
([ts _timeval]
[caplen _uint32]
[len _uint32])
(make-inspector))
(define-struct/provide/contract packet ([head pkthdr?]
[data (union cpointer? bytes?)])
(make-inspector))
(define-ctype-struct bpf-program
_bpf-program
([len _uint]
[ptr _pointer]))
(define-struct (exn:fail:pcap exn:fail) ())
(provide (struct exn:fail:pcap ()))
(define-syntax raise-pcap-exn
(syntax-rules ()
((_ msg)
(raise (make-exn:fail:pcap (string->immutable-string msg) (current-continuation-marks))))))
(define (cstring/string->string str)
(cond
[(string? str) str]
[else (cstring->string str)]))
(define (std-error-handler ret-val err-buf)
(if ret-val
ret-val
(raise-pcap-exn (cstring/string->string err-buf))))
(define (-1_0_1_error-handler ret-val err-buf)
(cond
[(= -1 ret-val) (raise-pcap-exn (cstring/string->string err-buf))]
[(= 0 ret-val) #f]
[else #t]))
(define (-1_any-error-handler ret-val err-buf val)
(cond
[(= -1 ret-val) (raise-pcap-exn (cstring/string->string err-buf))]
[else val]))
(define packet/c (flat-named-contract "packet"
(lambda (pkt)
(or (eof-object? pkt) (packet? pkt) (not pkt)))))
(define (next-ex-error-handler ret head data safe)
(cond
[(= 1 ret) (make-packet head
(if safe
(make-sized-byte-string
data
(pkthdr-caplen head))
data))]
[(= 0 ret) #f] [(= -1 ret) (raise-pcap-exn "Error reading packet")]
[(= -2 ret) eof]))
(ffi-func/contract libpcap
(("^" "pcap-")
("!$" "") ("~$" "") ("-" "_")
("[*]" "") )
([open-live
(string? integer? boolean? integer? . => . pcap?) (_fun _string _int _bool _int (eb : _string = (err-buf)) -> (ret : _pcap)
-> (std-error-handler ret eb))]
[open-dead
(symbol? integer? . => . pcap?) (_fun _datalink _int -> (ret : _pcap)
-> (std-error-handler ret "Unknown error: open-dead"))]
[open-offline
((union string? path?) . => . pcap?) (_fun _string (eb : _string = (err-buf)) -> (ret : _pcap)
-> (std-error-handler ret eb))]
[dump
(dumper? pkthdr? bytes? . => . void?)
(_fun _dumper (_ptr i _pkthdr) _bytes -> _void)]
[dump*
(dumper? pkthdr? cpointer? . => . void?)
(_fun _dumper (_ptr i _pkthdr) _pointer -> _void)]
[dump-open
(pcap? (union string? path?) . => . dumper?) (_fun (pcap : _pcap) _string -> (ret : _dumper)
-> (std-error-handler ret (geterr pcap)))]
[dump-flush
(dumper? . => . void?) (_fun _dumper -> (ret : _int)
-> (-1_any-error-handler
ret
"Unknown error: dump-flush"
(void)))]
[dump-close
(dumper? . => . void?)
(_fun _dumper -> _void)]
[setnonblock
(pcap? boolean? . => . void?) (_fun _pcap _bool (eb : _string = (err-buf)) -> (ret : _int)
-> (begin
(-1_0_1_error-handler ret eb)
(void)))]
[getnonblock
(pcap? . => . boolean?) (_fun _pcap (eb : _string = (err-buf)) -> (ret : _int)
-> (-1_0_1_error-handler ret eb))]
[freealldevs
(pointer/c . => . void?) (_fun _pointer -> _void)]
[findalldevs
(=> (listof iface?)) (_fun (ifaces : (_ptr o _iface-list)) (eb : _string = (err-buf)) -> (ret : _int)
-> (begin (-1_0_1_error-handler ret eb)
ifaces))]
[lookupdev
(=> string?) (_fun (eb : _string = (err-buf)) -> (ret : (if (eq? (system-type) 'windows) _string/utf-16 _string))
-> (std-error-handler ret eb))]
[lookupnet
(string? . => . net?) (_fun _string (netp : (_ptr o _ip-addr)) (maskp : (_ptr o _ip-addr)) (eb : _string = (err-buf)) -> (ret : _int)
->
(let ([bad "0.0.0.0"])
(-1_any-error-handler
(if (and (equal? netp bad) (equal? maskp bad))
-1
ret)
eb
(make-net netp maskp))))]
[compile~
(pcap? string? boolean? string? . => . bpf-program?) (_fun (pcap : _pcap) (bpf : (_ptr io _bpf-program) = (make-bpf-program 0 #f)) _string _bool _ip-addr -> (ret : _int)
->
(begin
(register-finalizer bpf
freecode)
(-1_any-error-handler ret (string-append "compile-filter error: " (geterr pcap)) bpf)))]
[setfilter
(pcap? bpf-program? . => . void?) (_fun (pcap : _pcap) (_ptr i _bpf-program) -> (ret : _int)
->
(-1_any-error-handler ret (geterr pcap) (void)))]
[freecode
(bpf-program? . => . void?)
(_fun (_ptr i _bpf-program) -> _void)]
[next
(pcap? . => . packet/c)
(_fun _pcap (head : (_ptr io _pkthdr) = (make-pkthdr (make-timeval 0 0) 0 0)) -> (ret : _pointer)
->
(if ret
(make-packet head (make-sized-byte-string ret (pkthdr-caplen head)))
#f))]
[next*
(pcap? . => . packet/c)
(_fun _pcap (head : (_ptr io _pkthdr) = (make-pkthdr (make-timeval 0 0) 0 0)) -> (ret : _pointer)
->
(if ret
(make-packet head ret)
#f))]
[next-ex
(pcap? . => . packet/c) (_fun _pcap (head : (_ptr o (_ptr o _pkthdr))) (data : (_ptr o _pointer)) -> (ret : _int)
-> (next-ex-error-handler ret head data #t))]
[next-ex*
(pcap? . => . packet/c) (_fun _pcap (head : (_ptr o (_ptr o _pkthdr))) (data : (_ptr o _pointer)) -> (ret : _int)
-> (next-ex-error-handler ret head data #f))]
[datalink
(pcap? . => . symbol?)
(_fun _pcap -> _datalink)]
[list-datalinks
(pcap? . => . (listof (union symbol? false/c))) (_fun _pcap (links : (_ptr o _pointer)) -> (ret : _int)
->
(begin
(register-finalizer links free)
(cblock->list
(-1_any-error-handler ret "Unknown error: list-datalinks" links)
_datalink ret)))]
[set-datalink!
(pcap? symbol? . => . void?) (_fun _pcap _datalink -> (ret : _int)
-> (begin
(-1_0_1_error-handler ret "Unknown error: set-datalink!")
(void)))]
[datalink-name-to-val
(string? . => . symbol?)
(_fun _string -> (ret : _datalink)
-> (if (eq? ret 'DLT-INVALID)
(std-error-handler #f "Invalid datalink")
ret))]
[datalink-val-to-name
(symbol? . => . string?)
(_fun _datalink -> (ret : _string) -> (std-error-handler ret "Invalid datalink"))]
[datalink-val-to-description
(symbol? . => . string?)
(_fun _datalink -> (ret : _string) -> (std-error-handler ret "Invalid datalink"))]
[snapshot
(pcap? . => . integer?)
(_fun _pcap -> _int)]
[is-swapped
(pcap? . => . boolean?)
(_fun _pcap -> _bool)]
[major-version
(pcap? . => . integer?)
(_fun _pcap -> _int)]
[minor-version
(pcap? . => . integer?)
(_fun _pcap -> _int)]
[stats
(pcap? . => . stat?) (_fun (pcap : _pcap) (val : (_ptr io _stat) = (make-stat 0 0 0 0)) -> (ret : _int)
-> (-1_any-error-handler ret (geterr pcap) val))]
[geterr
(pcap? . => . string?)
(_fun _pcap -> _string)]
[strerror
(integer? . => . string?)
(_fun _int -> _string)]
[lib-version
(=> string?)
(_fun -> _string)]
[close
(pcap? . => . void?)
(_fun _pcap -> _void)]
))
(define open-live-secure
(opt-lambda ([device (lookupdev)] [snaplen SNAPLEN] [promisc #t] [to-ms 1])
(security-check-network-server "open-live" 0)
(open-live device snaplen promisc (if (= to-ms +inf.0) 0 to-ms))))
(define open-offline-secure
(lambda (filename)
(security-check-file "open-offline" filename
'(SCHEME-GUARD-FILE-EXISTS
SCHEME-GUARD-FILE-READ))
(open-offline filename)))
(define dump-open-secure
(lambda (pcap filename)
(security-check-file "dump-open" filename
'(SCHEME-GUARD-FILE-EXISTS
SCHEME-GUARD-FILE-WRITE))
(dump-open pcap filename)))
(define compile-filter
(opt-lambda (pcap filter [optimize #t] [netmask "0.0.0.0"])
(compile~ pcap filter optimize netmask)))
(define/contract dump-fopen
(pcap? any/c . => . void?)
(lambda (pcap FILE)
(raise (make-exn:fail:unsupported "dump-fopen is not implemented as there is no way to use a FILE *" (current-continuation-marks)))))
(define/contract dump-file
(dumper? . => . void?)
(lambda (dumper)
(raise (make-exn:fail:unsupported "dump-file is not implemented as there is no way to use a FILE *" (current-continuation-marks)))))
(define/contract file
(pcap? . => . void?)
(lambda (pcap)
(raise (make-exn:fail:unsupported "file is not implemented as there is no way to use a FILE *" (current-continuation-marks)))))
(define/contract fileno
(pcap? . => . void?)
(lambda (pcap)
(raise (make-exn:fail:unsupported "fileno is not implemented as there is no way to use a file descriptor" (current-continuation-marks)))))
(define/contract get-selectable-fd
(pcap? . => . void?)
(lambda (pcap)
(raise (make-exn:fail:unsupported "get-selectable-fd is not implemented as there is no way to use a file descriptor" (current-continuation-marks)))))
(define/contract perror
(pcap? string? . => . void?)
(lambda (pcap prefix)
(fprintf (current-error-port) "~a: ~a\n" prefix (geterr pcap))))
(define/contract
compile-nopcap
(symbol? integer? string? boolean? string? . => . bpf-program?)
(opt-lambda (datalink snaplen filter [optimize #t] [netmask "0.0.0.0"])
(let ([pcap #f])
(dynamic-wind
(lambda () (set! pcap (open-dead datalink snaplen)))
(lambda () (compile~ pcap filter optimize netmask))
(lambda () (close pcap))))))
(define (make-loop/dispatch cont-on-timeout next-ex)
(lambda (pcap count callback)
(let/cc brk-k
(let loop ([sofar 0])
(cond
[(= sofar count) sofar]
[else
(let ([packet (next-ex pcap)])
(when (packet? packet)
(callback
(packet-head packet)
(packet-data packet)
(lambda () (brk-k #f))))
(if (or (packet? packet) (and (not packet) cont-on-timeout))
(loop (if packet (add1 sofar) sofar))
sofar))])))))
(define/contract dispatch
(pcap? integer? (pkthdr? bytes? (=> false/c) . => . any) . => . (union integer? false/c))
(make-loop/dispatch #f next-ex))
(define/contract loop
(pcap? integer? (pkthdr? bytes? (=> false/c) . => . any) . => . (union integer? false/c))
(make-loop/dispatch #t next-ex))
(define/contract dispatch*
(pcap? integer? (pkthdr? cpointer? (=> false/c) . => . any) . => . (union integer? false/c))
(make-loop/dispatch #f next-ex*))
(define/contract loop*
(pcap? integer? (pkthdr? cpointer? (=> false/c) . => . any) . => . (union integer? false/c))
(make-loop/dispatch #t next-ex*))
)