* I2CP, HostsTxtNamingService, I2PTunnel:
Implement Base32 Hash hostnames, via the naming service. Names are of the form [52-characters].i2p, where the 52 characters are the Base32 representation of our 256-byte hash. The client requests a lookup of the hash via a brief I2CP session using new I2CP request/reply messages. The router looks up the leaseset for the hash to convert the hash to a dest. Convert the I2PTunnel 'preview' links to use Base32 hostnames as a demonstration.
This commit is contained in:
@ -14,6 +14,7 @@ import net.i2p.I2PException;
|
|||||||
import net.i2p.client.I2PClient;
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.client.I2PClientFactory;
|
import net.i2p.client.I2PClientFactory;
|
||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.util.I2PThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -361,6 +362,19 @@ public class TunnelController implements Logging {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMyDestHashBase32() {
|
||||||
|
if (_tunnel != null) {
|
||||||
|
List sessions = _tunnel.getSessions();
|
||||||
|
for (int i = 0; i < sessions.size(); i++) {
|
||||||
|
I2PSession session = (I2PSession)sessions.get(i);
|
||||||
|
Destination dest = session.getMyDestination();
|
||||||
|
if (dest != null)
|
||||||
|
return Base32.encode(dest.calculateHash().getData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getIsRunning() { return _running; }
|
public boolean getIsRunning() { return _running; }
|
||||||
public boolean getIsStarting() { return _starting; }
|
public boolean getIsStarting() { return _starting; }
|
||||||
|
|
||||||
|
@ -437,6 +437,19 @@ public class IndexBean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDestHashBase32(int tunnel) {
|
||||||
|
TunnelController tun = getController(tunnel);
|
||||||
|
if (tun != null) {
|
||||||
|
String rv = tun.getMyDestHashBase32();
|
||||||
|
if (rv != null)
|
||||||
|
return rv;
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// bean props for form submission
|
/// bean props for form submission
|
||||||
///
|
///
|
||||||
|
@ -180,13 +180,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="targetField rowItem">
|
<div class="targetField rowItem">
|
||||||
<label>Points at:</label>
|
<label>Points at:</label>
|
||||||
<span class="text"><%=indexBean.getServerTarget(curServer)%></span>
|
<span class="text">
|
||||||
|
<%
|
||||||
|
if ("httpserver".equals(indexBean.getInternalType(curServer))) {
|
||||||
|
%>
|
||||||
|
<a href="http://<%=indexBean.getServerTarget(curServer)%>/" title="Test HTTP server, bypassing I2P"><%=indexBean.getServerTarget(curServer)%></a>
|
||||||
|
<%
|
||||||
|
} else {
|
||||||
|
%><%=indexBean.getServerTarget(curServer)%>
|
||||||
|
<%
|
||||||
|
}
|
||||||
|
%></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="previewField rowItem">
|
<div class="previewField rowItem">
|
||||||
<%
|
<%
|
||||||
if ("httpserver".equals(indexBean.getInternalType(curServer)) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
if ("httpserver".equals(indexBean.getInternalType(curServer)) && indexBean.getTunnelStatus(curServer) == IndexBean.RUNNING) {
|
||||||
%><label>Preview:</label>
|
%><label>Preview:</label>
|
||||||
<a class="control" title="Preview this Tunnel" href="http://<%=(new java.util.Random()).nextLong()%>.i2p/?i2paddresshelper=<%=indexBean.getDestinationBase64(curServer)%>" target="_new">Preview</a>
|
<a class="control" title="Test HTTP server through I2P" href="http://<%=indexBean.getDestHashBase32(curServer)%>.i2p">Preview</a>
|
||||||
<%
|
<%
|
||||||
} else {
|
} else {
|
||||||
%><span class="comment">No Preview</span>
|
%><span class="comment">No Preview</span>
|
||||||
|
25
core/java/src/net/i2p/client/DestReplyMessageHandler.java
Normal file
25
core/java/src/net/i2p/client/DestReplyMessageHandler.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package net.i2p.client;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle I2CP dest replies from the router
|
||||||
|
*/
|
||||||
|
class DestReplyMessageHandler extends HandlerImpl {
|
||||||
|
public DestReplyMessageHandler(I2PAppContext ctx) {
|
||||||
|
super(ctx, DestReplyMessage.MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||||
|
_log.debug("Handle message " + message);
|
||||||
|
DestReplyMessage msg = (DestReplyMessage) message;
|
||||||
|
((I2PSimpleSession)session).destReceived(msg.getDestination());
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@ import net.i2p.data.i2cp.MessageStatusMessage;
|
|||||||
import net.i2p.data.i2cp.RequestLeaseSetMessage;
|
import net.i2p.data.i2cp.RequestLeaseSetMessage;
|
||||||
import net.i2p.data.i2cp.SessionStatusMessage;
|
import net.i2p.data.i2cp.SessionStatusMessage;
|
||||||
import net.i2p.data.i2cp.SetDateMessage;
|
import net.i2p.data.i2cp.SetDateMessage;
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains a map of message handlers that a session will want to use
|
* Contains a map of message handlers that a session will want to use
|
||||||
@ -24,9 +23,11 @@ import net.i2p.util.Log;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
class I2PClientMessageHandlerMap {
|
class I2PClientMessageHandlerMap {
|
||||||
private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
|
|
||||||
/** map of message type id --> I2CPMessageHandler */
|
/** map of message type id --> I2CPMessageHandler */
|
||||||
private I2CPMessageHandler _handlers[];
|
protected I2CPMessageHandler _handlers[];
|
||||||
|
|
||||||
|
/** for extension */
|
||||||
|
public I2PClientMessageHandlerMap() {}
|
||||||
|
|
||||||
public I2PClientMessageHandlerMap(I2PAppContext context) {
|
public I2PClientMessageHandlerMap(I2PAppContext context) {
|
||||||
int highest = DisconnectMessage.MESSAGE_TYPE;
|
int highest = DisconnectMessage.MESSAGE_TYPE;
|
||||||
|
@ -12,6 +12,7 @@ package net.i2p.client;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
import net.i2p.data.SessionKey;
|
import net.i2p.data.SessionKey;
|
||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
@ -126,4 +127,10 @@ public interface I2PSession {
|
|||||||
* Retrieve the signing SigningPrivateKey associated with the Destination
|
* Retrieve the signing SigningPrivateKey associated with the Destination
|
||||||
*/
|
*/
|
||||||
public SigningPrivateKey getPrivateKey();
|
public SigningPrivateKey getPrivateKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup up a Hash
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Destination lookupDest(Hash h) throws I2PSessionException;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import java.util.Set;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
import net.i2p.data.SessionKey;
|
import net.i2p.data.SessionKey;
|
||||||
@ -48,7 +49,7 @@ import net.i2p.util.SimpleTimer;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessageEventListener {
|
abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessageEventListener {
|
||||||
private Log _log;
|
protected Log _log;
|
||||||
/** who we are */
|
/** who we are */
|
||||||
private Destination _myDestination;
|
private Destination _myDestination;
|
||||||
/** private key for decryption */
|
/** private key for decryption */
|
||||||
@ -63,15 +64,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
private LeaseSet _leaseSet;
|
private LeaseSet _leaseSet;
|
||||||
|
|
||||||
/** hostname of router */
|
/** hostname of router */
|
||||||
private String _hostname;
|
protected String _hostname;
|
||||||
/** port num to router */
|
/** port num to router */
|
||||||
private int _portNum;
|
protected int _portNum;
|
||||||
/** socket for comm */
|
/** socket for comm */
|
||||||
private Socket _socket;
|
protected Socket _socket;
|
||||||
/** reader that always searches for messages */
|
/** reader that always searches for messages */
|
||||||
private I2CPMessageReader _reader;
|
protected I2CPMessageReader _reader;
|
||||||
/** where we pipe our messages */
|
/** where we pipe our messages */
|
||||||
private OutputStream _out;
|
protected OutputStream _out;
|
||||||
|
|
||||||
/** who we send events to */
|
/** who we send events to */
|
||||||
private I2PSessionListener _sessionListener;
|
private I2PSessionListener _sessionListener;
|
||||||
@ -90,10 +91,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
private Object _leaseSetWait = new Object();
|
private Object _leaseSetWait = new Object();
|
||||||
|
|
||||||
/** whether the session connection has already been closed (or not yet opened) */
|
/** whether the session connection has already been closed (or not yet opened) */
|
||||||
private boolean _closed;
|
protected boolean _closed;
|
||||||
|
|
||||||
/** whether the session connection is in the process of being closed */
|
/** whether the session connection is in the process of being closed */
|
||||||
private boolean _closing;
|
protected boolean _closing;
|
||||||
|
|
||||||
/** have we received the current date from the router yet? */
|
/** have we received the current date from the router yet? */
|
||||||
private boolean _dateReceived;
|
private boolean _dateReceived;
|
||||||
@ -106,7 +107,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
* reading of other messages (in turn, potentially leading to deadlock)
|
* reading of other messages (in turn, potentially leading to deadlock)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private AvailabilityNotifier _availabilityNotifier;
|
protected AvailabilityNotifier _availabilityNotifier;
|
||||||
|
|
||||||
void dateUpdated() {
|
void dateUpdated() {
|
||||||
_dateReceived = true;
|
_dateReceived = true;
|
||||||
@ -117,6 +118,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
|
|
||||||
public static final int LISTEN_PORT = 7654;
|
public static final int LISTEN_PORT = 7654;
|
||||||
|
|
||||||
|
/** for extension */
|
||||||
|
public I2PSessionImpl() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
||||||
* from the destKeyStream, and using the specified options to connect to the router
|
* from the destKeyStream, and using the specified options to connect to the router
|
||||||
@ -151,7 +155,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
* Parse the config for anything we know about
|
* Parse the config for anything we know about
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private void loadConfig(Properties options) {
|
protected void loadConfig(Properties options) {
|
||||||
_options = new Properties();
|
_options = new Properties();
|
||||||
_options.putAll(filter(options));
|
_options.putAll(filter(options));
|
||||||
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||||
@ -385,7 +389,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AvailabilityNotifier implements Runnable {
|
protected class AvailabilityNotifier implements Runnable {
|
||||||
private List _pendingIds;
|
private List _pendingIds;
|
||||||
private List _pendingSizes;
|
private List _pendingSizes;
|
||||||
private boolean _alive;
|
private boolean _alive;
|
||||||
@ -566,7 +570,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
|
|
||||||
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Destroy the session", new Exception("DestroySession()"));
|
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Destroy the session", new Exception("DestroySession()"));
|
||||||
_closing = true; // we use this to prevent a race
|
_closing = true; // we use this to prevent a race
|
||||||
if (sendDisconnect) {
|
if (sendDisconnect && _producer != null) { // only null if overridden by I2PSimpleSession
|
||||||
try {
|
try {
|
||||||
_producer.disconnect(this);
|
_producer.disconnect(this);
|
||||||
} catch (I2PSessionException ipe) {
|
} catch (I2PSessionException ipe) {
|
||||||
@ -659,4 +663,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getPrefix() { return "[" + (_sessionId == null ? -1 : _sessionId.getSessionId()) + "]: "; }
|
protected String getPrefix() { return "[" + (_sessionId == null ? -1 : _sessionId.getSessionId()) + "]: "; }
|
||||||
|
|
||||||
|
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import net.i2p.util.Log;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
class I2PSessionImpl2 extends I2PSessionImpl {
|
class I2PSessionImpl2 extends I2PSessionImpl {
|
||||||
private Log _log;
|
|
||||||
|
|
||||||
/** set of MessageState objects, representing all of the messages in the process of being sent */
|
/** set of MessageState objects, representing all of the messages in the process of being sent */
|
||||||
private Set _sendingStates;
|
private Set _sendingStates;
|
||||||
@ -41,6 +40,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
private final static boolean SHOULD_COMPRESS = true;
|
private final static boolean SHOULD_COMPRESS = true;
|
||||||
private final static boolean SHOULD_DECOMPRESS = true;
|
private final static boolean SHOULD_DECOMPRESS = true;
|
||||||
|
|
||||||
|
/** for extension */
|
||||||
|
public I2PSessionImpl2() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
||||||
* from the destKeyStream, and using the specified options to connect to the router
|
* from the destKeyStream, and using the specified options to connect to the router
|
||||||
@ -396,6 +398,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearStates() {
|
private void clearStates() {
|
||||||
|
if (_sendingStates == null) // only null if overridden by I2PSimpleSession
|
||||||
|
return;
|
||||||
synchronized (_sendingStates) {
|
synchronized (_sendingStates) {
|
||||||
for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
|
for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
|
||||||
MessageState state = (MessageState) iter.next();
|
MessageState state = (MessageState) iter.next();
|
||||||
|
47
core/java/src/net/i2p/client/I2PSimpleClient.java
Normal file
47
core/java/src/net/i2p/client/I2PSimpleClient.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package net.i2p.client;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.data.Certificate;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple client implementation with no Destination,
|
||||||
|
* just used to talk to the router.
|
||||||
|
*/
|
||||||
|
public class I2PSimpleClient implements I2PClient {
|
||||||
|
/** Don't do this */
|
||||||
|
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** or this */
|
||||||
|
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new session (though do not connect it yet)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||||
|
return createSession(I2PAppContext.getGlobalContext(), options);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a new session (though do not connect it yet)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public I2PSession createSession(I2PAppContext context, Properties options) throws I2PSessionException {
|
||||||
|
return new I2PSimpleSession(context, options);
|
||||||
|
}
|
||||||
|
}
|
123
core/java/src/net/i2p/client/I2PSimpleSession.java
Normal file
123
core/java/src/net/i2p/client/I2PSimpleSession.java
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package net.i2p.client;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.i2cp.DestLookupMessage;
|
||||||
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
|
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new session for doing naming queries only. Do not create a Destination.
|
||||||
|
* Don't create a producer. Do not send/receive messages to other Destinations.
|
||||||
|
* Cannot handle multiple simultaneous queries atm.
|
||||||
|
* Could be expanded to ask the router other things.
|
||||||
|
*/
|
||||||
|
class I2PSimpleSession extends I2PSessionImpl2 {
|
||||||
|
private boolean _destReceived;
|
||||||
|
private Object _destReceivedLock;
|
||||||
|
private Destination _destination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new session for doing naming queries only. Do not create a destination.
|
||||||
|
*
|
||||||
|
* @throws I2PSessionException if there is a problem
|
||||||
|
*/
|
||||||
|
public I2PSimpleSession(I2PAppContext context, Properties options) throws I2PSessionException {
|
||||||
|
_context = context;
|
||||||
|
_log = context.logManager().getLog(I2PSimpleSession.class);
|
||||||
|
_handlerMap = new SimpleMessageHandlerMap(context);
|
||||||
|
_closed = true;
|
||||||
|
_closing = false;
|
||||||
|
_availabilityNotifier = new AvailabilityNotifier();
|
||||||
|
if (options == null)
|
||||||
|
options = System.getProperties();
|
||||||
|
loadConfig(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the router and establish a session. This call blocks until
|
||||||
|
* a session is granted.
|
||||||
|
*
|
||||||
|
* @throws I2PSessionException if there is a configuration error or the router is
|
||||||
|
* not reachable
|
||||||
|
*/
|
||||||
|
public void connect() throws I2PSessionException {
|
||||||
|
_closed = false;
|
||||||
|
_availabilityNotifier.stopNotifying();
|
||||||
|
I2PThread notifier = new I2PThread(_availabilityNotifier);
|
||||||
|
notifier.setName("Simple Notifier");
|
||||||
|
notifier.setDaemon(true);
|
||||||
|
notifier.start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
_socket = new Socket(_hostname, _portNum);
|
||||||
|
_out = _socket.getOutputStream();
|
||||||
|
synchronized (_out) {
|
||||||
|
_out.write(I2PClient.PROTOCOL_BYTE);
|
||||||
|
}
|
||||||
|
InputStream in = _socket.getInputStream();
|
||||||
|
_reader = new I2CPMessageReader(in, this);
|
||||||
|
_reader.startReading();
|
||||||
|
|
||||||
|
} catch (UnknownHostException uhe) {
|
||||||
|
_closed = true;
|
||||||
|
throw new I2PSessionException(getPrefix() + "Bad host ", uhe);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_closed = true;
|
||||||
|
throw new I2PSessionException(getPrefix() + "Problem connecting to " + _hostname + " on port " + _portNum, ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** called by the message handler */
|
||||||
|
void destReceived(Destination d) {
|
||||||
|
_destReceived = true;
|
||||||
|
_destination = d;
|
||||||
|
synchronized (_destReceivedLock) {
|
||||||
|
_destReceivedLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||||
|
if (_closed)
|
||||||
|
return null;
|
||||||
|
_destReceivedLock = new Object();
|
||||||
|
sendMessage(new DestLookupMessage(h));
|
||||||
|
for (int i = 0; i < 10 && !_destReceived; i++) {
|
||||||
|
try {
|
||||||
|
synchronized (_destReceivedLock) {
|
||||||
|
_destReceivedLock.wait(1000);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
_destReceived = false;
|
||||||
|
return _destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only map message handlers that we will use
|
||||||
|
*/
|
||||||
|
class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap {
|
||||||
|
public SimpleMessageHandlerMap(I2PAppContext context) {
|
||||||
|
int highest = DestReplyMessage.MESSAGE_TYPE;
|
||||||
|
_handlers = new I2CPMessageHandler[highest+1];
|
||||||
|
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -55,6 +55,8 @@ public class HostsTxtNamingService extends NamingService {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int BASE32_HASH_LENGTH = 52; // 1 + Hash.HASH_LENGTH * 8 / 5
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Destination lookup(String hostname) {
|
public Destination lookup(String hostname) {
|
||||||
Destination d = getCache(hostname);
|
Destination d = getCache(hostname);
|
||||||
@ -69,6 +71,15 @@ public class HostsTxtNamingService extends NamingService {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try Base32 decoding
|
||||||
|
if (hostname.length() == BASE32_HASH_LENGTH + 4 && hostname.endsWith(".i2p")) {
|
||||||
|
d = LookupDest.lookupBase32Hash(_context, hostname.substring(0, BASE32_HASH_LENGTH));
|
||||||
|
if (d != null) {
|
||||||
|
putCache(hostname, d);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List filenames = getFilenames();
|
List filenames = getFilenames();
|
||||||
for (int i = 0; i < filenames.size(); i++) {
|
for (int i = 0; i < filenames.size(); i++) {
|
||||||
String hostsfile = (String)filenames.get(i);
|
String hostsfile = (String)filenames.get(i);
|
||||||
|
72
core/java/src/net/i2p/client/naming/LookupDest.java
Normal file
72
core/java/src/net/i2p/client/naming/LookupDest.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
package net.i2p.client.naming;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.client.I2PClient;
|
||||||
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.client.I2PSimpleClient;
|
||||||
|
import net.i2p.data.Base32;
|
||||||
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.LeaseSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect via I2CP and ask the router to look up
|
||||||
|
* the lease of a hash, convert it to a Destination and return it.
|
||||||
|
* Obviously this can take a while.
|
||||||
|
*
|
||||||
|
* All calls are blocking and return null on failure.
|
||||||
|
* Timeout is set to 10 seconds in I2PSimpleSession.
|
||||||
|
*/
|
||||||
|
class LookupDest {
|
||||||
|
|
||||||
|
protected LookupDest(I2PAppContext context) {}
|
||||||
|
|
||||||
|
static Destination lookupBase32Hash(I2PAppContext ctx, String key) {
|
||||||
|
byte[] h = Base32.decode(key);
|
||||||
|
if (h == null)
|
||||||
|
return null;
|
||||||
|
return lookupHash(ctx, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Might be useful but not in the context of urls due to upper/lower case */
|
||||||
|
/****
|
||||||
|
static Destination lookupBase64Hash(I2PAppContext ctx, String key) {
|
||||||
|
byte[] h = Base64.decode(key);
|
||||||
|
if (h == null)
|
||||||
|
return null;
|
||||||
|
return lookupHash(ctx, h);
|
||||||
|
}
|
||||||
|
****/
|
||||||
|
|
||||||
|
static Destination lookupHash(I2PAppContext ctx, byte[] h) {
|
||||||
|
Hash key = new Hash(h);
|
||||||
|
Destination rv = null;
|
||||||
|
try {
|
||||||
|
I2PClient client = new I2PSimpleClient();
|
||||||
|
Properties opts = new Properties();
|
||||||
|
String s = ctx.getProperty(I2PClient.PROP_TCP_HOST);
|
||||||
|
if (s != null)
|
||||||
|
opts.put(I2PClient.PROP_TCP_HOST, s);
|
||||||
|
s = ctx.getProperty(I2PClient.PROP_TCP_PORT);
|
||||||
|
if (s != null)
|
||||||
|
opts.put(I2PClient.PROP_TCP_PORT, s);
|
||||||
|
I2PSession session = client.createSession(null, opts);
|
||||||
|
session.connect();
|
||||||
|
rv = session.lookupDest(key);
|
||||||
|
session.destroySession();
|
||||||
|
} catch (I2PSessionException ise) {}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
System.out.println(lookupBase32Hash(I2PAppContext.getGlobalContext(), args[0]));
|
||||||
|
}
|
||||||
|
}
|
245
core/java/src/net/i2p/data/Base32.java
Normal file
245
core/java/src/net/i2p/data/Base32.java
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
package net.i2p.data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes and decodes to and from Base32 notation.
|
||||||
|
* Ref: RFC 3548
|
||||||
|
*
|
||||||
|
* Don't bother with '=' padding characters on encode or
|
||||||
|
* accept them on decode (i.e. don't require 5-character groups).
|
||||||
|
* No whitespace allowed.
|
||||||
|
*
|
||||||
|
* Decode accepts upper or lower case.
|
||||||
|
*/
|
||||||
|
public class Base32 {
|
||||||
|
|
||||||
|
private final static Log _log = new Log(Base32.class);
|
||||||
|
|
||||||
|
/** The 64 valid Base32 values. */
|
||||||
|
private final static char[] ALPHABET = {'a', 'b', 'c', 'd',
|
||||||
|
'e', 'f', 'g', 'h', 'i', 'j',
|
||||||
|
'k', 'l', 'm', 'n', 'o', 'p',
|
||||||
|
'q', 'r', 's', 't', 'u', 'v',
|
||||||
|
'w', 'x', 'y', 'z',
|
||||||
|
'2', '3', '4', '5', '6', '7'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a Base32 value to either its 5-bit reconstruction value
|
||||||
|
* or a negative number indicating some other meaning.
|
||||||
|
* Allow upper or lower case.
|
||||||
|
**/
|
||||||
|
private final static byte[] DECODABET = {
|
||||||
|
26, 27, 28, 29, 30, 31, -9, -9, // Numbers two through nine
|
||||||
|
-9, -9, -9, // Decimal 58 - 60
|
||||||
|
-1, // Equals sign at decimal 61
|
||||||
|
-9, -9, -9, // Decimal 62 - 64
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // Letters 'A' through 'M'
|
||||||
|
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'N' through 'Z'
|
||||||
|
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // Letters 'a' through 'm'
|
||||||
|
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'n' through 'z'
|
||||||
|
-9, -9, -9, -9, -9 // Decimal 123 - 127
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static byte BAD_ENCODING = -9; // Indicates error in encoding
|
||||||
|
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
|
||||||
|
|
||||||
|
/** Defeats instantiation. */
|
||||||
|
private Base32() { // nop
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length == 0) {
|
||||||
|
help();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runApp(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runApp(String args[]) {
|
||||||
|
try {
|
||||||
|
if ("encodestring".equalsIgnoreCase(args[0])) {
|
||||||
|
System.out.println(encode(args[1].getBytes()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InputStream in = System.in;
|
||||||
|
OutputStream out = System.out;
|
||||||
|
if (args.length >= 3) {
|
||||||
|
out = new FileOutputStream(args[2]);
|
||||||
|
}
|
||||||
|
if (args.length >= 2) {
|
||||||
|
in = new FileInputStream(args[1]);
|
||||||
|
}
|
||||||
|
if ("encode".equalsIgnoreCase(args[0])) {
|
||||||
|
encode(in, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("decode".equalsIgnoreCase(args[0])) {
|
||||||
|
decode(in, out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] read(InputStream in) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
|
||||||
|
byte buf[] = new byte[4096];
|
||||||
|
while (true) {
|
||||||
|
int read = in.read(buf);
|
||||||
|
if (read < 0) break;
|
||||||
|
baos.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void encode(InputStream in, OutputStream out) throws IOException {
|
||||||
|
String encoded = encode(read(in));
|
||||||
|
for (int i = 0; i < encoded.length(); i++)
|
||||||
|
out.write((byte)(encoded.charAt(i) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void decode(InputStream in, OutputStream out) throws IOException {
|
||||||
|
byte decoded[] = decode(new String(read(in)));
|
||||||
|
if (decoded == null) {
|
||||||
|
System.out.println("FAIL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.write(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void help() {
|
||||||
|
System.out.println("Syntax: Base32 encode <inFile> <outFile>");
|
||||||
|
System.out.println("or : Base32 encode <inFile>");
|
||||||
|
System.out.println("or : Base32 encodestring <string>");
|
||||||
|
System.out.println("or : Base32 encode");
|
||||||
|
System.out.println("or : Base32 decode <inFile> <outFile>");
|
||||||
|
System.out.println("or : Base32 decode <inFile>");
|
||||||
|
System.out.println("or : Base32 decode");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(String source) {
|
||||||
|
return (source != null ? encode(source.getBytes()) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encode(byte[] source) {
|
||||||
|
StringBuffer buf = new StringBuffer((source.length + 7) * 8 / 5);
|
||||||
|
encodeBytes(source, buf);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static byte[] emask = { (byte) 0x1f,
|
||||||
|
(byte) 0x01, (byte) 0x03, (byte) 0x07, (byte) 0x0f };
|
||||||
|
/**
|
||||||
|
* Encodes a byte array into Base32 notation.
|
||||||
|
*
|
||||||
|
* @param source The data to convert
|
||||||
|
*/
|
||||||
|
private static void encodeBytes(byte[] source, StringBuffer out) {
|
||||||
|
int usedbits = 0;
|
||||||
|
for (int i = 0; i < source.length; ) {
|
||||||
|
int fivebits;
|
||||||
|
if (usedbits < 3) {
|
||||||
|
fivebits = (source[i] >> (3 - usedbits)) & 0x1f;
|
||||||
|
usedbits += 5;
|
||||||
|
} else if (usedbits == 3) {
|
||||||
|
fivebits = source[i++] & 0x1f;
|
||||||
|
usedbits = 0;
|
||||||
|
} else {
|
||||||
|
fivebits = (source[i++] << (usedbits - 3)) & 0x1f;
|
||||||
|
if (i < source.length) {
|
||||||
|
usedbits -= 3;
|
||||||
|
fivebits |= (source[i] >> (8 - usedbits)) & emask[usedbits];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.append(ALPHABET[fivebits]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes data from Base32 notation and
|
||||||
|
* returns it as a string.
|
||||||
|
*
|
||||||
|
* @param s the string to decode
|
||||||
|
* @return The data as a string or null on failure
|
||||||
|
*/
|
||||||
|
public static String decodeToString(String s) {
|
||||||
|
byte[] b = decode(s);
|
||||||
|
if (b == null)
|
||||||
|
return null;
|
||||||
|
return new String(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode(String s) {
|
||||||
|
return decode(s.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static byte[] dmask = { (byte) 0xf8, (byte) 0x7c, (byte) 0x3e, (byte) 0x1f,
|
||||||
|
(byte) 0x0f, (byte) 0x07, (byte) 0x03, (byte) 0x01 };
|
||||||
|
/**
|
||||||
|
* Decodes Base32 content in byte array format and returns
|
||||||
|
* the decoded byte array.
|
||||||
|
*
|
||||||
|
* @param source The Base32 encoded data
|
||||||
|
* @return decoded data
|
||||||
|
*/
|
||||||
|
private static byte[] decode(byte[] source) {
|
||||||
|
int len58;
|
||||||
|
if (source.length <= 1)
|
||||||
|
len58 = source.length;
|
||||||
|
else
|
||||||
|
len58 = source.length * 5 / 8;
|
||||||
|
byte[] outBuff = new byte[len58];
|
||||||
|
int outBuffPosn = 0;
|
||||||
|
|
||||||
|
int usedbits = 0;
|
||||||
|
for (int i = 0; i < source.length; i++) {
|
||||||
|
int fivebits;
|
||||||
|
if ((source[i] & 0x80) != 0 || source[i] < '2' || source[i] > 'z')
|
||||||
|
fivebits = BAD_ENCODING;
|
||||||
|
else
|
||||||
|
fivebits = DECODABET[source[i] - '2'];
|
||||||
|
|
||||||
|
if (fivebits >= 0) {
|
||||||
|
if (usedbits == 0) {
|
||||||
|
outBuff[outBuffPosn] = (byte) ((fivebits << 3) & 0xf8);
|
||||||
|
usedbits = 5;
|
||||||
|
} else if (usedbits < 3) {
|
||||||
|
outBuff[outBuffPosn] |= (fivebits << (3 - usedbits)) & dmask[usedbits];
|
||||||
|
usedbits += 5;
|
||||||
|
} else if (usedbits == 3) {
|
||||||
|
outBuff[outBuffPosn++] |= fivebits;
|
||||||
|
usedbits = 0;
|
||||||
|
} else {
|
||||||
|
outBuff[outBuffPosn++] |= (fivebits >> (usedbits - 3)) & dmask[usedbits];
|
||||||
|
byte next = (byte) (fivebits << (11 - usedbits));
|
||||||
|
if (outBuffPosn < len58) {
|
||||||
|
outBuff[outBuffPosn] = next;
|
||||||
|
usedbits -= 3;
|
||||||
|
} else if (next != 0) {
|
||||||
|
_log.warn("Extra data at the end: " + next + "(decimal)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_log.warn("Bad Base32 input character at " + i + ": " + source[i] + "(decimal)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outBuff;
|
||||||
|
}
|
||||||
|
}
|
76
core/java/src/net/i2p/data/i2cp/DestLookupMessage.java
Normal file
76
core/java/src/net/i2p/data/i2cp/DestLookupMessage.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the router look up the dest for a hash
|
||||||
|
*/
|
||||||
|
public class DestLookupMessage extends I2CPMessageImpl {
|
||||||
|
public final static int MESSAGE_TYPE = 34;
|
||||||
|
private Hash _hash;
|
||||||
|
|
||||||
|
public DestLookupMessage() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestLookupMessage(Hash h) {
|
||||||
|
_hash = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hash getHash() {
|
||||||
|
return _hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
Hash h = new Hash();
|
||||||
|
try {
|
||||||
|
h.readBytes(in);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Unable to load the hash", dfe);
|
||||||
|
}
|
||||||
|
_hash = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
if (_hash == null)
|
||||||
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(Hash.HASH_LENGTH);
|
||||||
|
try {
|
||||||
|
_hash.writeBytes(os);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Error writing out the hash", dfe);
|
||||||
|
}
|
||||||
|
return os.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof DestLookupMessage)) {
|
||||||
|
DestLookupMessage msg = (DestLookupMessage) object;
|
||||||
|
return DataHelper.eq(getHash(), msg.getHash());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[DestLookupMessage: ");
|
||||||
|
buf.append("\n\tHash: ").append(_hash);
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
78
core/java/src/net/i2p/data/i2cp/DestReplyMessage.java
Normal file
78
core/java/src/net/i2p/data/i2cp/DestReplyMessage.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response to DestLookupMessage
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DestReplyMessage extends I2CPMessageImpl {
|
||||||
|
public final static int MESSAGE_TYPE = 35;
|
||||||
|
private Destination _dest;
|
||||||
|
|
||||||
|
public DestReplyMessage() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DestReplyMessage(Destination d) {
|
||||||
|
_dest = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Destination getDestination() {
|
||||||
|
return _dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
try {
|
||||||
|
Destination d = new Destination();
|
||||||
|
d.readBytes(in);
|
||||||
|
_dest = d;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_dest = null; // null dest allowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
if (_dest == null)
|
||||||
|
return new byte[0]; // null response allowed
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(_dest.size());
|
||||||
|
try {
|
||||||
|
_dest.writeBytes(os);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Error writing out the dest", dfe);
|
||||||
|
}
|
||||||
|
return os.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof DestReplyMessage)) {
|
||||||
|
DestReplyMessage msg = (DestReplyMessage) object;
|
||||||
|
return DataHelper.eq(getDestination(), msg.getDestination());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[DestReplyMessage: ");
|
||||||
|
buf.append("\n\tDestination: ").append(_dest);
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -81,6 +81,10 @@ public class I2CPMessageHandler {
|
|||||||
return new GetDateMessage();
|
return new GetDateMessage();
|
||||||
case SetDateMessage.MESSAGE_TYPE:
|
case SetDateMessage.MESSAGE_TYPE:
|
||||||
return new SetDateMessage();
|
return new SetDateMessage();
|
||||||
|
case DestLookupMessage.MESSAGE_TYPE:
|
||||||
|
return new DestLookupMessage();
|
||||||
|
case DestReplyMessage.MESSAGE_TYPE:
|
||||||
|
return new DestReplyMessage();
|
||||||
default:
|
default:
|
||||||
throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
|
throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ package net.i2p.router.client;
|
|||||||
import net.i2p.data.Payload;
|
import net.i2p.data.Payload;
|
||||||
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
||||||
import net.i2p.data.i2cp.CreateSessionMessage;
|
import net.i2p.data.i2cp.CreateSessionMessage;
|
||||||
|
import net.i2p.data.i2cp.DestLookupMessage;
|
||||||
import net.i2p.data.i2cp.DestroySessionMessage;
|
import net.i2p.data.i2cp.DestroySessionMessage;
|
||||||
import net.i2p.data.i2cp.GetDateMessage;
|
import net.i2p.data.i2cp.GetDateMessage;
|
||||||
import net.i2p.data.i2cp.I2CPMessage;
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
@ -78,6 +79,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
case DestroySessionMessage.MESSAGE_TYPE:
|
case DestroySessionMessage.MESSAGE_TYPE:
|
||||||
handleDestroySession(reader, (DestroySessionMessage)message);
|
handleDestroySession(reader, (DestroySessionMessage)message);
|
||||||
break;
|
break;
|
||||||
|
case DestLookupMessage.MESSAGE_TYPE:
|
||||||
|
handleDestLookup(reader, (DestLookupMessage)message);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Unhandled I2CP type received: " + message.getType());
|
_log.error("Unhandled I2CP type received: " + message.getType());
|
||||||
@ -85,13 +89,14 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle notifiation that there was an error
|
* Handle notification that there was an error
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void readError(I2CPMessageReader reader, Exception error) {
|
public void readError(I2CPMessageReader reader, Exception error) {
|
||||||
if (_runner.isDead()) return;
|
if (_runner.isDead()) return;
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Error occurred", error);
|
_log.error("Error occurred", error);
|
||||||
|
// Is this is a little drastic for an unknown message type?
|
||||||
_runner.stopRunning();
|
_runner.stopRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +233,10 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
_runner.leaseSetCreated(message.getLeaseSet());
|
_runner.leaseSetCreated(message.getLeaseSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleDestLookup(I2CPMessageReader reader, DestLookupMessage message) {
|
||||||
|
_context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getHash()));
|
||||||
|
}
|
||||||
|
|
||||||
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
||||||
private final static int MAX_SESSION_ID = 32767;
|
private final static int MAX_SESSION_ID = 32767;
|
||||||
|
|
||||||
|
54
router/java/src/net/i2p/router/client/LookupDestJob.java
Normal file
54
router/java/src/net/i2p/router/client/LookupDestJob.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
package net.i2p.router.client;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.LeaseSet;
|
||||||
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
|
import net.i2p.data.i2cp.I2CPMessageException;
|
||||||
|
import net.i2p.router.JobImpl;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the lease of a hash, to convert it to a Destination for the client
|
||||||
|
*/
|
||||||
|
class LookupDestJob extends JobImpl {
|
||||||
|
private ClientConnectionRunner _runner;
|
||||||
|
private Hash _hash;
|
||||||
|
|
||||||
|
public LookupDestJob(RouterContext context, ClientConnectionRunner runner, Hash h) {
|
||||||
|
super(context);
|
||||||
|
_runner = runner;
|
||||||
|
_hash = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return "LeaseSet Lookup for Client"; }
|
||||||
|
public void runJob() {
|
||||||
|
DoneJob done = new DoneJob(getContext());
|
||||||
|
getContext().netDb().lookupLeaseSet(_hash, done, done, 10*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DoneJob extends JobImpl {
|
||||||
|
public DoneJob(RouterContext enclosingContext) {
|
||||||
|
super(enclosingContext);
|
||||||
|
}
|
||||||
|
public String getName() { return "LeaseSet Lookup Reply to Client"; }
|
||||||
|
public void runJob() {
|
||||||
|
LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_hash);
|
||||||
|
if (ls != null)
|
||||||
|
returnDest(ls.getDestination());
|
||||||
|
else
|
||||||
|
returnDest(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void returnDest(Destination d) {
|
||||||
|
DestReplyMessage msg = new DestReplyMessage(d);
|
||||||
|
try {
|
||||||
|
_runner.doSend(msg);
|
||||||
|
} catch (I2CPMessageException ime) {}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user