Skip to content

Commit a4868ee

Browse files
committed
chore: mirror serial2pcap in case it goes away
1 parent 951b4b1 commit a4868ee

25 files changed

+2045
-0
lines changed
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Disclaimer of Warranty
2+
This Work is provided "as is". 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 United States Government 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 Work, even if advised of the possibility of such damage.
3+
4+
The User of this Work agrees to hold harmless and indemnify the United States Government, its agents and employees from every claim or liability (whether in tort or in contract), including attorneys' fees, court costs, and expenses, arising in direct consequence of Recipient's use of the item, including but not limited to, claims or liabilities made for injury to or death of personnel of User or third parties, damage to or destruction of property of User or third parties, infringement or other violations of intellectual property or technical data rights.
5+
6+
Nothing in this Work is intended to constitute an endorsement, explicit or implied, by the United States Government of any particular manufacturer's product or service.
7+
8+
## Disclaimer of Endorsement
9+
Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise, in this Work does not constitute an endorsement, recommendation, or favoring by the United States Government and shall not be used for advertising or product endorsement purposes.

tools/mirrored/serial2pcap/HASHES

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
MD5 SHA-1
2+
-------------------------------------------------------------------------
3+
64ece21dd9b0da617238fcd98c6b344d 24e2f0a0becabb3dfaf8ea88614d898f76dafc2f serial2pcap-release\Lib\BlankConsumer.py
4+
fe3544e4a53fd20bbb0524d9b92abebe 4233c9efec69710bbdbfb7aba300792f28f61fe9 serial2pcap-release\Lib\BufferOutput.py
5+
c14022bc04c8f5a1351f14115a9afc0e 1947a0376c2ab1e29a2a61ab35ba35153d173740 serial2pcap-release\Lib\CaptureDataBuffer.py
6+
337e4d83c00b2033583205f03086d560 db3778238c777bdebf7beb672a0f9cbe1d80cff2 serial2pcap-release\Lib\DumpTimeFile.py
7+
213b491b7500e9c8319246468dfbfc40 1f6f6dd57f52d542d96ac4f24168595a99de48df serial2pcap-release\Lib\SerialPortReader.py
8+
67a6211a8d90e98865e949206b85c2e7 8076566896c949c800e0772de163dc1d86344961 serial2pcap-release\Lib\util.py
9+
d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 serial2pcap-release\Lib\__init__.py
10+
183e0af194aedc8721d5d3eb8a1c896b 8a980c7c8fae69a5345e5b7c10d42432701436e6 serial2pcap-release\libserial2pcap.py
11+
b90fbc29ace5fdcea1d4b2a1d8807397 c2582cc0d2a5c50db3c132342c088386415f0131 serial2pcap-release\libserial2pcapunittest.py
12+
c994c906fc62bd2bffe342b01935cfc6 48d58a55d5ed3518441667cd0be5c76089d9e284 serial2pcap-release\Plugins\Byte.py
13+
37ffd33b191ebecb6a9952a4ddf470a4 dc5b0762171a4376b7d641d8e99320d57e507109 serial2pcap-release\Plugins\df1.py
14+
58f6f0d2ab98e348b01d72a1baae8209 fd82bbf331b9a0a40f2ae97fe002a352e0428ea2 serial2pcap-release\Plugins\DNP3.py
15+
e850bed0ea08debf8a6ef46dd0b23d9c 8250ebe4eabdb5001f657b4c5c929d47cafe2198 serial2pcap-release\Plugins\ModbusASCII.py
16+
d99c88249f2df4150525a37c6c8be03a 481fede6ba631136c4d7631bb44dcd8db2f33591 serial2pcap-release\Plugins\ModbusRTU.py
17+
f13f39d70ae5faaba7eebb9b292defed 6d2f406cd0c6277d13bf19674949a7c05eb4ab56 serial2pcap-release\Plugins\PluginCore.py
18+
a2efadba5e741d2f12481e0fb25ecbcb 33721025d6787a3ea743a8b6501e7640a4200977 serial2pcap-release\Plugins\SELFM.py
19+
4b53bdbb95102b2240a8bfde16a24c55 ea2f6c02fbad6d493931653c02064b75014b83fe serial2pcap-release\Plugins\SLIP.py
20+
d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 serial2pcap-release\Plugins\__init__.py
21+
1c136b47f801afbfef6f00c91a4e20a9 5b84aba38967e3b2a9eb8f1e0b870e050628c094 serial2pcap-release\serial2pcap.py
22+
b5fefb987fded860ac6650e59a944143 9e013796c25fae0be2078676c23defcefc893dd6 serial2pcap-release\USAGE
23+
3cf918272ffa5de195752d73f3da3e5e 7959c969e092f2a5a8604e2287807ac5b1b384ad serial2pcap-release\VERSION

