File: sniff

package info (click to toggle)
newlisp 10.7.5-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 6,248 kB
  • sloc: ansic: 33,280; lisp: 4,181; sh: 609; makefile: 215
file content (283 lines) | stat: -rwxr-xr-x 8,561 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#!/usr/bin/env newlisp

; sniff  v.1.23 - net work packet snifffer
; 2010-March-26 added out-commented callback method
; 2010-April-10 documentation
; 2010-April-27 added 'packet' output format for whole packet
;               added output of unknown packets
; 2010-April-29 help text improvements
; 2010-May-29 added library path for OpenBSD 4.6 
; 2013-Sep-22 change stats to pstats (stats is function since 10.4.2)
;
; See also http://www.tcpdump.org/pcap.htm
; tested on Mac OS X 10.6 and UBUNTU 9.4 and Win32
; for a Win32 library goto http://www.winpcap.org/
; first version L.M.in March 2010
;
; This script must be run with super user prigileges.

(unless (main-args 2) 
	(println [text]
- sniff - network packet sniffer, version 1.21, April 2010

USAGE UNIX: 
    sudo sniff <device> [<filter> [<count> [hex | ascii | packet]]] 

USAGE Win32:
    newlisp sniff <device> [<filter> [<count> [hex | ascii | packet]]]   

The program must run in super user or administrator mode. On Unix the 
sudo utility can be used, and the OS will ask for the root password.

EXAMPLES: 
    sniff eth01 'tcp port 80' 100 hex
    sniff en1 ip 100 packet           <-- best all purpose format
    sniff eth0 'ip host 10.1.2.3'
    sniff en0
    sniff ""
    
Use an empty string "" or '' for <device> to let sniff find out a device.
This works well on Windows but frequently does not work on UNIX, where
it is better to use the ifconfig utility to find a connected device. 

The 'hex' option dumps data in HEX and ASCII, the 'ascii' option only in 
ASCII format. Both options only dump the data part of the packet after the 
TCP, UDP or frst 8 bytees of the ICMP header. The 'packet' option dumps
the whole packet starting with the IP header and in HEX and ASCII format. 

Default <filter> expression is 'ip' for all protocols. The protocol ids:
tcp, udp and icmp are supported. Default packet <count> is 10 and 
default output mode is no output.
[/text])
	(exit)
)

; import pcap library

(set 'bit-64 (= 256 (& (sys-info -1) 256)))

(set 'files '(
	"/usr/lib/libpcap.dylib"   ; Mac OS X 10.6
	"/usr/lib/libpcap.so.0.8"  ; UBUNTU 9.04
	"/usr/lib/libpcap.so.1.0.0"; UBUNTU 9.04
	"/usr/lib/libpcap.so.6.0"  ; OpenBSD 4.8
	"c:/windows/system32/wpcap.dll" ; Win32 XP
))

(set 'libpcap (files (or
			(find true (map file? files))
			(throw-error "cannot find  pcap packet capture library"))))

; import library functions
(import libpcap "pcap_lookupdev")
(import libpcap "pcap_lookupnet")
(import libpcap "pcap_open_live")
(import libpcap "pcap_compile")
(import libpcap "pcap_setfilter")
(import libpcap "pcap_next")
(import libpcap "pcap_loop")
(import libpcap "pcap_stats")

