diff --git a/apps/sam/java/src/net/i2p/sam/SAMDatagramReceiver.java b/apps/sam/java/src/net/i2p/sam/SAMDatagramReceiver.java
index 68971af31a46a208277ab550064144c2fd325a6b..b07ff8fbed71097a33238aed793137c7a917870d 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMDatagramReceiver.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMDatagramReceiver.java
@@ -22,9 +22,12 @@ interface SAMDatagramReceiver {
      *
      * @param sender Destination
      * @param data Byte array to be received
+     * @param proto I2CP protocol
+     * @param fromPort I2CP from port
+     * @param toPort I2CP to port
      * @throws IOException 
      */
-    public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException;
+    public void receiveDatagramBytes(Destination sender, byte data[], int proto, int fromPort, int toPort) throws IOException;
 
     /**
      * Stop receiving data.
diff --git a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
index ce3c1803c31b326e3c057109d0c73d9b74ee95b8..8324082be04c3848461ed79a0d446cb8361eaf40 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
@@ -95,7 +95,7 @@ class SAMDatagramSession extends SAMMessageSession {
                                               I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
     }
 
-    protected void messageReceived(byte[] msg) {
+    protected void messageReceived(byte[] msg, int proto, int fromPort, int toPort) {
         byte[] payload;
         Destination sender;
         try {
@@ -117,7 +117,7 @@ class SAMDatagramSession extends SAMMessageSession {
         }
 
         try {
-            recv.receiveDatagramBytes(sender, payload);
+            recv.receiveDatagramBytes(sender, payload, proto, fromPort, toPort);
         } catch (IOException e) {
             _log.error("Error forwarding message to receiver", e);
             close();
diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java
index 56d68878c612ec998fdc6a80f56e5b298f03f7e0..c7ec10388be2937cbda5a04c2944c76f8162c1fd 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java
@@ -35,8 +35,8 @@ abstract class SAMHandler implements Runnable, Handler {
     private final Object socketWLock = new Object(); // Guards writings on socket
     protected final SocketChannel socket;
 
-    protected final int verMajor;
-    protected final int verMinor;
+    public final int verMajor;
+    public final int verMinor;
     
     /** I2CP options configuring the I2CP connection (port, host, numHops, etc) */
     protected final Properties i2cpProps;
diff --git a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
index db2450b0113dca84f2117c4a4faf05e0378adc2a..a9531fd1ed57670889500d4fcd61d9a03b628350 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
@@ -18,7 +18,7 @@ import net.i2p.client.I2PClient;
 import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
 import net.i2p.client.I2PSessionException;
-import net.i2p.client.I2PSessionListener;
+import net.i2p.client.I2PSessionMuxedListener;
 import net.i2p.data.Base64;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.Destination;
@@ -135,7 +135,7 @@ abstract class SAMMessageSession {
      * Handle a new received message
      * @param msg Message payload
      */
-    protected abstract void messageReceived(byte[] msg);
+    protected abstract void messageReceived(byte[] msg, int proto, int fromPort, int toPort);
     
     /**
      * Do whatever is needed to shutdown the SAM session
@@ -157,7 +157,7 @@ abstract class SAMMessageSession {
      *
      * @author human
      */
-    class SAMMessageSessionHandler implements Runnable, I2PSessionListener {
+    class SAMMessageSessionHandler implements Runnable, I2PSessionMuxedListener {
 
         private final Object runningLock = new Object();
         private volatile boolean stillRunning = true;
@@ -186,7 +186,7 @@ abstract class SAMMessageSession {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("I2P session connected");
 
-            session.setSessionListener(this);
+            session.addMuxedSessionListener(this, I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
         }
 
         /**
@@ -217,6 +217,7 @@ abstract class SAMMessageSession {
                 _log.debug("Shutting down SAM message-based session handler");
             
             shutDown();
+            session.removeListener(I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
             
             try {
                 if (_log.shouldLog(Log.DEBUG))
@@ -242,7 +243,15 @@ abstract class SAMMessageSession {
             stopRunning();
         }
             
-        public void messageAvailable(I2PSession session, int msgId, long size){
+        public void messageAvailable(I2PSession session, int msgId, long size) {
+            messageAvailable(session, msgId, size, I2PSession.PROTO_UNSPECIFIED,
+                             I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
+        }
+
+        /** @since 0.9.22 */
+        public void messageAvailable(I2PSession session, int msgId, long size,
+                                     int proto, int fromPort, int toPort) {
+
             if (_log.shouldLog(Log.DEBUG)) {
                 _log.debug("I2P message available (id: " + msgId
                            + "; size: " + size + ")");
@@ -256,7 +265,7 @@ abstract class SAMMessageSession {
                                + HexDump.dump(msg));
                 }
                 
-                messageReceived(msg);
+                messageReceived(msg, proto, fromPort, toPort);
             } catch (I2PSessionException e) {
                 _log.error("Error fetching I2P message", e);
                 stopRunning();
diff --git a/apps/sam/java/src/net/i2p/sam/SAMRawReceiver.java b/apps/sam/java/src/net/i2p/sam/SAMRawReceiver.java
index 96ebe45d082102ffd98e4a35d1062a498fcfd5cd..564f95c3d8b39e53b2a20c07f3f4b1d5eac95d1f 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMRawReceiver.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMRawReceiver.java
@@ -20,9 +20,12 @@ interface SAMRawReceiver {
      * regarding the sender.
      *
      * @param data Byte array to be received
+     * @param proto I2CP protocol
+     * @param fromPort I2CP from port
+     * @param toPort I2CP to port
      * @throws IOException 
      */
-    public void receiveRawBytes(byte data[]) throws IOException;
+    public void receiveRawBytes(byte data[], int proto, int fromPort, int toPort) throws IOException;
 
     /**
      * Stop receiving data.
diff --git a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
index e3c42160834a7f46fd736f33d779b4472ce9bae4..666d87049cbe6ec7afa8d3d461c06332d410c1fe 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
@@ -80,9 +80,9 @@ class SAMRawSession extends SAMMessageSession {
                                               I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
     }
 
-    protected void messageReceived(byte[] msg) {
+    protected void messageReceived(byte[] msg, int proto, int fromPort, int toPort) {
         try {
-            recv.receiveRawBytes(msg);
+            recv.receiveRawBytes(msg, proto, fromPort, toPort);
         } catch (IOException e) {
             _log.error("Error forwarding message to receiver", e);
             close();
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
index cf4d46b62e63cd35bf5bf545bfcecd18d24b0760..85322d76b24d1da6b49c9feefb7cdd59632d8d51 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
@@ -796,16 +796,21 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
     }
     
     // SAMRawReceiver implementation
-    public void receiveRawBytes(byte data[]) throws IOException {
+    public void receiveRawBytes(byte data[], int proto, int fromPort, int toPort) throws IOException {
         if (getRawSession() == null) {
             _log.error("BUG! Received raw bytes, but session is null!");
             return;
         }
 
-        ByteArrayOutputStream msg = new ByteArrayOutputStream();
+        ByteArrayOutputStream msg = new ByteArrayOutputStream(64 + data.length);
 
-        String msgText = "RAW RECEIVED SIZE=" + data.length + "\n";
+        String msgText = "RAW RECEIVED SIZE=" + data.length;
         msg.write(DataHelper.getASCII(msgText));
+        if ((verMajor == 3 && verMinor >= 2) || verMajor > 3) {
+            msgText = " PROTOCOL=" + proto + " FROM_PORT=" + fromPort + " TO_PORT=" + toPort;
+            msg.write(DataHelper.getASCII(msgText));
+        }
+        msg.write((byte) '\n');
         msg.write(data);
         
         if (_log.shouldLog(Log.DEBUG))
@@ -832,17 +837,23 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
     }
 
     // SAMDatagramReceiver implementation
-    public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException {
+    public void receiveDatagramBytes(Destination sender, byte data[], int proto,
+                                     int fromPort, int toPort) throws IOException {
         if (getDatagramSession() == null) {
             _log.error("BUG! Received datagram bytes, but session is null!");
             return;
         }
 
-        ByteArrayOutputStream msg = new ByteArrayOutputStream();
+        ByteArrayOutputStream msg = new ByteArrayOutputStream(100 + data.length);
 
         String msgText = "DATAGRAM RECEIVED DESTINATION=" + sender.toBase64()
-                         + " SIZE=" + data.length + "\n";
+                         + " SIZE=" + data.length;
         msg.write(DataHelper.getASCII(msgText));
+        if ((verMajor == 3 && verMinor >= 2) || verMajor > 3) {
+            msgText = " FROM_PORT=" + fromPort + " TO_PORT=" + toPort;
+            msg.write(DataHelper.getASCII(msgText));
+        }
+        msg.write((byte) '\n');
         
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("sending to client: " + msgText);
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java
index a16c92327fe23cc2f055d850a3ab48cd08a546a9..268083b6384d67bb0f806421dbd51f515046d7d6 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java
@@ -71,11 +71,18 @@ class SAMv3DatagramSession extends SAMDatagramSession implements SAMv3Handler.Se
     	}
 	}
 
-	public void receiveDatagramBytes(Destination sender, byte[] data) throws IOException {
+	public void receiveDatagramBytes(Destination sender, byte[] data, int proto,
+	                                 int fromPort, int toPort) throws IOException {
 		if (this.clientAddress==null) {
-			this.handler.receiveDatagramBytes(sender, data);
+			this.handler.receiveDatagramBytes(sender, data, proto, fromPort, toPort);
 		} else {
-			String msg = sender.toBase64()+"\n";
+			StringBuilder buf = new StringBuilder(600);
+			buf.append(sender.toBase64());
+			if ((handler.verMajor == 3 && handler.verMinor >= 2) || handler.verMajor > 3) {
+				buf.append(" FROM_PORT=").append(fromPort).append(" TO_PORT=").append(toPort);
+			}
+			buf.append('\n');
+			String msg = buf.toString();
 			ByteBuffer msgBuf = ByteBuffer.allocate(msg.length()+data.length);
 			msgBuf.put(DataHelper.getASCII(msg));
 			msgBuf.put(data);
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
index 5e75dcf661d485c1fa1b94e8a54683b122dfa849..445f8d10aeede59188ccb1ced769ff17dbad060f 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
@@ -50,7 +50,7 @@ class SAMv3Handler extends SAMv1Handler
 	public static final SessionsDB sSessionsHash = new SessionsDB();
 	private volatile boolean stolenSocket;
 	private volatile boolean streamForwardingSocket;
-
+	private final boolean sendPorts;
 	
 	interface Session {
 		String getNick();
@@ -88,6 +88,7 @@ class SAMv3Handler extends SAMv1Handler
 	                    Properties i2cpProps, SAMBridge parent) throws SAMException, IOException
 	{
 		super(s, verMajor, verMinor, i2cpProps, parent);
+	        sendPorts = (verMajor == 3 && verMinor >= 2) || verMajor > 3;
 		if (_log.shouldLog(Log.DEBUG))
 			_log.debug("SAM version 3 handler instantiated");
 	}
@@ -808,7 +809,7 @@ class SAMv3Handler extends SAMv1Handler
 		try {
 			try {
 				streamForwardingSocket = true ;
-				((SAMv3StreamSession)streamSession).startForwardingIncoming(props);
+				((SAMv3StreamSession)streamSession).startForwardingIncoming(props, sendPorts);
 				notifyStreamResult( true, "OK", null );
 				return true ;
 			} catch (SAMException e) {
@@ -858,13 +859,18 @@ class SAMv3Handler extends SAMv1Handler
 		}
 	}
 
-	public void notifyStreamIncomingConnection(Destination d) throws IOException {
+	public void notifyStreamIncomingConnection(Destination d, int fromPort, int toPort) throws IOException {
 	    if (getStreamSession() == null) {
 	        _log.error("BUG! Received stream connection, but session is null!");
 	        throw new NullPointerException("BUG! STREAM session is null!");
 	    }
-
-	    if (!writeString(d.toBase64() + "\n")) {
+	    StringBuilder buf = new StringBuilder(600);
+	    buf.append(d.toBase64());
+	    if (sendPorts) {
+		buf.append(" FROM_PORT=").append(fromPort).append(" TO_PORT=").append(toPort);
+	    }
+	    buf.append('\n');
+	    if (!writeString(buf.toString())) {
 	        throw new IOException("Error notifying connection to SAM client");
 	    }
 	}
@@ -874,6 +880,14 @@ class SAMv3Handler extends SAMv1Handler
 	        throw new IOException("Error notifying connection to SAM client");
 	    }
 	}
+	
+	/** @since 0.9.22 */
+	public static void notifyStreamIncomingConnection(SocketChannel client, Destination d,
+	                                                  int fromPort, int toPort) throws IOException {
+	    if (!writeString(d.toBase64() + " FROM_PORT=" + fromPort + " TO_PORT=" + toPort + '\n', client)) {
+	        throw new IOException("Error notifying connection to SAM client");
+	    }
+	}
 
 }
 
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
index 90eae76f9315f5e71bf4e7d1574bb1b332c78b1f..a228a89437c615bf6fa5d236a7f852edef188039 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
@@ -77,9 +77,9 @@ class SAMv3RawSession extends SAMRawSession  implements SAMv3Handler.Session, SA
     	}
 	}
 	
-	public void receiveRawBytes(byte[] data) throws IOException {
+	public void receiveRawBytes(byte[] data, int proto, int fromPort, int toPort) throws IOException {
 		if (this.clientAddress==null) {
-			this.handler.receiveRawBytes(data);
+			this.handler.receiveRawBytes(data, proto, fromPort, toPort);
 		} else {
 			ByteBuffer msgBuf = ByteBuffer.allocate(data.length);
 			msgBuf.put(data);
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java
index fe9561cedbc4fca9edcb31dc4d6ec77753705441..544635aae59e7a18406b676e4c7f2f9b0efc05f5 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java
@@ -166,9 +166,10 @@ class SAMv3StreamSession  extends SAMStreamSession implements SAMv3Handler.Sessi
 
 		if ( rec==null || i2ps==null ) throw new InterruptedIOException() ;
 
-		if (verbose)
-			handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ;
-
+		if (verbose) {
+			handler.notifyStreamIncomingConnection(i2ps.getPeerDestination(),
+			                                       i2ps.getPort(), i2ps.getLocalPort());
+		}
 	        handler.stealSocket() ;
 	        ReadableByteChannel fromClient = handler.getClientSocket();
 	        ReadableByteChannel fromI2P    = Channels.newChannel(i2ps.getInputStream());
@@ -185,7 +186,7 @@ class SAMv3StreamSession  extends SAMStreamSession implements SAMv3Handler.Sessi
 	    }
 
 	    
-	    public void startForwardingIncoming( Properties props ) throws SAMException, InterruptedIOException
+	    public void startForwardingIncoming(Properties props, boolean sendPorts) throws SAMException, InterruptedIOException
 	    {
 	    	SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
 	    	boolean verbose = props.getProperty("SILENT", "false").equals("false");
@@ -218,7 +219,7 @@ class SAMv3StreamSession  extends SAMStreamSession implements SAMv3Handler.Sessi
 	    		this.socketServer = this.socketMgr.getServerSocket();
 	    	}
 	    	
-	    	SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose);
+	    	SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose, sendPorts);
 	    	(new Thread(rec.getThreadGroup(), forwarder, "SAMV3StreamForwarder")).start();
 	    }
 	    
@@ -227,13 +228,14 @@ class SAMv3StreamSession  extends SAMStreamSession implements SAMv3Handler.Sessi
 	    	private final String host;
 	    	private final int port;
 	    	private final SAMv3StreamSession session;
-	    	private final boolean verbose;
+	    	private final boolean verbose, sendPorts;
 	    	
-	    	SocketForwarder(String host, int port, SAMv3StreamSession session, boolean verbose) {
+	    	SocketForwarder(String host, int port, SAMv3StreamSession session, boolean verbose, boolean sendPorts) {
 	    		this.host = host ;
 	    		this.port = port ;
 	    		this.session = session ;
 	    		this.verbose = verbose ;
+	    		this.sendPorts = sendPorts;
 	    	}
 	    	
 	    	public void run()
@@ -264,9 +266,16 @@ class SAMv3StreamSession  extends SAMStreamSession implements SAMv3Handler.Sessi
 	    			// build pipes between both sockets
 	    			try {
 					clientServerSock.socket().setKeepAlive(true);
-	    				if (this.verbose)
-	    					SAMv3Handler.notifyStreamIncomingConnection(
+	    				if (this.verbose) {
+						if (sendPorts) {
+	    					       SAMv3Handler.notifyStreamIncomingConnection(
+	    							clientServerSock, i2ps.getPeerDestination(),
+								i2ps.getPort(), i2ps.getLocalPort());
+						} else {
+	    					       SAMv3Handler.notifyStreamIncomingConnection(
 	    							clientServerSock, i2ps.getPeerDestination());
+						}
+					}
 	    				ReadableByteChannel fromClient = clientServerSock ;
 	    				ReadableByteChannel fromI2P    = Channels.newChannel(i2ps.getInputStream());
 	    				WritableByteChannel toClient   = clientServerSock ;