From 5adbf9050a471c02a44e1bae2dff57e6a22c79d9 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 27 Nov 2015 18:23:06 +0000
Subject: [PATCH] Forwarded raw datagrams will include a header line if
 HEADER=true Add support for raw with headers to sink client

---
 .../java/src/net/i2p/sam/SAMv3RawSession.java | 18 ++++++++++++-
 .../src/net/i2p/sam/client/SAMStreamSink.java | 26 ++++++++++++-------
 2 files changed, 33 insertions(+), 11 deletions(-)

diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
index 658aaae0a4..b68f3a74a1 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
@@ -12,6 +12,7 @@ import java.util.Properties;
 
 import net.i2p.client.I2PSessionException;
 import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
 import net.i2p.util.Log;
 
 /**
@@ -24,6 +25,7 @@ class SAMv3RawSession extends SAMRawSession  implements SAMv3Handler.Session, SA
 	private final SAMv3Handler handler;
 	private final SAMv3DatagramServer server;
 	private final SocketAddress clientAddress;
+	private final boolean _sendHeader;
 
 	public String getNick() { return nick; }
 
@@ -66,13 +68,27 @@ class SAMv3RawSession extends SAMRawSession  implements SAMv3Handler.Session, SA
 			}
 			this.clientAddress = new InetSocketAddress(host, port);
 		}
+		_sendHeader = ((handler.verMajor == 3 && handler.verMinor >= 2) || handler.verMajor > 3) &&
+                              Boolean.parseBoolean(props.getProperty("HEADER"));
 	}
 	
 	public void receiveRawBytes(byte[] data, int proto, int fromPort, int toPort) throws IOException {
 		if (this.clientAddress==null) {
 			this.handler.receiveRawBytes(data, proto, fromPort, toPort);
 		} else {
-			ByteBuffer msgBuf = ByteBuffer.allocate(data.length);
+			ByteBuffer msgBuf;
+			if (_sendHeader) {
+				StringBuilder buf = new StringBuilder(64);
+				buf.append("PROTOCOL=").append(proto)
+				   .append(" FROM_PORT=").append(fromPort)
+				   .append(" TO_PORT=").append(toPort)
+				   .append('\n');
+				String msg = buf.toString();
+				msgBuf = ByteBuffer.allocate(msg.length()+data.length);
+				msgBuf.put(DataHelper.getASCII(msg));
+			} else {
+				msgBuf = ByteBuffer.allocate(data.length);
+			}
 			msgBuf.put(data);
 			msgBuf.flip();
 			this.server.send(this.clientAddress, msgBuf);
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
index cf6d142f40..05b79e1eeb 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
@@ -49,9 +49,9 @@ public class SAMStreamSink {
     private final Map<String, Sink> _remotePeers;
     private static I2PSSLSocketFactory _sslSocketFactory;
     
-    private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4;
+    private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4, RAWHDR = 5;
     private static final String USAGE = "Usage: SAMStreamSink [-s] [-m mode] [-v version] [-b samHost] [-p samPort] [-o opt=val] [-u user] [-w password] myDestFile sinkDir\n" +
-                                        "       modes: stream: 0; datagram: 1; v1datagram: 2; raw: 3; v1raw: 4\n" +
+                                        "       modes: stream: 0; datagram: 1; v1datagram: 2; raw: 3; v1raw: 4 raw-with-headers: 5\n" +
                                         "       -s: use SSL\n" +
                                         "       multiple -o session options are allowed";
     private static final int V3DGPORT=9999;
@@ -75,7 +75,7 @@ public class SAMStreamSink {
 
             case 'm':
                 mode = Integer.parseInt(g.getOptarg());
-                if (mode < 0 || mode > V1RAW) {
+                if (mode < 0 || mode > RAWHDR) {
                     System.err.println(USAGE);
                     return;
                 }
@@ -180,7 +180,7 @@ public class SAMStreamSink {
                     throw new IOException("2nd handshake failed");
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("Handshake2 complete.");
-            } else if (_isV3 && (mode == DG || mode == RAW)) {
+            } else if (_isV3 && (mode == DG || mode == RAW || mode == RAWHDR)) {
                 // set up a listening DatagramSocket
                 (new DGRcvr(mode)).start();
             }
@@ -207,21 +207,25 @@ public class SAMStreamSink {
                     int off = p.getOffset();
                     byte[] data = p.getData();
                     _log.info("Got datagram length " + len);
-                    if (_mode == DG) {
+                    if (_mode == DG || _mode == RAWHDR) {
                         ByteArrayInputStream bais = new ByteArrayInputStream(data, off, len);
                         String line = DataHelper.readLine(bais);
                         if (line == null) {
                             _log.error("DGRcvr no header line");
                             continue;
                         }
-                        if (line.length() < 516) {
+                        if (_mode == DG && line.length() < 516) {
                             _log.error("DGRcvr line too short: \"" + line + '\n');
                             continue;
                         }
                         String[] parts = line.split(" ");
-                        String dest = parts[0];
-                        _log.info("DG is from " + dest);
-                        for (int i = 1; i < parts.length; i++) {
+                        int i = 0;
+                        if (_mode == DG) {
+                            String dest = parts[0];
+                            _log.info("DG is from " + dest);
+                            i++;
+                        }
+                        for ( ; i < parts.length; i++) {
                             _log.info("Parameter: " + parts[i]);
                         }
                         int left = bais.available();
@@ -530,8 +534,10 @@ public class SAMStreamSink {
                     style = "DATAGRAM PORT=" + V3DGPORT;
                 else if (mode == V1RAW)
                     style = "RAW";
-                else
+                else if (mode == RAW)
                     style = "RAW PORT=" + V3DGPORT;
+                else
+                    style = "RAW HEADER=true PORT=" + V3DGPORT;
                 String req = "SESSION CREATE STYLE=" + style + " DESTINATION=" + dest + ' ' + _conOptions + ' ' + sopts + '\n';
                 samOut.write(req.getBytes());
                 samOut.flush();
-- 
GitLab