diff options
author | Damian Minkov <damencho@jitsi.org> | 2010-12-03 08:53:04 +0000 |
---|---|---|
committer | Damian Minkov <damencho@jitsi.org> | 2010-12-03 08:53:04 +0000 |
commit | a21b3d7353c9fc9ed47288333d3f793b01ab5689 (patch) | |
tree | 5b8210d76dc28c82998188252958e6c6beaec96a | |
parent | 4785e35f694e73a65b6163f31c9aac4f58aa925f (diff) | |
download | jitsi-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.
25 files changed, 2077 insertions, 33 deletions
@@ -920,7 +920,7 @@ bundle-replacement,bundle-youtube,bundle-dailymotion,bundle-smiley, bundle-vimeo,bundle-vbox7,bundle-metacafe,bundle-flickr,bundle-hulu, bundle-twitpic,bundle-directimage,bundle-bliptv,bundle-viddler, - bundle-plugin-chatconfig,bundle-certificate, + bundle-plugin-chatconfig,bundle-certificate,bundle-packetlogging, bundle-provdisc,bundle-provdisc-dhcp,bundle-provdisc-mdns, bundle-provisioning"/> @@ -2584,4 +2584,15 @@ org.apache.http.util"/> prefix="net/java/sip/communicator/impl/certificate" /> </jar> </target> + + + <target name="bundle-packetlogging"> + <jar compress="false" destfile="${bundles.dest}/packetlogging.jar" + manifest="${src}/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/service/packetlogging" + prefix="net/java/sip/communicator/service/packetlogging" /> + <zipfileset dir="${dest}/net/java/sip/communicator/impl/packetlogging" + prefix="net/java/sip/communicator/impl/packetlogging" /> + </jar> + </target> </project> diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index 21d80cf..1812171 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -47,6 +47,7 @@ felix.auto.start.40= \ felix.auto.start.42= \ reference:file:sc-bundles/credentialsstorage.jar \ + reference:file:sc-bundles/packetlogging.jar \ reference:file:sc-bundles/defaultresources.jar felix.auto.start.45= \ diff --git a/lib/felix.unit.test.properties b/lib/felix.unit.test.properties index a614912..04dbb47 100644 --- a/lib/felix.unit.test.properties +++ b/lib/felix.unit.test.properties @@ -73,6 +73,7 @@ felix.auto.start.5= \ reference:file:sc-bundles/protocol-media.jar \ reference:file:sc-bundles/httpcore.jar \ reference:file:sc-bundles/httpclient.jar \ + reference:file:sc-bundles/packetlogging.jar \ reference:file:sc-bundles/certificate.jar felix.auto.start.6= \ diff --git a/lib/installer-exclude/jain-sdp.jar b/lib/installer-exclude/jain-sdp.jar Binary files differindex bd45263..60eb511 100644 --- a/lib/installer-exclude/jain-sdp.jar +++ b/lib/installer-exclude/jain-sdp.jar diff --git a/lib/installer-exclude/jain-sip-api.jar b/lib/installer-exclude/jain-sip-api.jar Binary files differindex 06c2587..3ed1a74 100644 --- a/lib/installer-exclude/jain-sip-api.jar +++ b/lib/installer-exclude/jain-sip-api.jar diff --git a/lib/installer-exclude/jain-sip-ri.jar b/lib/installer-exclude/jain-sip-ri.jar Binary files differindex a887d0a..1011fd5 100644 --- a/lib/installer-exclude/jain-sip-ri.jar +++ b/lib/installer-exclude/jain-sip-ri.jar diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index d1dafc3..682694d 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -1094,3 +1094,13 @@ plugin.provisioning.DNS=DNS plugin.provisioning.BONJOUR=Bonjour
plugin.provisioning.MANUAL=Manually specify a provisioning URI
plugin.provisioning.URI=URI
+
+# packet logging service
+impl.packetlogging.PACKET_LOGGING_CONFIG=Packet Logging
+impl.packetlogging.ENABLE_DISABLE=Enable Packet Logging
+impl.packetlogging.PACKET_LOGGING_RTP=RTP
+impl.packetlogging.PACKET_LOGGING_RTP_DESCRIPTION=Will save only few initial packets and one on every 5000.
+impl.packetlogging.PACKET_LOGGING_DESCRIPTION=<html>Prints debug packets of various protocols in the <br>log folder using pcap format (tcpdump/wireshark).</html>
+impl.packetlogging.PACKET_LOGGING_FILE_COUNT=Number of log files
+impl.packetlogging.PACKET_LOGGING_FILE_SIZE=Maximum file size (in KB)
+
diff --git a/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java b/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java index 9acd772..346b28d 100644 --- a/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java +++ b/src/net/java/sip/communicator/impl/neomedia/NeomediaActivator.java @@ -14,6 +14,7 @@ import net.java.sip.communicator.service.fileaccess.*; import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.packetlogging.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; @@ -85,6 +86,12 @@ public class NeomediaActivator private ServiceRegistration mediaServiceRegistration; /** + * The OSGi <tt>PacketLoggingService</tt> of {@link #mediaServiceImpl} in + * {@link #bundleContext} and used for debugging. + */ + private static PacketLoggingService packetLoggingService = null; + + /** * Starts the execution of the neomedia bundle in the specified context. * * @param bundleContext the context in which the neomedia bundle is to start @@ -328,4 +335,27 @@ public class NeomediaActivator = ResourceManagementServiceUtils.getService(bundleContext); return resources; } + + /** + * Returns a reference to the <tt>PacketLoggingService</tt> implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a reference to a <tt>PacketLoggingService</tt> implementation + * currently registered in the bundle context or null if no such + * implementation was found. + */ + public static PacketLoggingService getPacketLogging() + { + if (packetLoggingService == null) + { + ServiceReference plReference + = bundleContext.getServiceReference( + PacketLoggingService.class.getName()); + + packetLoggingService + = (PacketLoggingService)bundleContext.getService(plReference); + } + return packetLoggingService; + } }
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java index 25f7f53..70cfd25 100755 --- a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java @@ -6,6 +6,8 @@ */
package net.java.sip.communicator.impl.neomedia;
+import net.java.sip.communicator.service.packetlogging.*;
+
import java.io.*;
import java.net.*;
@@ -19,7 +21,6 @@ public class RTPConnectorInputStream implements PushSourceStream,
Runnable
{
-
/**
* The value of the property <tt>controls</tt> of
* <tt>RTPConnectorInputStream</tt> when there are no controls. Explicitly
@@ -70,6 +71,12 @@ public class RTPConnectorInputStream private Thread receiverThread = null;
/**
+ * Used for debugging. As we don't log every packet
+ * we must count them and decide which to log.
+ */
+ private long numberOfPackets = 0;
+
+ /**
* Initializes a new <tt>RTPConnectorInputStream</tt> which is to receive
* packet data from a specific UDP socket.
*
@@ -246,6 +253,7 @@ public class RTPConnectorInputStream try
{
socket.receive(p);
+ numberOfPackets++;
}
catch (IOException e)
{
@@ -253,6 +261,24 @@ public class RTPConnectorInputStream break;
}
+ if(RTPConnectorOutputStream.logPacket(numberOfPackets)
+ && NeomediaActivator.getPacketLogging().isLoggingEnabled(
+ PacketLoggingService.ProtocolName.RTP))
+ {
+ NeomediaActivator.getPacketLogging()
+ .logPacket(
+ PacketLoggingService.ProtocolName.RTP,
+ p.getAddress().getAddress(),
+ p.getPort(),
+ socket.getLocalAddress().getAddress(),
+ socket.getLocalPort(),
+ PacketLoggingService.TransportName.UDP,
+ false,
+ p.getData(),
+ p.getOffset(),
+ p.getLength());
+ }
+
pkt = createRawPacket(p);
/*
diff --git a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java index 2c1eac6..4d1c2de 100755 --- a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java @@ -6,6 +6,8 @@ */
package net.java.sip.communicator.impl.neomedia;
+import net.java.sip.communicator.service.packetlogging.*;
+
import java.io.*;
import java.net.*;
import java.util.*;
@@ -60,6 +62,12 @@ public class RTPConnectorOutputStream = new LinkedBlockingQueue<RawPacket>();
/**
+ * Used for debugging. As we don't log every packet
+ * we must count them and decide which to log.
+ */
+ private long numberOfPackets = 0;
+
+ /**
* Initializes a new <tt>RTPConnectorOutputStream</tt> which is to send
* packet data out through a specific UDP socket.
*
@@ -158,6 +166,26 @@ public class RTPConnectorOutputStream }
/**
+ * We don't log every rtp traffic.
+ * We log only first then 300,500 and 1000 packets and
+ * then every 5000 packet.
+ *
+ * @param numOfPacket current packet number.
+ * @return wether we should log the current packet.
+ */
+ static boolean logPacket(long numOfPacket)
+ {
+ if(numOfPacket == 1
+ || numOfPacket == 300
+ || numOfPacket == 500
+ || numOfPacket == 1000
+ || numOfPacket%5000 == 0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
* Sends a specific RTP packet through the <tt>DatagramSocket</tt> of this
* <tt>OutputDataSource</tt>.
*
@@ -168,6 +196,7 @@ public class RTPConnectorOutputStream */
private boolean send(RawPacket packet)
{
+ numberOfPackets++;
for (InetSocketAddress target : targets)
{
try
@@ -179,6 +208,24 @@ public class RTPConnectorOutputStream packet.getLength(),
target.getAddress(),
target.getPort()));
+
+ if(logPacket(numberOfPackets)
+ && NeomediaActivator.getPacketLogging().isLoggingEnabled(
+ PacketLoggingService.ProtocolName.RTP))
+ {
+ NeomediaActivator.getPacketLogging()
+ .logPacket(
+ PacketLoggingService.ProtocolName.RTP,
+ socket.getLocalAddress().getAddress(),
+ socket.getLocalPort(),
+ target.getAddress().getAddress(),
+ target.getPort(),
+ PacketLoggingService.TransportName.UDP,
+ true,
+ packet.getBuffer(),
+ packet.getOffset(),
+ packet.getLength());
+ }
}
catch (IOException ex)
{
diff --git a/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf b/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf index fd5cbb2..68e373a 100644 --- a/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf +++ b/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf @@ -23,6 +23,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.fileaccess, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.service.packetlogging, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.event, net.java.sip.communicator.service.resources, diff --git a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingActivator.java b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingActivator.java new file mode 100644 index 0000000..9edcf2b --- /dev/null +++ b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingActivator.java @@ -0,0 +1,392 @@ +/* + * 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.configuration.*; +import net.java.sip.communicator.service.fileaccess.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.neomedia.*; +import net.java.sip.communicator.service.packetlogging.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.Logger; +import org.osgi.framework.*; + +import java.util.*; + +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.*; + +/** + * Creates and registers Packet Logging service into OSGi. + * Also handles saving and retrieving configuration options for + * the service and is used from the configuration form. + * + * @author Damian Minkov + */ +public class PacketLoggingActivator + implements BundleActivator +{ + /** + * Our logging. + */ + private static Logger logger = + Logger.getLogger(PacketLoggingActivator.class); + + /** + * The OSGI bundle context. + */ + private static BundleContext bundleContext = null; + + /** + * Our packet logging service instance. + */ + private static PacketLoggingServiceImpl packetLoggingService = null; + + /** + * The configuration service. + */ + private static ConfigurationService configurationService = null; + + /** + * The service giving access to files. + */ + private static FileAccessService fileAccessService; + + /** + * The resource service. + */ + private static ResourceManagementService resourceService; + + /** + * Configuration property for packet logging enabled/disabled. + */ + private final static String PACKET_LOGGING_ENABLED_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_ENABLED"; + + /** + * Configuration property for packet logging for + * sip protocol enabled/disabled. + */ + private final static String PACKET_LOGGING_SIP_ENABLED_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_SIP_ENABLED"; + + /** + * Configuration property for packet logging for + * jabber protocol enabled/disabled. + */ + private final static String PACKET_LOGGING_JABBER_ENABLED_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_JABBER_ENABLED"; + + /** + * Configuration property for packet logging for + * RTP enabled/disabled. + */ + private final static String PACKET_LOGGING_RTP_ENABLED_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_RTP_ENABLED"; + + /** + * Configuration property for packet logging file count. + */ + final static String PACKET_LOGGING_FILE_COUNT_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_FILE_COUNT"; + + /** + * Configuration property for packet logging file size. + */ + final static String PACKET_LOGGING_FILE_SIZE_PROPERTY_NAME + = "net.java.sip.communicator.packetlogging.PACKET_LOGGING_FILE_SIZE"; + + /** + * Is Packet Logging Service enabled. + */ + private static boolean globalLoggingEnabled = false; + + /** + * Is Packet Logging Service enabled for sip protocol. + */ + private static boolean sipLoggingEnabled = false; + + /** + * Is Packet Logging Service enabled for jabber protocol. + */ + private static boolean jabberLoggingEnabled = false; + + /** + * Is Packet Logging Service enabled for rtp. + */ + private static boolean rtpLoggingEnabled = false; + + /** + * Creates a PacketLoggingServiceImpl, starts it, and registers it as a + * PacketLoggingService. + * + * @param bundleContext OSGI bundle context + * @throws Exception if starting the PacketLoggingServiceImpl. + */ + public void start(BundleContext bundleContext) + throws + Exception + { + PacketLoggingActivator.bundleContext = bundleContext; + + packetLoggingService = new PacketLoggingServiceImpl(); + + getPacketLoggingService().start(); + + loadConfig(); + + bundleContext.registerService(PacketLoggingService.class.getName(), + getPacketLoggingService(), null); + + if (logger.isInfoEnabled()) + logger.info("Packet Logging Service ...[REGISTERED]"); + + // Config Form + Dictionary<String, String> packetLoggingProps + = new Hashtable<String, String>(); + packetLoggingProps.put( + ConfigurationForm.FORM_TYPE, + ConfigurationForm.ADVANCED_TYPE); + bundleContext.registerService( + ConfigurationForm.class.getName(), + new LazyConfigurationForm( + PacketLoggingConfigForm.class.getName(), + getClass().getClassLoader(), + null, + "impl.packetlogging.PACKET_LOGGING_CONFIG", + 1200, + true), + packetLoggingProps); + } + + /** + * Stops the Packet Logging bundle + * + * @param bundleContext the OSGI bundle context + */ + public void stop(BundleContext bundleContext) + throws + Exception + { + if(getPacketLoggingService() != null) + getPacketLoggingService().stop(); + if (logger.isInfoEnabled()) + logger.info("Packet Logging Service ...[STOPPED]"); + } + + /** + * Reads the configurations and loads them. + */ + private void loadConfig() + { + globalLoggingEnabled = getConfigurationService().getBoolean( + PACKET_LOGGING_ENABLED_PROPERTY_NAME, false); + + sipLoggingEnabled = getConfigurationService().getBoolean( + PACKET_LOGGING_SIP_ENABLED_PROPERTY_NAME, false); + + jabberLoggingEnabled = getConfigurationService().getBoolean( + PACKET_LOGGING_JABBER_ENABLED_PROPERTY_NAME, false); + + rtpLoggingEnabled = getConfigurationService().getBoolean( + PACKET_LOGGING_RTP_ENABLED_PROPERTY_NAME, false); + } + + /** + * Returns a reference to a ConfigurationService implementation currently + * registered in the bundle context or null if no such implementation was + * found. + * + * @return a currently valid implementation of the ConfigurationService. + */ + public static ConfigurationService getConfigurationService() + { + if (configurationService == null) + { + ServiceReference confReference + = bundleContext.getServiceReference( + ConfigurationService.class.getName()); + configurationService + = (ConfigurationService) bundleContext.getService(confReference); + } + return configurationService; + } + + /** + * Returns the <tt>FileAccessService</tt> obtained from the bundle context. + * + * @return the <tt>FileAccessService</tt> obtained from the bundle context + */ + public static FileAccessService getFileAccessService() + { + if (fileAccessService == null) + { + fileAccessService + = ServiceUtils.getService( + bundleContext, + FileAccessService.class); + } + return fileAccessService; + } + + /** + * Returns the <tt>ResourceManagementService</tt> obtained from the + * bundle context. + * + * @return the <tt>ResourceManagementService</tt> obtained from the + * bundle context + */ + public static ResourceManagementService getResourceService() + { + if (resourceService == null) + { + ServiceReference resourceReference + = bundleContext.getServiceReference( + ResourceManagementService.class.getName()); + + resourceService = + (ResourceManagementService) bundleContext + .getService(resourceReference); + } + + return resourceService; + } + + /** + * Checks whether packet logging is enabled in the configuration. + * @return <tt>true</tt> if packet logging is enabled. + */ + public static boolean isGlobalLoggingEnabled() + { + return globalLoggingEnabled; + } + + /** + * Checks whether packet logging is enabled in the configuration + * for sip protocol. + * @return <tt>true</tt> if packet logging is enabled for sip protocol. + */ + public static boolean isSipLoggingEnabled() + { + return sipLoggingEnabled; + } + + /** + * Checks whether packet logging is enabled in the configuration + * for jabber protocol. + * @return <tt>true</tt> if packet logging is enabled for jabber protocol. + */ + public static boolean isJabberLoggingEnabled() + { + return jabberLoggingEnabled; + } + + /** + * Checks whether packet logging is enabled in the configuration + * for RTP. + * @return <tt>true</tt> if packet logging is enabled for RTP. + */ + public static boolean isRTPLoggingEnabled() + { + return rtpLoggingEnabled; + } + + /** + * Change whether packet logging is enabled and save it in configuration. + * @param enabled <tt>true</tt> if we enable it. + */ + static void setGlobalLoggingEnabled(boolean enabled) + { + if(enabled) + { + getConfigurationService().setProperty( + PACKET_LOGGING_ENABLED_PROPERTY_NAME, Boolean.TRUE); + } + else + { + getConfigurationService().removeProperty( + PACKET_LOGGING_ENABLED_PROPERTY_NAME); + + // as we are globbally off, set it and to services + sipLoggingEnabled = false; + jabberLoggingEnabled = false; + rtpLoggingEnabled = false; + } + } + + /** + * Change whether packet logging for sip protocol is enabled + * and save it in configuration. + * @param enabled <tt>true</tt> if we enable it. + */ + public static void setSipLoggingEnabled(boolean enabled) + { + if(enabled) + { + getConfigurationService().setProperty( + PACKET_LOGGING_SIP_ENABLED_PROPERTY_NAME, Boolean.TRUE); + } + else + { + getConfigurationService().removeProperty( + PACKET_LOGGING_SIP_ENABLED_PROPERTY_NAME); + } + + sipLoggingEnabled = enabled; + } + + /** + * Change whether packet logging for jabber protocol is enabled + * and save it in configuration. + * @param enabled <tt>true</tt> if we enable it. + */ + public static void setJabberLoggingEnabled(boolean enabled) + { + if(enabled) + { + getConfigurationService().setProperty( + PACKET_LOGGING_JABBER_ENABLED_PROPERTY_NAME, Boolean.TRUE); + } + else + { + getConfigurationService().removeProperty( + PACKET_LOGGING_JABBER_ENABLED_PROPERTY_NAME); + } + + jabberLoggingEnabled = enabled; + } + + /** + * Change whether packet logging for RTP is enabled + * and save it in configuration. + * @param enabled <tt>true</tt> if we enable it. + */ + public static void setRTPLoggingEnabled(boolean enabled) + { + if(enabled) + { + getConfigurationService().setProperty( + PACKET_LOGGING_RTP_ENABLED_PROPERTY_NAME, Boolean.TRUE); + } + else + { + getConfigurationService().removeProperty( + PACKET_LOGGING_RTP_ENABLED_PROPERTY_NAME); + } + + rtpLoggingEnabled = true; + } + + /** + * Our packet logging service instance. + */ + public static PacketLoggingServiceImpl getPacketLoggingService() + { + return packetLoggingService; + } +} diff --git a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigForm.java b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigForm.java new file mode 100644 index 0000000..b268c3b --- /dev/null +++ b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigForm.java @@ -0,0 +1,301 @@ +/* + * 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.resources.*; +import net.java.sip.communicator.util.swing.*; + +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; + +/** + * The Packet Logging Service configuration form. + * @author Damian Minkov + */ +public class PacketLoggingConfigForm + extends TransparentPanel + implements ActionListener, + DocumentListener +{ + /** + * The enable packet logging check box. + */ + private JCheckBox enableCheckBox; + + /** + * Check box to enable/disable packet debug of sip protocol. + */ + private JCheckBox sipProtocolCheckBox; + + /** + * Check box to enable/disable packet debug of jabber protocol. + */ + private JCheckBox jabberProtocolCheckBox; + + /** + * Check box to enable/disable packet debug of media protocol/RTP. + */ + private JCheckBox rtpProtocolCheckBox; + + /** + * The file count label. + */ + private JLabel fileCountLabel; + + /** + * The filed for file count value. + */ + private JTextField fileCountField = new JTextField(); + + /** + * The file size label. + */ + private JLabel fileSizeLabel; + + /** + * The filed for file size value. + */ + private JTextField fileSizeField = new JTextField(); + + + /** + * Creates Packet Logging Config form. + */ + public PacketLoggingConfigForm() + { + super(new BorderLayout()); + + init(); + loadValues(); + } + + /** + * Creating the configuration form + */ + private void init() + { + ResourceManagementService resources = + PacketLoggingActivator.getResourceService(); + + enableCheckBox = new JCheckBox( + resources.getI18NString("impl.packetlogging.ENABLE_DISABLE")); + enableCheckBox.addActionListener(this); + + sipProtocolCheckBox = new JCheckBox( + resources.getI18NString("plugin.sipaccregwizz.PROTOCOL_NAME")); + sipProtocolCheckBox.addActionListener(this); + + jabberProtocolCheckBox = new JCheckBox( + resources.getI18NString("plugin.jabberaccregwizz.PROTOCOL_NAME")); + jabberProtocolCheckBox.addActionListener(this); + + rtpProtocolCheckBox = new JCheckBox( + resources.getI18NString("impl.packetlogging.PACKET_LOGGING_RTP")); + rtpProtocolCheckBox.addActionListener(this); + rtpProtocolCheckBox.setToolTipText(resources.getI18NString( + "impl.packetlogging.PACKET_LOGGING_RTP_DESCRIPTION")); + + JPanel mainPanel = new TransparentPanel(); + + add(mainPanel, BorderLayout.NORTH); + + mainPanel.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + + enableCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT); + + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1.0; + c.gridx = 0; + c.gridy = 0; + mainPanel.add(enableCheckBox, c); + + String label = resources.getI18NString( + "impl.packetlogging.PACKET_LOGGING_DESCRIPTION"); + JLabel descriptionLabel = new JLabel(label); + descriptionLabel.setToolTipText(label); + enableCheckBox.setToolTipText(label); + descriptionLabel.setForeground(Color.GRAY); + descriptionLabel.setFont(descriptionLabel.getFont().deriveFont(8)); + c.gridy = 1; + c.insets = new Insets(0, 40, 10, 0); + mainPanel.add(descriptionLabel, c); + + final JPanel loggersButtonPanel + = new TransparentPanel(new GridLayout(0, 1)); + + loggersButtonPanel.setBorder(BorderFactory.createTitledBorder( + resources.getI18NString("service.gui.PROTOCOL"))); + + loggersButtonPanel.add(sipProtocolCheckBox); + loggersButtonPanel.add(jabberProtocolCheckBox); + loggersButtonPanel.add(rtpProtocolCheckBox); + + c.insets = new Insets(0, 20, 10, 0); + c.gridy = 2; + mainPanel.add(loggersButtonPanel, c); + + final JPanel advancedPanel + = new TransparentPanel(new GridLayout(0, 2)); + + advancedPanel.setBorder(BorderFactory.createTitledBorder( + resources.getI18NString("service.gui.ADVANCED"))); + + fileCountField.getDocument().addDocumentListener(this); + fileSizeField.getDocument().addDocumentListener(this); + + fileCountLabel = new JLabel(resources.getI18NString( + "impl.packetlogging.PACKET_LOGGING_FILE_COUNT")); + advancedPanel.add(fileCountLabel); + advancedPanel.add(fileCountField); + fileSizeLabel = new JLabel(resources.getI18NString( + "impl.packetlogging.PACKET_LOGGING_FILE_SIZE")); + advancedPanel.add(fileSizeLabel); + advancedPanel.add(fileSizeField); + + c.gridy = 3; + mainPanel.add(advancedPanel, c); + + } + + /** + * Loading the values stored into configuration form + */ + private void loadValues() + { + enableCheckBox.setSelected( + PacketLoggingActivator.isGlobalLoggingEnabled()); + sipProtocolCheckBox.setSelected( + PacketLoggingActivator.isSipLoggingEnabled()); + jabberProtocolCheckBox.setSelected( + PacketLoggingActivator.isJabberLoggingEnabled()); + rtpProtocolCheckBox.setSelected( + PacketLoggingActivator.isRTPLoggingEnabled()); + fileCountField.setText(String.valueOf(PacketLoggingActivator + .getPacketLoggingService().getLogfileCount())); + fileSizeField.setText(String.valueOf(PacketLoggingActivator + .getPacketLoggingService().getLimit()/1000)); + + updateButtonsState(); + } + + /** + * Update button enable/disable state according enableCheckBox. + */ + private void updateButtonsState() + { + sipProtocolCheckBox.setEnabled(enableCheckBox.isSelected()); + jabberProtocolCheckBox.setEnabled(enableCheckBox.isSelected()); + rtpProtocolCheckBox.setEnabled(enableCheckBox.isSelected()); + fileCountField.setEnabled(enableCheckBox.isSelected()); + fileSizeField.setEnabled(enableCheckBox.isSelected()); + fileSizeLabel.setEnabled(enableCheckBox.isSelected()); + fileCountLabel.setEnabled(enableCheckBox.isSelected()); + } + + /** + * Invoked when an action occurs. + */ + public void actionPerformed(ActionEvent e) + { + Object source = e.getSource(); + + if(source.equals(enableCheckBox)) + { + // turn it on/off in activator + PacketLoggingActivator.setGlobalLoggingEnabled( + enableCheckBox.isSelected()); + updateButtonsState(); + } + else if(source.equals(sipProtocolCheckBox)) + { + PacketLoggingActivator.setSipLoggingEnabled( + sipProtocolCheckBox.isSelected()); + } + else if(source.equals(jabberProtocolCheckBox)) + { + PacketLoggingActivator.setJabberLoggingEnabled( + jabberProtocolCheckBox.isSelected()); + } + else if(source.equals(rtpProtocolCheckBox)) + { + PacketLoggingActivator.setRTPLoggingEnabled( + rtpProtocolCheckBox.isSelected()); + } + } + + /** + * Gives notification that there was an insert into the document. The + * range given by the DocumentEvent bounds the freshly inserted region. + * + * @param e the document event + */ + public void insertUpdate(DocumentEvent e) + { + documentChanged(e); + } + + /** + * Gives notification that a portion of the document has been + * removed. The range is given in terms of what the view last + * saw (that is, before updating sticky positions). + * + * @param e the document event + */ + public void removeUpdate(DocumentEvent e) + { + documentChanged(e); + } + + /** + * Not used. + * + * @param e the document event + */ + public void changedUpdate(DocumentEvent e) + {} + + /** + * A change in the text fields. + * @param e the document event. + */ + private void documentChanged(DocumentEvent e) + { + if(e.getDocument().equals(fileCountField.getDocument())) + { + // set file count only if its un integer + try + { + int newFileCount = Integer.valueOf(fileCountField.getText()); + fileCountField.setForeground(Color.black); + PacketLoggingActivator.getPacketLoggingService() + .setLogfileCount(newFileCount); + } + catch(Throwable t) + { + fileCountField.setForeground(Color.red); + } + } + else if(e.getDocument().equals(fileSizeField.getDocument())) + { + // set file size only if its un integer + try + { + int newFileSize = Integer.valueOf(fileSizeField.getText()); + fileSizeField.setForeground(Color.black); + PacketLoggingActivator.getPacketLoggingService() + .setLimit(newFileSize * 1000); + } + catch(Throwable t) + { + fileSizeField.setForeground(Color.red); + } + } + } +} 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(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf b/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf new file mode 100644 index 0000000..593c203 --- /dev/null +++ b/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf @@ -0,0 +1,20 @@ +Bundle-Activator: net.java.sip.communicator.impl.packetlogging.PacketLoggingActivator +Bundle-Name: Packet Logging service +Bundle-Description: A bundle that provides packet logging in pcap file format +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Import-Package: org.osgi.framework, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.fileaccess, + net.java.sip.communicator.service.gui, + net.java.sip.communicator.service.neomedia, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.resources, + net.java.sip.communicator.util, + net.java.sip.communicator.util.swing, + javax.swing, + javax.swing.border, + javax.swing.event, + javax.swing.text +Export-Package: net.java.sip.communicator.service.packetlogging diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java index 94181b6..552c8be 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java @@ -13,6 +13,7 @@ import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.hid.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.packetlogging.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.resources.*; import net.java.sip.communicator.util.*; @@ -83,6 +84,8 @@ public class JabberActivator */ private static HIDService hidService = null; + private static PacketLoggingService packetLoggingService = null; + /** * Called when this bundle is started so the Framework can perform the * bundle-specific activities necessary to start this bundle. @@ -300,4 +303,24 @@ public class JabberActivator } return hidService; } + + /** + * Returns a reference to the PacketLoggingService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a reference to a PacketLoggingService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + */ + public static PacketLoggingService getPacketLogging() + { + if (packetLoggingService == null) + { + packetLoggingService + = ServiceUtils.getService( + bundleContext, PacketLoggingService.class); + } + return packetLoggingService; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index b2396c7..a2bdae5 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -13,6 +13,7 @@ import java.text.*; import java.util.*; import javax.net.ssl.*; +import net.java.sip.communicator.impl.protocol.jabber.debugger.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.jabberconstants.*; @@ -231,6 +232,11 @@ public class ProtocolProviderServiceJabberImpl } /** + * The debugger who logs packets. + */ + private SmackPacketDebugger debugger = null; + + /** * Returns the state of the registration of this protocol provider * @return the <tt>RegistrationState</tt> that this provider is * currently in or null in case it is in a unknown state. @@ -744,6 +750,14 @@ public class ProtocolProviderServiceJabberImpl logger.error("Error creating custom trust manager", e); } + if(debugger == null) + debugger = new SmackPacketDebugger(); + + // setts the debugger + debugger.setConnection(connection); + connection.addPacketListener(debugger, null); + connection.addPacketInterceptor(debugger, null); + connection.connect(); registerServiceDiscoveryManager(); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java b/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java new file mode 100644 index 0000000..72a0b75 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/debugger/SmackPacketDebugger.java @@ -0,0 +1,150 @@ +/* + * 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.protocol.jabber.debugger; + +import net.java.sip.communicator.impl.protocol.jabber.*; +import net.java.sip.communicator.service.packetlogging.*; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.packet.*; + +import java.net.*; + +/** + * The jabber packet listener that logs the packets to the packet logging + * service. + * @author Damian Minkov + */ +public class SmackPacketDebugger + implements PacketListener, + PacketInterceptor +{ + /** + * The current jabber connection. + */ + private Connection connection = null; + + /** + * Local address for the connection. + */ + private byte[] localAddress = new byte[4]; + + /** + * The local port we are using for the connection. + * As currently we don't have information for this + * we use this static value. + */ + private int localPort = 5222; + + /** + * Remote address for the connection. + */ + private byte[] remoteAddress = new byte[4]; + + /** + * Instance for the packet logging service. + */ + private PacketLoggingService packetLogging = null; + + /** + * Creates the SmackPacketDebugger instance. + */ + public SmackPacketDebugger() + { + packetLogging = JabberActivator.getPacketLogging(); + } + + /** + * Sets current connection. + * @param connection the connection. + */ + public void setConnection(Connection connection) + { + this.connection = connection; + + try + { + remoteAddress = InetAddress.getByName(connection.getHost()).getAddress(); + + // to create empty ipv6 address default is ipv4 + if(remoteAddress.length != localAddress.length) + localAddress = new byte[remoteAddress.length]; + } + catch(Throwable t) + { + t.printStackTrace(); + } + } + + /** + * Process the packet that is about to be sent to the server. The intercepted + * packet can be modified by the interceptor.<p> + * <p/> + * Interceptors are invoked using the same thread that requested the packet + * to be sent, so it's very important that implementations of this method + * not block for any extended period of time. + * + * @param packet the packet to is going to be sent to the server. + */ + public void interceptPacket(Packet packet) + { + try + { + if(packetLogging.isLoggingEnabled( + PacketLoggingService.ProtocolName.JABBER)) + { + packetLogging.logPacket( + PacketLoggingService.ProtocolName.JABBER, + localAddress, + localPort, + remoteAddress, + connection.getPort(), + PacketLoggingService.TransportName.TCP, + true, + packet.toXML().getBytes("UTF-8") + ); + } + } + catch(Throwable t) + { + t.printStackTrace(); + } + } + + /** + * Process the next packet sent to this packet listener.<p> + * <p/> + * A single thread is responsible for invoking all listeners, so + * it's very important that implementations of this method not block + * for any extended period of time. + * + * @param packet the packet to process. + */ + public void processPacket(Packet packet) + { + try + { + if(packetLogging.isLoggingEnabled( + PacketLoggingService.ProtocolName.JABBER)) + { + packetLogging.logPacket( + PacketLoggingService.ProtocolName.JABBER, + remoteAddress, + connection.getPort(), + localAddress, + localPort, + PacketLoggingService.TransportName.TCP, + false, + packet.toXML().getBytes("UTF-8") + ); + } + } + catch(Throwable t) + { + t.printStackTrace(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf index eb93379..4370577 100755 --- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf @@ -31,6 +31,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.configuration, net.java.sip.communicator.service.resources, net.java.sip.communicator.util, + net.java.sip.communicator.service.packetlogging, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.jabberconstants, net.java.sip.communicator.service.protocol.event, diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java index f1c9ebb..fe3e749 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java @@ -8,6 +8,7 @@ package net.java.sip.communicator.impl.protocol.sip; import java.util.*; +import net.java.sip.communicator.service.packetlogging.*; import org.osgi.framework.*; import net.java.sip.communicator.service.configuration.*; @@ -38,6 +39,7 @@ public class SipActivator private static VersionService versionService = null; private static UIService uiService = null; private static HIDService hidService = null; + private static PacketLoggingService packetLoggingService = null; /** * The resource service. Used for checking for default values @@ -249,6 +251,26 @@ public class SipActivator } /** + * Returns a reference to the <tt>PacketLoggingService</tt> implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a reference to a <tt>PacketLoggingService</tt> implementation + * currently registered in the bundle context or null if no such + * implementation was found. + */ + public static PacketLoggingService getPacketLogging() + { + if (packetLoggingService == null) + { + packetLoggingService + = ServiceUtils.getService( + bundleContext, PacketLoggingService.class); + } + return packetLoggingService; + } + + /** * Called when this bundle is stopped so the Framework can perform the * bundle-specific activities necessary to stop the bundle. * diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipLogger.java b/src/net/java/sip/communicator/impl/protocol/sip/SipLogger.java index 9511b3e..b61c406 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipLogger.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipLogger.java @@ -9,8 +9,10 @@ package net.java.sip.communicator.impl.protocol.sip; import gov.nist.core.*; import gov.nist.javax.sip.message.*; import javax.sip.*; +import java.io.*; import java.util.*; +import net.java.sip.communicator.service.packetlogging.*; import net.java.sip.communicator.util.*; /** @@ -129,10 +131,12 @@ public class SipLogger */ public boolean isLoggingEnabled(int logLevel) { + // always enable trace messages so we can receive packets + // and log them to packet logging service if (logLevel == TRACE_DEBUG) return logger.isDebugEnabled(); if (logLevel == TRACE_MESSAGES) // same as TRACE_INFO - return logger.isInfoEnabled(); + return true; if (logLevel == TRACE_NONE) return false; @@ -218,19 +222,7 @@ public class SipLogger public void logMessage(SIPMessage message, String from, String to, boolean sender, long time) { - if (!logger.isInfoEnabled()) - return; - - String msgHeader; - - if(sender) - msgHeader = "JAIN-SIP sent a message from=\""; - else - msgHeader = "JAIN-SIP received a message from=\""; - - if (logger.isInfoEnabled()) - logger.info( msgHeader + from + "\" to=\"" + to + "\" at=" + time - + ":\n" + message); + logMessage(message, from, to, null, sender, time); } /** @@ -246,6 +238,8 @@ public class SipLogger public void logMessage(SIPMessage message, String from, String to, String status, boolean sender, long time) { + logPacket(message, sender); + if (!logger.isInfoEnabled()) return; @@ -262,6 +256,59 @@ public class SipLogger } /** + * Logs the specified message and details to the packet logging service + * if enabled. + * + * @param message the message to log + * @param sender determines whether we are the origin of this message. + */ + public void logPacket(SIPMessage message, boolean sender) + { + try + { + if(!SipActivator.getPacketLogging().isLoggingEnabled( + PacketLoggingService.ProtocolName.SIP)) + return; + + boolean isTransportUDP = message.getTopmostVia().getTransport() + .equalsIgnoreCase("UDP"); + + byte[] srcAddr; + int srcPort; + byte[] dstAddr; + int dstPort; + + if(sender) + { + srcAddr = message.getLocalAddress().getAddress(); + srcPort = message.getLocalPort(); + dstAddr = message.getRemoteAddress().getAddress(); + dstPort = message.getRemotePort(); + } + else + { + dstPort = message.getLocalPort(); + dstAddr = message.getLocalAddress().getAddress(); + srcAddr = message.getRemoteAddress().getAddress(); + srcPort = message.getRemotePort(); + } + + byte[] msg = message.toString().getBytes("UTF-8"); + SipActivator.getPacketLogging().logPacket( + PacketLoggingService.ProtocolName.SIP, + srcAddr, srcPort, + dstAddr, dstPort, + isTransportUDP ? PacketLoggingService.TransportName.UDP : + PacketLoggingService.TransportName.TCP, + sender, msg); + } + catch(UnsupportedEncodingException e) + { + logger.error("Cannot obtain message body", e); + } + } + + /** * Logs the specified message and details. * * @param message the message to log diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java index 95c0fc4..0636df2 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java @@ -1046,7 +1046,7 @@ public class SipStackSharing * @return the SocketAddress that this handler would use when connecting to * the specified destination address and port. * - * @throws IOException !!!!!!!!!!!!!!!!!!!!!!! FILL IN !!!!!!!!!!!!!! + * @throws IOException if we fail binding the local socket */ public java.net.InetSocketAddress getLocalAddressForDestination( java.net.InetAddress dst, @@ -1055,12 +1055,12 @@ public class SipStackSharing String transport) throws IOException { -// if(ListeningPoint.TLS.equalsIgnoreCase(transport)) -// return (java.net.InetSocketAddress)(((SipStackImpl)this.stack) -// .getLocalTLSAddressForDestination(dst, dstPort, localAddress)); -// else + if(ListeningPoint.TLS.equalsIgnoreCase(transport)) return (java.net.InetSocketAddress)(((SipStackImpl)this.stack) - .obtainLocalAddress(dst, dstPort, localAddress, 0)); + .getLocalAddressForTlsDst(dst, dstPort, localAddress)); + else + return (java.net.InetSocketAddress)(((SipStackImpl)this.stack) + .getLocalAddressForTcpDst(dst, dstPort, localAddress, 0)); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf index 5bd8275..4aa9575 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -44,6 +44,7 @@ Import-Package: org.apache.log4j, net.java.sip.communicator.service.neomedia.format, net.java.sip.communicator.service.hid, net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.service.packetlogging, net.java.sip.communicator.service.protocol, net.java.sip.communicator.service.protocol.event, net.java.sip.communicator.service.protocol.media, diff --git a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java index 3fa6b31..26a5986 100644 --- a/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java +++ b/src/net/java/sip/communicator/plugin/generalconfig/GeneralConfigurationPanel.java @@ -595,7 +595,18 @@ public class GeneralConfigurationPanel } } }); - localeConfigPanel.add(localesConfigComboBox); + localeConfigPanel.add(localesConfigComboBox, BorderLayout.CENTER); + + String label = "* " + + Resources.getString( + "plugin.generalconfig.DEFAULT_LANGUAGE_RESTART_WARN"); + JLabel warnLabel = new JLabel(label); + warnLabel.setToolTipText(label); + warnLabel.setForeground(Color.GRAY); + warnLabel.setFont(warnLabel.getFont().deriveFont(8)); + warnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); + warnLabel.setHorizontalAlignment(JLabel.RIGHT); + localeConfigPanel.add(warnLabel, BorderLayout.SOUTH); return localeConfigPanel; } @@ -724,16 +735,6 @@ public class GeneralConfigurationPanel } }); - String label = "* " + - Resources.getString( - "plugin.generalconfig.DEFAULT_LANGUAGE_RESTART_WARN"); - JLabel warnLabel = new JLabel(label); - warnLabel.setToolTipText(label); - warnLabel.setForeground(Color.GRAY); - warnLabel.setFont(warnLabel.getFont().deriveFont(8)); - warnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); - valuePanel.add(warnLabel); - return callConfigPanel; } diff --git a/src/net/java/sip/communicator/service/packetlogging/PacketLoggingService.java b/src/net/java/sip/communicator/service/packetlogging/PacketLoggingService.java new file mode 100644 index 0000000..bdf4103 --- /dev/null +++ b/src/net/java/sip/communicator/service/packetlogging/PacketLoggingService.java @@ -0,0 +1,101 @@ +/* + * 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.service.packetlogging; + +/** + * A Packet Logging Service to log packets that were send/received + * by protocols or any other network related services in various formats. + * Its for debugging purposes. + * + * @author Damian Minkov + */ +public interface PacketLoggingService +{ + /** + * These are the services that this packet logging service + * cab handle. + */ + public enum ProtocolName + { + SIP, + JABBER, + RTP, + ICE4J + } + + /** + * The transport names. + */ + public enum TransportName + { + UDP, + TCP + } + + /** + * Checks is logging globally enabled for the service. + * @return is logging enabled. + */ + public boolean isLoggingEnabled(); + + /** + * Checks is logging globally enabled for and is it currently + * available fo the given protocol. + *. + * @param protocol that is checked. + * @return is logging enabled. + */ + public boolean isLoggingEnabled(ProtocolName protocol); + + /** + * 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); + + /** + * 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); +} |