3232# and prevent the Flask server from starting with a bad configuration.
3333try :
3434 # Attempt a simple database operation (list collection names) to force connection/auth check
35- print ("Attempting connection and write test to MongoDB..." )
35+ app . logger . info ("Attempting connection and write test to MongoDB..." )
3636 # 1. Attempt to insert a test document
3737 db .test_connection .insert_one ({"status" : "startup_check" , "timestamp" : int (time .time ())})
3838 # 2. Attempt to delete the test document
3939 db .test_connection .delete_one ({"status" : "startup_check" })
4040 # 3. Final check to ensure we can list collections
4141 db .list_collection_names ()
42- print ("Successfully connected and confirmed write access to MongoDB." )
42+ app . logger . info ("Successfully connected and confirmed write access to MongoDB." )
4343except Exception as e :
44- print ("=" * 50 )
45- print ("FATAL ERROR: Could not connect to MongoDB or authentication failed." )
46- print (f"Connection URI: { mongo_uri } " )
47- print (f"Exception: { e } " )
48- print ("=" * 50 )
44+ app . logger . info ("=" * 50 )
45+ app . logger . info ("FATAL ERROR: Could not connect to MongoDB or authentication failed." )
46+ app . logger . info (f"Connection URI: { mongo_uri } " )
47+ app . logger . info (f"Exception: { e } " )
48+ app . logger . info ("=" * 50 )
4949 sys .exit (1 )
5050 # If using gunicorn/uwsgi, this exit won't stop the workers, but will prevent the app from running correctly.
5151 # If running with 'python app.py', this will stop the application.
@@ -101,22 +101,40 @@ def label_packets():
101101 400: Error if required fields are missing or packet decoding fails.
102102 500: Error if database insertion fails.
103103 """
104+ raw_packets = []
105+ capture_times = []
106+ capture_lengths = []
107+
104108 data = request .get_json ()
105- print ("Received POST data:" , json .dumps (data , indent = 4 ))
109+ app . logger . info ("Received POST data:" , json .dumps (data , indent = 4 ))
106110 required_keys = ["packets" , "prolific_id" , "mac_address" , "device_name" , "activity_label" , "start_time" , "end_time" ]
107111 if not data or not all (key in data for key in required_keys ):
112+ app .logger .warning ("Missing required fields in POST data" )
108113 return jsonify ({"error" : "Missing required fields" }), 400
114+
115+ if not is_prolific_id_valid (data ["prolific_id" ]):
116+ app .logger .warning ("Invalid Prolific ID received" )
117+ return jsonify ({"error" : "Prolific ID is invalid" }), 500
118+
109119 try :
110- raw_packets = [base64 .b64decode (pkt ) for pkt in data ["packets" ]]
120+ for pkt_metadata in data ["packets" ]:
121+ # Validate essential keys are present
122+ if not isinstance (pkt_metadata , dict ) or 'time' not in pkt_metadata or 'raw_data' not in pkt_metadata :
123+ app .logger .error ("Packet object missing 'time' or 'raw_data' key." )
124+ return jsonify ({"error" : "Packet metadata structure is invalid" }), 400
125+
126+ raw_data_bytes = base64 .b64decode (pkt_metadata ["raw_data" ])
127+ raw_packets .append (raw_data_bytes )
128+ capture_times .append (float (pkt_metadata ["time" ]))
129+ capture_lengths .append (len (raw_data_bytes ))
111130 except Exception as e :
112- print (f"Packet decoding occurred for collection '{ data ['prolific_id' ]} ': { e } " )
131+ app . logger . warning (f"Packet decoding occurred for collection '{ data ['prolific_id' ]} ': { e } " )
113132 return jsonify ({"error" : "Packet decoding failed" }), 400
114133
115- if not is_prolific_id_valid (data ["prolific_id" ]):
116- return jsonify ({"error" : "Prolific ID is invalid" }), 500
117134 folder_path : str = os .path .join (str (data ["prolific_id" ]), str (data ["device_name" ]), str (data ["activity_label" ]))
118135 fullpath = os .path .normpath (os .path .join (packet_root_dir , folder_path ))
119136 if not fullpath .startswith (packet_root_dir ):
137+ app .logger .warning ("Invalid characters detected in path components" )
120138 return jsonify ({"error" : "Seems like invalid characters used in prolific ID, device name or activity label" }), 500
121139
122140 prolific_user_packets_collected = db [data ["prolific_id" ]]
@@ -127,22 +145,24 @@ def label_packets():
127145 "activity_label" : data ["activity_label" ],
128146 "start_time" : int (data ["start_time" ]),
129147 "end_time" : int (data ["end_time" ]),
130- "raw_packets" : raw_packets
148+ "raw_packets" : raw_packets ,
149+ "capture_times" : capture_times ,
150+ "capture_lengths" : capture_lengths
131151 }
132152 try :
133153 prolific_user_packets_collected .insert_one (doc )
134154 except Exception as e :
135- print (f"MongoDB Insert FAILED for collection '{ data ['prolific_id' ]} ': { e } " )
155+ app . logger . warning (f"MongoDB Insert FAILED for collection '{ data ['prolific_id' ]} ': { e } " )
136156 return jsonify ({"error" : "Database insert failed" }), 500
137157
138158 try :
139159 os .makedirs (fullpath , exist_ok = True )
140160 pcap_file_name : str = make_pcap_filename (int (data ["start_time" ]), int (data ["end_time" ]))
141161 pcap_name : str = os .path .join (fullpath , pcap_file_name )
142- save_packets_to_pcap (raw_packets , pcap_name )
162+ save_packets_to_pcap (raw_packets , capture_times , pcap_name )
143163 except Exception as e :
144164 # If file saving fails, return a 500 but note that the DB save succeeded
145- print (f"PCAP File Save FAILED for ID: { e } " )
165+ app . logger . warning (f"PCAP File Save FAILED for ID: { e } " )
146166 return jsonify ({
147167 "status" : "partial_success" ,
148168 "inserted" : 1 ,
@@ -170,22 +190,27 @@ def make_pcap_filename(start_time: int, end_time: int) -> str:
170190 return filename
171191
172192
173- def save_packets_to_pcap (raw_packets : list , filename = "output.pcap" ):
193+ def save_packets_to_pcap (raw_packets : list , capture_times : list , filename = "output.pcap" ):
174194 """
175195 Saves a list of raw packet bytes to a pcap file.
176196
177197 Args:
178198 raw_packets (list): List of bytes objects representing raw packets.
199+ capture_times (list): The epoch time of each packet when it was captured by IoT Inspector.
179200 filename (str): Output pcap file name.
180201 """
181202 scapy_packets = []
182- for pkt_bytes in raw_packets :
203+ for i , pkt_bytes in enumerate ( raw_packets ) :
183204 try :
184205 pkt = Ether (pkt_bytes )
185206 if pkt .__class__ .__name__ == "Raw" :
186207 pkt = IP (pkt_bytes )
187208 except Exception :
188209 pkt = IP (pkt_bytes )
210+
211+ # CRITICAL STEP: Assign the supplied original capture time
212+ # This is guaranteed to be correct for ALL packets.
213+ pkt .time = capture_times [i ]
189214 scapy_packets .append (pkt )
190215 wrpcap (filename , scapy_packets )
191216
0 commit comments