propagate from branch 'i2p.i2p' (head 833ef88c125ba48423bc704701303ba55858336f)

to branch 'i2p.i2p.zzz.sam' (head 7814184e3e7cb4b819a0d7b4ceeda5befbe536c3)
This commit is contained in:
zzz
2016-01-03 13:51:03 +00:00
915 changed files with 81925 additions and 56871 deletions

View File

@ -3,13 +3,14 @@ package net.i2p.sam;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
/**
* Modified from I2PTunnelHTTPServer
* Handles UTF-8. Does not buffer past the end of line.
*
* @since 0.9.24
*/
@ -24,39 +25,44 @@ class ReadLine {
* Warning - 8KB line length limit as of 0.7.13, @throws IOException if exceeded
*
* @param buf output
* @param timeout throws SocketTimeoutException immediately if zero or negative
* @param timeout forever if if zero or negative
* @throws SocketTimeoutException if timeout is reached before newline
* @throws EOFException if EOF is reached before newline
* @throws LineTooLongException if too long
* @throws IOException on other errors in the underlying stream
*/
public static void readLine(Socket socket, StringBuilder buf, int timeout) throws IOException {
if (timeout <= 0)
throw new SocketTimeoutException();
long expires = System.currentTimeMillis() + timeout;
// this reads and buffers extra bytes, so we can't use it
// unless we're going to decode UTF-8 on-the-fly, we're stuck with ASCII
//InputStreamReader in = new InputStreamReader(socket.getInputStream(), "UTF-8");
InputStream in = socket.getInputStream();
final int origTimeout = timeout;
int c;
int i = 0;
socket.setSoTimeout(timeout);
while ( (c = in.read()) != -1) {
final long expires;
if (origTimeout > 0) {
socket.setSoTimeout(timeout);
expires = System.currentTimeMillis() + timeout;
} else {
expires = 0;
}
final Reader reader = new UTF8Reader(socket.getInputStream());
while ( (c = reader.read()) != -1) {
if (++i > MAX_LINE_LENGTH)
throw new LineTooLongException("Line too long - max " + MAX_LINE_LENGTH);
if (c == '\n')
break;
int newTimeout = (int) (expires - System.currentTimeMillis());
if (newTimeout <= 0)
throw new SocketTimeoutException();
buf.append((char)c);
if (newTimeout != timeout) {
timeout = newTimeout;
socket.setSoTimeout(timeout);
if (origTimeout > 0) {
int newTimeout = (int) (expires - System.currentTimeMillis());
if (newTimeout <= 0)
throw new SocketTimeoutException();
buf.append((char)c);
if (newTimeout != timeout) {
timeout = newTimeout;
socket.setSoTimeout(timeout);
}
} else {
buf.append((char)c);
}
}
if (c == -1) {
if (System.currentTimeMillis() >= expires)
if (origTimeout > 0 && System.currentTimeMillis() >= expires)
throw new SocketTimeoutException();
else
throw new EOFException();

View File

@ -42,6 +42,7 @@ import net.i2p.data.Destination;
import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PSSLSocketFactory;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.PortMapper;
import net.i2p.util.SystemVersion;
@ -206,8 +207,8 @@ public class SAMBridge implements Runnable, ClientApp {
*
* @param name name of the destination
* @return null if the name does not exist, or if it is improperly formatted
* @deprecated unused
*/
/****
public Destination getDestination(String name) {
synchronized (nameToPrivKeys) {
String val = nameToPrivKeys.get(name);
@ -223,6 +224,7 @@ public class SAMBridge implements Runnable, ClientApp {
}
}
}
****/
/**
* Retrieve the I2P private keystream for the given name, formatted
@ -255,59 +257,51 @@ public class SAMBridge implements Runnable, ClientApp {
/**
* Load up the keys from the persistFilename.
* TODO use DataHelper
* TODO store in config dir, not base dir
*/
@SuppressWarnings("unchecked")
private void loadKeys() {
synchronized (nameToPrivKeys) {
nameToPrivKeys.clear();
BufferedReader br = null;
File file = new File(persistFilename);
// now in config dir but check base dir too...
if (!file.exists()) {
if (file.isAbsolute())
return;
file = new File(I2PAppContext.getGlobalContext().getConfigDir(), persistFilename);
if (!file.exists())
return;
}
try {
br = new BufferedReader(new InputStreamReader(
new FileInputStream(persistFilename), "UTF-8"));
String line = null;
while ( (line = br.readLine()) != null) {
int eq = line.indexOf('=');
String name = line.substring(0, eq);
String privKeys = line.substring(eq+1);
nameToPrivKeys.put(name, privKeys);
}
Properties props = new Properties();
DataHelper.loadProps(props, file);
// unchecked
Map foo = props;
nameToPrivKeys.putAll(foo);
if (_log.shouldInfo())
_log.info("Loaded " + nameToPrivKeys.size() + " private keys from " + persistFilename);
} catch (FileNotFoundException fnfe) {
_log.warn("Key file does not exist at " + persistFilename);
_log.info("Loaded " + nameToPrivKeys.size() + " private keys from " + file);
} catch (IOException ioe) {
_log.error("Unable to read the keys from " + persistFilename, ioe);
} finally {
if (br != null) try { br.close(); } catch (IOException ioe) {}
_log.error("Unable to read the keys from " + file, ioe);
}
}
}
/**
* Store the current keys to disk in the location specified on creation.
* TODO use DataHelper
* TODO store in config dir, not base dir
*/
private void storeKeys() {
synchronized (nameToPrivKeys) {
FileOutputStream out = null;
File file = new File(persistFilename);
// now in config dir but check base dir too...
if (!file.exists() && !file.isAbsolute())
file = new File(I2PAppContext.getGlobalContext().getConfigDir(), persistFilename);
try {
out = new FileOutputStream(persistFilename);
for (Map.Entry<String, String> entry : nameToPrivKeys.entrySet()) {
String name = entry.getKey();
String privKeys = entry.getValue();
out.write(name.getBytes("UTF-8"));
out.write('=');
out.write(privKeys.getBytes("UTF-8"));
out.write('\n');
}
Properties props = new OrderedProperties();
props.putAll(nameToPrivKeys);
DataHelper.storeProps(props, file);
if (_log.shouldInfo())
_log.info("Saved " + nameToPrivKeys.size() + " private keys to " + persistFilename);
_log.info("Saved " + nameToPrivKeys.size() + " private keys to " + file);
} catch (IOException ioe) {
_log.error("Error writing out the SAM keys to " + persistFilename, ioe);
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
_log.error("Error writing out the SAM keys to " + file, ioe);
}
}
}
@ -365,7 +359,7 @@ public class SAMBridge implements Runnable, ClientApp {
* TODO we could have multiple servers on different hosts/ports in the future.
*
* @param props non-null instantiate and start server if it doesn't exist
* @param return non-null
* @return non-null
* @throws IOException if can't bind to host/port, or if different than existing
* @since 0.9.24
*/
@ -715,7 +709,9 @@ public class SAMBridge implements Runnable, ClientApp {
changeState(RUNNING);
if (_mgr != null)
_mgr.register(this);
I2PAppContext.getGlobalContext().portMapper().register(PortMapper.SVC_SAM, _listenPort);
I2PAppContext.getGlobalContext().portMapper().register(_useSSL ? PortMapper.SVC_SAM_SSL : PortMapper.SVC_SAM,
_listenHost != null ? _listenHost : "127.0.0.1",
_listenPort);
try {
while (acceptConnections) {
SocketChannel s = serverSocket.accept();
@ -781,7 +777,7 @@ public class SAMBridge implements Runnable, ClientApp {
if (serverSocket != null)
serverSocket.close();
} catch (IOException e) {}
I2PAppContext.getGlobalContext().portMapper().unregister(PortMapper.SVC_SAM);
I2PAppContext.getGlobalContext().portMapper().unregister(_useSSL ? PortMapper.SVC_SAM_SSL : PortMapper.SVC_SAM);
stopHandlers();
changeState(STOPPED);
}

View File

@ -142,7 +142,7 @@ abstract class SAMHandler implements Runnable, Handler {
public static boolean writeString(String str, SocketChannel out)
{
try {
writeBytes(ByteBuffer.wrap(DataHelper.getASCII(str)), out);
writeBytes(ByteBuffer.wrap(DataHelper.getUTF8(str)), out);
} catch (IOException e) {
//_log.debug("Caught IOException", e);
return false;

View File

@ -13,7 +13,6 @@ import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.SocketChannel;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
@ -41,7 +40,7 @@ class SAMHandlerFactory {
*/
public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps,
SAMBridge parent) throws SAMException {
StringTokenizer tok;
String line;
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);
try {
@ -49,9 +48,8 @@ class SAMHandlerFactory {
sock.setKeepAlive(true);
StringBuilder buf = new StringBuilder(128);
ReadLine.readLine(sock, buf, HELLO_TIMEOUT);
String line = buf.toString();
sock.setSoTimeout(0);
tok = new StringTokenizer(line.trim(), " ");
line = buf.toString();
} catch (SocketTimeoutException e) {
throw new SAMException("Timeout waiting for HELLO VERSION", e);
} catch (IOException e) {
@ -59,17 +57,15 @@ class SAMHandlerFactory {
} catch (RuntimeException e) {
throw new SAMException("Unexpected error", e);
}
if (log.shouldDebug())
log.debug("New message received: [" + line + ']');
// Message format: HELLO VERSION [MIN=v1] [MAX=v2]
if (tok.countTokens() < 2) {
Properties props = SAMUtils.parseParams(line);
if (!"HELLO".equals(props.remove(SAMUtils.COMMAND)) ||
!"VERSION".equals(props.remove(SAMUtils.OPCODE))) {
throw new SAMException("Must start with HELLO VERSION");
}
if (!tok.nextToken().equals("HELLO") ||
!tok.nextToken().equals("VERSION")) {
throw new SAMException("Must start with HELLO VERSION");
}
Properties props = SAMUtils.parseParams(tok);
String minVer = props.getProperty("MIN");
if (minVer == null) {

View File

@ -9,6 +9,7 @@ package net.i2p.sam;
*/
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@ -32,7 +33,7 @@ import net.i2p.util.Log;
*
* @author human
*/
abstract class SAMMessageSession {
abstract class SAMMessageSession implements Closeable {
protected final Log _log;
private I2PSession session;

View File

@ -226,8 +226,7 @@ class SAMStreamSession {
return false;
}
Destination d = new Destination();
d.fromBase64(dest);
Destination d = SAMUtils.getDest(dest);
I2PSocketOptions opts = socketMgr.buildOptions(props);
if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)

View File

@ -11,9 +11,9 @@ package net.i2p.sam;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -82,6 +82,7 @@ class SAMUtils {
*
* @return True if the destination is valid, false otherwise
*/
/****
public static boolean checkDestination(String dest) {
try {
Destination d = new Destination();
@ -92,6 +93,7 @@ class SAMUtils {
return false;
}
}
****/
/**
* Check whether a base64-encoded {dest,privkey,signingprivkey} is valid
@ -105,8 +107,7 @@ class SAMUtils {
return false;
ByteArrayInputStream destKeyStream = new ByteArrayInputStream(b);
try {
Destination d = new Destination();
d.readBytes(destKeyStream);
Destination d = Destination.create(destKeyStream);
new PrivateKey().readBytes(destKeyStream);
SigningPrivateKey spk = new SigningPrivateKey(d.getSigningPublicKey().getType());
spk.readBytes(destKeyStream);
@ -159,95 +160,186 @@ class SAMUtils {
return d;
}
public static final String COMMAND = "\"\"COMMAND\"\"";
public static final String OPCODE = "\"\"OPCODE\"\"";
/**
* Parse SAM parameters, and put them into a Propetries object
* Parse SAM parameters, and put them into a Propetries object
*
* @param tok A StringTokenizer pointing to the SAM parameters
* Modified from EepGet.
* COMMAND and OPCODE are mapped to upper case; keys, values, and ping data are not.
* Double quotes around values are stripped.
*
* @throws SAMException if the data was formatted incorrectly
* @return Properties with the parsed SAM params, never null
* Possible input:
*<pre>
* COMMAND
* COMMAND OPCODE
* COMMAND OPCODE [key=val]...
* COMMAND OPCODE [key=" val with spaces "]...
* PING
* PONG
* PING any thing goes
* PONG any thing goes
*
* Escaping is allowed with a backslash, e.g. \"
* No spaces before or after '=' allowed
* Keys may not be quoted
* COMMAND, OPCODE, and keys may not have '=' or whitespace unless escaped
* Duplicate keys not allowed
*</pre>
*
* A key without a value is not allowed by the spec, but is
* returned with the value "true".
*
* COMMAND is returned as the value of the key ""COMMAND"".
* OPCODE, or the remainder of the PING/PONG line if any, is returned as the value of the key ""OPCODE"".
*
* @param args non-null
* @throws SAMException on some errors but not all
* @return non-null, may be empty. Does not throw on missing COMMAND or OPCODE; caller must check.
*/
public static Properties parseParams(StringTokenizer tok) throws SAMException {
int ntoks = tok.countTokens();
Properties props = new Properties();
StringBuilder value = new StringBuilder();
for (int i = 0; i < ntoks; ++i) {
String token = tok.nextToken();
public static Properties parseParams(String args) throws SAMException {
final Properties rv = new Properties();
final StringBuilder buf = new StringBuilder(32);
final int length = args.length();
boolean isQuoted = false;
String key = null;
// We go one past the end to force a fake trailing space
// to make things easier, so we don't need cleanup at the end
for (int i = 0; i <= length; i++) {
char c = (i < length) ? args.charAt(i) : ' ';
switch (c) {
case '"':
if (isQuoted) {
// keys never quoted
if (key != null) {
if (rv.setProperty(key, buf.length() > 0 ? buf.toString() : "true") != null)
throw new SAMException("Duplicate parameter " + key);
key = null;
}
buf.setLength(0);
}
isQuoted = !isQuoted;
break;
int pos = token.indexOf("=");
if (pos <= 0) {
//_log.debug("Error in params format");
if (pos == 0) {
throw new SAMException("No param specified [" + token + "]");
} else {
throw new SAMException("Bad formatting for param [" + token + "]");
}
}
String param = token.substring(0, pos);
value.append(token.substring(pos+1));
if (value.length() == 0)
throw new SAMException("Empty value for param " + param);
// FIXME: The following code does not take into account that there
// may have been multiple subsequent space chars in the input that
// StringTokenizer treates as one.
if (value.charAt(0) == '"') {
while ( (i < ntoks) && (value.lastIndexOf("\"") <= 0) ) {
value.append(' ').append(tok.nextToken());
i++;
}
}
case '\r':
case '\n':
break;
props.setProperty(param, value.toString());
value.setLength(0);
case ' ':
case '\b':
case '\f':
case '\t':
// whitespace - if we're in a quoted section, keep this as part of the quote,
// otherwise use it as a delim
if (isQuoted) {
buf.append(c);
} else {
if (key != null) {
if (rv.setProperty(key, buf.length() > 0 ? buf.toString() : "true") != null)
throw new SAMException("Duplicate parameter " + key);
key = null;
} else if (buf.length() > 0) {
// key without value
String k = buf.toString();
if (rv.isEmpty()) {
k = k.toUpperCase(Locale.US);
rv.setProperty(COMMAND, k);
if (k.equals("PING") || k.equals("PONG")) {
// eat the rest of the line
if (i + 1 < args.length()) {
String pingData = args.substring(i + 1);
rv.setProperty(OPCODE, pingData);
}
// this will force an end of the loop
i = length + 1;
}
} else if (rv.size() == 1) {
rv.setProperty(OPCODE, k.toUpperCase(Locale.US));
} else {
if (rv.setProperty(k, "true") != null)
throw new SAMException("Duplicate parameter " + k);
}
}
buf.setLength(0);
}
break;
case '=':
if (isQuoted) {
buf.append(c);
} else if (key != null) {
// '=' in a value
buf.append(c);
} else {
if (buf.length() == 0)
throw new SAMException("Empty parameter name");
key = buf.toString();
buf.setLength(0);
}
break;
case '\\':
if (++i >= length)
throw new SAMException("Unterminated escape");
c = args.charAt(i);
// fall through...
default:
buf.append(c);
break;
}
}
//if (_log.shouldLog(Log.DEBUG)) {
// _log.debug("Parsed properties: " + dumpProperties(props));
//}
return props;
// nothing needed here, as we forced a trailing space in the loop
// unterminated quoted content will be lost
if (isQuoted)
throw new SAMException("Unterminated quote");
return rv;
}
/* Dump a Properties object in an human-readable form */
/****
private static String dumpProperties(Properties props) {
StringBuilder builder = new StringBuilder();
String key, val;
boolean firstIter = true;
for (Map.Entry<Object, Object> entry : props.entrySet()) {
key = (String) entry.getKey();
val = (String) entry.getValue();
if (!firstIter) {
builder.append(";");
} else {
firstIter = false;
}
builder.append(" \"" + key + "\" -> \"" + val + "\"");
}
return builder.toString();
}
****/
/****
public static void main(String args[]) {
try {
test("a=b c=d e=\"f g h\"");
test("a=\"b c d\" e=\"f g h\" i=\"j\"");
test("a=\"b c d\" e=f i=\"j\"");
if (args.length == 0) {
System.out.println("Usage: CommandParser file || CommandParser text to parse");
return;
}
if (args.length > 1 || !(new java.io.File(args[0])).exists()) {
StringBuilder buf = new StringBuilder(128);
for (int i = 0; i < args.length; i++) {
if (i != 0)
buf.append(' ');
buf.append(args[i]);
}
test(buf.toString());
} else {
java.io.InputStream in = new java.io.FileInputStream(args[0]);
String line;
while ((line = net.i2p.data.DataHelper.readLine(in)) != null) {
try {
test(line);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void test(String props) throws Exception {
StringTokenizer tok = new StringTokenizer(props);
Properties p = parseParams(tok);
System.out.println(p);
System.out.println("Testing: " + props);
Properties m = parseParams(props);
System.out.println("Found " + m.size() + " keys");
for (Map.Entry e : m.entrySet()) {
System.out.println(e.getKey() + "=[" + e.getValue() + ']');
}
System.out.println("-------------");
}
****/
}

View File

@ -15,10 +15,11 @@ import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.SocketChannel;
import java.nio.ByteBuffer;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PException;
@ -49,6 +50,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
protected final long _id;
private static final AtomicLong __id = new AtomicLong();
private static final int FIRST_READ_TIMEOUT = 60*1000;
/**
* Create a new SAM version 1 handler. This constructor expects
@ -98,14 +100,15 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
String domain = null;
String opcode = null;
boolean canContinue = false;
StringTokenizer tok;
Properties props;
final StringBuilder buf = new StringBuilder(128);
this.thread.setName("SAMv1Handler " + _id);
if (_log.shouldLog(Log.DEBUG))
_log.debug("SAM handling started");
try {
boolean gotFirstLine = false;
while (true) {
if (shouldStop()) {
if (_log.shouldLog(Log.DEBUG))
@ -122,43 +125,39 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
_log.info("Connection closed by client");
break;
}
java.io.InputStream is = clientSocketChannel.socket().getInputStream();
if (is == null) {
_log.info("Connection closed by client");
break;
}
msg = DataHelper.readLine(is);
if (msg == null) {
_log.info("Connection closed by client (line read : null)");
buf.setLength(0);
// first time, set a timeout
try {
Socket sock = clientSocketChannel.socket();
ReadLine.readLine(sock, buf, gotFirstLine ? 0 : FIRST_READ_TIMEOUT);
sock.setSoTimeout(0);
} catch (SocketTimeoutException ste) {
writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
break;
}
msg = msg.trim();
msg = buf.toString();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("New message received: [" + msg + "]");
_log.debug("New message received: [" + msg + ']');
}
if(msg.equals("")) {
props = SAMUtils.parseParams(msg);
domain = (String) props.remove(SAMUtils.COMMAND);
if (domain == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Ignoring newline");
continue;
}
tok = new StringTokenizer(msg, " ");
if (tok.countTokens() < 2) {
// This is not a correct message, for sure
opcode = (String) props.remove(SAMUtils.OPCODE);
if (opcode == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Error in message format");
break;
}
domain = tok.nextToken();
opcode = tok.nextToken();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Parsing (domain: \"" + domain
+ "\"; opcode: \"" + opcode + "\")");
}
props = SAMUtils.parseParams(tok);
gotFirstLine = true;
if (domain.equals("STREAM")) {
canContinue = execStreamMessage(opcode, props);
} else if (domain.equals("DATAGRAM")) {
@ -370,16 +369,11 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
/* Parse and execute a NAMING message */
protected boolean execNamingMessage(String opcode, Properties props) {
if (opcode.equals("LOOKUP")) {
if (props.isEmpty()) {
_log.debug("No parameters specified in NAMING LOOKUP message");
return false;
}
String name = props.getProperty("NAME");
if (name == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Name to resolve not specified in NAMING message");
return false;
return writeString("NAMING REPLY RESULT=KEY_NOT_FOUND NAME=\"\" MESSAGE=\"Must specify NAME\"\n");
}
Destination dest = null ;
@ -1012,10 +1006,10 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
msg = msg.replace("\r", " ");
if (!msg.startsWith("\"")) {
msg = msg.replace("\"", "");
if (msg.contains("\"") || msg.contains("\t"))
if (msg.contains(" ") || msg.contains("\t"))
msg = '"' + msg + '"';
}
rv = " MESSAGE=\"" + msg + "\"";
rv = " MESSAGE=" + msg;
} else {
rv = "";
}

View File

@ -105,29 +105,18 @@ class SAMv2StreamSession extends SAMStreamSession
return false ;
}
Destination d = new Destination();
d.fromBase64 ( dest );
Destination d = SAMUtils.getDest(dest);
I2PSocketOptions opts = socketMgr.buildOptions ( props );
if ( props.getProperty ( I2PSocketOptions.PROP_CONNECT_TIMEOUT ) == null )
opts.setConnectTimeout ( 60 * 1000 );
if (_log.shouldLog(Log.DEBUG))
_log.debug ( "Connecting new I2PSocket..." );
// non-blocking connection (SAMv2)
StreamConnector connector ;
connector = new StreamConnector ( id, d, opts );
StreamConnector connector = new StreamConnector ( id, d, opts );
I2PAppThread connectThread = new I2PAppThread ( connector, "StreamConnector" + id ) ;
connectThread.start() ;
return true ;
}

View File

@ -24,6 +24,7 @@ import net.i2p.client.I2PSession;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.PortMapper;
/**
* This is the thread listening on 127.0.0.1:7655 or as specified by
@ -90,7 +91,7 @@ class SAMv3DatagramServer implements Handler {
/** @since 0.9.24 */
public int getPort() { return _port; }
private static class Listener implements Runnable {
private class Listener implements Runnable {
private final DatagramChannel server;
@ -98,8 +99,17 @@ class SAMv3DatagramServer implements Handler {
{
this.server = server ;
}
public void run()
{
public void run() {
I2PAppContext.getGlobalContext().portMapper().register(PortMapper.SVC_SAM_UDP, _host, _port);
try {
run2();
} finally {
I2PAppContext.getGlobalContext().portMapper().unregister(PortMapper.SVC_SAM_UDP);
}
}
private void run2() {
ByteBuffer inBuf = ByteBuffer.allocateDirect(SAMRawSession.RAW_SIZE_MAX+1024);
while (!Thread.interrupted())
@ -135,6 +145,7 @@ class SAMv3DatagramServer implements Handler {
public void run() {
try {
String header = DataHelper.readLine(is).trim();
// we cannot use SAMUtils.parseParams() here
StringTokenizer tok = new StringTokenizer(header, " ");
if (tok.countTokens() < 3) {
// This is not a correct message, for sure

View File

@ -24,7 +24,6 @@ import java.nio.channels.SocketChannel;
import java.nio.ByteBuffer;
import java.util.Properties;
import java.util.HashMap;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -55,6 +54,7 @@ class SAMv3Handler extends SAMv1Handler
private volatile boolean streamForwardingSocket;
private final boolean sendPorts;
private long _lastPing;
private static final int FIRST_READ_TIMEOUT = 60*1000;
private static final int READ_TIMEOUT = 3*60*1000;
interface Session {
@ -262,7 +262,6 @@ class SAMv3Handler extends SAMv1Handler
String domain = null;
String opcode = null;
boolean canContinue = false;
StringTokenizer tok;
Properties props;
this.thread.setName("SAMv3Handler " + _id);
@ -274,6 +273,7 @@ class SAMv3Handler extends SAMv1Handler
InputStream in = socket.getInputStream();
StringBuilder buf = new StringBuilder(1024);
boolean gotFirstLine = false;
while (true) {
if (shouldStop()) {
if (_log.shouldLog(Log.DEBUG))
@ -331,63 +331,53 @@ class SAMv3Handler extends SAMv1Handler
}
} else {
buf.setLength(0);
if (DataHelper.readLine(in, buf))
line = buf.toString();
else
line = null;
}
if (line==null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connection closed by client (line read : null)");
break;
}
msg = line.trim();
if (_log.shouldLog(Log.DEBUG)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("New message received: [" + msg + "]");
// first time, set a timeout
try {
ReadLine.readLine(socket, buf, gotFirstLine ? 0 : FIRST_READ_TIMEOUT);
socket.setSoTimeout(0);
} catch (SocketTimeoutException ste) {
writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"command timeout, bye\"\n");
break;
}
line = buf.toString();
}
if(msg.equals("")) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("New message received: [" + line + ']');
props = SAMUtils.parseParams(line);
domain = (String) props.remove(SAMUtils.COMMAND);
if (domain == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Ignoring newline");
continue;
}
tok = new StringTokenizer(msg, " ");
int count = tok.countTokens();
if (count <= 0) {
// This is not a correct message, for sure
if (_log.shouldLog(Log.DEBUG))
_log.debug("Ignoring whitespace");
continue;
gotFirstLine = true;
opcode = (String) props.remove(SAMUtils.OPCODE);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Parsing (domain: \"" + domain
+ "\"; opcode: \"" + opcode + "\")");
}
domain = tok.nextToken();
// these may not have a second token
if (domain.equals("PING")) {
execPingMessage(tok);
execPingMessage(opcode);
continue;
} else if (domain.equals("PONG")) {
execPongMessage(tok);
execPongMessage(opcode);
continue;
} else if (domain.equals("QUIT") || domain.equals("STOP") ||
domain.equals("EXIT")) {
writeString(domain + " STATUS RESULT=OK MESSAGE=bye\n");
break;
}
if (count <= 1) {
if (opcode == null) {
// This is not a correct message, for sure
if (writeString(domain + " STATUS RESULT=I2P_ERROR MESSAGE=\"command not specified\"\n"))
continue;
else
break;
}
opcode = tok.nextToken();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Parsing (domain: \"" + domain
+ "\"; opcode: \"" + opcode + "\")");
}
props = SAMUtils.parseParams(tok);
if (domain.equals("STREAM")) {
canContinue = execStreamMessage(opcode, props);
@ -909,13 +899,15 @@ class SAMv3Handler extends SAMv1Handler
/**
* Handle a PING.
* Send a PONG.
*
* @param msg to append, may be null
* @since 0.9.24
*/
private void execPingMessage(StringTokenizer tok) {
private void execPingMessage(String msg) {
StringBuilder buf = new StringBuilder();
buf.append("PONG");
while (tok.hasMoreTokens()) {
buf.append(' ').append(tok.nextToken());
if (msg != null) {
buf.append(' ').append(msg);
}
buf.append('\n');
writeString(buf.toString());
@ -923,13 +915,12 @@ class SAMv3Handler extends SAMv1Handler
/**
* Handle a PONG.
*
* @param s received, may be null
* @since 0.9.24
*/
private void execPongMessage(StringTokenizer tok) {
String s;
if (tok.hasMoreTokens()) {
s = tok.nextToken();
} else {
private void execPongMessage(String s) {
if (s == null) {
s = "";
}
if (_lastPing > 0) {

View File

@ -149,10 +149,10 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
SAMBridge bridge = handler.getBridge();
(new Thread(rec.getThreadGroup(),
(new I2PAppThread(rec.getThreadGroup(),
new Pipe(fromClient, toI2P, bridge),
"ConnectV3 SAMPipeClientToI2P")).start();
(new Thread(rec.getThreadGroup(),
(new I2PAppThread(rec.getThreadGroup(),
new Pipe(fromI2P, toClient, bridge),
"ConnectV3 SAMPipeI2PToClient")).start();
}
@ -208,10 +208,10 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
SAMBridge bridge = handler.getBridge();
(new Thread(rec.getThreadGroup(),
(new I2PAppThread(rec.getThreadGroup(),
new Pipe(fromClient, toI2P, bridge),
"AcceptV3 SAMPipeClientToI2P")).start();
(new Thread(rec.getThreadGroup(),
(new I2PAppThread(rec.getThreadGroup(),
new Pipe(fromI2P, toClient, bridge),
"AcceptV3 SAMPipeI2PToClient")).start();
}
@ -258,7 +258,7 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
}
SocketForwarder forwarder = new SocketForwarder(host, port, isSSL, this, verbose, sendPorts);
(new Thread(rec.getThreadGroup(), forwarder, "SAMV3StreamForwarder")).start();
(new I2PAppThread(rec.getThreadGroup(), forwarder, "SAMV3StreamForwarder")).start();
}
/**

View File

@ -0,0 +1,152 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
/**
* An unbuffered version of InputStreamReader.
*
* Does not read any extra characters, as long as input is well-formed.
* This permits the partial reading of an InputStream as UTF-8
* and then passing the remainder of the input stream elsewhere.
* This isn't the most robust for malformed input, so it
* may not be appropriate for e.g. HTTP headers.
*
* Not thread-safe, obviously.
*
* May be moved to net.i2p.util if anybody else needs it.
*
* @since 0.9.24 somewhat adapted from net.i2p.util.TranslateReader
*/
public class UTF8Reader extends Reader {
private final InputStream _in;
// following three are lazily initialized when needed
private ByteBuffer _bb;
private CharBuffer _cb;
private CharsetDecoder _dc;
// Charset.forName("UTF-8").newDecoder().replacement().charAt(0) & 0xffff
private static final int REPLACEMENT = 0xfffd;
/**
* @param in UTF-8
*/
public UTF8Reader(InputStream in) {
super();
_in = in;
}
/**
* @return replacement character on decoding error
*/
@Override
public int read() throws IOException {
int b = _in.read();
if (b < 0)
return b;
// https://en.wikipedia.org/wiki/Utf-8
if ((b & 0x80) == 0)
return b;
if (_bb == null) {
_bb = ByteBuffer.allocate(6);
_cb = CharBuffer.allocate(1);
_dc = Charset.forName("UTF-8").newDecoder();
} else {
_bb.clear();
_cb.clear();
}
_bb.put((byte) b);
int end; // how many more
if ((b & 0xe0) == 0xc0)
end = 1;
else if ((b & 0xf0) == 0xe0)
end = 2;
else if ((b & 0xf8) == 0xf0)
end = 3;
else if ((b & 0xfc) == 0xf8)
end = 4;
else if ((b & 0xfe) == 0xfc)
end = 5;
else // error, 10xxxxxx
return REPLACEMENT;
for (int i = 0; i < end; i++) {
b = _in.read();
if (b < 0)
return REPLACEMENT; // next read will return EOF
// we aren't going to check for all errors,
// but let's fail fast on this one
if ((b & 0x80) == 0)
return REPLACEMENT;
_bb.put((byte) b);
}
_dc.reset();
_bb.flip();
CoderResult result = _dc.decode(_bb, _cb, true);
// Overflow and underflow are not errors.
// It seems to return underflow every time.
// So just check if we got a character back in the buffer.
_cb.flip();
if (result.isError() || !_cb.hasRemaining())
return REPLACEMENT;
// let underflow and overflow go, return first
return _cb.get() & 0xffff;
}
@Override
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
public int read(char cbuf[], int off, int len) throws IOException {
for (int i = 0; i < len; i++) {
int c = read();
if (c < 0) {
if (i == 0)
return -1;
return i;
}
cbuf[off + i] = (char) c;
}
return len;
}
public void close() throws IOException {
_in.close();
}
/****
public static void main(String[] args) {
try {
String s = "Consider the encoding of the Euro sign, €." +
" The Unicode code point for \"€\" is U+20AC.";
byte[] test = s.getBytes("UTF-8");
InputStream bais = new java.io.ByteArrayInputStream(test);
UTF8Reader r = new UTF8Reader(bais);
int b;
StringBuilder buf = new StringBuilder(128);
while ((b = r.read()) >= 0) {
buf.append((char) b);
}
System.out.println("Received: " + buf);
System.out.println("Test passed? " + buf.toString().equals(s));
buf.setLength(0);
bais = new java.io.ByteArrayInputStream(new byte[] { 'x', (byte) 0xcc, 'x' } );
r = new UTF8Reader(bais);
while ((b = r.read()) >= 0) {
buf.append((char) b);
}
System.out.println("Received: " + buf);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
****/
}

View File

@ -5,8 +5,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
@ -21,6 +23,7 @@ public class SAMReader {
private final SAMClientEventListener _listener;
private volatile boolean _live;
private Thread _thread;
private static final AtomicInteger _count = new AtomicInteger();
public SAMReader(I2PAppContext context, InputStream samIn, SAMClientEventListener listener) {
_log = context.logManager().getLog(SAMReader.class);
@ -32,7 +35,7 @@ public class SAMReader {
if (_live)
throw new IllegalStateException();
_live = true;
I2PAppThread t = new I2PAppThread(new Runner(), "SAM reader");
I2PAppThread t = new I2PAppThread(new Runner(), "SAM reader " + _count.incrementAndGet());
t.start();
_thread = t;
}
@ -111,10 +114,7 @@ public class SAMReader {
break;
}
String line = "";
try {
line = new String(baos.toByteArray(), "ISO-8859-1");
} catch (IOException ioe) {}
String line = DataHelper.getUTF8(baos.toByteArray());
baos.reset();
if (_log.shouldDebug())
@ -275,7 +275,7 @@ public class SAMReader {
String pr = params.getProperty("PROTOCOL");
int fromPort = 0;
int toPort = 0;
int protocol = 18;
int protocol = I2PSession.PROTO_DATAGRAM_RAW;
try {
if (fp != null)
fromPort = Integer.parseInt(fp);

View File

@ -69,7 +69,7 @@ public class SAMStreamSend {
String port = "7656";
String user = null;
String password = null;
String opts = "";
String opts = "inbound.length=0 outbound.length=0";
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
@ -237,9 +237,10 @@ public class SAMStreamSend {
synchronized (samOut) {
try {
if (user != null && password != null)
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=" + user + " PASSWORD=" + password + '\n').getBytes());
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=\"" + user.replace("\"", "\\\"") +
"\" PASSWORD=\"" + password.replace("\"", "\\\"") + "\"\n").getBytes("UTF-8"));
else
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hello sent");
@ -256,6 +257,8 @@ public class SAMStreamSend {
byte[] id = new byte[5];
_context.random().nextBytes(id);
_v3ID = Base32.encode(id);
if (_isV32)
_v3ID = "xx€€xx" + _v3ID;
_conOptions = "ID=" + _v3ID;
}
String style;
@ -266,7 +269,7 @@ public class SAMStreamSend {
else
style = "RAW";
String req = "SESSION CREATE STYLE=" + style + " DESTINATION=TRANSIENT " + _conOptions + ' ' + opts + '\n';
samOut.write(req.getBytes());
samOut.write(req.getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session create sent");
@ -277,7 +280,7 @@ public class SAMStreamSend {
_log.debug("Session create reply found: " + ok);
req = "NAMING LOOKUP NAME=ME\n";
samOut.write(req.getBytes());
samOut.write(req.getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Naming lookup sent");
@ -350,7 +353,7 @@ public class SAMStreamSend {
byte dest[] = new byte[1024];
int read = DataHelper.read(fin, dest);
_remoteDestination = new String(dest, 0, read);
_remoteDestination = DataHelper.getUTF8(dest, 0, read);
_context.statManager().createRateStat("send." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("send." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
@ -363,7 +366,7 @@ public class SAMStreamSend {
if (_isV3)
buf.append(" FROM_PORT=1234 TO_PORT=5678");
buf.append('\n');
byte[] msg = DataHelper.getASCII(buf.toString());
byte[] msg = DataHelper.getUTF8(buf.toString());
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
@ -431,7 +434,7 @@ public class SAMStreamSend {
} else {
throw new IOException("unsupported mode " + _mode);
}
byte msg[] = DataHelper.getASCII(m);
byte msg[] = DataHelper.getUTF8(m);
_samOut.write(msg);
}
_samOut.write(data, 0, read);
@ -440,16 +443,16 @@ public class SAMStreamSend {
} else {
// real datagrams
ByteArrayOutputStream baos = new ByteArrayOutputStream(read + 1024);
baos.write(DataHelper.getASCII("3.0 "));
baos.write(DataHelper.getASCII(_v3ID));
baos.write(DataHelper.getUTF8("3.0 "));
baos.write(DataHelper.getUTF8(_v3ID));
baos.write((byte) ' ');
baos.write(DataHelper.getASCII(_remoteDestination));
baos.write(DataHelper.getUTF8(_remoteDestination));
if (_isV32) {
// only set TO_PORT to test session setting of FROM_PORT
if (_mode == RAW)
baos.write(DataHelper.getASCII(" PROTOCOL=123 TO_PORT=5678"));
baos.write(DataHelper.getUTF8(" PROTOCOL=123 TO_PORT=5678"));
else
baos.write(DataHelper.getASCII(" TO_PORT=5678"));
baos.write(DataHelper.getUTF8(" TO_PORT=5678"));
}
baos.write((byte) '\n');
baos.write(data, 0, read);
@ -476,12 +479,13 @@ public class SAMStreamSend {
_log.info("Error closing", ioe);
}
} else {
byte msg[] = ("STREAM CLOSE ID=" + _connectionId + "\n").getBytes();
try {
byte msg[] = ("STREAM CLOSE ID=" + _connectionId + "\n").getBytes("UTF-8");
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
_samOut.close();
// we can't close this yet, we will lose data
//_samOut.close();
}
} catch (IOException ioe) {
_log.info("Error closing", ioe);
@ -492,20 +496,18 @@ public class SAMStreamSend {
}
closed();
// stop the reader, since we're only doing this once for testing
// you wouldn't do this in a real application
// closing the master socket too fast will kill the data socket flushing through
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Runner exiting");
if (toSend != _totalSent)
_log.error("Only sent " + _totalSent + " of " + toSend + " bytes");
if (_reader2 != null)
_reader2.stopReading();
// stop the reader, since we're only doing this once for testing
// you wouldn't do this in a real application
if (_isV3) {
// closing the master socket too fast will kill the data socket flushing through
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {}
}
_reader.stopReading();
}
}

View File

@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Properties;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import gnu.getopt.Getopt;
@ -56,10 +57,13 @@ 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, RAWHDR = 5, FORWARD = 6;
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; raw-with-headers: 5; stream-forward: 6\n" +
" -s: use SSL\n" +
private static final int STREAM=0, DG=1, V1DG=2, RAW=3, V1RAW=4, RAWHDR = 5, FORWARD = 6, FORWARDSSL=7;
private static final String USAGE = "Usage: SAMStreamSink [-s] [-m mode] [-v version] [-b samHost] [-p samPort]\n" +
" [-o opt=val] [-u user] [-w password] myDestFile sinkDir\n" +
" modes: stream: 0; datagram: 1; v1datagram: 2;\n" +
" raw: 3; v1raw: 4; raw-with-headers: 5;\n" +
" stream-forward: 6; stream-forward-ssl: 7\n" +
" -s: use SSL to connect to bridge\n" +
" multiple -o session options are allowed";
private static final int V3FORWARDPORT=9998;
private static final int V3DGPORT=9999;
@ -73,7 +77,7 @@ public class SAMStreamSink {
String port = "7656";
String user = null;
String password = null;
String opts = "";
String opts = "inbound.length=0 outbound.length=0";
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
@ -83,7 +87,7 @@ public class SAMStreamSink {
case 'm':
mode = Integer.parseInt(g.getOptarg());
if (mode < 0 || mode > FORWARD) {
if (mode < 0 || mode > FORWARDSSL) {
System.err.println(USAGE);
return;
}
@ -174,7 +178,7 @@ public class SAMStreamSink {
Thread t = new Pinger(out);
t.start();
}
if (_isV3 && (mode == STREAM || mode == FORWARD)) {
if (_isV3 && (mode == STREAM || mode == FORWARD || mode == FORWARDSSL)) {
// test multiple acceptors, only works in 3.2
int acceptors = (_isV32 && mode == STREAM) ? 4 : 1;
for (int i = 0; i < acceptors; i++) {
@ -193,7 +197,18 @@ public class SAMStreamSink {
}
if (mode == FORWARD) {
// set up a listening ServerSocket
(new FwdRcvr(isSSL)).start();
(new FwdRcvr(false, null)).start();
} else if (mode == FORWARDSSL) {
// set up a listening ServerSocket
String scfile = SSLUtil.DEFAULT_SAMCLIENT_CONFIGFILE;
File file = new File(scfile);
Properties opts = new Properties();
if (file.exists())
DataHelper.loadProps(opts, file);
boolean shouldSave = SSLUtil.verifyKeyStore(opts);
if (shouldSave)
DataHelper.storeProps(opts, file);
(new FwdRcvr(true, opts)).start();
}
} else if (_isV3 && (mode == DG || mode == RAW || mode == RAWHDR)) {
// set up a listening DatagramSocket
@ -208,7 +223,10 @@ public class SAMStreamSink {
private class DGRcvr extends I2PAppThread {
private final int _mode;
public DGRcvr(int mode) { _mode = mode; }
public DGRcvr(int mode) {
super("SAM DG Rcvr");
_mode = mode;
}
public void run() {
byte[] buf = new byte[32768];
@ -257,18 +275,23 @@ public class SAMStreamSink {
private class FwdRcvr extends I2PAppThread {
private final boolean _isSSL;
// for SSL only
private final Properties _opts;
public FwdRcvr(boolean isSSL) {
if (isSSL)
throw new UnsupportedOperationException("TODO");
public FwdRcvr(boolean isSSL, Properties opts) {
super("SAM Fwd Rcvr");
_isSSL = isSSL;
_opts = opts;
}
public void run() {
try {
ServerSocket ss;
if (_isSSL) {
throw new UnsupportedOperationException("TODO");
SSLServerSocketFactory fact = SSLUtil.initializeFactory(_opts);
SSLServerSocket sock = (SSLServerSocket) fact.createServerSocket(V3FORWARDPORT);
I2PSSLSocketFactory.setProtocolsAndCiphers(sock);
ss = sock;
} else {
ss = new ServerSocket(V3FORWARDPORT);
}
@ -277,10 +300,40 @@ public class SAMStreamSink {
Sink sink = new Sink("FAKE", "FAKEFROM");
try {
InputStream in = s.getInputStream();
boolean gotDest = false;
byte[] dest = new byte[1024];
int dlen = 0;
byte[] buf = new byte[32768];
int len;
while((len = in.read(buf)) >= 0) {
sink.received(buf, 0, len);
if (!gotDest) {
// eat the dest line
for (int i = 0; i < len; i++) {
byte b = buf[i];
if (b == (byte) '\n') {
gotDest = true;
if (_log.shouldInfo()) {
try {
_log.info("Got incoming accept from: \"" + new String(dest, 0, dlen, "ISO-8859-1") + '"');
} catch (IOException uee) {}
}
// feed any remaining to the sink
i++;
if (i < len)
sink.received(buf, i, len - i);
break;
} else {
if (dlen < dest.length) {
dest[dlen++] = b;
} else if (dlen == dest.length) {
dlen++;
_log.error("first line overflow on accept");
}
}
}
} else {
sink.received(buf, 0, len);
}
}
sink.closed();
} catch (IOException ioe) {
@ -307,7 +360,7 @@ public class SAMStreamSink {
try {
Thread.sleep(127*1000);
synchronized(_out) {
_out.write(DataHelper.getASCII("PING " + System.currentTimeMillis() + '\n'));
_out.write(DataHelper.getUTF8("PING " + System.currentTimeMillis() + '\n'));
_out.flush();
}
} catch (InterruptedException ie) {
@ -377,7 +430,7 @@ public class SAMStreamSink {
_log.info("Got PING " + data + ", sending PONG " + data);
synchronized (_out) {
try {
_out.write(("PONG " + data + '\n').getBytes());
_out.write(("PONG " + data + '\n').getBytes("UTF-8"));
_out.flush();
} catch (IOException ioe) {
_log.error("PONG fail", ioe);
@ -514,9 +567,9 @@ public class SAMStreamSink {
synchronized (samOut) {
try {
if (user != null && password != null)
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=" + user + " PASSWORD=" + password + '\n').getBytes());
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + " USER=" + user + " PASSWORD=" + password + '\n').getBytes("UTF-8"));
else
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hello sent");
@ -534,13 +587,15 @@ public class SAMStreamSink {
req = "STREAM ACCEPT SILENT=false TO_PORT=5678 ID=" + _v3ID + "\n";
else if (mode == FORWARD)
req = "STREAM FORWARD ID=" + _v3ID + " PORT=" + V3FORWARDPORT + '\n';
else if (mode == FORWARDSSL)
req = "STREAM FORWARD ID=" + _v3ID + " PORT=" + V3FORWARDPORT + " SSL=true\n";
else
throw new IllegalStateException("mode " + mode);
samOut.write(req.getBytes());
samOut.write(req.getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("STREAM ACCEPT/FORWARD sent");
if (mode == FORWARD) {
if (mode == FORWARD || mode == FORWARDSSL) {
// docs were wrong, we do not get a STREAM STATUS if SILENT=true for ACCEPT
boolean ok = eventHandler.waitForStreamStatusReply();
if (!ok)
@ -587,7 +642,7 @@ public class SAMStreamSink {
dest = _destFile;
}
String style;
if (mode == STREAM || mode == FORWARD)
if (mode == STREAM || mode == FORWARD || mode == FORWARDSSL)
style = "STREAM";
else if (mode == V1DG)
style = "DATAGRAM";
@ -600,7 +655,7 @@ public class SAMStreamSink {
else
style = "RAW HEADER=true PORT=" + V3DGPORT;
String req = "SESSION CREATE STYLE=" + style + " DESTINATION=" + dest + ' ' + _conOptions + ' ' + sopts + '\n';
samOut.write(req.getBytes());
samOut.write(req.getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session create sent");
@ -612,7 +667,7 @@ public class SAMStreamSink {
_log.debug("Session create reply found: " + ok);
}
req = "NAMING LOOKUP NAME=ME\n";
samOut.write(req.getBytes());
samOut.write(req.getBytes("UTF-8"));
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Naming lookup sent");
@ -649,7 +704,7 @@ public class SAMStreamSink {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
fos.write(dest.getBytes());
fos.write(dest.getBytes("UTF-8"));
if (_log.shouldLog(Log.DEBUG))
_log.debug("My destination written to " + _destFile);
} catch (IOException e) {

View File

@ -0,0 +1,189 @@
package net.i2p.sam.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.util.Properties;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLContext;
import net.i2p.I2PAppContext;
import net.i2p.crypto.KeyStoreUtil;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
/**
* Utilities for SAM SSL server sockets.
*
* @since 0.9.24 copied from net.i2p.sam for testing SSL stream forwarding
*/
class SSLUtil {
public static final String DEFAULT_SAMCLIENT_CONFIGFILE = "samclient.config";
private static final String PROP_KEYSTORE_PASSWORD = "samclient.keystorePassword";
private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
private static final String PROP_KEY_PASSWORD = "samclient.keyPassword";
private static final String PROP_KEY_ALIAS = "samclient.keyAlias";
private static final String ASCII_KEYFILE_SUFFIX = ".local.crt";
private static final String PROP_KS_NAME = "samclient.keystoreFile";
private static final String KS_DIR = "keystore";
private static final String PREFIX = "samclient-";
private static final String KS_SUFFIX = ".ks";
private static final String CERT_DIR = "certificates/samclient";
/**
* Create a new selfsigned cert and keystore and pubkey cert if they don't exist.
* May take a while.
*
* @param opts in/out, updated if rv is true
* @return false if it already exists; if true, caller must save opts
* @throws IOException on creation fail
*/
public static boolean verifyKeyStore(Properties opts) throws IOException {
String name = opts.getProperty(PROP_KEY_ALIAS);
if (name == null) {
name = KeyStoreUtil.randomString();
opts.setProperty(PROP_KEY_ALIAS, name);
}
String ksname = opts.getProperty(PROP_KS_NAME);
if (ksname == null) {
ksname = PREFIX + name + KS_SUFFIX;
opts.setProperty(PROP_KS_NAME, ksname);
}
File ks = new File(ksname);
if (!ks.isAbsolute()) {
ks = new File(I2PAppContext.getGlobalContext().getConfigDir(), KS_DIR);
ks = new File(ks, ksname);
}
if (ks.exists())
return false;
File dir = ks.getParentFile();
if (!dir.exists()) {
File sdir = new SecureDirectory(dir.getAbsolutePath());
if (!sdir.mkdirs())
throw new IOException("Unable to create keystore " + ks);
}
boolean rv = createKeyStore(ks, name, opts);
if (!rv)
throw new IOException("Unable to create keystore " + ks);
// Now read it back out of the new keystore and save it in ascii form
// where the clients can get to it.
// Failure of this part is not fatal.
exportCert(ks, name, opts);
return true;
}
/**
* Call out to keytool to create a new keystore with a keypair in it.
*
* @param name used in CNAME
* @param opts in/out, updated if rv is true, must contain PROP_KEY_ALIAS
* @return success, if true, opts will have password properties added to be saved
*/
private static boolean createKeyStore(File ks, String name, Properties opts) {
// make a random 48 character password (30 * 8 / 5)
String keyPassword = KeyStoreUtil.randomString();
// and one for the cname
String cname = name + ".sam.i2p.net";
String keyName = opts.getProperty(PROP_KEY_ALIAS);
boolean success = KeyStoreUtil.createKeys(ks, keyName, cname, "SAM", keyPassword);
if (success) {
success = ks.exists();
if (success) {
opts.setProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
opts.setProperty(PROP_KEY_PASSWORD, keyPassword);
}
}
if (success) {
logAlways("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
"The certificate name was generated randomly, and is not associated with your " +
"IP address, host name, router identity, or destination keys.");
} else {
error("Failed to create SAM SSL keystore.\n" +
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
" to " + (new File(I2PAppContext.getGlobalContext().getConfigDir(), DEFAULT_SAMCLIENT_CONFIGFILE)).getAbsolutePath());
}
return success;
}
/**
* Pull the cert back OUT of the keystore and save it as ascii
* so the clients can get to it.
*
* @param name used to generate output file name
* @param opts must contain PROP_KEY_ALIAS
*/
private static void exportCert(File ks, String name, Properties opts) {
File sdir = new SecureDirectory(I2PAppContext.getGlobalContext().getConfigDir(), CERT_DIR);
if (sdir.exists() || sdir.mkdirs()) {
String keyAlias = opts.getProperty(PROP_KEY_ALIAS);
String ksPass = opts.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
File out = new File(sdir, PREFIX + name + ASCII_KEYFILE_SUFFIX);
boolean success = KeyStoreUtil.exportCert(ks, ksPass, keyAlias, out);
if (!success)
error("Error getting SSL cert to save as ASCII");
} else {
error("Error saving ASCII SSL keys");
}
}
/**
* Sets up the SSLContext and sets the socket factory.
* No option prefix allowed.
*
* @throws IOException; GeneralSecurityExceptions are wrapped in IOE for convenience
* @return factory, throws on all errors
*/
public static SSLServerSocketFactory initializeFactory(Properties opts) throws IOException {
String ksPass = opts.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
String keyPass = opts.getProperty(PROP_KEY_PASSWORD);
if (keyPass == null) {
throw new IOException("No key password, set " + PROP_KEY_PASSWORD + " in " +
(new File(I2PAppContext.getGlobalContext().getConfigDir(), DEFAULT_SAMCLIENT_CONFIGFILE)).getAbsolutePath());
}
String ksname = opts.getProperty(PROP_KS_NAME);
if (ksname == null) {
throw new IOException("No keystore, set " + PROP_KS_NAME + " in " +
(new File(I2PAppContext.getGlobalContext().getConfigDir(), DEFAULT_SAMCLIENT_CONFIGFILE)).getAbsolutePath());
}
File ks = new File(ksname);
if (!ks.isAbsolute()) {
ks = new File(I2PAppContext.getGlobalContext().getConfigDir(), KS_DIR);
ks = new File(ks, ksname);
}
InputStream fis = null;
try {
SSLContext sslc = SSLContext.getInstance("TLS");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
fis = new FileInputStream(ks);
keyStore.load(fis, ksPass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPass.toCharArray());
sslc.init(kmf.getKeyManagers(), null, I2PAppContext.getGlobalContext().random());
return sslc.getServerSocketFactory();
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("keystore error");
ioe.initCause(gse);
throw ioe;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
private static void error(String s) {
I2PAppContext.getGlobalContext().logManager().getLog(SSLUtil.class).error(s);
}
private static void logAlways(String s) {
I2PAppContext.getGlobalContext().logManager().getLog(SSLUtil.class).logAlways(Log.INFO, s);
}
}

View File

@ -40,12 +40,12 @@ public class TestCreateSessionDatagram {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=DATAGRAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");

View File

@ -56,12 +56,12 @@ public class TestCreateSessionRaw {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
//_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=RAW DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK");

View File

@ -5,6 +5,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
public class TestCreateSessionStream {
@ -40,12 +41,12 @@ public class TestCreateSessionStream {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");

View File

@ -7,6 +7,7 @@ import java.net.Socket;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
public class TestDatagramTransfer {
@ -22,17 +23,17 @@ public class TestDatagramTransfer {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=DATAGRAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=ME\n";
out.write(lookup.getBytes());
out.write(DataHelper.getASCII(lookup));
line = reader.readLine();
_log.info("Response from the lookup for ME: " + line);
_log.debug("The above should be a NAMING REPLY");
@ -50,7 +51,7 @@ public class TestDatagramTransfer {
}
String send = "DATAGRAM SEND DESTINATION=" + value + " SIZE=3\nYo!";
out.write(send.getBytes());
out.write(DataHelper.getASCII(send));
line = reader.readLine();
tok = new StringTokenizer(line);
maj = tok.nextToken();

View File

@ -19,17 +19,17 @@ public class TestDest {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=testNaming " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.debug("Response to creating the session with destination testNaming: " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "DEST GENERATE\n";
out.write(lookup.getBytes());
out.write(DataHelper.getASCII(lookup));
line = reader.readLine();
_log.info("Response from the dest generate: " + line);
_log.debug("The abouve should be a DEST REPLY");

View File

@ -5,6 +5,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
public class TestHello {
@ -21,7 +22,7 @@ public class TestHello {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for valid version: " + line);
@ -36,7 +37,7 @@ public class TestHello {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=9.0 MAX=8.3\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=9.0 MAX=8.3\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for invalid version: " + line);
@ -51,7 +52,7 @@ public class TestHello {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO h0 h0 h0\n".getBytes());
out.write(DataHelper.getASCII("HELLO h0 h0 h0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for valid version: " + line);

View File

@ -39,17 +39,17 @@ public class TestNaming {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=testNaming " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.debug("Response to creating the session with destination testNaming: " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=" + name + "\n";
out.write(lookup.getBytes());
out.write(DataHelper.getASCII(lookup));
line = reader.readLine();
_log.info("Response from the lookup for [" + name +"]: " + line);
_log.debug("The abouve should be a NAMING REPLY");

View File

@ -22,17 +22,17 @@ public class TestRawTransfer {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=RAW DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=ME\n";
out.write(lookup.getBytes());
out.write(DataHelper.getASCII(lookup));
line = reader.readLine();
_log.info("Response from the lookup for ME: " + line);
_log.debug("The above should be a NAMING REPLY");
@ -50,7 +50,7 @@ public class TestRawTransfer {
}
String send = "RAW SEND DESTINATION=" + value + " SIZE=3\nYo!";
out.write(send.getBytes());
out.write(DataHelper.getASCII(send));
line = reader.readLine();
try {
tok = new StringTokenizer(line);

View File

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@ -62,17 +63,17 @@ public class TestStreamTransfer {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=Alice " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination Alice: " + line);
req = "NAMING LOOKUP NAME=ME\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
@ -212,16 +213,16 @@ public class TestStreamTransfer {
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
out.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + sessionName + " " + conOptions + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to creating the session with destination "+ sessionName+": " + line);
req = "STREAM CONNECT ID=42 DESTINATION=" + _alice + "\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
line = reader.readLine();
_log.info("Response to the stream connect from "+sessionName+" to Alice: " + line);
StringTokenizer tok = new StringTokenizer(line);
@ -238,18 +239,18 @@ public class TestStreamTransfer {
try { Thread.sleep(5*1000) ; } catch (InterruptedException ie) {}
req = "STREAM SEND ID=42 SIZE=10\nBlahBlah!!";
_log.info("\n** Sending BlahBlah!!");
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
out.flush();
try { Thread.sleep(5*1000) ; } catch (InterruptedException ie) {}
req = "STREAM SEND ID=42 SIZE=10\nFooBarBaz!";
_log.info("\n** Sending FooBarBaz!");
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
out.flush();
/* Don't delay here, so we can test whether all data is
sent even if we do a STREAM CLOSE immediately. */
_log.info("Sending close");
req = "STREAM CLOSE ID=42\n";
out.write(req.getBytes());
out.write(DataHelper.getASCII(req));
out.flush();
synchronized (_counterLock) {
_closeCounter++;

View File

@ -140,7 +140,7 @@ public class TestSwarm {
private String handshake() {
synchronized (_samOut) {
try {
_samOut.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
_samOut.write(DataHelper.getASCII("HELLO VERSION MIN=1.0 MAX=1.0\n"));
_samOut.flush();
_log.debug("Hello sent");
boolean ok = _eventHandler.waitForHelloReply();
@ -148,14 +148,14 @@ public class TestSwarm {
if (!ok)
throw new IOException("wtf, hello failed?");
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + _destFile + " " + _conOptions + "\n";
_samOut.write(req.getBytes());
_samOut.write(DataHelper.getUTF8(req));
_samOut.flush();
_log.debug("Session create sent");
ok = _eventHandler.waitForSessionCreateReply();
_log.debug("Session create reply found: " + ok);
req = "NAMING LOOKUP NAME=ME\n";
_samOut.write(req.getBytes());
_samOut.write(DataHelper.getASCII(req));
_samOut.flush();
_log.debug("Naming lookup sent");
String destination = _eventHandler.waitForNamingReply("ME");
@ -177,7 +177,7 @@ public class TestSwarm {
private boolean writeDest(String dest) {
try {
FileOutputStream fos = new FileOutputStream(_destFile);
fos.write(dest.getBytes());
fos.write(DataHelper.getASCII(dest));
fos.close();
return true;
} catch (Exception e) {
@ -203,7 +203,7 @@ public class TestSwarm {
_remotePeers.put(new Integer(con), flooder);
}
byte msg[] = ("STREAM CONNECT ID=" + con + " DESTINATION=" + remDest + "\n").getBytes();
byte msg[] = (DataHelper.getUTF8("STREAM CONNECT ID=" + con + " DESTINATION=" + remDest + "\n"));
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
@ -257,7 +257,7 @@ public class TestSwarm {
long value = 0;
long lastSend = _context.clock().now();
while (!_closed) {
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + data.length + "\n").getBytes();
byte msg[] = (DataHelper.getASCII("STREAM SEND ID=" + _connectionId + " SIZE=" + data.length + "\n"));
DataHelper.toLong(data, 0, 4, value);
try {
synchronized (_samOut) {