tools/mirrored/serial2pcap/LICENSE.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This Work was prepared by a United States Government employee and, therefore, is excluded from copyright by Section 105 of the Copyright Act of 1976.
2+
3+
Copyright and Related Rights in the Work worldwide are waived through the [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) [Universal license](https://creativecommons.org/publicdomain/zero/1.0/legalcode).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/python
2+
3+
import time
4+
import os
5+
import signal
6+
7+
def BlankConsumer(datastorage, stopevent):
8+
while not stopevent.isSet():
9+
if len(datastorage.raw) > 1:
10+
datastorage.pop_front(1)
11+
else:
12+
datastorage.newdata.clear()
13+
#let other threads exit
14+
time.sleep(1)
15+
#simulate a ctrl-c event so that the higher level thread will exit
16+
os.kill(os.getpid(), signal.SIGINT)
17+
#need to break so that this thread doesn't try to catch the keyboard interrupt
18+
break
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#!/usr/bin/python
2+
3+
import time
4+
import struct
5+
import logging
6+
import traceback
7+
8+
from CaptureDataBuffer import FileNameTracking
9+
10+
def MakePCAPHeader(filehandle, datalinktype):
11+
magic_number = struct.pack("I", int(0xa1b2c3d4))
12+
version_major = struct.pack("H", 2)
13+
version_minor = struct.pack("H", 4)
14+
thiszone = struct.pack("I", 0)
15+
sigfigs = struct.pack("I", 0)
16+
snaplen = struct.pack("I", 65535)
17+
network = struct.pack("I", datalinktype)
18+
19+
filehandle.write(magic_number)
20+
filehandle.write(version_major)
21+
filehandle.write(version_minor)
22+
filehandle.write(thiszone)
23+
filehandle.write(sigfigs)
24+
filehandle.write(snaplen)
25+
filehandle.write(network)
26+
27+
filehandle.flush()
28+
29+
#This function takes some data and creates a packet entry within a PCAP file.
30+
#filehandle is a file handle to an open PCAP file
31+
#data is the data to be written
32+
#CallBackFunction is a function that can intercept the data and return data to be added to the packet - it can also modify the data if it wants
33+
def MakePCAPEntry(filehandle, data, CallBackFunction):
34+
databuffer = ""
35+
36+
for byte in data:
37+
databuffer += byte
38+
39+
#retrieve the capture time from the last byte in the array
40+
ftime = data[len(data)-1].GetTime()
41+
42+
#do timing calculations to separate seconds from ms
43+
seconds = int(ftime)
44+
mseconds = int((ftime - int(ftime)) * 1000000)
45+
46+
#run the callback function - this allows programs to intercept the output before its written to disk
47+
callbackdata = CallBackFunction(seconds, mseconds, databuffer, None)
48+
49+
#Build the packet header within the PCAP file - this includes the time stamp and length
50+
ts_sec = struct.pack("I", seconds)
51+
ts_usec = struct.pack("I", mseconds)
52+
incl_len = struct.pack("I", len(data) + len(callbackdata))
53+
orig_len = struct.pack("I", len(data) + len(callbackdata))
54+
55+
#write the packet header to disk
56+
filehandle.write(ts_sec)
57+
filehandle.write(ts_usec)
58+
filehandle.write(incl_len)
59+
filehandle.write(orig_len)
60+
61+
#if there is any callback data then write it to disk
62+
if callbackdata is not None:
63+
filehandle.write(callbackdata)
64+
65+
#finally write the actual data to disk
66+
filehandle.write(databuffer)
67+
68+
#flush the data to make sure it hits disk
69+
filehandle.flush()
70+
71+
#this function is used when used with libserial2pcap - it is responsible for taking identified packets from BufferOutput and storing them in a shared PCAP buffer
72+
#pcapbuffer - internal buffer used to store processed packets and header information
73+
#data - actual packet data that will be stored in the pcapbuffer
74+
#CallBackFunction - function pointer from a processing plugin that needs to be called before storing data
75+
#datalinktype - the data link type as provided from the processing plugin
76+
def AddToPCAPBuffer(pcapbuffer, data, CallBackFunction, datalinktype):
77+
databuffer = ""
78+
79+
for byte in data:
80+
databuffer += byte
81+
82+
#retrieve the capture time from the last byte in the array
83+
ftime = data[len(data)-1].GetTime()
84+
85+
#do timing calculations to separate seconds from ms
86+
seconds = int(ftime)
87+
mseconds = int((ftime - int(ftime)) * 1000000)
88+
89+
#run the callback function - this allows programs to intercept the output before its written to disk
90+
callbackdata = CallBackFunction(seconds, mseconds, databuffer, None)
91+
92+
#create the packetdata structure
93+
packetdata = {}
94+
95+
packetdata["timestamp_s"] = seconds
96+
packetdata["timestamp_ms"] = mseconds
97+
packetdata["length"] = len(callbackdata)
98+
packetdata["dlt"] = datalinktype
99+
100+
pcapbuffer.lock.acquire()
101+
pcapbuffer.raw.append({"pkthdr": packetdata, "packet": callbackdata + databuffer})
102+
pcapbuffer.lock.release()
103+
104+
def BufferOutput(datastorage, outfilename, plugin, stopevent, debug, pcapbuffer):
105+
logger = logging.getLogger("serial2pcap").getChild(__name__)
106+
if debug:
107+
logger.setLevel(logging.DEBUG)
108+
109+
#this should check to make sure that either outfilename or (xor) pcap buffer are used
110+
#logic further down relies on one or the other being set
111+
if outfilename is not None and pcapbuffer is not None:
112+
raise RuntimeError("Can Not Use Output File And PCAP Buffer Together")
113+
elif outfilename is None and pcapbuffer is None:
114+
raise RuntimeError("Must Use Either Output File Or PCAP Buffer")
115+
116+
fnt = FileNameTracking()
117+
118+
unknownbuffer = []
119+
120+
logger.info("Starting to Process Data")
121+
122+
#this only needs to be done if using an outfile
123+
if outfilename is not None:
124+
try:
125+
filehandle = open(outfilename, "wb")
126+
except IOError:
127+
logger.error("Could Not Open Output File")
128+
stopevent.set()
129+
return
130+
MakePCAPHeader(filehandle, plugin.PluginDataLinkType)
131+
132+
while not stopevent.isSet():
133+
if datastorage.newdata.isSet():
134+
datastorage.lock.acquire()
135+
136+
try:
137+
(pluginresult,packetlength) = plugin.Identify(datastorage.raw, datastorage.capture_info)
138+
except Exception as e:
139+
logger.error("Caught Exception in the plugin, Shutting Down")
140+
logger.error("Exception Was: " + traceback.format_exc())
141+
stopevent.set()
142+
datastorage.lock.release()
143+
return
144+
145+
if pluginresult == plugin.Status.UNKNOWN:
146+
logger.debug("Got Status.UNKNOWN, data was: " + datastorage.raw[0])
147+
unknownbuffer.append(datastorage.raw[0])
148+
datastorage.pop_front(1)
149+
150+
elif pluginresult == plugin.Status.OK or pluginresult == plugin.Status.INVALID:
151+
#if packet length is 0 then this is probably an error. Treat it the same as an UNKNOWN event otherwise a deadlock may occur
152+
if packetlength <= 0:
153+
logger.debug("Got Status.OK | Status.INVALID, but the packet length was <= 0. Treating as Status.UNKNOWN")
154+
unknownbuffer.append(datastorage.raw[0])
155+
datastorage.pop_front(1)
156+
157+
#otherwise process it as normal
158+
else:
159+
if len(unknownbuffer) > 0:
160+
logger.debug("Flushing Unknown Buffer To PCAP:")
161+
logger.debug(unknownbuffer)
162+
163+
#this if/else checks to see if output needs to go to a file or the pcapbuffer
164+
if outfilename is not None:
165+
#check to see if the output file needs to be split
166+
if datastorage.DoSplit(filehandle) == True:
167+
#if so then close the original file
168+
filehandle.close()
169+
#get a new file handle with the new naming convention
170+
filehandle = fnt.GetNewFileHandle(outfilename)
171+
#make sure it gets the pcap header
172+
MakePCAPHeader(filehandle, plugin.PluginDataLinkType)
173+
174+
MakePCAPEntry(filehandle, unknownbuffer, plugin.OutputCallback)
175+
176+
else:
177+
#adds the unknown buffer to the pcap buffer
178+
AddToPCAPBuffer(pcapbuffer, unknownbuffer, plugin.OutputCallback, plugin.PluginDataLinkType)
179+
180+
datastorage.inc_packet_counter()
181+
182+
unknownbuffer = []
183+
184+
logger.debug("Got Status.OK | Status.INVALID, Packet Length: " + str(packetlength) + " flushing packet to PCAP")
185+
logger.debug(datastorage.raw[:packetlength])
186+
187+
#this if/else checks to see if output needs to go to a file or the pcapbuffer
188+
if outfilename is not None:
189+
#check to see if the output file needs to be split
190+
if datastorage.DoSplit(filehandle) == True:
191+
#if so then close the original file
192+
filehandle.close()
193+
#get a new file handle with the new naming convention
194+
filehandle = fnt.GetNewFileHandle(outfilename)
195+
#make sure it gets the pcap header
196+
MakePCAPHeader(filehandle, plugin.PluginDataLinkType)
197+
198+
MakePCAPEntry(filehandle, datastorage.raw[:packetlength], plugin.OutputCallback)
199+
200+
else:
201+
#adds the identified packet to the pcap buffer
202+
AddToPCAPBuffer(pcapbuffer, datastorage.raw[:packetlength], plugin.OutputCallback, plugin.PluginDataLinkType)
203+
204+
datastorage.inc_packet_counter()
205+
206+
datastorage.pop_front(packetlength)
207+
208+
elif pluginresult == plugin.Status.TOOSHORT:
209+
logger.debug("Got Status.TOOSHORT")
210+
datastorage.newdata.clear()
211+
212+
else:
213+
logger.warning("Unknown Response Code From Plugin - This Will Be Ignored")
214+
215+
if len(datastorage.raw) == 0:
216+
datastorage.newdata.clear()
217+
218+
datastorage.lock.release()
219+
220+
else:
221+
time.sleep(.1)
222+
223+
#end of while loop
224+
225+
#if there is any data still left after the main while loop then move it over to the unknown buffer
226+
for i in range(0,len(datastorage.raw)):
227+
unknownbuffer.append(datastorage.raw[i])
228+
229+
#dump anything in the unknown buffer to a single packet
230+
if len(unknownbuffer) > 0:
231+
logger.debug("Flushing Unknown Buffer To PCAP:")
232+
logger.debug(unknownbuffer)
233+
#this if/else checks to see if output needs to go to a file or the pcapbuffer
234+
if outfilename is not None:
235+
#for now not splitting files at this code since the code writes one big packet and there is not fine grained control over the size
236+
MakePCAPEntry(filehandle, unknownbuffer, plugin.OutputCallback)
237+
else:
238+
#adds the unknown buffer to the pcap buffer
239+
AddToPCAPBuffer(pcapbuffer, unknownbuffer, plugin.OutputCallback, plugin.PluginDataLinkType)
240+
datastorage.inc_packet_counter()
241+
logger.info("Done Processing")
242+
243+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/python
2+
3+
import threading
4+
import time
5+
6+
class FileNameTracking:
7+
def __init__(self):
8+
pass
9+
10+
filecount = 0
11+
12+
def GetNewFileHandle(self, OriginalFileName, mode="wb"):
13+
self.filecount += 1
14+
15+
return open(OriginalFileName + "." + str(self.filecount), mode)
16+
17+
18+
19+
class CaptureInformation:
20+
def __init__(self):
21+
pass
22+
23+
backing = None
24+
baudrate = None
25+
bytesize = None
26+
parity = None
27+
stopbits = None
28+
29+
class CaptureDataBuffer:
30+
lock = None
31+
newdata = None
32+
raw = None
33+
34+
total_packets_captured = 0
35+
total_bytes_captured = 0
36+
start_time = 0.0
37+
file_split_size = 0
38+
39+
capture_info = None
40+
41+
def __init__(self):
42+
self.lock = threading.Lock()
43+
self.newdata = threading.Event()
44+
self.raw = []
45+
46+
self.capture_info = CaptureInformation()
47+
48+
self.start_time = time.time()
49+
50+
def pop_front(self, count):
51+
if count >= len(self.raw):
52+
self.raw = []
53+
54+
else:
55+
for i in range(0, count):
56+
self.raw.pop(0)
57+
58+
def inc_packet_counter(self, count=1):
59+
self.total_packets_captured += count
60+
61+
def inc_bytes_counter(self, count=1):
62+
self.total_bytes_captured += count
63+
64+
def DoSplit(self, filehandle):
65+
if self.file_split_size != 0 and filehandle.tell() >= self.file_split_size:
66+
return True
67+
else:
68+
return False
69+
70+
class CaptureDataBufferSimple:
71+
lock = None
72+
raw = None
73+
74+
def __init__(self):
75+
self.lock = threading.Lock()
76+
self.raw = []
77+
78+

0 commit comments

Comments
 (0)