/*
* Copyright 2010 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.pcap.file;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.util.ArrayList;
import java.util.List;
import org.krakenapps.pcap.PcapOutputStream;
import org.krakenapps.pcap.packet.PacketHeader;
import org.krakenapps.pcap.packet.PcapPacket;
import org.krakenapps.pcap.util.Buffer;
/**
* PcapFileOutputStream writes pcap packet stream to pcap file.
*
* @see http://wiki.wireshark.org/Development/LibpcapFileFormat
* @author mindori
* @since 1.1
*/
public class PcapFileOutputStream implements PcapOutputStream {
private static final int MAX_CACHED_PACKET_NUMBER = 1000;
private int cachedPacketNum = 0;
private FileOutputStream fos;
private List<Byte> list;
public PcapFileOutputStream(File file) throws IOException {
try {
if (file.exists())
throw new IOException("file exists: " + file.getName());
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
list = new ArrayList<Byte>();
createGlobalHeader();
}
public PcapFileOutputStream(File file, GlobalHeader header) throws IOException {
try {
if (file.exists()) {
fos = new FileOutputStream(file, true);
list = new ArrayList<Byte>();
}
else {
fos = new FileOutputStream(file);
list = new ArrayList<Byte>();
copyGlobalHeader(header);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void createGlobalHeader() {
/* magic number(swapped) */
list.add((byte) 0xd4);
list.add((byte) 0xc3);
list.add((byte) 0xb2);
list.add((byte) 0xa1);
/* major version number */
list.add((byte) 0x02);
list.add((byte) 0x00);
/* minor version number */
list.add((byte) 0x04);
list.add((byte) 0x00);
/* GMT to local correction */
list.add((byte) 0x00);
list.add((byte) 0x00);
list.add((byte) 0x00);
list.add((byte) 0x00);
/* accuracy of timestamps */
list.add((byte) 0x00);
list.add((byte) 0x00);
list.add((byte) 0x00);
list.add((byte) 0x00);
/* max length of captured packets, in octets */
list.add((byte) 0xff);
list.add((byte) 0xff);
list.add((byte) 0x00);
list.add((byte) 0x00);
/* data link type(ethernet) */
list.add((byte) 0x01);
list.add((byte) 0x00);
list.add((byte) 0x00);
list.add((byte) 0x00);
}
private void copyGlobalHeader(GlobalHeader header) {
byte[] a = intToByteArray(header.getMagicNumber());
byte[] b = shortToByteArray(header.getMajorVersion());
byte[] c = shortToByteArray(header.getMinorVersion());
byte[] d = intToByteArray(header.getThiszone());
byte[] e = intToByteArray(header.getSigfigs());
byte[] f = intToByteArray(header.getSnaplen());
byte[] g = intToByteArray(header.getNetwork());
list.add(a[0]);
list.add(a[1]);
list.add(a[2]);
list.add(a[3]);
list.add(b[1]);
list.add(b[0]);
list.add(c[1]);
list.add(c[0]);
list.add(d[3]);
list.add(d[2]);
list.add(d[1]);
list.add(d[0]);
list.add(e[3]);
list.add(e[2]);
list.add(e[1]);
list.add(e[0]);
list.add(f[3]);
list.add(f[2]);
list.add(f[1]);
list.add(f[0]);
list.add(g[3]);
list.add(g[2]);
list.add(g[1]);
list.add(g[0]);
}
public void write(PcapPacket packet) throws IOException {
PacketHeader packetHeader = packet.getPacketHeader();
int tsSec = packetHeader.getTsSec();
int tsUsec = packetHeader.getTsUsec();
int inclLen = packetHeader.getInclLen();
int origLen = packetHeader.getOrigLen();
addInt(tsSec);
addInt(tsUsec);
addInt(inclLen);
addInt(origLen);
Buffer payload = packet.getPacketData();
try {
payload.mark();
while (true) {
list.add(payload.get());
}
} catch (BufferUnderflowException e) {
payload.reset();
}
cachedPacketNum++;
if (cachedPacketNum == MAX_CACHED_PACKET_NUMBER)
flush();
}
private void addInt(int d) {
list.add((byte) ((d & 0xff)));
list.add((byte) ((d & 0xff00) >> 8));
list.add((byte) ((d & 0xff0000) >> 16));
list.add((byte) ((d & 0xff000000) >> 24));
}
private byte[] intToByteArray(int d) {
return new byte[] { (byte) (d >>> 24), (byte) (d >>> 16), (byte) (d >>> 8), (byte) d };
}
private byte[] shortToByteArray(short s) {
return new byte[] { (byte) (s >>> 8), (byte) s };
}
@Override
public void flush() throws IOException {
byte[] fileBinary = new byte[list.size()];
for (int i = 0; i < fileBinary.length; i++) {
fileBinary[i] = (byte) list.get(i);
}
list.clear();
fos.write(fileBinary);
cachedPacketNum = 0;
}
@Override
public void close() throws IOException {
flush();
fos.close();
}
}