pcap: Move plugins to their own sub-directory
[deliverable/tracecompass.git] / pcap / org.eclipse.tracecompass.pcap.core / src / org / eclipse / tracecompass / internal / pcap / core / trace / PcapFile.java
1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.internal.pcap.core.trace;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
17 import java.io.Closeable;
18 import java.io.IOException;
19 import java.nio.ByteBuffer;
20 import java.nio.ByteOrder;
21 import java.nio.channels.SeekableByteChannel;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.util.TreeMap;
25
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.tracecompass.internal.pcap.core.packet.BadPacketException;
28 import org.eclipse.tracecompass.internal.pcap.core.protocol.pcap.PcapPacket;
29 import org.eclipse.tracecompass.internal.pcap.core.util.ConversionHelper;
30 import org.eclipse.tracecompass.internal.pcap.core.util.PcapTimestampScale;
31
32 /**
33 * Class that allows the interaction with a pcap file.
34 *
35 * @author Vincent Perot
36 */
37 public class PcapFile implements Closeable {
38
39 // TODO add pcapng support.
40 // TODO Make parsing faster by buffering the data.
41
42 private final Path fPcapFilePath;
43 private final ByteOrder fByteOrder;
44 private final SeekableByteChannel fFileChannel;
45 private final PcapTimestampScale fTimestampPrecision;
46
47 private final int fMajorVersion;
48 private final int fMinorVersion;
49 private final long fTimeAccuracy;
50 private final long fTimeZoneCorrection;
51 private final long fSnapshotLength;
52 private final long fDataLinkType;
53
54 private final TreeMap<Long, Long> fFileIndex;
55
56 private long fCurrentRank;
57 private long fTotalNumberPackets;
58
59 /**
60 * Constructor of the PcapFile Class.
61 *
62 * @param filePath
63 * The path to the pcap file.
64 *
65 * @throws BadPcapFileException
66 * Thrown if the Pcap File is not valid.
67 * @throws IOException
68 * Thrown if there is an IO error while reading the file.
69 */
70 public PcapFile(Path filePath) throws BadPcapFileException, IOException {
71
72 fFileIndex = new TreeMap<>();
73 fCurrentRank = 0;
74 fTotalNumberPackets = -1;
75 fPcapFilePath = filePath;
76
77 // Check file validity
78 if (Files.notExists(fPcapFilePath) || !Files.isRegularFile(fPcapFilePath) ||
79 Files.size(fPcapFilePath) < PcapFileValues.GLOBAL_HEADER_SIZE) {
80 throw new BadPcapFileException("Bad Pcap File."); //$NON-NLS-1$
81 }
82
83 if (!Files.isReadable(fPcapFilePath)) {
84 throw new BadPcapFileException("File is not readable."); //$NON-NLS-1$
85 }
86
87 // File is not empty. Try to open.
88 fFileChannel = checkNotNull(Files.newByteChannel(fPcapFilePath));
89
90 // Parse the global header.
91 // Read the magic number (4 bytes) from the input stream
92 // and determine the mode (big endian or little endian)
93 ByteBuffer globalHeader = ByteBuffer.allocate(PcapFileValues.GLOBAL_HEADER_SIZE);
94 globalHeader.clear();
95 fFileChannel.read(globalHeader);
96 globalHeader.flip();
97 int magicNumber = globalHeader.getInt();
98
99 switch (magicNumber) {
100 case PcapFileValues.MAGIC_BIG_ENDIAN_MICRO: // file is big endian
101 fByteOrder = checkNotNull(ByteOrder.BIG_ENDIAN);
102 fTimestampPrecision = PcapTimestampScale.MICROSECOND;
103 break;
104 case PcapFileValues.MAGIC_LITTLE_ENDIAN_MICRO: // file is little endian
105 fByteOrder = checkNotNull(ByteOrder.LITTLE_ENDIAN);
106 fTimestampPrecision = PcapTimestampScale.MICROSECOND;
107 break;
108 case PcapFileValues.MAGIC_BIG_ENDIAN_NANO: // file is big endian
109 fByteOrder = checkNotNull(ByteOrder.BIG_ENDIAN);
110 fTimestampPrecision = PcapTimestampScale.NANOSECOND;
111 break;
112 case PcapFileValues.MAGIC_LITTLE_ENDIAN_NANO: // file is little endian
113 fByteOrder = checkNotNull(ByteOrder.LITTLE_ENDIAN);
114 fTimestampPrecision = PcapTimestampScale.NANOSECOND;
115 break;
116 default:
117 this.close();
118 throw new BadPcapFileException(String.format("%08x", magicNumber) + " is not a known magic number."); //$NON-NLS-1$ //$NON-NLS-2$
119 }
120
121 // Put the rest of the buffer in file endian.
122 globalHeader.order(fByteOrder);
123
124 // Initialization of global header fields.
125 fMajorVersion = ConversionHelper.unsignedShortToInt(globalHeader.getShort());
126 fMinorVersion = ConversionHelper.unsignedShortToInt(globalHeader.getShort());
127 fTimeAccuracy = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
128 fTimeZoneCorrection = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
129 fSnapshotLength = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
130 fDataLinkType = ConversionHelper.unsignedIntToLong(globalHeader.getInt());
131
132 fFileIndex.put(fCurrentRank, fFileChannel.position());
133
134 }
135
136 /**
137 * Method that allows the parsing of a packet at the current position.
138 *
139 * @return The parsed Pcap Packet.
140 * @throws IOException
141 * Thrown when there is an error while reading the file.
142 * @throws BadPcapFileException
143 * Thrown when a packet header is invalid.
144 * @throws BadPacketException
145 * Thrown when the packet is erroneous.
146 */
147 public synchronized @Nullable PcapPacket parseNextPacket() throws IOException, BadPcapFileException, BadPacketException {
148
149 // Parse the packet header
150 if (fFileChannel.size() - fFileChannel.position() == 0) {
151 return null;
152 }
153 if (fFileChannel.size() - fFileChannel.position() < PcapFileValues.PACKET_HEADER_SIZE) {
154 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
155 }
156
157 ByteBuffer pcapPacketHeader = ByteBuffer.allocate(PcapFileValues.PACKET_HEADER_SIZE);
158 pcapPacketHeader.clear();
159 pcapPacketHeader.order(fByteOrder);
160
161 fFileChannel.read(pcapPacketHeader);
162
163 pcapPacketHeader.flip();
164 pcapPacketHeader.position(PcapFileValues.INCLUDED_LENGTH_POSITION);
165 long includedPacketLength = ConversionHelper.unsignedIntToLong(pcapPacketHeader.getInt());
166
167 if (fFileChannel.size() - fFileChannel.position() < includedPacketLength) {
168 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
169 }
170
171 if (includedPacketLength > Integer.MAX_VALUE) {
172 throw new BadPacketException("Packets that are bigger than 2^31-1 bytes are not supported."); //$NON-NLS-1$
173 }
174
175 ByteBuffer pcapPacketData = ByteBuffer.allocate((int) includedPacketLength);
176 pcapPacketData.clear();
177 pcapPacketHeader.order(ByteOrder.BIG_ENDIAN); // Not really needed.
178 fFileChannel.read(pcapPacketData);
179
180 pcapPacketData.flip();
181
182 fFileIndex.put(++fCurrentRank, fFileChannel.position());
183
184 return new PcapPacket(this, null, pcapPacketHeader, pcapPacketData, fCurrentRank - 1);
185
186 }
187
188 /**
189 * Method that allows to skip a packet at the current position.
190 *
191 * @throws IOException
192 * Thrown when there is an error while reading the file.
193 * @throws BadPcapFileException
194 * Thrown when a packet header is invalid.
195 */
196 public synchronized void skipNextPacket() throws IOException, BadPcapFileException {
197
198 // Parse the packet header
199 if (fFileChannel.size() - fFileChannel.position() == 0) {
200 return;
201 }
202 if (fFileChannel.size() - fFileChannel.position() < PcapFileValues.PACKET_HEADER_SIZE) {
203 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
204 }
205
206 ByteBuffer pcapPacketHeader = ByteBuffer.allocate(PcapFileValues.PACKET_HEADER_SIZE);
207 pcapPacketHeader.clear();
208 pcapPacketHeader.order(fByteOrder);
209
210 fFileChannel.read(pcapPacketHeader);
211
212 pcapPacketHeader.flip();
213 pcapPacketHeader.position(PcapFileValues.INCLUDED_LENGTH_POSITION);
214 long includedPacketLength = ConversionHelper.unsignedIntToLong(pcapPacketHeader.getInt());
215
216 if (fFileChannel.size() - fFileChannel.position() < includedPacketLength) {
217 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
218 }
219
220 fFileChannel.position(fFileChannel.position() + includedPacketLength);
221
222 fFileIndex.put(++fCurrentRank, fFileChannel.position());
223
224 }
225
226 /**
227 * Method that moves the position to the specified rank.
228 *
229 * @param rank
230 * The rank of the packet.
231 *
232 * @throws IOException
233 * Thrown when there is an error while reading the file.
234 * @throws BadPcapFileException
235 * Thrown when a packet header is invalid.
236 */
237 public synchronized void seekPacket(long rank) throws IOException, BadPcapFileException {
238
239 // Verify argument
240 if (rank < 0) {
241 throw new IllegalArgumentException();
242 }
243
244 Long positionInBytes = fFileIndex.get(rank);
245
246 if (positionInBytes != null) {
247 // Index is known. Move to position.
248 fFileChannel.position(positionInBytes.longValue());
249 fCurrentRank = rank;
250 } else {
251 // Index is unknown. Find the corresponding position.
252 // Find closest index
253 fCurrentRank = fFileIndex.floorKey(rank);
254 // skip until wanted packet is found
255 do {
256 skipNextPacket();
257 } while (fCurrentRank != rank && hasNextPacket());
258 }
259 }
260
261 /**
262 * Method that indicates if there are packets remaining to read. It is an
263 * end of file indicator.
264 *
265 * @return Whether the pcap still has packets or not.
266 * @throws IOException
267 * If some IO error occurs.
268 */
269 public synchronized boolean hasNextPacket() throws IOException {
270 return ((fFileChannel.size() - fFileChannel.position()) > 0);
271 }
272
273 /**
274 * Getter method for the Byte Order of the file.
275 *
276 * @return The byte Order of the file.
277 */
278 public ByteOrder getByteOrder() {
279 return fByteOrder;
280 }
281
282 /**
283 * Getter method for the Major Version of the file.
284 *
285 * @return The Major Version of the file.
286 */
287 public int getMajorVersion() {
288 return fMajorVersion;
289 }
290
291 /**
292 * Getter method for the Minor Version of the file.
293 *
294 * @return The Minor Version of the file.
295 */
296 public int getMinorVersion() {
297 return fMinorVersion;
298 }
299
300 /**
301 * Getter method for the time accuracy of the file.
302 *
303 * @return The time accuracy of the file.
304 */
305 public long getTimeAccuracy() {
306 return fTimeAccuracy;
307 }
308
309 /**
310 * Getter method for the time zone correction of the file.
311 *
312 * @return The time zone correction of the file.
313 */
314 public long getTimeZoneCorrection() {
315 return fTimeZoneCorrection;
316 }
317
318 /**
319 * Getter method for the snapshot length of the file.
320 *
321 * @return The snapshot length of the file.
322 */
323 public long getSnapLength() {
324 return fSnapshotLength;
325 }
326
327 /**
328 * Getter method for the datalink type of the file. This parameter is used
329 * to determine higher-level protocols (Ethernet, WLAN, SLL).
330 *
331 * @return The datalink type of the file.
332 */
333 public long getDataLinkType() {
334 return fDataLinkType;
335 }
336
337 /**
338 * Getter method for the path of the file.
339 *
340 * @return The path of the file.
341 */
342 public Path getPath() {
343 return fPcapFilePath;
344 }
345
346 /**
347 * Method that returns the total number of packets in the file.
348 *
349 * @return The total number of packets.
350 * @throws IOException
351 * Thrown when some IO error occurs.
352 * @throws BadPcapFileException
353 * Thrown when a packet header is invalid.
354 */
355 public synchronized long getTotalNbPackets() throws IOException, BadPcapFileException {
356 if (fTotalNumberPackets == -1) {
357 long rank = fCurrentRank;
358 fCurrentRank = fFileIndex.floorKey(rank);
359
360 // skip until end of file.
361 while (hasNextPacket()) {
362 skipNextPacket();
363 }
364 fTotalNumberPackets = fCurrentRank;
365 fCurrentRank = rank;
366 seekPacket(rank);
367 }
368 return fTotalNumberPackets;
369 }
370
371 /**
372 * Getter method that returns the current rank in the file (the packet
373 * number).
374 *
375 * @return The current rank.
376 */
377 public synchronized long getCurrentRank() {
378 return fCurrentRank;
379 }
380
381 /**
382 * Getter method that returns the timestamp precision of the file.
383 *
384 * @return The the timestamp precision of the file.
385 */
386 public PcapTimestampScale getTimestampPrecision() {
387 return fTimestampPrecision;
388 }
389
390 @Override
391 public void close() throws IOException {
392 fFileChannel.close();
393 }
394
395 }
This page took 0.055706 seconds and 5 git commands to generate.