; allocate ptr varables used in library API
(set 'errbuf (dup "\000" 256)) ; for error message
(set 'fp (dup "\000" 8)) ; sizeof pointer (32 and 64 bit)
(set 'net "\000\000\000\000") ; IP number of sniffing device
(set 'mask "\000\000\000\000") ; net mask of sniffing device
(set 'header (dup "\000" 24)) ; space for packet header (32 and 64 bit)
(set 'protocols '((6 TCP) (17 UDP) (1 ICMP) (0 IP)))

; constants
(constant 'PCAP_BUFFSIZE 1518)
(constant 'PROMISCUOUS 1)
(constant 'SIZE_ETHER 14) 

; get device
(set 'dev (main-args 2))
(when (= dev "")
	(set 'dev (pcap_lookupdev errbuf)))

(when (zero? dev)
	(println errbuf)
	(println "must exit")
	(exit 1))

(when (= (pcap_lookupnet dev net mask errbuf) -1)
	(println (get-string errbuf))
	(println "must exit")
	(exit 1))

; get filter expression
(if (main-args 3)
	(set 'filter-exp (lower-case (main-args 3)))
	(set 'filter-exp "ip")) ; default is all packets

; get packet count
(set 'packet-count (int (main-args 4) 10)) ; default 10

; get output options
(set 'out-opt (or (main-args 5) "none"))	

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(set 'handle (pcap_open_live dev PCAP_BUFFSIZE PROMISCUOUS 1000 errbuf))
(when (= handle 0)
	(println (get-string errbuf))
	(println "must exit")
	(exit 2))

(when (= (pcap_compile handle fp filter-exp 0 net) -1)
	(println "could not compile filter:" filter-exp)
	(println "must exit")
	(exit 3))

(when (= (pcap_setfilter handle fp) -1)
	(println "could not install filter:" filter-exp)
	(println "must exit")
	(exit 4))

; transform ip number to n.n.n.n
(define (ip-to-ascii ip)
	(join (map string (unpack "bbbb" (pack ">lu" ip))) "."))
	
; tansform mac address to n:n::n:n:n:n
(define (eth-to-ascii host)
	(join (map (fn (x) (format "%02x" x)) (unpack "bbbbbb" host) ) ":"))

; packet handler, called when packet arrives
(define (report-packet header packet)

	(if bit-64
		(map set '(tm usec dummy caplen len)  (unpack "Lu lu lu lu lu" header))
		(map set '(tm usec caplen len)  (unpack "lu lu lu lu" header)))
	(println "time: " (date tm 0 "%H:%M:%S.") usec " captured:" caplen " wire:" len)

	; get pcap header
	(map set '(dhost shost type)  (unpack "s6 s6 d" packet))
	(println "from ether addr: " (eth-to-ascii shost) 
			" to: " (eth-to-ascii dhost))

	; IP header at p + SIZE_ETHER: sniff_ip
	(map set '(vhl tos ip-len id off ttl proto sum src dest) 
		(unpack "> b b u u u b b u lu lu" (+ packet SIZE_ETHER)))
	(set 'IP_HL (& vhl 0x0f))
	(set 'IP_V (>> vhl 4))

	(when (or (< IP_HL 5) (< ip-len (* IP_HL 4)))
		(println "ERROR malformed IP datagram")
		(throw true))

	(set 'protocol (lookup proto protocols))

	(if ; with multiple <condition> <body> terms
	; TCP header: sniff_tcp
	(= protocol 'TCP) ; TCP 6
	(begin
		(map set '(sport dport seq ack offx2) 
			(unpack "> u u lu lu b" (+ packet SIZE_ETHER (* IP_HL 4))))
		(set 'TH_OFF  (>> (& offx2 0xf0) 4)) ; * 4 -> offset
		(when (< TH_OFF 5)
			(println "ERROR malformed TCP segment")
			(throw true))

		(println protocol " from ip addr: " (ip-to-ascii src) " port:" sport
                 " to: " (ip-to-ascii dest) " port:" dport)
	)

	(= protocol 'UDP) ; UDP 17
	(begin
		(map set '(sport dport ulen sum) 
			(unpack "> u u u u" (+ packet SIZE_ETHER (* IP_HL 4))))
		(set 'TH_OFF 2) ; 2*4=8 bytes of UDP header length

		(println protocol " from " (ip-to-ascii src) " port:" sport
                 " to " (ip-to-ascii dest) " port:" dport)
	)

	(= protocol 'ICMP) ; ICMP 1
	(begin
		(map set '(type code cksum other) 
			(unpack "> b b u lu" (+ packet SIZE_ETHER (* IP_HL 4))))
		(set 'TH_OFF 2) ; 2*4=8 bytes of ICMP header length
		(println protocol 
			" from: " (ip-to-ascii src) " to: " (ip-to-ascii dest)) 
		(println "type: " type " code: " code  " checksum: " cksum)
	)

	true ; catch all other
	(begin
		(set 'TH_OFF 0)
		(print "Protocol: " proto
			" from: " (ip-to-ascii src) " to: " (ip-to-ascii dest)) )
	) ; end if

	; dump payload
	;(set 'ip-len (min len (- caplen SIZE_ETHER)))
	(set 'payload-len (- ip-len (* IP_HL 4) (* TH_OFF 4)))
	(print "packet total len w/o ether-header:" ip-len)
	(println ", payload length:" payload-len)

	(when (and (zero? payload-len) (!= out-opt "packet"))
		(throw true))

	(if (= out-opt "packet")
		(set 'data (unpack (dup "b" ip-len) 
			(+ packet SIZE_ETHER )))
		(set 'data (unpack (dup "b" payload-len) 
			(+ packet SIZE_ETHER (* IP_HL 4) (* TH_OFF 4))))
	)

	; dump data as hex and ascii
	(when (or (= out-opt "hex") (= out-opt "packet"))
		(set 'addr 0)
		(dolist (line (explode data 16) )
			(print (format "%05d " addr))
			(println (format "%-48s" (join (map (fn (b) (format "%02x " b)) line))) " "
				(join (map (fn (c) (if (or (< c 32) (> c 126)) "." (char c))) line)))
		(inc addr 16))
	)

	; dump ascii only
	(when (= out-opt "ascii")
		(dolist (line (explode data 64) )
			(println (join (map (fn (c) (if (or (< c 32) (> c 126)) "." (char c))) line)))
		)
	)

	true
)

(set 'counter 0)
(println "device: " (get-string dev))

; Method using pcap_next and newLISP controlling
; this method leves more time in newLISP to do 
; stuff in the while loop.
(while (< counter packet-count)
	(set 'capture '())
	(set 'packet (pcap_next handle header))
	(unless (= packet 0)
		(print "\n--- " (inc counter) "---  ")
		(catch (report-packet header packet ))
))

; Alternative method using callback with pcap_loop
; and pcap library controlling. This leaves less
; time for newLISP as the waiting for new packets
; happens inside libpcap
;(define (callback-handler params header packet)
;	(unless (= packet 0)
;		(print "\n--- " (inc counter) "---  ")
;		(catch (report-packet header packet ))
;))
;(pcap_loop handle 20 (callback 0 'callback-handler) 0)


; report pcap status
(set 'pstats (dup "\000" 8))
(pcap_stats handle pstats)
(map set '(received dropped) (unpack "lu lu" pstats))
(println "\npackages dropped: " dropped)

(exit)

; eof