1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
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
10 * Vincent Perot - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.internal
.pcap
.core
.trace
;
15 import java
.io
.Closeable
;
16 import java
.io
.IOException
;
17 import java
.nio
.ByteBuffer
;
18 import java
.nio
.ByteOrder
;
19 import java
.nio
.channels
.SeekableByteChannel
;
20 import java
.nio
.file
.Files
;
21 import java
.nio
.file
.Path
;
22 import java
.util
.TreeMap
;
24 import org
.eclipse
.jdt
.annotation
.NonNull
;
25 import org
.eclipse
.jdt
.annotation
.Nullable
;
26 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.packet
.BadPacketException
;
27 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.protocol
.pcap
.PcapPacket
;
28 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.util
.ConversionHelper
;
29 import org
.eclipse
.linuxtools
.internal
.pcap
.core
.util
.PcapTimestampScale
;
32 * Class that allows the interaction with a pcap file.
34 * @author Vincent Perot
36 public class PcapFile
implements Closeable
{
38 // TODO add pcapng support.
39 // TODO Make parsing faster by buffering the data.
41 private final Path fPcapFilePath
;
42 private final ByteOrder fByteOrder
;
43 private final SeekableByteChannel fFileChannel
;
44 private final PcapTimestampScale fTimestampPrecision
;
46 private final int fMajorVersion
;
47 private final int fMinorVersion
;
48 private final long fTimeAccuracy
;
49 private final long fTimeZoneCorrection
;
50 private final long fSnapshotLength
;
51 private final long fDataLinkType
;
53 private final TreeMap
<Long
, Long
> fFileIndex
;
55 private long fCurrentRank
;
56 private long fTotalNumberPackets
;
59 * Constructor of the PcapFile Class.
62 * The path to the pcap file.
64 * @throws BadPcapFileException
65 * Thrown if the Pcap File is not valid.
67 * Thrown if there is an IO error while reading the file.
69 public PcapFile(Path filePath
) throws BadPcapFileException
, IOException
{
71 fFileIndex
= new TreeMap
<>();
73 fTotalNumberPackets
= -1;
74 fPcapFilePath
= filePath
;
76 // Check file validity
77 if (Files
.notExists(fPcapFilePath
) || !Files
.isRegularFile(fPcapFilePath
) ||
78 Files
.size(fPcapFilePath
) < PcapFileValues
.GLOBAL_HEADER_SIZE
) {
79 throw new BadPcapFileException("Bad Pcap File."); //$NON-NLS-1$
82 if (!Files
.isReadable(fPcapFilePath
)) {
83 throw new BadPcapFileException("File is not readable."); //$NON-NLS-1$
86 // File is not empty. Try to open.
87 @SuppressWarnings("null")
88 @NonNull SeekableByteChannel channel
= Files
.newByteChannel(fPcapFilePath
);
89 fFileChannel
= channel
;
91 // Parse the global header.
92 // Read the magic number (4 bytes) from the input stream
93 // and determine the mode (big endian or little endian)
94 ByteBuffer globalHeader
= ByteBuffer
.allocate(PcapFileValues
.GLOBAL_HEADER_SIZE
);
96 fFileChannel
.read(globalHeader
);
98 int magicNumber
= globalHeader
.getInt();
100 @SuppressWarnings("null")
101 @NonNull ByteOrder be
= ByteOrder
.BIG_ENDIAN
;
102 @SuppressWarnings("null")
103 @NonNull ByteOrder le
= ByteOrder
.LITTLE_ENDIAN
;
105 switch (magicNumber
) {
106 case PcapFileValues
.MAGIC_BIG_ENDIAN_MICRO
: // file is big endian
108 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
110 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_MICRO
: // file is little endian
112 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
114 case PcapFileValues
.MAGIC_BIG_ENDIAN_NANO
: // file is big endian
116 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
118 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_NANO
: // file is little endian
120 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
124 throw new BadPcapFileException(String
.format("%08x", magicNumber
) + " is not a known magic number."); //$NON-NLS-1$ //$NON-NLS-2$
127 // Put the rest of the buffer in file endian.
128 globalHeader
.order(fByteOrder
);
130 // Initialization of global header fields.
131 fMajorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
132 fMinorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
133 fTimeAccuracy
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
134 fTimeZoneCorrection
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
135 fSnapshotLength
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
136 fDataLinkType
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
138 fFileIndex
.put(fCurrentRank
, fFileChannel
.position());
143 * Method that allows the parsing of a packet at the current position.
145 * @return The parsed Pcap Packet.
146 * @throws IOException
147 * Thrown when there is an error while reading the file.
148 * @throws BadPcapFileException
149 * Thrown when a packet header is invalid.
150 * @throws BadPacketException
151 * Thrown when the packet is erroneous.
153 public synchronized @Nullable PcapPacket
parseNextPacket() throws IOException
, BadPcapFileException
, BadPacketException
{
155 // Parse the packet header
156 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
159 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
160 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
163 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
164 pcapPacketHeader
.clear();
165 pcapPacketHeader
.order(fByteOrder
);
167 fFileChannel
.read(pcapPacketHeader
);
169 pcapPacketHeader
.flip();
170 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
171 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
173 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
174 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
177 if (includedPacketLength
> Integer
.MAX_VALUE
) {
178 throw new BadPacketException("Packets that are bigger than 2^31-1 bytes are not supported."); //$NON-NLS-1$
181 ByteBuffer pcapPacketData
= ByteBuffer
.allocate((int) includedPacketLength
);
182 pcapPacketData
.clear();
183 pcapPacketHeader
.order(ByteOrder
.BIG_ENDIAN
); // Not really needed.
184 fFileChannel
.read(pcapPacketData
);
186 pcapPacketData
.flip();
188 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
190 return new PcapPacket(this, null, pcapPacketHeader
, pcapPacketData
, fCurrentRank
- 1);
195 * Method that allows to skip a packet at the current position.
197 * @throws IOException
198 * Thrown when there is an error while reading the file.
199 * @throws BadPcapFileException
200 * Thrown when a packet header is invalid.
202 public synchronized void skipNextPacket() throws IOException
, BadPcapFileException
{
204 // Parse the packet header
205 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
208 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
209 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
212 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
213 pcapPacketHeader
.clear();
214 pcapPacketHeader
.order(fByteOrder
);
216 fFileChannel
.read(pcapPacketHeader
);
218 pcapPacketHeader
.flip();
219 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
220 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
222 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
223 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
226 fFileChannel
.position(fFileChannel
.position() + includedPacketLength
);
228 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
233 * Method that moves the position to the specified rank.
236 * The rank of the packet.
238 * @throws IOException
239 * Thrown when there is an error while reading the file.
240 * @throws BadPcapFileException
241 * Thrown when a packet header is invalid.
243 public synchronized void seekPacket(long rank
) throws IOException
, BadPcapFileException
{
247 throw new IllegalArgumentException();
250 Long positionInBytes
= fFileIndex
.get(rank
);
252 if (positionInBytes
!= null) {
253 // Index is known. Move to position.
254 fFileChannel
.position(positionInBytes
.longValue());
257 // Index is unknown. Find the corresponding position.
258 // Find closest index
259 fCurrentRank
= fFileIndex
.floorKey(rank
);
260 // skip until wanted packet is found
263 } while (fCurrentRank
!= rank
&& hasNextPacket());
268 * Method that indicates if there are packets remaining to read. It is an
269 * end of file indicator.
271 * @return Whether the pcap still has packets or not.
272 * @throws IOException
273 * If some IO error occurs.
275 public synchronized boolean hasNextPacket() throws IOException
{
276 return ((fFileChannel
.size() - fFileChannel
.position()) > 0);
280 * Getter method for the Byte Order of the file.
282 * @return The byte Order of the file.
284 public ByteOrder
getByteOrder() {
289 * Getter method for the Major Version of the file.
291 * @return The Major Version of the file.
293 public int getMajorVersion() {
294 return fMajorVersion
;
298 * Getter method for the Minor Version of the file.
300 * @return The Minor Version of the file.
302 public int getMinorVersion() {
303 return fMinorVersion
;
307 * Getter method for the time accuracy of the file.
309 * @return The time accuracy of the file.
311 public long getTimeAccuracy() {
312 return fTimeAccuracy
;
316 * Getter method for the time zone correction of the file.
318 * @return The time zone correction of the file.
320 public long getTimeZoneCorrection() {
321 return fTimeZoneCorrection
;
325 * Getter method for the snapshot length of the file.
327 * @return The snapshot length of the file.
329 public long getSnapLength() {
330 return fSnapshotLength
;
334 * Getter method for the datalink type of the file. This parameter is used
335 * to determine higher-level protocols (Ethernet, WLAN, SLL).
337 * @return The datalink type of the file.
339 public long getDataLinkType() {
340 return fDataLinkType
;
344 * Getter method for the path of the file.
346 * @return The path of the file.
348 public Path
getPath() {
349 return fPcapFilePath
;
353 * Method that returns the total number of packets in the file.
355 * @return The total number of packets.
356 * @throws IOException
357 * Thrown when some IO error occurs.
358 * @throws BadPcapFileException
359 * Thrown when a packet header is invalid.
361 public synchronized long getTotalNbPackets() throws IOException
, BadPcapFileException
{
362 if (fTotalNumberPackets
== -1) {
363 long rank
= fCurrentRank
;
364 fCurrentRank
= fFileIndex
.floorKey(rank
);
366 // skip until end of file.
367 while (hasNextPacket()) {
370 fTotalNumberPackets
= fCurrentRank
;
374 return fTotalNumberPackets
;
378 * Getter method that returns the current rank in the file (the packet
381 * @return The current rank.
383 public synchronized long getCurrentRank() {
388 * Getter method that returns the timestamp precision of the file.
390 * @return The the timestamp precision of the file.
392 public PcapTimestampScale
getTimestampPrecision() {
393 return fTimestampPrecision
;
397 public void close() throws IOException
{
398 fFileChannel
.close();