Skip to content

Commit 043fde3

Browse files
committed
New distribution [0.15.2]
* added register interface to protocols for extensibility * revised pcapkit.foundation.analysis, now supports extensibility through register function * minor bugfix in traceflow for consistency in format and output
1 parent 6b5ac99 commit 043fde3

File tree

10 files changed

+124
-75
lines changed

10 files changed

+124
-75
lines changed

doc/sphinx/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
author = 'Jarry Shaw'
2323

2424
# The full version, including alpha/beta/rc tags
25-
release = '0.15.1.post1'
25+
release = '0.15.2'
2626

2727

2828
# -- General configuration ---------------------------------------------------

pcapkit/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from pcapkit.interface import JSON, PLIST, TREE
1616

1717
#: version number
18-
__version__ = '0.7.0'
18+
__version__ = '0.15.2'
1919

2020

2121
def get_parser():

pcapkit/foundation/analysis.py

Lines changed: 43 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
corresponding modules and functions to extract the attributes.
88
99
"""
10+
import importlib
1011
import os
1112

1213
from pcapkit.protocols.raw import Raw
@@ -21,6 +22,13 @@
2122

2223
__all__ = ['analyse']
2324

25+
#: List of protocols supported by the analyser.
26+
ANALYSE_PROTO = [
27+
('pcapkit.protocols.application.ftp', 'FTP'),
28+
('pcapkit.protocols.application.httpv1', 'HTTPv1'),
29+
('pcapkit.protocols.application.httpv2', 'HTTPv2'),
30+
]
31+
2432

2533
def analyse(file, length=None, *, termination=False):
2634
"""Analyse application layer packets.
@@ -44,23 +52,22 @@ def analyse(file, length=None, *, termination=False):
4452
4553
and :class:`~pcapkit.protocols.raw.Raw` as the fallback result.
4654
55+
See Also:
56+
The analysis processes order is defined by :data:`~pcapkit.foundation.analysis.ANALYSE_PROTO`.
57+
4758
"""
4859
seekset = file.tell()
4960
if not termination:
50-
# FTP analysis
51-
flag, ftp = _analyse_ftp(file, length, seekset=seekset)
52-
if flag:
53-
return ftp
61+
for (module, name) in ANALYSE_PROTO:
62+
try:
63+
protocol = getattr(importlib.import_module(module), name)
64+
except (ImportError, AttributeError):
65+
continue
5466

55-
# HTTP/1.* analysis
56-
flag, http = _analyse_httpv1(file, length, seekset=seekset)
57-
if flag:
58-
return http
59-
60-
# HTTP/2 analysis
61-
flag, http = _analyse_httpv2(file, length, seekset=seekset)
62-
if flag:
63-
return http
67+
packet = _analyse(protocol, file, length, seekset=seekset)
68+
if packet is None:
69+
continue
70+
return packet
6471

6572
# backup file offset
6673
file.seek(seekset, os.SEEK_SET)
@@ -70,75 +77,46 @@ def analyse(file, length=None, *, termination=False):
7077

7178

7279
@seekset_ng
73-
def _analyse_httpv1(file, length=None, *, seekset=os.SEEK_SET): # pylint: disable=unused-argument
74-
"""Analyse HTTP/1.* packet.
80+
def _analyse(protocol, file, length=None, *, seekset=os.SEEK_SET): # pylint: disable=unused-argument
81+
"""Analyse packet.
7582
7683
Args:
84+
protocol (Protocol): target protocol class
7785
file (io.BytesIO): source data stream
7886
length (Optional[int]): packet length
7987
8088
Keyword Args:
8189
seekset (int): original file offset
8290
8391
Returns:
84-
Tuple[bool, Optional[HTTPv1]]: If the packet is HTTP/1.*,
85-
returns :data:`True` and parsed HTTP/1.* packet; otherwise
86-
returns :data:`False` and :data:`None`.
92+
Optional[Protocol]: If the packet is parsed successfully,
93+
returns the parsed packet; otherwise returns :data:`None`.
8794
8895
"""
8996
try:
90-
from pcapkit.protocols.application.httpv1 import HTTPv1
91-
http = HTTPv1(file, length)
97+
packet = protocol(file, length)
9298
except ProtocolError:
93-
return False, None
94-
return True, http
99+
packet = None
100+
return packet
95101

96102

