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
.pcap
.core
.trace
;
15 import java
.io
.Closeable
;
17 import java
.io
.FileInputStream
;
18 import java
.io
.IOException
;
19 import java
.nio
.ByteBuffer
;
20 import java
.nio
.ByteOrder
;
21 import java
.nio
.channels
.FileChannel
;
22 import java
.util
.TreeMap
;
24 import org
.eclipse
.jdt
.annotation
.NonNull
;
25 import org
.eclipse
.jdt
.annotation
.Nullable
;
26 import org
.eclipse
.linuxtools
.pcap
.core
.packet
.BadPacketException
;
27 import org
.eclipse
.linuxtools
.pcap
.core
.protocol
.pcap
.PcapPacket
;
28 import org
.eclipse
.linuxtools
.pcap
.core
.util
.ConversionHelper
;
29 import org
.eclipse
.linuxtools
.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 String fPcapFilePath
;
42 private final ByteOrder fByteOrder
;
43 private final FileChannel fFileChannel
;
44 private final FileInputStream fFileInputStream
;
45 private final PcapTimestampScale fTimestampPrecision
;
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
;
54 private final TreeMap
<Long
, Long
> fFileIndex
;
56 private long fCurrentRank
;
57 private long fTotalNumberPackets
;
60 * Constructor of the PcapFile Class.
63 * The path to the pcap file.
65 * @throws BadPcapFileException
66 * Thrown if the Pcap File is not valid.
68 * Thrown if there is an IO error while reading the file.
70 public PcapFile(String filePath
) throws BadPcapFileException
, IOException
{
72 fFileIndex
= new TreeMap
<>();
74 fTotalNumberPackets
= -1;
75 fPcapFilePath
= filePath
;
77 // Check file validity
78 File pcapFile
= new File(fPcapFilePath
);
79 if ((!fPcapFilePath
.endsWith(".cap") && !fPcapFilePath
.endsWith(".pcap")) || //$NON-NLS-1$ //$NON-NLS-2$
80 !pcapFile
.exists() || !pcapFile
.isFile() || pcapFile
.length() < PcapFileValues
.GLOBAL_HEADER_SIZE
) {
81 throw new BadPcapFileException("Bad Pcap File."); //$NON-NLS-1$
84 if (!pcapFile
.canRead()) {
85 throw new IOException("File is not readable."); //$NON-NLS-1$
88 // File is not empty. Try to open.
89 fFileInputStream
= new FileInputStream(fPcapFilePath
);
91 @SuppressWarnings("null")
92 @NonNull FileChannel fileChannel
= fFileInputStream
.getChannel();
93 fFileChannel
= fileChannel
;
95 // Parse the global header.
96 // Read the magic number (4 bytes) from the input stream
97 // and determine the mode (big endian or little endian)
98 ByteBuffer globalHeader
= ByteBuffer
.allocate(PcapFileValues
.GLOBAL_HEADER_SIZE
);
100 fFileChannel
.read(globalHeader
);
102 int magicNumber
= globalHeader
.getInt();
104 @SuppressWarnings("null")
105 @NonNull ByteOrder be
= ByteOrder
.BIG_ENDIAN
;
106 @SuppressWarnings("null")
107 @NonNull ByteOrder le
= ByteOrder
.LITTLE_ENDIAN
;
109 switch (magicNumber
) {
110 case PcapFileValues
.MAGIC_BIG_ENDIAN_MICRO
: // file is big endian
112 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
113 System
.out
.println();
115 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_MICRO
: // file is little endian
117 fTimestampPrecision
= PcapTimestampScale
.MICROSECOND
;
119 case PcapFileValues
.MAGIC_BIG_ENDIAN_NANO
: // file is big endian
121 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
123 case PcapFileValues
.MAGIC_LITTLE_ENDIAN_NANO
: // file is little endian
125 fTimestampPrecision
= PcapTimestampScale
.NANOSECOND
;
129 throw new BadPcapFileException(String
.format("%08x", magicNumber
) + " is not a known magic number."); //$NON-NLS-1$ //$NON-NLS-2$
132 // Put the rest of the buffer in file endian.
133 globalHeader
.order(fByteOrder
);
135 // Initialization of global header fields.
136 fMajorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
137 fMinorVersion
= ConversionHelper
.unsignedShortToInt(globalHeader
.getShort());
138 fTimeAccuracy
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
139 fTimeZoneCorrection
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
140 fSnapshotLength
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
141 fDataLinkType
= ConversionHelper
.unsignedIntToLong(globalHeader
.getInt());
143 fFileIndex
.put(fCurrentRank
, fFileChannel
.position());
148 * Method that allows the parsing of a packet at the current position.
150 * @return The parsed Pcap Packet.
151 * @throws IOException
152 * Thrown when there is an error while reading the file.
153 * @throws BadPcapFileException
154 * Thrown when a packet header is invalid.
155 * @throws BadPacketException
156 * Thrown when the packet is erroneous.
158 public synchronized @Nullable PcapPacket
parseNextPacket() throws IOException
, BadPcapFileException
, BadPacketException
{
160 // Parse the packet header
161 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
164 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
165 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
168 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
169 pcapPacketHeader
.clear();
170 pcapPacketHeader
.order(fByteOrder
);
172 fFileChannel
.read(pcapPacketHeader
);
174 pcapPacketHeader
.flip();
175 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
176 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
178 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
179 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
182 if (includedPacketLength
> Integer
.MAX_VALUE
) {
183 throw new BadPacketException("Packets that are bigger than 2^31-1 bytes are not supported."); //$NON-NLS-1$
186 ByteBuffer pcapPacketData
= ByteBuffer
.allocate((int) includedPacketLength
);
187 pcapPacketData
.clear();
188 pcapPacketHeader
.order(ByteOrder
.BIG_ENDIAN
); // Not really needed.
189 fFileChannel
.read(pcapPacketData
);
191 pcapPacketData
.flip();
193 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
195 return new PcapPacket(this, null, pcapPacketHeader
, pcapPacketData
, fCurrentRank
- 1);
200 * Method that allows to skip a packet at the current position.
202 * @throws IOException
203 * Thrown when there is an error while reading the file.
204 * @throws BadPcapFileException
205 * Thrown when a packet header is invalid.
207 public synchronized void skipNextPacket() throws IOException
, BadPcapFileException
{
209 // Parse the packet header
210 if (fFileChannel
.size() - fFileChannel
.position() == 0) {
213 if (fFileChannel
.size() - fFileChannel
.position() < PcapFileValues
.PACKET_HEADER_SIZE
) {
214 throw new BadPcapFileException("A pcap header is invalid."); //$NON-NLS-1$
217 ByteBuffer pcapPacketHeader
= ByteBuffer
.allocate(PcapFileValues
.PACKET_HEADER_SIZE
);
218 pcapPacketHeader
.clear();
219 pcapPacketHeader
.order(fByteOrder
);
221 fFileChannel
.read(pcapPacketHeader
);
223 pcapPacketHeader
.flip();
224 pcapPacketHeader
.position(PcapFileValues
.INCLUDED_LENGTH_POSITION
);
225 long includedPacketLength
= ConversionHelper
.unsignedIntToLong(pcapPacketHeader
.getInt());
227 if (fFileChannel
.size() - fFileChannel
.position() < includedPacketLength
) {
228 throw new BadPcapFileException("A packet header is invalid."); //$NON-NLS-1$
231 fFileChannel
.position(fFileChannel
.position() + includedPacketLength
);
233 fFileIndex
.put(++fCurrentRank
, fFileChannel
.position());
238 * Method that moves the position to the specified rank.
241 * The rank of the packet.
243 * @throws IOException
244 * Thrown when there is an error while reading the file.
245 * @throws BadPcapFileException
246 * Thrown when a packet header is invalid.
248 public synchronized void seekPacket(long rank
) throws IOException
, BadPcapFileException
{
252 throw new IllegalArgumentException();
255 Long positionInBytes
= fFileIndex
.get(rank
);
257 if (positionInBytes
!= null) {
258 // Index is known. Move to position.
259 fFileChannel
.position(positionInBytes
.longValue());
262 // Index is unknown. Find the corresponding position.
263 // Find closest index
264 fCurrentRank
= fFileIndex
.floorKey(rank
);
265 // skip until wanted packet is found
268 } while (fCurrentRank
!= rank
&& hasNextPacket());
273 * Method that indicates if there are packets remaining to read. It is an
274 * end of file indicator.
276 * @return Whether the pcap still has packets or not.
277 * @throws IOException
278 * If some IO error occurs.
280 public synchronized boolean hasNextPacket() throws IOException
{
281 return ((fFileChannel
.size() - fFileChannel
.position()) > 0);
285 * Getter method for the Byte Order of the file.
287 * @return The byte Order of the file.
289 public ByteOrder
getByteOrder() {
294 * Getter method for the Major Version of the file.
296 * @return The Major Version of the file.
298 public int getMajorVersion() {
299 return fMajorVersion
;
303 * Getter method for the Minor Version of the file.
305 * @return The Minor Version of the file.
307 public int getMinorVersion() {
308 return fMinorVersion
;
312 * Getter method for the time accuracy of the file.
314 * @return The time accuracy of the file.
316 public long getTimeAccuracy() {
317 return fTimeAccuracy
;
321 * Getter method for the time zone correction of the file.
323 * @return The time zone correction of the file.
325 public long getTimeZoneCorrection() {
326 return fTimeZoneCorrection
;
330 * Getter method for the snapshot length of the file.
332 * @return The snapshot length of the file.
334 public long getSnapLength() {
335 return fSnapshotLength
;
339 * Getter method for the datalink type of the file. This parameter is used
340 * to determine higher-level protocols (Ethernet, WLAN, SLL).
342 * @return The datalink type of the file.
344 public long getDataLinkType() {
345 return fDataLinkType
;
349 * Getter method for the path of the file.
351 * @return The path of the file.
353 public String
getPath() {
354 return fPcapFilePath
;
358 * Method that returns the total number of packets in the file.
360 * @return The total number of packets.
361 * @throws IOException
362 * Thrown when some IO error occurs.
363 * @throws BadPcapFileException
364 * Thrown when a packet header is invalid.
366 public synchronized long getTotalNbPackets() throws IOException
, BadPcapFileException
{
367 if (fTotalNumberPackets
== -1) {
368 long rank
= fCurrentRank
;
369 fCurrentRank
= fFileIndex
.floorKey(rank
);
371 // skip until end of file.
372 while (hasNextPacket()) {
375 fTotalNumberPackets
= fCurrentRank
;
379 return fTotalNumberPackets
;
383 * Getter method that returns the current rank in the file (the packet
386 * @return The current rank.
388 public synchronized long getCurrentRank() {
393 * Getter method that returns the timestamp precision of the file.
395 * @return The the timestamp precision of the file.
397 public PcapTimestampScale
getTimestampPrecision() {
398 return fTimestampPrecision
;
402 public void close() throws IOException
{
403 fFileChannel
.close();
404 fFileInputStream
.close();