aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
diff options
context:
space:
mode:
authorDamian Minkov <damencho@jitsi.org>2010-12-03 08:53:04 +0000
committerDamian Minkov <damencho@jitsi.org>2010-12-03 08:53:04 +0000
commita21b3d7353c9fc9ed47288333d3f793b01ab5689 (patch)
tree5b8210d76dc28c82998188252958e6c6beaec96a /src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
parent4785e35f694e73a65b6163f31c9aac4f58aa925f (diff)
downloadjitsi-a21b3d7353c9fc9ed47288333d3f793b01ab5689.zip
jitsi-a21b3d7353c9fc9ed47288333d3f793b01ab5689.tar.gz
jitsi-a21b3d7353c9fc9ed47288333d3f793b01ab5689.tar.bz2
Introduce Packet Logging Service with its configuration form and implementation for sip,jabber and rtp.
Diffstat (limited to 'src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java')
-rw-r--r--src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java844
1 files changed, 844 insertions, 0 deletions
diff --git a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
new file mode 100644
index 0000000..924bbdb
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
@@ -0,0 +1,844 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.packetlogging;
+
+import net.java.sip.communicator.service.packetlogging.*;
+import net.java.sip.communicator.util.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Packet Logging Service implementation dumping logs in
+ * pcap(tcpdump/wireshark) format file.
+ *
+ * @author Damian Minkov
+ */
+public class PacketLoggingServiceImpl
+ implements PacketLoggingService
+{
+ /**
+ * Our Logger.
+ */
+ private static final Logger logger
+ = Logger.getLogger(PacketLoggingServiceImpl.class);
+
+ /**
+ * The OutputStream we are currently writing to.
+ */
+ private FileOutputStream outputStream = null;
+
+ /**
+ * The thread that queues packets and saves them to file.
+ */
+ private SaverThread saverThread = new SaverThread();
+
+ /**
+ * The fake ethernet header we use as template.
+ */
+ private final static byte[] fakeEthernetHeader =
+ new byte[]{
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x08, (byte)0x00
+ };
+
+ /**
+ * The fake ipv4 header we use as template.
+ */
+ private final static byte[] ipHeaderTemplate =
+ new byte[]{
+ (byte)0x45, (byte)0x00,
+ (byte)0x03, (byte)0x48, (byte)0xc9, (byte)0x14,
+ (byte)0x00, (byte)0x00, (byte)0x35,(byte)0x11,
+ (byte)0x00, (byte)0x00, // check sum
+ (byte)0xd5, (byte)0xc0, (byte)0x3b, (byte)0x4b,//src
+ (byte)0xc0, (byte)0xa8, (byte)0x00, (byte)0x34 //dst
+ };
+
+ /**
+ * The fake ipv6 header we use as template.
+ */
+ private final static byte[] ip6HeaderTemplate =
+ new byte[]{
+ (byte)0x60, (byte)0x00, (byte)0x00, (byte)0x00, // version, traffic, flowable
+ (byte)0x00, (byte)0x00, // length
+ (byte)0x11, // next header
+ (byte)0x40, // hop limit
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // src
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // src
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // src
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // src
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // dst
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // dst
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // dst
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 // dst
+ };
+
+ /**
+ * The fake udp header we use as template.
+ */
+ private final static byte[] udpHeaderTemplate =
+ new byte[]{
+ (byte)0x13, (byte)0xc4,
+ (byte)0x13, (byte)0xc4,
+ (byte)0x03, (byte)0x34,
+ (byte)0x00, (byte)0x00// checksum
+
+ };
+
+ /**
+ * The fake tcp header we use as template.
+ */
+ private final static byte[] tcpHeaderTemplate =
+ new byte[]{
+ (byte)0xb7, (byte)0x61, // src port
+ (byte)0x13, (byte)0xc4, // dst port
+ (byte)0x4f, (byte)0x20, (byte)0x37, (byte)0x3b, // seq number
+ (byte)0x11, (byte)0x1d, (byte)0xbc, (byte)0x54, // ack number
+ (byte)0x80, // header length
+ (byte)0x18, // flags
+ (byte)0x00, (byte)0x2e, // windows size
+ (byte)0xac, (byte)0x78, // check sum
+ (byte)0x00, (byte)0x00,
+ (byte)0x01, (byte)0x01, (byte)0x08, (byte)0x0a, // options
+ (byte)0x00, (byte)0x06, (byte)0xd4, (byte)0x48, // options
+ (byte)0x6e, (byte)0xcc, (byte)0x76, (byte)0xbd // options
+ };
+
+ /**
+ * Using this object to lock and protectd the two counters
+ * used for tcp seq and ack numbers.
+ */
+ private Object tcpCounterLock = new Object();
+
+ /**
+ * The seq that the sender will send.
+ */
+ private long srcCount = 1;
+
+ /**
+ * This is the ack number send from the sender.
+ */
+ private long dstCount = 1;
+
+ /**
+ * A counter watching how much has been written to the file.
+ */
+ private long written = 0;
+
+ /**
+ * The limit for the file size.
+ * 0 means no limit.
+ */
+ private long limit = 5000000;
+
+ /**
+ * The counter for number of files.
+ */
+ private int logfileCount = 3;
+
+ /**
+ * All the files we can use for writing.
+ */
+ private File[] files;
+
+ /**
+ * Starting the packet logger. Generating the files we can use,
+ * rotate any previous files and open the current file for writing.
+ */
+ public void start()
+ {
+ limit = PacketLoggingActivator.getConfigurationService().getLong(
+ PacketLoggingActivator.PACKET_LOGGING_FILE_SIZE_PROPERTY_NAME,
+ limit);
+
+ logfileCount = PacketLoggingActivator.getConfigurationService().getInt(
+ PacketLoggingActivator.PACKET_LOGGING_FILE_COUNT_PROPERTY_NAME,
+ logfileCount);
+
+ saverThread.start();
+ }
+
+ /**
+ * Generates the files we will later use for writing.
+ * @throws Exception
+ */
+ private void getFileNames()
+ throws Exception
+ {
+ files = new File[getLogfileCount()];
+ for(int i = 0; i < getLogfileCount(); i++)
+ {
+ files[i] = PacketLoggingActivator.getFileAccessService()
+ .getPrivatePersistentFile("log/sip-communicator" + i + ".pcap");
+ }
+ }
+
+ /**
+ * Rotates any existing files and use the newly created first one
+ * for writing.
+ * @throws IOException
+ */
+ private void rotateFiles()
+ throws IOException
+ {
+ if(outputStream != null)
+ {
+ outputStream.flush();
+ outputStream.close();
+ }
+
+ for (int i = getLogfileCount() -2; i >= 0; i--)
+ {
+ File f1 = files[i];
+ File f2 = files[i+1];
+ if (f1.exists())
+ {
+ if (f2.exists())
+ {
+ f2.delete();
+ }
+ f1.renameTo(f2);
+ }
+ }
+
+ outputStream = new FileOutputStream(files[0]);
+ written = 0;
+ createGlobalHeader();
+ }
+
+ /**
+ * Stops the packet logging.
+ */
+ public void stop()
+ {
+ saverThread.stopRunning();
+
+ if(outputStream != null)
+ {
+ try
+ {
+ outputStream.flush();
+ outputStream.close();
+ }
+ catch(IOException e)
+ {
+ e.printStackTrace();
+ }
+ outputStream = null;
+ }
+ }
+
+ /**
+ * Creates pcap file global header.
+ * @throws IOException
+ */
+ private void createGlobalHeader()
+ throws IOException
+ {
+ /* magic number(swapped) */
+ outputStream.write(0xd4);
+ outputStream.write(0xc3);
+ outputStream.write(0xb2);
+ outputStream.write(0xa1);
+
+ /* major version number */
+ outputStream.write(0x02);
+ outputStream.write(0x00);
+
+ /* minor version number */
+ outputStream.write(0x04);
+ outputStream.write(0x00);
+
+ /* GMT to local correction */
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+
+ /* accuracy of timestamps */
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+
+ /* max length of captured packets, in octets */
+ outputStream.write(0xff);
+ outputStream.write(0xff);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+
+ /* data link type(ethernet) */
+ outputStream.write(0x01);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ outputStream.write(0x00);
+ }
+
+ /**
+ * Checks is logging globally enabled for the service.
+ *
+ * @return is logging enabled.
+ */
+ public boolean isLoggingEnabled()
+ {
+ return PacketLoggingActivator.isGlobalLoggingEnabled();
+ }
+
+ /**
+ * Checks is logging globally enabled for and is it currently
+ * available fo the given service.
+ *
+ * @param protocol that is checked.
+ * @return is logging enabled.
+ */
+ public boolean isLoggingEnabled(ProtocolName protocol)
+ {
+ switch(protocol)
+ {
+ case SIP:
+ return PacketLoggingActivator.isGlobalLoggingEnabled()
+ && PacketLoggingActivator.isSipLoggingEnabled();
+ case JABBER:
+ return PacketLoggingActivator.isGlobalLoggingEnabled()
+ && PacketLoggingActivator.isJabberLoggingEnabled();
+ case RTP:
+ return PacketLoggingActivator.isGlobalLoggingEnabled()
+ && PacketLoggingActivator.isRTPLoggingEnabled();
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Log a packet with all the required information.
+ *
+ * @param protocol the source protocol that logs this packet.
+ * @param sourceAddress the source address of the packet.
+ * @param sourcePort the source port of the packet.
+ * @param destinationAddress the destination address.
+ * @param destinationPort the destination port.
+ * @param transport the transport this packet uses.
+ * @param sender are we the sender of the packet or not.
+ * @param packetContent the packet content.
+ */
+ public void logPacket(
+ ProtocolName protocol,
+ byte[] sourceAddress, int sourcePort,
+ byte[] destinationAddress, int destinationPort,
+ TransportName transport,
+ boolean sender,
+ byte[] packetContent)
+ {
+ this.logPacket(protocol, sourceAddress, sourcePort,
+ destinationAddress, destinationPort,
+ transport, sender,
+ packetContent, 0, packetContent.length);
+ }
+
+ /**
+ * Log a packet with all the required information.
+ *
+ * @param protocol the source protocol that logs this packet.
+ * @param sourceAddress the source address of the packet.
+ * @param sourcePort the source port of the packet.
+ * @param destinationAddress the destination address.
+ * @param destinationPort the destination port.
+ * @param transport the transport this packet uses.
+ * @param sender are we the sender of the packet or not.
+ * @param packetContent the packet content.
+ * @param packetOffset the packet content offset.
+ * @param packetLength the packet content length.
+ */
+ public void logPacket(
+ ProtocolName protocol,
+ byte[] sourceAddress,
+ int sourcePort,
+ byte[] destinationAddress,
+ int destinationPort,
+ TransportName transport,
+ boolean sender,
+ byte[] packetContent,
+ int packetOffset,
+ int packetLength)
+ {
+ saverThread.queuePacket(
+ new Packet(protocol,
+ sourceAddress,
+ sourcePort,
+ destinationAddress,
+ destinationPort,
+ transport,
+ sender,
+ packetContent,
+ packetOffset,
+ packetLength));
+ }
+
+ /**
+ * Dump the packet to the output file stream.
+ *
+ * @param packet the packet ot save.
+ * @throws Exception when error occurs saving to file stream or when
+ * rotating files.
+ */
+ private void savePacket(Packet packet)
+ throws Exception
+ {
+ // if one of the addresses is ipv4 we are using ipv4,
+ // local udp addresses come as 0.0.0.0.0....0.0.0 when
+ // ipv6 is enabled in the underlying os
+ boolean isIPv4 = packet.sourceAddress.length == 4
+ || packet.destinationAddress.length == 4;
+
+ byte[] ipHeader;
+
+ if(isIPv4)
+ {
+ ipHeader = new byte[ipHeaderTemplate.length];
+ System.arraycopy(
+ ipHeaderTemplate, 0, ipHeader, 0, ipHeader.length);
+ System.arraycopy(packet.sourceAddress,
+ 0,
+ ipHeader,
+ 12,
+ 4);
+ System.arraycopy(packet.destinationAddress,
+ 0,
+ ipHeader,
+ 16,
+ 4);
+ }
+ else
+ {
+ ipHeader = new byte[ip6HeaderTemplate.length];
+ System.arraycopy(
+ ip6HeaderTemplate, 0, ipHeader, 0, ipHeader.length);
+ System.arraycopy(packet.sourceAddress,
+ 0,
+ ipHeader,
+ 8,
+ 16);
+
+ System.arraycopy(packet.destinationAddress,
+ 0,
+ ipHeader,
+ 24,
+ 16);
+ }
+
+ byte[] transportHeader;
+ short len;
+ if(packet.transport == TransportName.UDP)
+ {
+ byte[] udpHeader = new byte[udpHeaderTemplate.length];
+ transportHeader = udpHeader;
+ System.arraycopy(udpHeaderTemplate, 0,
+ udpHeader, 0, udpHeader.length);
+
+ writeShort(packet.sourcePort, udpHeader, 0);
+ writeShort(packet.destinationPort, udpHeader, 2);
+ len = (short)(packet.packetLength + udpHeader.length);
+ writeShort(len, udpHeader, 4);
+ }
+ else
+ {
+ transportHeader = new byte[tcpHeaderTemplate.length];
+ System.arraycopy(tcpHeaderTemplate, 0, transportHeader,
+ 0, transportHeader.length);
+
+ writeShort(packet.sourcePort, transportHeader, 0);
+ writeShort(packet.destinationPort, transportHeader, 2);
+
+ len = (short)(packet.packetLength + transportHeader.length);
+
+ if(packet.sender)
+ {
+ long seqnum;
+ long acknum;
+ synchronized(tcpCounterLock)
+ {
+ seqnum = srcCount;
+ srcCount += packet.packetLength;
+ acknum = dstCount;
+ }
+
+ intToBytes((int)(seqnum & 0xffffffff),
+ transportHeader, 4);
+ intToBytes((int)(acknum & 0xffffffff),
+ transportHeader, 8);
+ }
+ else
+ {
+ long seqnum;
+ long acknum;
+ synchronized(tcpCounterLock)
+ {
+ seqnum = dstCount;
+ dstCount += packet.packetLength;
+ acknum = srcCount;
+ }
+
+ intToBytes((int)(seqnum & 0xffffffff),
+ transportHeader, 4);
+ intToBytes((int)(acknum & 0xffffffff),
+ transportHeader, 8);
+ }
+ }
+
+ // now set ip header total length
+ if(isIPv4)
+ {
+ short ipTotalLen = (short)(len + ipHeader.length);
+ writeShort(ipTotalLen, ipHeader, 2);
+
+ if(packet.transport == TransportName.UDP)
+ ipHeader[9] = (byte)0x11;
+ else
+ ipHeader[9] = (byte)0x06;
+
+ int chk2 = computeChecksum(ipHeader);
+ ipHeader[10] = (byte) (chk2 >> 8);
+ ipHeader[11] = (byte) (chk2 & 0xff);
+ }
+ else
+ {
+ writeShort(len, ipHeader, 4);
+
+ if(packet.transport == TransportName.UDP)
+ ipHeader[6] = (byte)0x11;
+ else
+ ipHeader[6] = (byte)0x06;
+ }
+
+ long current = System.currentTimeMillis();
+ int tsSec = (int)(current/1000);
+ int tsUsec = (int)((current%1000) * 1000);
+ int feakHeaderLen = fakeEthernetHeader.length +
+ ipHeader.length + transportHeader.length;
+ int inclLen = packet.packetLength + feakHeaderLen;
+ int origLen = inclLen;
+
+ synchronized(this)
+ {
+ // open files only if needed
+ if(outputStream == null)
+ {
+ getFileNames();
+ rotateFiles();// this one opens the file for write
+ }
+
+ if(getLimit() > 0 && written > getLimit())
+ rotateFiles();
+
+ addInt(tsSec);
+ addInt(tsUsec);
+ addInt(inclLen);
+ addInt(origLen);
+
+ outputStream.write(fakeEthernetHeader);
+ outputStream.write(ipHeader);
+ outputStream.write(transportHeader);
+ outputStream.write(
+ packet.packetContent,
+ packet.packetOffset,
+ packet.packetLength);
+ outputStream.flush();
+
+ written += inclLen + 16;
+ }
+ }
+
+ /**
+ * Writes int to the file. Used for packet headers.
+ * @param d the value to write.
+ * @throws IOException
+ */
+ private void addInt(int d)
+ throws IOException
+ {
+ outputStream.write ((d & 0xff));
+ outputStream.write(((d & 0xff00) >> 8));
+ outputStream.write(((d & 0xff0000) >> 16));
+ outputStream.write(((d & 0xff000000) >> 24));
+ }
+
+ /**
+ * Converts a 32-bit word representation of an IPv4 address to a
+ * byte array.
+ *
+ * @param address The 32-bit word representation of the IPv4 address.
+ * @param data The byte array in which to store the IPv4 data.
+ * @param offset The offset into the array where the data start.
+ */
+ private static final void intToBytes(int address, byte[] data,
+ int offset)
+ {
+ data[offset] = (byte)(0xff & (address >>> 24));
+ data[offset + 1] = (byte)(0xff & (address >>> 16));
+ data[offset + 2] = (byte)(0xff & (address >>> 8));
+ data[offset + 3] = (byte)(0xff & address);
+ }
+
+ /**
+ * Puts the short value to the array.
+ * @param value value to convert to bytes.
+ * @param data destination data
+ * @param offset offset in the data
+ */
+ private static void writeShort(int value, byte[] data, int offset)
+ {
+ data[offset] = (byte) (value >> 8);
+ data[offset + 1] = (byte) value;
+ }
+
+ /**
+ * Calculates checksums assuming the checksum is a 16-bit header field.
+ */
+ private int computeChecksum(byte[] data)
+ {
+ int total = 0;
+ int i = 0;
+
+ // Don't Skip existing checksum cause its set to 0000
+ int imax = data.length - (data.length % 2);
+
+ while(i < imax)
+ total+=(((data[i++] & 0xff) << 8) | (data[i++] & 0xff));
+
+ if(i < data.length)
+ total+=((data[i] & 0xff) << 8);
+
+ // Fold to 16 bits
+ while((total & 0xffff0000) != 0)
+ total = (total & 0xffff) + (total >>> 16);
+
+ total = (~total & 0xffff);
+
+ return total;
+ }
+
+ /**
+ * The limit for the file size. 0 means no limit.
+ * @return the file size limit.
+ */
+ public long getLimit()
+ {
+ return limit;
+ }
+
+ /**
+ * Changes the file size limit.
+ * @param limit the new limit size.
+ */
+ public void setLimit(long limit)
+ {
+ this.limit = limit;
+
+ PacketLoggingActivator.getConfigurationService().setProperty(
+ PacketLoggingActivator.PACKET_LOGGING_FILE_SIZE_PROPERTY_NAME,
+ limit);
+ }
+
+ /**
+ * The counter for number of files.
+ * @return the number of file counts.
+ */
+ public int getLogfileCount()
+ {
+ return logfileCount;
+ }
+
+ /**
+ * Changes file count.
+ * @param logfileCount the new file count.
+ */
+ public void setLogfileCount(int logfileCount)
+ {
+ this.logfileCount = logfileCount;
+
+ PacketLoggingActivator.getConfigurationService().setProperty(
+ PacketLoggingActivator.PACKET_LOGGING_FILE_COUNT_PROPERTY_NAME,
+ logfileCount);
+ }
+
+ /**
+ * The data we receive and that we will dump in a file.
+ */
+ private class Packet
+ {
+ /**
+ * The protocol logging this packet.
+ */
+ ProtocolName protocol;
+
+ /**
+ * The source address of the packet.
+ */
+ byte[] sourceAddress;
+
+ /**
+ * The source port of the packet.
+ */
+ int sourcePort;
+
+ /**
+ * The destination address of the packet.
+ */
+ byte[] destinationAddress;
+
+ /**
+ * The destination port of the packet.
+ */
+ int destinationPort;
+
+ /**
+ * Is the packet a udp one.
+ */
+ TransportName transport;
+
+ /**
+ * Are we sending the packet, or false if we are receiving.
+ */
+ boolean sender;
+
+ /**
+ * Array containing packet content.
+ */
+ byte[] packetContent;
+
+ /**
+ * The offset in the packetContent where packet content is.
+ */
+ int packetOffset;
+
+ /**
+ * The length of the packet content.
+ */
+ int packetLength;
+
+ /**
+ * Creates a packet with the needed data.
+ * @param protocol the source protocol that logs this packet.
+ * @param sourceAddress The source address of the packet.
+ * @param sourcePort The source port of the packet.
+ * @param destinationAddress The destination address of the packet.
+ * @param destinationPort The destination port of the packet.
+ * @param transport the transport this packet uses.
+ * @param sender Are we sending the packet,
+ * or false if we are receiving.
+ * @param packetContent Array containing packet content.
+ * @param packetOffset The offset in the packetContent
+ * where packet content is.
+ * @param packetLength The length of the packet content.
+ */
+ private Packet(ProtocolName protocol,
+ byte[] sourceAddress,
+ int sourcePort,
+ byte[] destinationAddress,
+ int destinationPort,
+ TransportName transport,
+ boolean sender,
+ byte[] packetContent,
+ int packetOffset,
+ int packetLength)
+ {
+ this.protocol = protocol;
+ this.sourceAddress = sourceAddress;
+ this.sourcePort = sourcePort;
+ this.destinationAddress = destinationAddress;
+ this.destinationPort = destinationPort;
+ this.transport = transport;
+ this.sender = sender;
+ this.packetContent = packetContent;
+ this.packetOffset = packetOffset;
+ this.packetLength = packetLength;
+ }
+ }
+
+ /**
+ * Dumps packet in separate thread so we don't block
+ * our calling thread.
+ */
+ private class SaverThread
+ extends Thread
+ {
+ /**
+ * start/stop indicator.
+ */
+ private boolean stopped = true;
+
+ /**
+ * List of packets queued to be written in the file.
+ */
+ private List<Packet> packetsToDump = new ArrayList<Packet>();
+
+ /**
+ * Sends instant messages in separate thread so we don't block
+ * our calling thread.
+ */
+ public void run()
+ {
+ stopped = false;
+
+ while(!stopped)
+ {
+ Packet pktToSave = null;
+
+ synchronized(this)
+ {
+ if(packetsToDump.isEmpty())
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException iex)
+ {
+ }
+ }
+
+ if(!packetsToDump.isEmpty())
+ pktToSave = packetsToDump.remove(0);
+ }
+
+ if(pktToSave != null)
+ {
+ try
+ {
+ savePacket(pktToSave);
+ }
+ catch(Throwable t)
+ {
+ logger.error("Error writing packet to file", t);
+ }
+ }
+ }
+ }
+
+ /**
+ * Interrupts this sender so that it would no longer send messages.
+ */
+ public synchronized void stopRunning()
+ {
+ stopped = true;
+ notifyAll();
+ }
+
+ /**
+ * Schedule new packet for save.
+ * @param packet new packet to save.
+ */
+ public synchronized void queuePacket(Packet packet)
+ {
+ packetsToDump.add(packet);
+ notifyAll();
+ }
+ }
+}