97-
@seekset_ng
98-
def _analyse_httpv2(file, length, *, seekset=os.SEEK_SET): # pylint: disable=unused-argument
99-
"""Analyse HTTP/2 packet.
103+
def register(module, class_, *, index=None):
104+
"""Register a new protocol class.
100105
101-
Args:
102-
file (io.BytesIO): source data stream
103-
length (Optional[int]): packet length
106+
Arguments:
107+
module (str): module name
108+
class_ (str): class name
104109
105-
Keyword Args:
106-
seekset (int): original file offset
107-
108-
Returns:
109-
Tuple[bool, Optional[HTTPv1]]: If the packet is HTTP/2,
110-
returns :data:`True` and parsed HTTP/2 packet; otherwise
111-
returns :data:`False` and :data:`None`.
112-
113-
"""
114-
try:
115-
from pcapkit.protocols.application.httpv2 import HTTPv2
116-
http = HTTPv2(file, length)
117-
except ProtocolError:
118-
return False, None
119-
return True, http
110+
Keyword Arguments:
111+
index (Optional[int]): Index of the protocol class
112+
when inserted to :data:`~pcapkit.foundation.analysis.ANALYSE_PROTO`.
120113
121-
122-
@seekset_ng
123-
def _analyse_ftp(file, length, *, seekset=os.SEEK_SET): # pylint: disable=unused-argument
124-
"""Analyse FTP packet.
125-
126-
Args:
127-
file (io.BytesIO): source data stream
128-
length (Optional[int]): packet length
129-
130-
Keyword Args:
131-
seekset (int): original file offset
132-
133-
Returns:
134-
Tuple[bool, Optional[HTTPv1]]: If the packet is FTP,
135-
returns :data:`True` and parsed FTP packet; otherwise
136-
returns :data:`False` and :data:`None`.
114+
Notes:
115+
The full qualified class name of the new protocol class
116+
should be as ``{module}.{class_}``.
137117
138118
"""
139-
try:
140-
from pcapkit.protocols.application.ftp import FTP
141-
ftp = FTP(file, length)
142-
except ProtocolError:
143-
return False, None
144-
return True, ftp
119+
if index is None:
120+
ANALYSE_PROTO.append((module, class_))
121+
else:
122+
ANALYSE_PROTO.insert(index, (module, class_))

pcapkit/foundation/traceflow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ def make_fout(fout='./tmp', fmt='pcap'):
126126
FileExists: If ``fout`` exists and ``fmt`` is **NOT** :data:`None`.
127127
128128
"""
129+
if fout is None:
130+
fout = './tmp'
131+
129132
if fmt == 'pcap': # output PCAP file
130133
from pcapkit.dumpkit import PCAP as output
131134
elif fmt == 'plist': # output PLIST file

pcapkit/protocols/internet/internet.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,26 @@ def layer(self):
6161
"""
6262
return self.__layer__
6363

64+
##########################################################################
65+
# Methods.
66+
##########################################################################
67+
68+
@classmethod
69+
def register(cls, code, module, class_):
70+
"""Register a new protocol class.
71+
72+
Arguments:
73+
code (int): protocol code as in :class:`~pcapkit.const.reg.transtype.TransType`
74+
module (str): module name
75+
class_ (str): class name
76+
77+
Notes:
78+
The full qualified class name of the new protocol class
79+
should be as ``{module}.{class_}``.
80+
81+
"""
82+
cls.__proto__[code] = (module, class_)
83+
6484
##########################################################################
6585
# Utilities.
6686
##########################################################################
@@ -168,7 +188,10 @@ def _import_next_layer(self, proto, length=None, *, version=4, extension=False):
168188
from pcapkit.protocols.raw import Raw as protocol # pylint: disable=import-outside-toplevel
169189
else:
170190
module, name = self.__proto__[proto]
171-
protocol = getattr(importlib.import_module(module), name)
191+
try:
192+
protocol = getattr(importlib.import_module(module), name)
193+
except (ImportError, AttributeError):
194+
from pcapkit.protocols.raw import Raw as protocol # pylint: disable=import-outside-toplevel
172195

173196
next_ = protocol(self._file, length, version=version, extension=extension,
174197
error=self._onerror, layer=self._exlayer, protocol=self._exproto)

pcapkit/protocols/link/link.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ def layer(self):
5353
"""
5454
return self.__layer__
5555

