File: ncap.pyx

package info (click to toggle)
ncap 1.9.2-8
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,704 kB
  • sloc: sh: 10,135; ansic: 5,829; perl: 68; makefile: 42; python: 33
file content (326 lines) | stat: -rw-r--r-- 10,105 bytes parent folder | download | duplicates (7)
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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# Python bindings for ISC's Ncap library
#
# Copyright (c) 2008 Niels Provos.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

cdef extern from "Python.h":
  ctypedef void PyObject

cdef extern from "sys/types.h":
  ctypedef unsigned size_t

cdef extern from "stdio.h":
  ctypedef void FILE

  FILE *fdopen(int filedes, char *mode)
  void fclose(FILE *fp)

cdef extern from "time.h":
  ctypedef long time_t
  struct timespec:
    time_t tv_sec
    long tv_nsec

cdef extern from "stdlib.h":
  void *malloc(int len)
  void free(void *buf)
  int sizeof()

cdef extern from "ncap.h":
    ctypedef enum ncap_np_e:
        ncap_ip4
        ncap_ip6

    ctypedef enum ncap_tp_e:
        ncap_udp
        ncap_tcp
        ncap_icmp

    ctypedef enum ncap_result_e:
        ncap_success
        ncap_failure

    ctypedef struct ncap_pvt
    ctypedef ncap_pvt *ncap_pvt_t

    ctypedef union ncap_np
    ctypedef union ncap_tp
    
    struct ncap_msg:
      timespec ts
      unsigned user1
      unsigned user2
      ncap_np_e np
      ncap_tp_e tp
      size_t paylen
      char *payload

    ctypedef ncap_msg *ncap_msg_t
    ctypedef ncap_msg *ncap_msg_ct

    ctypedef struct ncap
    ctypedef ncap *ncap_t

    ctypedef void (*ncap_callback_t)(ncap *ncap, void *ctx,
                                     ncap_msg_ct msg_ct,
                                     char *msg)

    ctypedef struct ncap:
        ncap_pvt_t pvt
        char *errstr
        ncap_result_e (*add_if)(ncap_t ncap, char *name, char *bpf,
                               int promisc, int vlans[], int vlan, int *fdes)
        ncap_result_e (*drop_if)(ncap_t ncap, int fdes)
        ncap_result_e (*add_nf)(ncap_t ncap, int fdes, char *)
        ncap_result_e (*drop_nf)(ncap_t ncap, int fdes)
        ncap_result_e (*add_pf)(ncap_t ncap, FILE *, char *)
        ncap_result_e (*drop_pf)(ncap_t ncap, FILE *)
        ncap_result_e (*add_dg)(ncap_t, int fdes, char *)
        ncap_result_e (*drop_dg)(ncap_t, int fdes)
        ncap_result_e (*filter)(ncap_t ncap, char *filter)
        ncap_result_e (*collect)(ncap_t ncap, int polling, ncap_callback_t cb,
                                 void *closure)
        ncap_result_e (*write)(ncap_t ncap, ncap_msg_ct msg, int fdes)
        ncap_result_e (*send)(ncap_t, ncap_msg_ct, int fdes, int flags)
        void (*stop)(ncap *obj)
        void (*destroy)(ncap *obj)

    ncap_t ncap_create(int maxmsg)

cdef extern from "wrap.h":
  object wrap_ncap_msg_to_python(ncap_msg_t msg)
  int wrap_python_to_ncap_msg(PyObject *src, ncap_msg_t dst)

class NCapError(Exception):
    pass

#
# Deal with the callback from collect
#

cdef void callback(ncap_t ncap, void *ctx, ncap_msg_ct msg, char *some):
  cdef object converted

  converted = wrap_ncap_msg_to_python(msg)
  
  (<object>ctx)(some, converted)

#
# The ncap interfaces are not well suited for binding to Python,
# we need to fake up a file class
#

cdef class NCapFile:
  """Convert a Python File object into a FILE object."""
  cdef FILE *_fp
  
  def __cinit__(self, file):
    self._fp = fdopen(file.fileno(), "r")
    if not self._fp:
      raise NCapError, "Cannot create file from %s" % file

  def __dealloc__(self):
    fclose(self._fp)

