diff --git a/core/java/src/net/i2p/client/naming/AddressDB.java b/core/java/src/net/i2p/client/naming/AddressDB.java new file mode 100644 index 0000000000000000000000000000000000000000..2ad38c3e22706eb8aa540d2804f9111d3b8c0260 --- /dev/null +++ b/core/java/src/net/i2p/client/naming/AddressDB.java @@ -0,0 +1,59 @@ +package net.i2p.client.naming; + +import java.lang.reflect.Constructor; +import java.util.Collection; + +import net.i2p.I2PAppContext; +import net.i2p.util.Log; +import net.i2p.data.Address; + +public abstract class AddressDB { + + private final static Log _log = new Log(NamingService.class); + protected I2PAppContext _context; + + /** what classname should be used as the address db impl? */ + public static final String PROP_IMPL = "i2p.addressdb.impl"; + private static final String DEFAULT_IMPL = "net.i2p.client.naming.FilesystemAddressDB"; + + /** + * The address db should only be constructed and accessed through the + * application context. This constructor should only be used by the + * appropriate application context itself. + * + */ + protected AddressDB(I2PAppContext context) { + _context = context; + } + + private AddressDB() { // nop + } + + /** + * Get an address db instance. This method ensures that there + * will be only one address db instance (singleton) as well as + * choose the implementation from the "i2p.addressdb.impl" system + * property. + */ + public static final synchronized AddressDB createInstance(I2PAppContext context) { + AddressDB instance = null; + String impl = context.getProperty(PROP_IMPL, DEFAULT_IMPL); + try { + Class cls = Class.forName(impl); + Constructor con = cls.getConstructor(new Class[] { I2PAppContext.class }); + instance = (AddressDB)con.newInstance(new Object[] { context }); + } catch (Exception ex) { + _log.error("Cannot load address db implementation", ex); + instance = new DummyAddressDB(context); // fallback + } + return instance; + } + + public abstract Address get(String hostname); + public abstract Address put(Address address); + public abstract Address remove(String hostname); + public abstract Address remove(Address address); + public abstract boolean contains(Address address); + public abstract boolean contains(String hostname); + public abstract Collection hostnames(); +} diff --git a/core/java/src/net/i2p/client/naming/AddressDBNamingService.java b/core/java/src/net/i2p/client/naming/AddressDBNamingService.java new file mode 100644 index 0000000000000000000000000000000000000000..04abba456fc05b32a283f5f7c19e687c0d5a9bd0 --- /dev/null +++ b/core/java/src/net/i2p/client/naming/AddressDBNamingService.java @@ -0,0 +1,42 @@ +package net.i2p.client.naming; + +import java.util.Iterator; + +import net.i2p.I2PAppContext; +import net.i2p.data.Destination; +import net.i2p.data.Address; + +public class AddressDBNamingService extends NamingService { + + private AddressDB _addressdb; + + public AddressDBNamingService(I2PAppContext context) { + super(context); + _addressdb = AddressDB.createInstance(context); + } + + private AddressDBNamingService() { + super(null); + } + + public Destination lookup(String hostname) { + Address addr = _addressdb.get(hostname); + if (addr != null) { + return addr.getDestination(); + } else { + // If we can't find hostname in the addressdb, assume it's a key. + return lookupBase64(hostname); + } + } + + public String reverseLookup(Destination dest) { + Iterator iter = _addressdb.hostnames().iterator(); + while (iter.hasNext()) { + Address addr = _addressdb.get((String)iter.next()); + if (addr != null && addr.getDestination().equals(dest)) { + return addr.getHostname(); + } + } + return null; + } +} diff --git a/core/java/src/net/i2p/client/naming/DummyAddressDB.java b/core/java/src/net/i2p/client/naming/DummyAddressDB.java new file mode 100644 index 0000000000000000000000000000000000000000..3d151b587afe18806d81836474c0bae715097839 --- /dev/null +++ b/core/java/src/net/i2p/client/naming/DummyAddressDB.java @@ -0,0 +1,42 @@ +package net.i2p.client.naming; + +import java.util.Collection; + +import net.i2p.I2PAppContext; +import net.i2p.data.Address; + +public class DummyAddressDB extends AddressDB { + + public DummyAddressDB(I2PAppContext context) { + super(context); + } + + public Address get(String hostname) { + return null; + } + + public Address put(Address address) { + return null; + } + + public Address remove(String hostname) { + return null; + } + + public Address remove(Address address) { + return null; + } + + public boolean contains(Address address) { + return false; + } + + public boolean contains(String hostname) { + return false; + } + + public Collection hostnames() { + return null; + } + +} diff --git a/core/java/src/net/i2p/client/naming/FilesystemAddressDB.java b/core/java/src/net/i2p/client/naming/FilesystemAddressDB.java new file mode 100644 index 0000000000000000000000000000000000000000..4a2e37e2799d267fe7dc3a385812a27e669fa663 --- /dev/null +++ b/core/java/src/net/i2p/client/naming/FilesystemAddressDB.java @@ -0,0 +1,118 @@ +package net.i2p.client.naming; + +import java.util.Collection; +import java.util.Arrays; +import java.util.Properties; +import java.util.Iterator; +import java.io.*; + +import net.i2p.I2PAppContext; +import net.i2p.data.Address; +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.util.Log; + +public class FilesystemAddressDB extends AddressDB { + + public final static String PROP_ADDRESS_DIR = "i2p.addressdir"; + public final static String DEFAULT_ADDRESS_DIR = "addressDb"; + private final static Log _log = new Log(FilesystemAddressDB.class); + + public FilesystemAddressDB(I2PAppContext context) { + super(context); + + //If the address db directory doesn't exist, create it, using the + //contents of hosts.txt. + String dir = _context.getProperty(PROP_ADDRESS_DIR, DEFAULT_ADDRESS_DIR); + File addrDir = new File(dir); + if (!addrDir.exists()) { + addrDir.mkdir(); + Properties hosts = new Properties(); + File hostsFile = new File("hosts.txt"); + if (hostsFile.exists() && hostsFile.canRead()) { + try { + DataHelper.loadProps(hosts, hostsFile); + } catch (IOException ioe) { + _log.error("Error loading hosts file " + hostsFile, ioe); + } + } + Iterator iter = hosts.keySet().iterator(); + while (iter.hasNext()) { + String hostname = (String)iter.next(); + Address addr = new Address(); + addr.setHostname(hostname); + addr.setDestination(hosts.getProperty(hostname)); + put(addr); + } + } + } + + public Address get(String hostname) { + String dir = _context.getProperty(PROP_ADDRESS_DIR, DEFAULT_ADDRESS_DIR); + File f = new File(dir, hostname); + if (f.exists() && f.canRead()) { + Address addr = new Address(); + try { + addr.readBytes(new FileInputStream(f)); + } catch (FileNotFoundException exp) { + return null; + } catch (DataFormatException exp) { + _log.error(f.getPath() + " is not a valid address file."); + return null; + } catch (IOException exp) { + _log.error("Error reading " + f.getPath()); + return null; + } + return addr; + } else { + _log.warn(f.getPath() + " does not exist."); + return null; + } + } + + public Address put(Address address) { + Address previous = get(address.getHostname()); + + String dir = _context.getProperty(PROP_ADDRESS_DIR, DEFAULT_ADDRESS_DIR); + File f = new File(dir, address.getHostname()); + try { + address.writeBytes(new FileOutputStream(f)); + } catch (Exception exp) { + _log.error("Error writing " + f.getPath(), exp); + } + return previous; + } + + public Address remove(String hostname) { + Address previous = get(hostname); + + String dir = _context.getProperty(PROP_ADDRESS_DIR, DEFAULT_ADDRESS_DIR); + File f = new File(dir, hostname); + f.delete(); + return previous; + } + + public Address remove(Address address) { + if (contains(address)) { + return remove(address.getHostname()); + } else { + return null; + } + } + + public boolean contains(Address address) { + Address inDb = get(address.getHostname()); + return inDb.equals(address); + } + + public boolean contains(String hostname) { + return hostnames().contains(hostname); + } + + public Collection hostnames() { + String dir = _context.getProperty(PROP_ADDRESS_DIR, DEFAULT_ADDRESS_DIR); + File f = new File(dir); + return Arrays.asList(f.list()); + } + +} diff --git a/core/java/src/net/i2p/data/Address.java b/core/java/src/net/i2p/data/Address.java new file mode 100644 index 0000000000000000000000000000000000000000..9d0358a820716be11c56fa58e661f07991a46bd4 --- /dev/null +++ b/core/java/src/net/i2p/data/Address.java @@ -0,0 +1,81 @@ +package net.i2p.data; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import net.i2p.util.Log; + +public class Address extends DataStructureImpl { + private final static Log _log = new Log(Address.class); + private String _hostname; + private Destination _destination; + + public Address() { + _hostname = null; + _destination = null; + } + + public String getHostname() { + return _hostname; + } + + public void setHostname(String hostname) { + _hostname = hostname; + } + + public Destination getDestination() { + return _destination; + } + + public void setDestination(Destination destination) { + _destination = destination; + } + + public void setDestination(String base64) { + try { + Destination result = new Destination(); + result.fromBase64(base64); + _destination = result; + } catch (DataFormatException dfe) { + _destination = null; + } + } + + public void readBytes(InputStream in) throws DataFormatException, + IOException { + _hostname = DataHelper.readString(in); + _destination = new Destination(); + _destination.readBytes(in); + } + + public void writeBytes(OutputStream out) throws DataFormatException, + IOException { + if ((_hostname == null) || (_destination == null)) + throw new DataFormatException("Not enough data to write address"); + DataHelper.writeString(out, _hostname); + _destination.writeBytes(out); + } + + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof Address)) return false; + Address addr = (Address) obj; + return DataHelper.eq(_hostname, addr.getHostname()) + && DataHelper.eq(_destination, addr.getDestination()); + } + + public int hashCode() { + return DataHelper.hashCode(getHostname()) + + DataHelper.hashCode(getDestination()); + } + + public String toString() { + StringBuffer buf = new StringBuffer(64); + buf.append("[Address: "); + buf.append("\n\tHostname: ").append(getHostname()); + buf.append("\n\tDestination: ").append(getDestination()); + buf.append("]"); + return buf.toString(); + } + +}