56+
##########################################################################
57+
# Methods.
58+
##########################################################################
59+
60+
@classmethod
61+
def register(cls, code, module, class_):
62+
"""Register a new protocol class.
63+
64+
Arguments:
65+
code (int): protocol code as in :class:`~pcapkit.const.reg.ethertype.EtherType`
66+
module (str): module name
67+
class_ (str): class name
68+
69+
Notes:
70+
The full qualified class name of the new protocol class
71+
should be as ``{module}.{class_}``.
72+
73+
"""
74+
cls.__proto__[code] = (module, class_)
75+
5676
##########################################################################
5777
# Utilities.
5878
##########################################################################
@@ -110,7 +130,10 @@ def _import_next_layer(self, proto, length=None):
110130
from pcapkit.protocols.raw import Raw as protocol # pylint: disable=import-outside-toplevel
111131
else:
112132
module, name = self.__proto__[proto]
113-
protocol = getattr(importlib.import_module(module), name)
133+
try:
134+
protocol = getattr(importlib.import_module(module), name)
135+
except (ImportError, AttributeError):
136+
from pcapkit.protocols.raw import Raw as protocol # pylint: disable=import-outside-toplevel
114137

115138
next_ = protocol(self._file, length, error=self._onerror,
116139
layer=self._exlayer, protocol=self._exproto)

pcapkit/protocols/pcap/frame.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ def length(self):
7979
# Methods.
8080
##########################################################################
8181

82+
@classmethod
83+
def register(cls, code, module, class_):
84+
"""Register a new protocol class.
85+
86+
Arguments:
87+
code (int): protocol code as in :class:`~pcapkit.const.reg.linktype.LinkType`
88+
module (str): module name
89+
class_ (str): class name
90+
91+
Notes:
92+
The full qualified class name of the new protocol class
93+
should be as ``{module}.{class_}``.
94+
95+
"""
96+
cls.__proto__[code] = (module, class_)
97+
8298
def index(self, name):
8399
"""Call :meth:`ProtoChain.index <pcapkit.corekit.protochain.ProtoChain.index>`.
84100
@@ -411,7 +427,11 @@ def _import_next_layer(self, proto, length, error=False): # pylint: disable=arg
411427
412428
"""
413429
module, name = self.__proto__[int(proto)]
414-
protocol = getattr(importlib.import_module(module), name)
430+
try:
431+
protocol = getattr(importlib.import_module(module), name)
432+
except (ImportError, AttributeError):
433+
from pcapkit.protocols.raw import Raw as protocol # pylint: disable=import-outside-toplevel
434+
415435
next_ = protocol(self._file, length, error=error,
416436
layer=self._exlayer, protocol=self._exproto)
417437
return next_

pcapkit/utilities/decorators.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,20 @@ def seekset_ng(func):
6363
Note:
6464
The decorated function should have following signature::
6565
66-
func(file, *args, seekset=os.SEEK_SET, **kw)
66+
func(protocol, file, *args, seekset=os.SEEK_SET, **kw)
67+
68+
c.f. :func:`pcapkit.foundation.analysis._analyse`.
6769
6870
See Also:
6971
:mod:`pcapkit.foundation.analysis`
7072
7173
:meta decorator:
7274
"""
7375
@functools.wraps(func)
74-
def seekcur(file, *args, seekset=os.SEEK_SET, **kw): # pylint: disable=redefined-outer-name
76+
def seekcur(protocol, file, *args, seekset=os.SEEK_SET, **kw): # pylint: disable=redefined-outer-name
7577
# seek_cur = file.tell()
7678
file.seek(seekset, os.SEEK_SET)
77-
return_ = func(file, *args, seekset=seekset, **kw)
79+
return_ = func(protocol, file, *args, seekset=seekset, **kw)
7880
# file.seek(seek_cur, os.SEEK_SET)
7981
return return_
8082
return seekcur

pcapkit/vendor/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pcapkit.vendor import __all__ as vendor_all
1212

1313
#: version string
14-
__version__ = '0.15.1.post1'
14+
__version__ = '0.15.2'
1515

1616

1717
def get_parser():

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sys
55

66
# version string
7-
__version__ = '0.15.1.post1'
7+
__version__ = '0.15.2'
88

99
# README
1010
with open('README.md', encoding='utf-8') as file:

0 commit comments

Comments
 (0)