#
# Make NCap into a proper class
#
cdef class NCap:
    cdef ncap_t _ncap
    cdef object _files

    def __cinit__(self, maxmsg):
      """Creates an NCap instances with messages up to maxmsg bytes."""
      self._ncap = ncap_create(maxmsg)
      self._files = {}

    def __dealloc__(self):
      self._ncap.destroy(self._ncap)

    def LastError(self):
      """Returns the last encountered error string."""
      return self._ncap.errstr

    def AddIf(self, name, bpf, promisc, vlans):
      """Adds capture to the interface called "name" with the bpf filter
      "bpf". The capture is promiscuous if "promisc" is True. A list of
      VLANs can be passed in via "vlans"
      """
      cdef int fdes
      cdef ncap_result_e result
      cdef int *c_vlans
      
      c_vlans = <int *>malloc(8 * len(vlans))
      for off in range(len(vlans)):
        c_vlans[off] = vlans[off]
        
      result = self._ncap.add_if(self._ncap, name, bpf, promisc,
                                   c_vlans, len(vlans), &fdes)
      free(c_vlans)
        
      if result != ncap_success:
        raise NCapError, self._ncap.errstr

      return fdes

    def DropIf(self, fdes):
      """Drops the interface associated with the file descriptor fdes.
      Returns true on success and false otherwise."""
      cdef ncap_result_e result
      
      result = self._ncap.drop_if(self._ncap, fdes)
      return result == ncap_success

    def AddNf(self, file, label):
      """Adds the opened ncap file to the data collection.  The label is used to
      label the data stream."""
      cdef ncap_result_e result

      if self._files.has_key(file.fileno()):
        raise NCapError, "already associated fd %d" % file.fileno()

      # this has side effects, even if the add_fp fails, we have create this
      # file object and it needs to be dropped with drop_fp
      nf = NCapFile(file)
      self._files[file.fileno()] = nf

      result = self._ncap.add_nf(self._ncap, file.fileno(), label)

      return result == ncap_success

    def DropNf(self, file):
      """Drop a previously added ncap file object."""
      cdef ncap_result_e result

      if not self._files.has_key(file.fileno()):
        raise NCapError, "the fd is not associated: %d" % file.fileno()

      nf = self._files[file.fileno()]

      result = self._ncap.drop_nf(self._ncap, file.fileno())

      del self._files[file.fileno()]

      return result == ncap_success

    def AddPf(self, file, label):
      """Adds the opened pcap file to the data collection.  The label is used to
      label the data stream."""
      cdef ncap_result_e rseult

      if self._files.has_key(file.fileno()):
        raise NCapError, "already associated fd %d" % file.fileno()

      # this has side effects, even if the add_fp fails, we have create this
      # file object and it needs to be dropped with drop_fp
      nf = NCapFile(file)
      self._files[file.fileno()] = nf

      result = self._ncap.add_pf(self._ncap, <FILE*>nf._fp, label)

      return result == ncap_success

    def DropPf(self, file):
      """Drop a previously added pcap file object."""
      cdef ncap_result_e result

      if not self._files.has_key(file.fileno()):
        raise NCapError, "the fd is not associated: %d" % file.fileno()

      nf = self._files[file.fileno()]

      result = self._ncap.drop_pf(self._ncap, <FILE*>nf._fp)

      del self._files[file.fileno()]

      return result == ncap_success

    def AddDg(self, fdes, label):
      cdef ncap_result_e result

      result = self._ncap.add_dg(self._ncap, fdes, label)
      return result == ncap_success

    def DropDg(self, fdes):
      cdef ncap_result_e result

      result = self._ncap.drop_dg(self._ncap, fdes)
      return result == ncap_success

    def Filter(self, filter):
      """Installs a new pcap filter on the capture thingy.
      Returns true on success, false otherwise."""

      cdef ncap_result_e result

      result = self._ncap.filter(self._ncap, filter)
      return result == ncap_success

    def Stop(self):
      """Stops the collect loop."""

      self._ncap.stop(self._ncap)

    def Write(self, msg, file):
      """Writes the msg to the specified file.  If msg is None, an
      ncap header is output.  This should be done periodically."""
      cdef ncap_msg tmp
      cdef ncap_result_e result

      if not msg:
        result = self._ncap.write(self._ncap, NULL, file.fileno())
      else:
        if wrap_python_to_ncap_msg(<PyObject *>msg, &tmp) == -1:
          raise NCapError, "cannot convert to ncap_msg"

        result = self._ncap.write(self._ncap, &tmp, file.fileno())

      return result == ncap_success

    def Send(self, msg, fdes, flags):
      """Sends the msg to the specified socket."""
      cdef ncap_msg tmp
      cdef ncap_result_e result

      if not msg:
        result = self._ncap.send(self._ncap, NULL, fdes, flags)
      else:
        if wrap_python_to_ncap_msg(<PyObject *>msg, &tmp) == -1:
          raise NCapError, "cannot convert to ncap_msg"

        result = self._ncap.send(self._ncap, &tmp, fdes, flags)

      return result == ncap_success

    def Collect(self, polling, f):
      """Run data collection, either once if polling is set or
      until Stop() has been called.   The callback is invoked for
      each collected message."""
      cdef ncap_result_e result

      result = self._ncap.collect(self._ncap, polling,
                                  <ncap_callback_t>callback, <void*>f)

      return result == ncap_success