@@ -4,8 +4,11 @@ pub mod iterator;
44pub mod offline;
55
66use std:: {
7+ any:: Any ,
8+ convert:: TryInto ,
79 ffi:: CString ,
810 fmt, mem,
11+ panic:: { catch_unwind, resume_unwind, AssertUnwindSafe } ,
912 path:: Path ,
1013 ptr:: { self , NonNull } ,
1114 slice,
@@ -199,6 +202,39 @@ impl<T: Activated + ?Sized> Capture<T> {
199202 PacketIter :: new ( self , codec)
200203 }
201204
205+ pub fn for_each < F > ( & mut self , count : Option < usize > , handler : F ) -> Result < ( ) , Error >
206+ where
207+ F : FnMut ( Packet ) ,
208+ {
209+ let cnt = match count {
210+ // Actually passing 0 down to pcap_loop would mean read forever.
211+ // We interpret it as "read nothing", so we just succeed immediately.
212+ Some ( 0 ) => return Ok ( ( ) ) ,
213+ Some ( cnt) => cnt
214+ . try_into ( )
215+ . expect ( "count of packets to read cannot exceed c_int::MAX" ) ,
216+ None => -1 ,
217+ } ;
218+
219+ let mut handler = Handler {
220+ func : AssertUnwindSafe ( handler) ,
221+ panic_payload : None ,
222+ handle : self . handle ,
223+ } ;
224+ let return_code = unsafe {
225+ raw:: pcap_loop (
226+ self . handle . as_ptr ( ) ,
227+ cnt,
228+ Handler :: < F > :: callback,
229+ & mut handler as * mut Handler < AssertUnwindSafe < F > > as * mut u8 ,
230+ )
231+ } ;
232+ if let Some ( e) = handler. panic_payload {
233+ resume_unwind ( e) ;
234+ }
235+ self . check_err ( return_code == 0 )
236+ }
237+
202238 /// Compiles the string into a filter program using `pcap_compile`.
203239 pub fn compile ( & self , program : & str , optimize : bool ) -> Result < BpfProgram , Error > {
204240 let program = CString :: new ( program) ?;
@@ -241,6 +277,45 @@ impl<T: Activated + ?Sized> Capture<T> {
241277 }
242278}
243279
280+ // Handler and its associated function let us create an extern "C" fn which dispatches to a normal
281+ // Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
282+ // generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
283+ // pointer and the right data pointer to pcap_loop.
284+ struct Handler < F > {
285+ func : F ,
286+ panic_payload : Option < Box < dyn Any + Send > > ,
287+ handle : NonNull < raw:: pcap_t > ,
288+ }
289+
290+ impl < F > Handler < F >
291+ where
292+ F : FnMut ( Packet ) ,
293+ {
294+ extern "C" fn callback (
295+ slf : * mut libc:: c_uchar ,
296+ header : * const raw:: pcap_pkthdr ,
297+ packet : * const libc:: c_uchar ,
298+ ) {
299+ unsafe {
300+ let packet = Packet :: new (
301+ & * ( header as * const PacketHeader ) ,
302+ slice:: from_raw_parts ( packet, ( * header) . caplen as _ ) ,
303+ ) ;
304+
305+ let slf = slf as * mut Self ;
306+ let func = & mut ( * slf) . func ;
307+ let mut func = AssertUnwindSafe ( func) ;
308+ // If our handler function panics, we need to prevent it from unwinding across the
309+ // FFI boundary. If the handler panics we catch the unwind here, break out of
310+ // pcap_loop, and resume the unwind outside.
311+ if let Err ( e) = catch_unwind ( move || func ( packet) ) {
312+ ( * slf) . panic_payload = Some ( e) ;
313+ raw:: pcap_breakloop ( ( * slf) . handle . as_ptr ( ) ) ;
314+ }
315+ }
316+ }
317+ }
318+
244319impl < T : Activated > From < Capture < T > > for Capture < dyn Activated > {
245320 fn from ( cap : Capture < T > ) -> Capture < dyn Activated > {
246321 unsafe { mem:: transmute ( cap) }
@@ -920,4 +995,134 @@ mod tests {
920995 } ) ;
921996 assert_eq ! ( format!( "{}" , instr) , "1 2 3 4" ) ;
922997 }
998+
999+ #[ test]
1000+ fn read_packet_via_pcap_loop ( ) {
1001+ let _m = RAWMTX . lock ( ) ;
1002+
1003+ let mut value: isize = 777 ;
1004+ let pcap = as_pcap_t ( & mut value) ;
1005+
1006+ let test_capture = test_capture :: < Active > ( pcap) ;
1007+ let mut capture: Capture < dyn Activated > = test_capture. capture . into ( ) ;
1008+
1009+ let ctx = raw:: pcap_loop_context ( ) ;
1010+ ctx. expect ( )
1011+ . withf_st ( move |arg1, cnt, _, _| * arg1 == pcap && * cnt == -1 )
1012+ . return_once_st ( move |_, _, func, data| {
1013+ let header = raw:: pcap_pkthdr {
1014+ ts : libc:: timeval {
1015+ tv_sec : 0 ,
1016+ tv_usec : 0 ,
1017+ } ,
1018+ caplen : 0 ,
1019+ len : 0 ,
1020+ } ;
1021+ let packet_data = & [ ] ;
1022+ func ( data, & header, packet_data. as_ptr ( ) ) ;
1023+ 0
1024+ } ) ;
1025+
1026+ let mut packets = 0 ;
1027+ capture
1028+ . for_each ( None , |_| {
1029+ packets += 1 ;
1030+ } )
1031+ . unwrap ( ) ;
1032+ assert_eq ! ( packets, 1 ) ;
1033+ }
1034+
1035+ #[ test]
1036+ #[ should_panic = "panic in callback" ]
1037+ fn panic_in_pcap_loop ( ) {
1038+ let _m = RAWMTX . lock ( ) ;
1039+
1040+ let mut value: isize = 777 ;
1041+ let pcap = as_pcap_t ( & mut value) ;
1042+
1043+ let test_capture = test_capture :: < Active > ( pcap) ;
1044+ let mut capture: Capture < dyn Activated > = test_capture. capture . into ( ) ;
1045+
1046+ let ctx = raw:: pcap_loop_context ( ) ;
1047+ ctx. expect ( )
1048+ . withf_st ( move |arg1, cnt, _, _| * arg1 == pcap && * cnt == -1 )
1049+ . return_once_st ( move |_, _, func, data| {
1050+ let header = raw:: pcap_pkthdr {
1051+ ts : libc:: timeval {
1052+ tv_sec : 0 ,
1053+ tv_usec : 0 ,
1054+ } ,
1055+ caplen : 0 ,
1056+ len : 0 ,
1057+ } ;
1058+ let packet_data = & [ ] ;
1059+ func ( data, & header, packet_data. as_ptr ( ) ) ;
1060+ 0
1061+ } ) ;
1062+
1063+ let ctx = raw:: pcap_breakloop_context ( ) ;
1064+ ctx. expect ( )
1065+ . withf_st ( move |arg1| * arg1 == pcap)
1066+ . return_once_st ( move |_| { } ) ;
1067+
1068+ capture
1069+ . for_each ( None , |_| panic ! ( "panic in callback" ) )
1070+ . unwrap ( ) ;
1071+ }
1072+
1073+ #[ test]
1074+ fn for_each_with_count ( ) {
1075+ let _m = RAWMTX . lock ( ) ;
1076+
1077+ let mut value: isize = 777 ;
1078+ let pcap = as_pcap_t ( & mut value) ;
1079+
1080+ let test_capture = test_capture :: < Active > ( pcap) ;
1081+ let mut capture: Capture < dyn Activated > = test_capture. capture . into ( ) ;
1082+
1083+ let ctx = raw:: pcap_loop_context ( ) ;
1084+ ctx. expect ( )
1085+ . withf_st ( move |arg1, cnt, _, _| * arg1 == pcap && * cnt == 2 )
1086+ . return_once_st ( move |_, _, func, data| {
1087+ let header = raw:: pcap_pkthdr {
1088+ ts : libc:: timeval {
1089+ tv_sec : 0 ,
1090+ tv_usec : 0 ,
1091+ } ,
1092+ caplen : 0 ,
1093+ len : 0 ,
1094+ } ;
1095+ let packet_data = & [ ] ;
1096+ func ( data, & header, packet_data. as_ptr ( ) ) ;
1097+ func ( data, & header, packet_data. as_ptr ( ) ) ;
1098+ 0
1099+ } ) ;
1100+
1101+ let mut packets = 0 ;
1102+ capture
1103+ . for_each ( Some ( 2 ) , |_| {
1104+ packets += 1 ;
1105+ } )
1106+ . unwrap ( ) ;
1107+ assert_eq ! ( packets, 2 ) ;
1108+ }
1109+
1110+ #[ test]
1111+ fn for_each_with_count_0 ( ) {
1112+ let _m = RAWMTX . lock ( ) ;
1113+
1114+ let mut value: isize = 777 ;
1115+ let pcap = as_pcap_t ( & mut value) ;
1116+
1117+ let test_capture = test_capture :: < Active > ( pcap) ;
1118+ let mut capture: Capture < dyn Activated > = test_capture. capture . into ( ) ;
1119+
1120+ let mut packets = 0 ;
1121+ capture
1122+ . for_each ( Some ( 0 ) , |_| {
1123+ packets += 1 ;
1124+ } )
1125+ . unwrap ( ) ;
1126+ assert_eq ! ( packets, 0 ) ;
1127+ }
9231128}
0 commit comments