diff --git a/.mtn-ignore b/.mtn-ignore
index 93fb7bf7c..433ecc709 100644
--- a/.mtn-ignore
+++ b/.mtn-ignore
@@ -1,5 +1,7 @@
# Just to try and prevent some noob disasters.
# Use mtn add --no-respect-ignore foo.jar to ignore this ignore list
+
+# Temporary/build files
_jsp\.java$
\.bz2$
\.tar$
@@ -19,13 +21,27 @@ _jsp\.java$
.\deb$
\.zip$
^\.
-^build
-^pkg-temp/
~$
+web-fragment.xml
+web-out.xml
+
+# Temporary/build dirs
+^build
+^pkg-temp
/build
-/classes/
+/classes
+/dist
+/mo
+/tmp
+^apps/jetty/jettylib
+
+# Debian-related
^debian/copyright
^debian/changelog
+
+# Build property overrides
override.properties
+
+# Reporting
sloccount.sc
^reports/
diff --git a/.tx/config b/.tx/config
index f8f1cf095..43be42266 100644
--- a/.tx/config
+++ b/.tx/config
@@ -19,6 +19,26 @@ trans.uk_UA = apps/i2ptunnel/locale/messages_uk.po
trans.vi = apps/i2ptunnel/locale/messages_vi.po
trans.zh_CN = apps/i2ptunnel/locale/messages_zh.po
+[I2P.proxy]
+source_file = apps/i2ptunnel/locale-proxy/messages_en.po
+source_lang = en
+trans.ar = apps/i2ptunnel/locale-proxy/messages_ar.po
+trans.cs = apps/i2ptunnel/locale-proxy/messages_cs.po
+trans.de = apps/i2ptunnel/locale-proxy/messages_de.po
+trans.es = apps/i2ptunnel/locale-proxy/messages_es.po
+trans.fr = apps/i2ptunnel/locale-proxy/messages_fr.po
+trans.hu = apps/i2ptunnel/locale-proxy/messages_hu.po
+trans.it = apps/i2ptunnel/locale-proxy/messages_it.po
+trans.nb = apps/i2ptunnel/locale-proxy/messages_nb.po
+trans.nl = apps/i2ptunnel/locale-proxy/messages_nl.po
+trans.pl = apps/i2ptunnel/locale-proxy/messages_pl.po
+trans.pt = apps/i2ptunnel/locale-proxy/messages_pt.po
+trans.ru_RU = apps/i2ptunnel/locale-proxy/messages_ru.po
+trans.sv_SE = apps/i2ptunnel/locale-proxy/messages_sv.po
+trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
+trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
+trans.zh_CN = apps/i2ptunnel/locale-proxy/messages_zh.po
+
[I2P.routerconsole]
source_file = apps/routerconsole/locale/messages_en.po
source_lang = en
@@ -44,6 +64,18 @@ trans.uk_UA = apps/routerconsole/locale/messages_uk.po
trans.vi = apps/routerconsole/locale/messages_vi.po
trans.zh_CN = apps/routerconsole/locale/messages_zh.po
+[I2P.welcome]
+source_file = apps/routerconsole/locale-news/messages_en.po
+source_lang = en
+trans.ar = apps/routerconsole/locale-news/messages_ar.po
+trans.de = apps/routerconsole/locale-news/messages_de.po
+trans.es = apps/routerconsole/locale-news/messages_es.po
+trans.fr = apps/routerconsole/locale-news/messages_fr.po
+trans.nl = apps/routerconsole/locale-news/messages_nl.po
+trans.pt = apps/routerconsole/locale-news/messages_pt.po
+trans.ru_RU = apps/routerconsole/locale-news/messages_ru.po
+trans.sv_SE = apps/routerconsole/locale-news/messages_sv.po
+
[I2P.i2psnark]
source_file = apps/i2psnark/locale/messages_en.po
source_lang = en
diff --git a/LICENSE.txt b/LICENSE.txt
index b6213a261..fdc899aa3 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -207,7 +207,7 @@ Applications:
FatCow icons: See licenses/LICENSE-FatCowIcons.txt
GeoIP Data:
- Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
+ This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/
See licenses/LICENSE-GeoIP.txt
Router Console and I2PSnark themes:
diff --git a/apps/BOB/.classpath b/apps/BOB/.classpath
new file mode 100644
index 000000000..52bf51f32
--- /dev/null
+++ b/apps/BOB/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/apps/BOB/.project b/apps/BOB/.project
new file mode 100644
index 000000000..59f25717f
--- /dev/null
+++ b/apps/BOB/.project
@@ -0,0 +1,17 @@
+
+
+ BOB
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/apps/addressbook/.classpath b/apps/addressbook/.classpath
new file mode 100644
index 000000000..8e9af0830
--- /dev/null
+++ b/apps/addressbook/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/apps/addressbook/.project b/apps/addressbook/.project
new file mode 100644
index 000000000..4132fe9ac
--- /dev/null
+++ b/apps/addressbook/.project
@@ -0,0 +1,17 @@
+
+
+ addressbook
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index a59ef4465..42696da25 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -1863,6 +1863,14 @@ public class SnarkManager implements CompleteListener {
private final Snark snark;
public ThreadedStarter(Snark s) { snark = s; }
public void run() {
+ try {
+ run2();
+ } catch (Exception e) {
+ _log.error("Error starting", e);
+ }
+ }
+
+ private void run2() {
if (snark != null) {
if (snark.isStopped())
snark.startTorrent();
diff --git a/apps/i2ptunnel/java/build.xml b/apps/i2ptunnel/java/build.xml
index adee1e190..76ea9e92c 100644
--- a/apps/i2ptunnel/java/build.xml
+++ b/apps/i2ptunnel/java/build.xml
@@ -56,7 +56,7 @@
-
+
@@ -87,6 +87,7 @@
+
@@ -118,6 +119,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/i2ptunnel/java/bundle-messages-proxy.sh b/apps/i2ptunnel/java/bundle-messages-proxy.sh
new file mode 100755
index 000000000..17a2f9ace
--- /dev/null
+++ b/apps/i2ptunnel/java/bundle-messages-proxy.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+# Update messages_xx.po and messages_xx.class files,
+# from both java and jsp sources.
+# Requires installed programs xgettext, msgfmt, msgmerge, and find.
+#
+# usage:
+# bundle-messages.sh (generates the resource bundle from the .po file)
+# bundle-messages.sh -p (updates the .po file from the source tags, then generates the resource bundle)
+#
+# zzz - public domain
+#
+CLASS=net.i2p.i2ptunnel.proxy.messages
+TMPFILE=build/javafiles-proxy.txt
+export TZ=UTC
+RC=0
+
+if [ "$1" = "-p" ]
+then
+ POUPDATE=1
+fi
+
+# on windows, one must specify the path of commnad find
+# since windows has its own retarded version of find.
+if which find|grep -q -i windows ; then
+ export PATH=.:/bin:/usr/local/bin:$PATH
+fi
+# Fast mode - update ondemond
+# set LG2 to the language you need in envrionment varibales to enable this
+
+# add ../java/ so the refs will work in the po file
+JPATHS="../java/build/Proxy.java ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java"
+for i in ../locale-proxy/messages_*.po
+do
+ # get language
+ LG=${i#../locale-proxy/messages_}
+ LG=${LG%.po}
+
+ # skip, if specified
+ if [ $LG2 ]; then
+ [ $LG != $LG2 ] && continue || echo INFO: Language update is set to [$LG2] only.
+ fi
+
+ if [ "$POUPDATE" = "1" ]
+ then
+ # make list of java files newer than the .po file
+ find $JPATHS -name *.java -newer $i > $TMPFILE
+ fi
+ if [ -s build/obj/net/i2p/i2ptunnel/proxy/messages_$LG.class -a \
+ build/obj/net/i2p/i2ptunnel/proxy/messages_$LG.class -nt $i -a \
+ ! -s $TMPFILE ]
+ then
+ continue
+ fi
+
+ if [ "$POUPDATE" = "1" ]
+ then
+ echo "Updating the $i file from the tags..."
+ # extract strings from java and jsp files, and update messages.po files
+ # translate calls must be one of the forms:
+ # _("foo")
+ # _x("foo")
+ # intl._("foo")
+ # intl.title("foo")
+ # In a jsp, you must use a helper or handler that has the context set.
+ # To start a new translation, copy the header from an old translation to the new .po file,
+ # then ant distclean updater.
+ find $JPATHS -name *.java > $TMPFILE
+ xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
+ --keyword=_ \
+ -o ${i}t
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - xgettext failed on ${i}, not updating translations"
+ rm -f ${i}t
+ RC=1
+ break
+ fi
+ msgmerge -U --backup=none $i ${i}t
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - msgmerge failed on ${i}, not updating translations"
+ rm -f ${i}t
+ RC=1
+ break
+ fi
+ rm -f ${i}t
+ # so we don't do this again
+ touch $i
+ fi
+
+ if [ "$LG" != "en" ]
+ then
+ # only generate for non-source language
+ echo "Generating ${CLASS}_$LG ResourceBundle..."
+
+ # convert to class files in build/obj
+ msgfmt --java --statistics -r $CLASS -l $LG -d build/obj $i
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - msgfmt failed on ${i}, not updating translations"
+ # msgfmt leaves the class file there so the build would work the next time
+ find build/obj -name messages_${LG}.class -exec rm -f {} \;
+ RC=1
+ break
+ fi
+ fi
+done
+rm -f $TMPFILE
+exit $RC
diff --git a/apps/i2ptunnel/java/bundle-messages.sh b/apps/i2ptunnel/java/bundle-messages.sh
index f0b5f8225..15d731932 100755
--- a/apps/i2ptunnel/java/bundle-messages.sh
+++ b/apps/i2ptunnel/java/bundle-messages.sh
@@ -29,7 +29,7 @@ fi
# set LG2 to the language you need in envrionment varibales to enable this
# add ../java/ so the refs will work in the po file
-JPATHS="../java/src ../jsp/WEB-INF"
+JPATHS="../java/src/net/i2p/i2ptunnel/web ../jsp/WEB-INF"
for i in ../locale/messages_*.po
do
# get language
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
index e0484ac7f..e641d8085 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
@@ -1409,8 +1409,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
}
****/
- /** */
- private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
+
+ /** these strings go in the jar, not the war */
+ private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
/** lang in routerconsole.lang property, else current locale */
protected static String _(String key) {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
index e8cdd8d27..e9cfb12b5 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java
@@ -3,9 +3,9 @@
*/
package net.i2p.i2ptunnel;
-import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
@@ -27,6 +27,7 @@ import net.i2p.util.EventDispatcher;
import net.i2p.util.InternalSocket;
import net.i2p.util.Log;
import net.i2p.util.PasswordManager;
+import net.i2p.util.TranslateReader;
/**
* Common things for HTTPClient and ConnectClient
@@ -496,44 +497,35 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
*/
protected static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) {
File errorDir = new File(ctx.getBaseDir(), "docs");
- String lang = ctx.getProperty("routerconsole.lang", Locale.getDefault().getLanguage());
- if(lang != null && lang.length() > 0 && !lang.equals("en")) {
- File file = new File(errorDir, base + "-header_" + lang + ".ht");
- try {
- return readFile(file);
- } catch(IOException ioe) {
- // try the english version now
- }
- }
File file = new File(errorDir, base + "-header.ht");
try {
- return readFile(file);
+ return readFile(ctx, file);
} catch(IOException ioe) {
return backup;
}
}
+ private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
+
/**
* @since 0.9.4 moved from I2PTunnelHTTPClient
*/
- private static byte[] readFile(File file) throws IOException {
- FileInputStream fis = null;
- byte[] buf = new byte[2048];
- ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
+ private static byte[] readFile(I2PAppContext ctx, File file) throws IOException {
+ Reader reader = null;
+ char[] buf = new char[512];
+ StringBuilder out = new StringBuilder(2048);
try {
- int len = 0;
- fis = new FileInputStream(file);
- while((len = fis.read(buf)) > 0) {
- baos.write(buf, 0, len);
+ int len;
+ reader = new TranslateReader(ctx, BUNDLE_NAME, new FileInputStream(file));
+ while((len = reader.read(buf)) > 0) {
+ out.append(buf, 0, len);
}
- return baos.toByteArray();
+ return out.toString().getBytes("UTF-8");
} finally {
try {
- if(fis != null) {
- fis.close();
- }
- } catch(IOException foo) {
- }
+ if(reader != null)
+ reader.close();
+ } catch(IOException foo) {}
}
// we won't ever get here
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
index f90603646..8822779fc 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java
@@ -39,7 +39,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
private final Socket s;
private final I2PSocket i2ps;
private final Object slock, finishLock = new Object();
- boolean finished = false;
+ volatile boolean finished = false;
private final byte[] initialI2PData;
private final byte[] initialSocketData;
/** when the last data was sent/received (or -1 if never) */
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 24e2c58ea..1b16007b7 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ThreadFactory;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
+import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
@@ -37,17 +38,16 @@ import net.i2p.util.Log;
public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
protected final Log _log;
- protected I2PSocketManager sockMgr;
+ protected final I2PSocketManager sockMgr;
protected I2PServerSocket i2pss;
private final Object lock = new Object();
protected final Object slock = new Object();
- protected InetAddress remoteHost;
- protected int remotePort;
- private boolean _usePool;
-
- protected Logging l;
+ protected final InetAddress remoteHost;
+ protected final int remotePort;
+ private final boolean _usePool;
+ protected final Logging l;
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000;
/** default timeout to 5 minutes - override if desired */
@@ -56,6 +56,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** do we use threads? default true (ignored for standard servers, always false) */
private static final String PROP_USE_POOL = "i2ptunnel.usePool";
private static final boolean DEFAULT_USE_POOL = true;
+ /** apparently unused */
protected static volatile long __serverId = 0;
/** max number of threads - this many slowlorisses will DOS this server, but too high could OOM the JVM */
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
@@ -65,8 +66,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** how long to wait before dropping an idle thread */
private static final long HANDLER_KEEPALIVE_MS = 30*1000;
- protected I2PTunnelTask task = null;
- protected boolean bidir = false;
+ protected I2PTunnelTask task;
+ protected boolean bidir;
private ThreadPoolExecutor _executor;
/** unused? port should always be specified */
@@ -74,8 +75,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
protected int localPort = DEFAULT_LOCALPORT;
/**
- * Warning, blocks in constructor while connecting to router and building tunnels;
- * TODO move that to startRunning()
+ * Non-blocking
*
* @param privData Base64-encoded private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
@@ -86,16 +86,19 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
super("Server at " + host + ':' + port, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
- init(host, port, bais, privData, l);
+ this.l = l;
+ this.remoteHost = host;
+ this.remotePort = port;
+ _usePool = getUsePool();
+ sockMgr = createManager(bais);
}
/**
- * Warning, blocks in constructor while connecting to router and building tunnels;
- * TODO move that to startRunning()
+ * Non-blocking
*
* @param privkey file containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
- * @param privkeyname the name of the privKey file, not clear why we need this too
+ * @param privkeyname the name of the privKey file, just for logging
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
*/
@@ -103,13 +106,18 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
EventDispatcher notifyThis, I2PTunnel tunnel) {
super("Server at " + host + ':' + port, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
+ this.l = l;
+ this.remoteHost = host;
+ this.remotePort = port;
+ _usePool = getUsePool();
FileInputStream fis = null;
try {
fis = new FileInputStream(privkey);
- init(host, port, fis, privkeyname, l);
+ sockMgr = createManager(fis);
} catch (IOException ioe) {
- _log.error("Error starting server", ioe);
+ _log.error("Cannot read private key data for " + privkeyname, ioe);
notifyEvent("openServerResult", "error");
+ throw new IllegalArgumentException("Error starting server", ioe);
} finally {
if (fis != null)
try { fis.close(); } catch (IOException ioe) {}
@@ -117,19 +125,22 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
/**
- * Warning, blocks in constructor while connecting to router and building tunnels;
- * TODO move that to startRunning()
+ * Non-blocking
*
* @param privData stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
- * @param privkeyname the name of the privKey file, not clear why we need this too
+ * @param privkeyname the name of the privKey file, just for logging
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
*/
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super("Server at " + host + ':' + port, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
- init(host, port, privData, privkeyname, l);
+ this.l = l;
+ this.remoteHost = host;
+ this.remotePort = port;
+ _usePool = getUsePool();
+ sockMgr = createManager(privData);
}
/**
@@ -145,27 +156,35 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
this.remoteHost = host;
this.remotePort = port;
_log = tunnel.getContext().logManager().getLog(getClass());
+ _usePool = false;
sockMgr = sktMgr;
open = true;
}
+ /** @since 0.9.8 */
+ private boolean getUsePool() {
+ // extending classes default to threaded, but for a standard server, we can't get slowlorissed
+ boolean rv = !getClass().equals(I2PTunnelServer.class);
+ if (rv) {
+ String usePool = getTunnel().getClientOptions().getProperty(PROP_USE_POOL);
+ if (usePool != null)
+ rv = Boolean.parseBoolean(usePool);
+ else
+ rv = DEFAULT_USE_POOL;
+ }
+ return rv;
+ }
+
private static final int RETRY_DELAY = 20*1000;
private static final int MAX_RETRIES = 4;
/**
- * Warning, blocks while connecting to router and building tunnels;
- * TODO move that to startRunning()
*
- * @param privData stream containing the private key data,
- * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
- * @param privkeyname the name of the privKey file, not clear why we need this too
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
+ * @since 0.9.8
*/
- private void init(InetAddress host, int port, InputStream privData, String privkeyname, Logging l) {
- this.l = l;
- this.remoteHost = host;
- this.remotePort = port;
+ private I2PSocketManager createManager(InputStream privData) {
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
int portNum = 7654;
@@ -176,54 +195,54 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
_log.error("Invalid port specified [" + getTunnel().port + "], reverting to " + portNum);
}
}
-
- // copy the privData to a new BAIS, so we can always reset() it if we have to retry
- ByteArrayInputStream privDataCopy;
try {
- privDataCopy = copyOfInputStream(privData);
- } catch (IOException ioe) {
- _log.log(Log.CRIT, "Cannot read private key data for " + privkeyname, ioe);
- return;
+ I2PSocketManager rv = I2PSocketManagerFactory.createDisconnectedManager(privData, getTunnel().host,
+ portNum, props);
+ rv.setName("Server");
+ getTunnel().addSession(rv.getSession());
+ return rv;
+ } catch (I2PSessionException ise) {
+ throw new IllegalArgumentException("Can't create socket manager", ise);
+ } finally {
+ try { privData.close(); } catch (IOException ioe) {}
}
+ }
- // extending classes default to threaded, but for a standard server, we can't get slowlorissed
- _usePool = !getClass().equals(I2PTunnelServer.class);
- if (_usePool) {
- String usePool = getTunnel().getClientOptions().getProperty(PROP_USE_POOL);
- if (usePool != null)
- _usePool = Boolean.parseBoolean(usePool);
- else
- _usePool = DEFAULT_USE_POOL;
- }
- // Todo: Can't stop a tunnel from the UI while it's in this loop (no session yet)
+ /**
+ * Warning, blocks while connecting to router and building tunnels;
+ *
+ * @throws IllegalArgumentException if the I2CP configuration is b0rked so
+ * badly that we cant create a socketManager
+ * @since 0.9.8
+ */
+ private void connectManager() {
int retries = 0;
- while (sockMgr == null) {
- synchronized (slock) {
- sockMgr = I2PSocketManagerFactory.createManager(privDataCopy, getTunnel().host, portNum,
- props);
-
- }
- if (sockMgr == null) {
+ while (sockMgr.getSession().isClosed()) {
+ try {
+ sockMgr.getSession().connect();
+ } catch (I2PSessionException ise) {
// try to make this error sensible as it will happen...
+ String portNum = getTunnel().port;
+ if (portNum == null)
+ portNum = "7654";
String msg = "Unable to connect to the router at " + getTunnel().host + ':' + portNum +
- " and build tunnels for the server at " + host.getHostAddress() + ':' + port;
+ " and build tunnels for the server at " + remoteHost.getHostAddress() + ':' + remotePort;
if (++retries < MAX_RETRIES) {
- this.l.log(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
- _log.error(msg + ", retrying in " + (RETRY_DELAY / 1000) + " seconds");
+ msg += ", retrying in " + (RETRY_DELAY / 1000) + " seconds";
+ this.l.log(msg);
+ _log.error(msg);
} else {
- this.l.log(msg + ", giving up");
- _log.log(Log.CRIT, msg + ", giving up");
- throw new IllegalArgumentException(msg);
+ msg += ", giving up";
+ this.l.log(msg);
+ _log.log(Log.CRIT, msg, ise);
+ throw new IllegalArgumentException(msg, ise);
}
try { Thread.sleep(RETRY_DELAY); } catch (InterruptedException ie) {}
- privDataCopy.reset();
}
}
- sockMgr.setName("Server");
- getTunnel().addSession(sockMgr.getSession());
- l.log("Tunnels ready for server at " + host.getHostAddress() + ':' + port);
+ l.log("Tunnels ready for server at " + remoteHost.getHostAddress() + ':' + remotePort);
notifyEvent("openServerResult", "ok");
open = true;
}
@@ -249,10 +268,13 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/**
* Start running the I2PTunnelServer.
+ * Warning, blocks while connecting to router and building tunnels;
*
- * TODO: Wait to connect to router until here.
+ * @throws IllegalArgumentException if the I2CP configuration is b0rked so
+ * badly that we cant create a socketManager
*/
- public void startRunning() {
+ public synchronized void startRunning() {
+ connectManager();
// prevent JVM exit when running outside the router
boolean isDaemon = getTunnel().getContext().isRouterContext();
Thread t = new I2PAppThread(this, "Server " + remoteHost + ':' + remotePort, isDaemon);
@@ -405,7 +427,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** just to set the name and set Daemon */
private static class CustomThreadFactory implements ThreadFactory {
- private String _name;
+ private final String _name;
public CustomThreadFactory(String name) {
_name = name;
@@ -425,7 +447,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
* Run the blockingHandler.
*/
private class Handler implements Runnable {
- private I2PSocket _i2ps;
+ private final I2PSocket _i2ps;
public Handler(I2PSocket socket) {
_i2ps = socket;
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
index 3a35697f3..77848cf1b 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java
@@ -233,7 +233,8 @@ public abstract class LocalHTTPServer {
return buf.toString();
}
- private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
+ /** these strings go in the jar, not the war */
+ private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
/** lang in routerconsole.lang property, else current locale */
protected static String _(String key) {
diff --git a/apps/i2ptunnel/locale-proxy/messages_ar.po b/apps/i2ptunnel/locale-proxy/messages_ar.po
new file mode 100644
index 000000000..c94ddc8ea
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_ar.po
@@ -0,0 +1,442 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo , 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P i2ptunnel\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:48+0000\n"
+"PO-Revision-Date: 2011-03-03 17:02-0000\n"
+"Last-Translator: hamada \n"
+"Language-Team: duck \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "اعدادات البروكسي الخارجي"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "اعدادات البروكسي الخارجي"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "تم حفظ تغيرات الاعدادات"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr ""
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "أضف الى دفتر العناوين"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "وجهة محلية"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "اسم او اتجاه"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr "تحديث بروكسي الى"
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "اسم او اتجاه"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "اتجاه"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "بروكسي خارجي"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "تحديث من {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr ""
diff --git a/apps/i2ptunnel/locale-proxy/messages_cs.po b/apps/i2ptunnel/locale-proxy/messages_cs.po
new file mode 100644
index 000000000..82432364b
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_cs.po
@@ -0,0 +1,443 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# Waseihou Watashi , 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:48+0000\n"
+"PO-Revision-Date: 2012-02-17 11:26+0000\n"
+"Last-Translator: kytv \n"
+"Language-Team: Czech (http://www.transifex.net/projects/p/I2P/language/cs/)\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr ""
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr ""
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr ""
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr ""
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Cíl tunelu"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "jméno nebo cíl"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "jméno nebo cíl"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP bidir"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr ""
diff --git a/apps/i2ptunnel/locale-proxy/messages_de.po b/apps/i2ptunnel/locale-proxy/messages_de.po
new file mode 100644
index 000000000..2b44583ef
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_de.po
@@ -0,0 +1,459 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# blabla, 2011
+# blabla , 2011-2012
+# D.A. Loader <>, 2012
+# driz , 2012
+# ducki2p , 2011
+# foo , 2009
+# mixxy, 2011
+# pirr , 2012
+# zeroflag , 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-05-08 13:15+0000\n"
+"Last-Translator: zeroflag \n"
+"Language-Team: German (http://www.transifex.com/projects/p/I2P/language/"
+"de/)\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Outproxy Autorisation"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Outproxy Autorisation"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Routerkonsole"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Routerkonsole"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Einstellungen"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Hilfe"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Adressbuch"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "lokales Ziel"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Tunnel Ziel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Dein Browser unterstützt keine iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "Der HTTP-Proxy ist nicht bereit"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Dies scheint kein gültiges Ziel zu sein:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "Der I2P-Adresshelfer kann dir bei solch einem Ziel nicht helfen."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Um das Ziel in Ihrer Host-Datenbank zu besuchen, klicken Sie hier, und um das Ziel aus der kollidierenden Adresshelfer-"
+"Anfrage zu besuchen, hier!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Ziel"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Weiter zu {0}, ohne zu speichern"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "{0} im Router-Adressbuch speichern und auf die Eepseite weiterleiten"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "{0} im Master-Adressbuch speichern und auf die Eepseite weiterleiten"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "{0} im privaten Adressbuch speichern und auf die Eepseite weiterleiten"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP-Outproxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Durch Klicken auf einen der untenstehenden Links bekommen Sie einen "
+"Adresshelfer von einem \"Sprung\"-Service:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "Weiterleitungsdienst {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Hinzugefüg über den Adressenhelfer von {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Durch Adresshelfer hinzugefügt"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Weiterleitung zu {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+"{0} wurde ins {1} Adressbuch geschrieben. Du wirst nun weitergeleitet."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+"Konnte {0} nicht im {1} Adressbuch speichern. Du wirst nun weitergeleitet."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Klick hier, wenn du nicht automatisch weitergeleitet wirst!"
diff --git a/apps/i2ptunnel/locale-proxy/messages_en.po b/apps/i2ptunnel/locale-proxy/messages_en.po
new file mode 100644
index 000000000..cee219299
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_en.po
@@ -0,0 +1,442 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo , 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P i2ptunnel\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2010-06-15 14:09+0100\n"
+"Last-Translator: duck \n"
+"Language-Team: duck \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr ""
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr ""
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr ""
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr ""
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr ""
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr ""
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr ""
diff --git a/apps/i2ptunnel/locale-proxy/messages_es.po b/apps/i2ptunnel/locale-proxy/messages_es.po
new file mode 100644
index 000000000..03e361d5e
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_es.po
@@ -0,0 +1,459 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# "blabla", 2011
+# blabla , 2011, 2012
+# Daniel Mustieles , 2012-2013
+# ducki2p , 2011
+# foo , 2009
+# punkibastardo , 2011, 2012
+# punkibastardo , 2011
+# trolly , 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-04-15 10:42+0000\n"
+"Last-Translator: trolly \n"
+"Language-Team: Spanish (http://www.transifex.com/projects/p/I2P/language/"
+"es/)\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Autorización outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Autorización outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Consola del Router"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Consola del Router"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Ajustes"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Ayuda"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Libreta de direcciones"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "destino local"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Destino del túnel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Tu navegador no soporta iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "El proxy HTTP no está activo"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Éste parece ser un destino falso:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "El ayudante de direcciones no te puede ayudar con un destino así."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Para visitar el destino en la base de datos de hosts, ¡pincha aquí! Para visitar el destino del ayudante de direcciones en "
+"conflicto, ¡pincha aquí!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destino"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Acceder a {0} sin guardar"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+"Guardar {0} a la libreta de direcciones del router y acceder al sitio i2p."
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+"Guardar {0} a la libreta de direcciones principal y acceder al sitio i2p."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+"Guardar {0} a la libreta de direcciones privada y acceder al sitio i2p."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Puerta de salida HTTP"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Pincha en un enlace de debajo para buscar un ayudante de direcciones "
+"mediante el uso de un servicio de \"salto\":"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "servicio saltador {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Agregado por el ayudante de direcciones desde {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Agregado por el ayudante de direcciones."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Redireccionando a {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} ha sido guardado en la libreta {1}, accediendo ahora."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "No se ha podido guardar {0} en la libreta {1}, accediendo ahora."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "¡Haz click aquí si no estás siendo enviado automáticamente!"
diff --git a/apps/i2ptunnel/locale-proxy/messages_fr.po b/apps/i2ptunnel/locale-proxy/messages_fr.po
new file mode 100644
index 000000000..4fc76dbc7
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_fr.po
@@ -0,0 +1,453 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# ducki2p , 2011
+# foo , 2009
+# Boxoa590, 2013
+# Boxoa590, 2012
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-05-29 21:00+0000\n"
+"Last-Translator: Boxoa590\n"
+"Language-Team: French (http://www.transifex.com/projects/p/I2P/language/"
+"fr/)\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Autorisation de mandataire sortant"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Autorisation de mandataire sortant"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Console du routeur"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Console du routeur"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Configuration"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Aide"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Carnet d'adresses"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Destination locale"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Destination du tunnel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Votre navigateur n'est pas compatible iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "Le proxy HTTP n'est pas en place"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Cela semble être une mauvaise destination"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+"i2paddresshelper ne peut pas vous aider avec une destination comme cela !"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Pour aller à la destination de votre base de données d''hôtes, cliquez here. Pour aller à la destination de l''aide d''adresse en "
+"conflit, cliquez ici."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Hôte"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destination"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Aller sur le site i2p {0} sans enregistrer dans un carnet d'adresses. "
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Ajouter au carnet d''adresse du routeur {0} et aller sur le site i2p"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Ajouter au carnet d''adresse principal {0} et aller sur le site i2p"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Ajouter au carnet d''adresse privé {0} et aller sur le site i2p"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "proxy sortant HTTP"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Cliquez sur un des liens ci-dessous pour chercher une aide d'adresse en "
+"utilisant un \"service de saut\":"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} jump service"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Ajouté via assistant d''adresse depuis {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Ajouté via assitant d'adresse"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Redirection vers {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} ajouté au carnet d''adresses {1}. Redirection en cours."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "{0} non ajouté au carnet d''adresses {1}. Redirection en cours."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Cliquez ici si vous n'êtes pas redirigé automatiquement."
diff --git a/apps/i2ptunnel/locale-proxy/messages_hu.po b/apps/i2ptunnel/locale-proxy/messages_hu.po
new file mode 100644
index 000000000..9da4f1321
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_hu.po
@@ -0,0 +1,448 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2012-06-01 21:37+0000\n"
+"Last-Translator: AdminLMH \n"
+"Language-Team: Hungarian (http://www.transifex.net/projects/p/I2P/language/"
+"hu/)\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Kimenő proxy Hitelesítés"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Kimenő proxy Hitelesítés"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Router Konzol"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Router Konzol"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Beállítások"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Segítség"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Címjegyzék"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Helyi célállomást"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Alagút célállomása"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "A HTTP proxy nincs bekapcsolva"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Ez rossz célállomásnak tűnik:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "i2pcímsegítő nem tud segíteni ilyen célállomással!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"A hoszt adatbázisodban lévő célállomás megtekintéséhez kattints ide. Az ütközésben lévő címsegítő célállomás megtekintéséhez "
+"kattints ide."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Hoszt"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Bázis 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Célállomás"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Mentés nélkül tovább ide: {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "{0} mentése a router címjegyzékbe és tovább az eep oldalra"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "{0} mentése a mester címjegyzékbe és tovább az eep oldalra"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "{0} mentése a privát címjegyzékbe és tovább az eep oldalra"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP Kimenő proxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Kattints az alábbi linkre, hogy címsegítő után nézz, \"ugrás\" szolgáltatás "
+"segítségével:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Címsegítőn keresztül hozzáadva {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Címsegítőn keresztül hozzáadva"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Átirányítás ide: {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} mentése {1} címjegyzékbe, átirányítás alatt."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "{0} mentése {1} címjegyzékbe sikertelen, átirányítás alatt."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Kattints ide, amennyiben nem történt meg az automatikus átirányítás."
diff --git a/apps/i2ptunnel/locale-proxy/messages_it.po b/apps/i2ptunnel/locale-proxy/messages_it.po
new file mode 100644
index 000000000..6dd97a88f
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_it.po
@@ -0,0 +1,454 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# , 2012.
+# , 2012.
+# , 2012.
+# , 2011.
+# Tony Goodyear , 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2012-11-18 22:16+0000\n"
+"Last-Translator: Tony Goodyear \n"
+"Language-Team: Italian (http://www.transifex.com/projects/p/I2P/language/"
+"it/)\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Autorizzazione Outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Autorizzazione Outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Console del router"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Console del router"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Configurazione"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Aiuto"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Rubrica"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Destinazione locale"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Destinazione tunnel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Il tuo browse non supporta gli iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "Il proxy HTTP non è avviato"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Questa non sembra essere una buona destinazione:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "i2paddresshelper non ti può aiutare con una destinazione del genere!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Per visitare la destinazione nel tuo database host, cliccare qui. \n"
+"Per visitare la destinazione addresshelper in conflitto, cliccare qui."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destinazione"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Continua a {0} senza salvare"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Salva {0} nella rubrica del router e continua verso l'eepsite"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Salva {0} nella rubrica master e continua verso l'eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Salva {0} nella rubrica privata e continua verso l'eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Ourproxy HTTP"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Cliccare un link quì sotto per cercare un address helper usando un servizio "
+"di \"salto\":"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} salta al servizio"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Aggiunto con address helper {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Aggiunto con address helper"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Redirezionamento a {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} salvato sulla rubrica {1}, in redirezionamento."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Salvataggio di {0} sulla rubrica {1} fallito, in redirezionamento."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Clicca qui se non sei redirezionato automaticamente."
diff --git a/apps/i2ptunnel/locale-proxy/messages_nb.po b/apps/i2ptunnel/locale-proxy/messages_nb.po
new file mode 100644
index 000000000..c2adeefc1
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_nb.po
@@ -0,0 +1,449 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# Mikal , 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-01-29 18:12+0000\n"
+"Last-Translator: mikalv \n"
+"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/I2P/"
+"language/nb/)\n"
+"Language: nb\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "utproxy autorisasjon"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "utproxy autorisasjon"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Ruter Konsoll"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Ruter Konsoll"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Konfigurasjon"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Hjelp"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Adressebok"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Lokal destinasjon"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Tunnel destinasjon"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Dette synes å være en dårlig destinasjon:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "i2paddresshelper kan ikke hjelpe deg med en destinasjon som dette!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"For å besøke destinasjonen i din vert database, klikk her"
+"a>. For å å besøke motstridende adressehjelper destinasjon, klikk her."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Vert"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destinasjon"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Fortsett til {0} uten å lagre"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Lagre {0} til ruter adressebok og fortsett til eepsite"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Save {0} til hoved adressebok og fortsett til eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Save {0} til privat adressebok og fortsett til eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP Ut-proxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Klikk på en link under for en adressehjelper ved å bruke en \"hoppe\" "
+"tjeneste:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} hopp tjeneste"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Lagt til via addresse hjelper fra {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Lagt til via adresse helper"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Videresender til {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "Lagret {0} til {1} adresseboken, videresender nå."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Feilet med å lagre {0} til {1} adresseboken, videresender nå."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Klikk her hvis du ikke blir videresendt automatisk."
diff --git a/apps/i2ptunnel/locale-proxy/messages_nl.po b/apps/i2ptunnel/locale-proxy/messages_nl.po
new file mode 100644
index 000000000..d80511e2f
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_nl.po
@@ -0,0 +1,447 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo , 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2011-02-20 12:14+0000\n"
+"Last-Translator: ducki2p \n"
+"Language-Team: Dutch <>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Uitgaande Proxy Autorisatie"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Uitgaande Proxy Autorisatie"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Configuratie wijzigingen opgeslagen"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr ""
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Toevoegen aan lokaal adresboek"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Lokale destination"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Tunnel Destinations"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "De HTTP proxy is niet ingeschakeld"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Tunnel Destinations"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Om de destination in je host database te bezoeken, klik hier. Om de conflicterende adreshelper destination te bezoeken, "
+"klik hier."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destination"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Uitgaande proxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Klik op een onderstaande link om te zoeken naar een adreshelper via een "
+"\"jump\" service:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Updaten van {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr ""
diff --git a/apps/i2ptunnel/locale-proxy/messages_pl.po b/apps/i2ptunnel/locale-proxy/messages_pl.po
new file mode 100644
index 000000000..3dd475be9
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_pl.po
@@ -0,0 +1,453 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# , 2012.
+# , 2011, 2012.
+# Smert i2p , 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-02-11 21:31+0000\n"
+"Last-Translator: Smert \n"
+"Language-Team: Polish (http://www.transifex.com/projects/p/I2P/language/"
+"pl/)\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Autoryzacja Outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Autoryzacja Outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Konsola Routera"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Konsola Routera"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Konfiguracja"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Pomoc"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Książka adresowa"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Cel localny"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "nazwa lub cel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Wydaje się to być złym celem:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+"i2paddresshelper, pomocnik adresowy i2p, nie może pomóc ci z takim celem!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"By odwiedzić cel w bazie danych twojego hosta, naciśnij tutaj. By odwiedzić konfliktujący cel pomocnika adresów, "
+"naciśnij tutaj."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Cel"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Kontynuuj do {0} bez zapisywania"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Zapisz {0} do książki adresowej rutera i kontynuuj do eepsite'u"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Zapisz {0} do głównej książki adresowej i kontynuuj do eepsite'u"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Zapisz {0} do prywatnej książki adresowej i kontynuuj do eepsite'u"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP Outproxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Kliknij na poniższy link by poszukać pomocnika adresów poprzez użycie "
+"\"skaczącego\" serwisu."
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Dodano poprzez pomocnika adresów {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Dodano poprzez pomocnika adresów"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Przekierowywuje do {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "Zapisano {0} do książki adresowej {1}, przekierowywuje."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Nie udało sie zapisać {0} do książki adresowej {1}, przkierowywuje."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Sprawdź tutaj jeżeli nie jesteś automatycznie przekierowany."
diff --git a/apps/i2ptunnel/locale-proxy/messages_pt.po b/apps/i2ptunnel/locale-proxy/messages_pt.po
new file mode 100644
index 000000000..d1fbe9332
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_pt.po
@@ -0,0 +1,451 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# rafaelbf, 2013
+# tuliouel, 2013
+# wicked, 2012
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-07-04 19:15+0000\n"
+"Last-Translator: tuliouel\n"
+"Language-Team: Portuguese (http://www.transifex.com/projects/p/I2P/language/"
+"pt/)\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Autorização do proxy de saída"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Autorização do proxy de saída"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Painel do Roteador"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Painel do Roteador"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Configuração"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Ajuda"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Livro de endereços"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Destino local"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Destino do túnel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Seu navegador não suporta iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "O proxy HTTP não está alcançável"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Este destino parece estar incorreto:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "Auxiliar de endereços não pode ajudar em destinos como este!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Para visitar o destino da sua base de dados, clique aqui"
+"a>. Para visitar o endereço de ajuda do conflito, clique aqui."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Host"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Destino"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Continuar para {0} sem salvar"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Salvar {0} ao livro de endereços do roteador e continuar no eepsite"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Salvar {0} ao livro de endereços master e continuar no eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Salvar {0} no livro de endereços particular e continuar no eepsite"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Proxy de saída HTTP"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Clique no link abaixo para procurar um ajudante de endereços usando um "
+"serviço \"jump\":"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} serviço de salto"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Adicionado pelo auxiliar de endereços de {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Adicionado pelo auxiliar de endereços"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Redirecionando para {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} salvo no livro de endereços {1}, redirecionando agora."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Falha ao salvar {0} no livro de endereços {1}, redirecionando agora."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Clique aqui se você não for redirecionado automaticamente."
diff --git a/apps/i2ptunnel/locale-proxy/messages_ru.po b/apps/i2ptunnel/locale-proxy/messages_ru.po
new file mode 100644
index 000000000..53cfc88ad
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_ru.po
@@ -0,0 +1,460 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# sfix , 2013
+# ducki2p , 2011
+# foo , 2009
+# gmind , 2012-2013
+# gmind , 2013
+# gmind , 2012
+# sfix , 2013
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:49+0000\n"
+"PO-Revision-Date: 2013-07-05 08:09+0000\n"
+"Last-Translator: Roman Azarenko \n"
+"Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/I2P/"
+"language/ru_RU/)\n"
+"Language: ru_RU\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Авторизация outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Авторизация outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Консоль маршрутизатора I2P"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Консоль маршрутизатора I2P"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Настройки"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Помощь"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Адресная книга"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Локальный адрес назначения"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Назначение туннеля"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Ваш браузер не поддерживает iFrames"
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "HTTP-прокси не поднят"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Кажется это плохой адрес назначения:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "С таким адресом назначения i2paddresshelper вам не поможет!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Для перехода по ссылке из локальной адресной книги, нажмите здесь. Для перехода по новой addresshelper-ссылке, нажмите здесь."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Адрес"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Base 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Адрес назначения"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Продолжить переход к {0} без сохранения"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+"Сохранить {0} в адресную книгу роутера (router address book) и продолжить "
+"переход к eep-сайту"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+"Сохранить {0} в основную (master) адресную книгу и продолжить переход к eep-"
+"сайту"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+"Сохранить {0} в приватную адресную книгу и продолжить переход к eep-сайту"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP Outproxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Jump-сервисы, которые, возможно, знают нужную Вам addresshelper-ссылку:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} jump service"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Добавлено через address helper из {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Добавлен через address helper"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Перенаправляем к {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} сохранён в {1}, перенаправляем."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Не удалось сохранить {0} в {1} адресную книгу, перенаправляем."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Нажмите сюда если автоматическое перенаправление не сработало"
diff --git a/apps/i2ptunnel/locale-proxy/messages_sv.po b/apps/i2ptunnel/locale-proxy/messages_sv.po
new file mode 100644
index 000000000..cb69b06ae
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_sv.po
@@ -0,0 +1,451 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# , 2013.
+# , 2012.
+# Martin Svensson , 2011-2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:50+0000\n"
+"PO-Revision-Date: 2013-01-19 16:53+0000\n"
+"Last-Translator: hottuna \n"
+"Language-Team: Swedish (Sweden) (http://www.transifex.com/projects/p/I2P/"
+"language/sv_SE/)\n"
+"Language: sv_SE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Tillstånd för utproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Tillstånd för utproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Router konsol "
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Router konsol "
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Konfiguration"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Hjälp"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Adressbok"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Lokalt mål"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Mål för tunnel"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "Din webbläsare stödjer inte iFrames."
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "HTTP proxyn är inte uppe"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Detta verkar vara ett felaktigt mål"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "i2padresshjälp kan inte hjälpa dig med ett sådant mål!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"För att besöka målet i din värd databas href=\"{0}\"> klicka . "
+"För att besöka de motstridiga hjälpaddresserna, klicka ."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Värd"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr "Bas 32"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Mål"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Fortsätt till {0} utan att spara"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Spara {0} till routeradressboken och fortsätt till eepsite "
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Spara {0} till huvudadressboken och fortsätt till eepsite "
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "Spara {0} till privatadressbok och fortsätt till eepsite "
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "HTTP Utproxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Klicka på en länk nedan för att söka efter en hjälpaddress genom att använda "
+"en \"hopp\" tjänst"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr "{0} hopptjänst"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Tillagd via adresshjälpare från {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Tillagd via adresshjälpen "
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Om dirigerar till {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "Spara {0} till {1}adressboken, omdirigerar nu "
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Misslyckades med att spara {0} till {1}adressboken, omdirigerar nu "
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Klicka här om du inte omdirigeras automatiskt "
diff --git a/apps/i2ptunnel/locale-proxy/messages_uk.po b/apps/i2ptunnel/locale-proxy/messages_uk.po
new file mode 100644
index 000000000..c39d457c9
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_uk.po
@@ -0,0 +1,451 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# Translators:
+# Denis , 2011.
+# Denis Lysenko , 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:50+0000\n"
+"PO-Revision-Date: 2012-02-17 12:01+0000\n"
+"Last-Translator: Denis Lysenko \n"
+"Language-Team: Ukrainian (Ukraine) (http://www.transifex.net/projects/p/I2P/"
+"language/uk_UA/)\n"
+"Language: uk_UA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Авторизація outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Авторизація outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "Консоль роутера"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "Консоль роутера"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "Настройки"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "Допомога"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "Адресна книга"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Локальний адрес призначення"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "Місце призначення тунелю"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "Скоріше всього це поганий адрес призначення:"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr "i2paddresshelper не може допомогти Вам з місцем призначення як це!"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Для того щоб перейти по локальному посиланню з локальної адресної книги, "
+"натисніть осьтут. Для переходу по новому addresshelper-"
+"посиланню, натисніть будь-ласка тут."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Адрес"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Адреса призначення"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "Продовжити до {0} без збереження"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "Зберегти {0} в адресну книгу роутера и продовжити перехід до eep-сайту"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "Зберегти {0} в основну адресну книгу і продовжити перехід до еер-сайту"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+"Зберегти {0} в приватну адресну книгу і продовжити перехід до еер-сайту."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Вихідний HTTP проксі"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"\"Jump\" сервіси, які, можливо, знають необхідне Вам addresshelper-посилання."
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "Додано через address helper {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "Додано через address helper"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "Перенаправляємо до {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "{0} збережено в {1} адресної книги, перенапрявляємо."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "Не вдалось зберегти {0} в {1} адресної книги, перенаправляємо."
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "Клацніть тут якщо вас не перенаправило автоматично."
diff --git a/apps/i2ptunnel/locale-proxy/messages_vi.po b/apps/i2ptunnel/locale-proxy/messages_vi.po
new file mode 100644
index 000000000..c92e0fbc9
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_vi.po
@@ -0,0 +1,448 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# , 2011.
+# dich_tran , 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:50+0000\n"
+"PO-Revision-Date: 2011-06-11 08:42+0000\n"
+"Last-Translator: ducki2p \n"
+"Language-Team: Vietnamese (http://www.transifex.net/projects/p/I2P/team/"
+"vi/)\n"
+"Language: vi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "Cho phép Outproxy"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "Cho phép Outproxy"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr ""
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr ""
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr ""
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "Điểm đến nội bộ"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "tên hay điểm đến"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr ""
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr ""
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "tên hay điểm đến"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"Để truy cập vào điểm đến trong cơ sở dữ liệu máy chủ, bấm vào đây. Để truy cập vào địa chỉ giúp đỡ xung đột, bấm vào đây."
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "Máy chủ"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "Đích đến"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr ""
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "Outproxy"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"Bấm vào đường dẫn dưới đây để tìm địa chỉ giúp đỡ bằng dịch vụ \"nhảy\":"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr ""
diff --git a/apps/i2ptunnel/locale-proxy/messages_zh.po b/apps/i2ptunnel/locale-proxy/messages_zh.po
new file mode 100644
index 000000000..d308fef09
--- /dev/null
+++ b/apps/i2ptunnel/locale-proxy/messages_zh.po
@@ -0,0 +1,449 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the i2ptunnel package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+# ducki2p , 2011.
+# , 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 18:50+0000\n"
+"PO-Revision-Date: 2011-08-20 06:00+0000\n"
+"Last-Translator: walking \n"
+"Language-Team: Chinese (China) (http://www.transifex.net/projects/p/I2P/team/"
+"zh_CN/)\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: ../java/build/Proxy.java:5
+msgid "Proxy Authorization Required"
+msgstr "出口代理认证"
+
+#: ../java/build/Proxy.java:6
+msgid "I2P HTTP Proxy Authorization Required"
+msgstr "出口代理认证"
+
+#: ../java/build/Proxy.java:7
+msgid "This proxy is configured to require a username and password for access."
+msgstr ""
+
+#: ../java/build/Proxy.java:8
+#, java-format
+msgid ""
+"Please enter your username and password, or check your {0}router "
+"configuration{1} or {2}I2PTunnel configuration{3}."
+msgstr ""
+
+#: ../java/build/Proxy.java:9
+#, java-format
+msgid ""
+"To disable authorization, remove the configuration {0}i2ptunnel.proxy."
+"auth=basic{1}, then stop and restart the HTTP Proxy tunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:10 ../java/build/Proxy.java:16
+msgid "Warning: No Outproxy Configured"
+msgstr ""
+
+#: ../java/build/Proxy.java:11 ../java/build/Proxy.java:20
+#: ../java/build/Proxy.java:32 ../java/build/Proxy.java:44
+#: ../java/build/Proxy.java:52 ../java/build/Proxy.java:61
+#: ../java/build/Proxy.java:74 ../java/build/Proxy.java:83
+#: ../java/build/Proxy.java:93 ../java/build/Proxy.java:103
+#: ../java/build/Proxy.java:115
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:196
+msgid "Router Console"
+msgstr "路由控制台"
+
+#: ../java/build/Proxy.java:12 ../java/build/Proxy.java:21
+#: ../java/build/Proxy.java:33 ../java/build/Proxy.java:45
+#: ../java/build/Proxy.java:53 ../java/build/Proxy.java:62
+#: ../java/build/Proxy.java:75 ../java/build/Proxy.java:84
+#: ../java/build/Proxy.java:94 ../java/build/Proxy.java:104
+#: ../java/build/Proxy.java:116
+msgid "I2P Router Console"
+msgstr "路由控制台"
+
+#: ../java/build/Proxy.java:13 ../java/build/Proxy.java:22
+#: ../java/build/Proxy.java:34 ../java/build/Proxy.java:46
+#: ../java/build/Proxy.java:54 ../java/build/Proxy.java:63
+#: ../java/build/Proxy.java:76 ../java/build/Proxy.java:85
+#: ../java/build/Proxy.java:95 ../java/build/Proxy.java:105
+#: ../java/build/Proxy.java:117
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Configuration"
+msgstr "配置"
+
+#: ../java/build/Proxy.java:14 ../java/build/Proxy.java:23
+#: ../java/build/Proxy.java:35 ../java/build/Proxy.java:47
+#: ../java/build/Proxy.java:55 ../java/build/Proxy.java:64
+#: ../java/build/Proxy.java:77 ../java/build/Proxy.java:86
+#: ../java/build/Proxy.java:96 ../java/build/Proxy.java:106
+#: ../java/build/Proxy.java:118
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Help"
+msgstr "帮助"
+
+#: ../java/build/Proxy.java:15 ../java/build/Proxy.java:24
+#: ../java/build/Proxy.java:36 ../java/build/Proxy.java:48
+#: ../java/build/Proxy.java:56 ../java/build/Proxy.java:65
+#: ../java/build/Proxy.java:78 ../java/build/Proxy.java:87
+#: ../java/build/Proxy.java:97 ../java/build/Proxy.java:107
+#: ../java/build/Proxy.java:119
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:197
+msgid "Addressbook"
+msgstr "地址簿"
+
+#: ../java/build/Proxy.java:17
+msgid ""
+"Your request was for a site outside of I2P, but you have no HTTP outproxy "
+"configured."
+msgstr ""
+
+#: ../java/build/Proxy.java:18
+msgid "Please configure an outproxy in I2PTunnel."
+msgstr ""
+
+#: ../java/build/Proxy.java:19
+msgid "Information: New Host Name"
+msgstr ""
+
+#: ../java/build/Proxy.java:25
+msgid "Information: New Host Name with Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:26
+msgid ""
+"The address helper link you followed is for a new host name that is not in "
+"your address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:27
+msgid "You may save this host name to your local address book."
+msgstr ""
+
+#: ../java/build/Proxy.java:28
+msgid ""
+"If you save it to your address book, you will not see this message again."
+msgstr ""
+
+#: ../java/build/Proxy.java:29
+msgid ""
+"If you do not save it, the host name will be forgotten after the next router "
+"restart."
+msgstr ""
+
+#: ../java/build/Proxy.java:30
+msgid ""
+"If you do not wish to visit this host, click the \"back\" button on your "
+"browser."
+msgstr ""
+
+#: ../java/build/Proxy.java:31 ../java/build/Proxy.java:37
+msgid "Warning: Invalid Destination"
+msgstr "本地目标"
+
+#: ../java/build/Proxy.java:38
+msgid ""
+"The eepsite destination specified was not valid, or was otherwise "
+"unreachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:39
+msgid ""
+"Perhaps you pasted in the wrong Base 64 string or the link you are following "
+"is bad."
+msgstr ""
+
+#: ../java/build/Proxy.java:40
+msgid "The I2P host could also be offline."
+msgstr ""
+
+#: ../java/build/Proxy.java:41 ../java/build/Proxy.java:123
+#, java-format
+msgid "You may want to {0}retry{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:42 ../java/build/Proxy.java:72
+#: ../java/build/Proxy.java:113 ../java/build/Proxy.java:124
+msgid "Could not find the following destination:"
+msgstr "隧道目标"
+
+#: ../java/build/Proxy.java:43 ../java/build/Proxy.java:49
+msgid "Warning: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:50
+msgid "You attempted to connect to a non-I2P website or location."
+msgstr ""
+
+#: ../java/build/Proxy.java:51 ../java/build/Proxy.java:57
+msgid "Warning: Invalid Request URI"
+msgstr ""
+
+#: ../java/build/Proxy.java:58
+msgid "The request URI is invalid, and probably contains illegal characters."
+msgstr ""
+
+#: ../java/build/Proxy.java:59
+msgid ""
+"If you clicked a link, check the end of the URI for any characters the "
+"browser has mistakenly added on."
+msgstr ""
+
+#: ../java/build/Proxy.java:60
+msgid "Warning: Eepsite Unknown"
+msgstr ""
+
+#: ../java/build/Proxy.java:66
+msgid "Warning: Eepsite Not Found in Addressbook"
+msgstr ""
+
+#: ../java/build/Proxy.java:67
+msgid "The eepsite was not found in your router's addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:68
+msgid "Check the link or find a Base 32 or Base 64 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:69
+#, java-format
+msgid "If you have the Base 64 address, {0}add it to your addressbook{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:70
+msgid ""
+"Otherwise, find a Base 32 or address helper link, or use a jump service link "
+"below."
+msgstr ""
+
+#: ../java/build/Proxy.java:71
+#, java-format
+msgid ""
+"Seeing this page often? See {0}the FAQ{1} for help in {2}adding some "
+"subscriptions{3} to your addressbook."
+msgstr ""
+
+#: ../java/build/Proxy.java:73
+msgid "Error: Request Denied"
+msgstr ""
+
+#: ../java/build/Proxy.java:79
+msgid "Error: Local Access"
+msgstr ""
+
+#: ../java/build/Proxy.java:80
+msgid "Your browser is misconfigured."
+msgstr "您的浏览器不支持iFrame。"
+
+#: ../java/build/Proxy.java:81
+msgid ""
+"Do not use the proxy to access the router console, localhost, or local LAN "
+"destinations."
+msgstr ""
+
+#: ../java/build/Proxy.java:82 ../java/build/Proxy.java:88
+msgid "Warning: Non-HTTP Protocol"
+msgstr ""
+
+#: ../java/build/Proxy.java:89
+msgid "The request uses a bad protocol."
+msgstr ""
+
+#: ../java/build/Proxy.java:90
+#, java-format
+msgid "The I2P HTTP Proxy supports {0}http://{1} requests ONLY."
+msgstr ""
+
+#: ../java/build/Proxy.java:91
+#, java-format
+msgid ""
+"Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed."
+msgstr ""
+
+#: ../java/build/Proxy.java:92 ../java/build/Proxy.java:98
+msgid "Warning: Bad Address Helper"
+msgstr ""
+
+#: ../java/build/Proxy.java:99
+#, java-format
+msgid "The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable."
+msgstr ""
+
+#: ../java/build/Proxy.java:100
+msgid "It seems to be garbage data, or a mistyped Base 32 address."
+msgstr ""
+
+#: ../java/build/Proxy.java:101
+msgid ""
+"Check your URL to try and fix the helper key to be a valid Base 32 hostname "
+"or Base 64 key."
+msgstr ""
+
+#: ../java/build/Proxy.java:102 ../java/build/Proxy.java:108
+msgid "Warning: Outproxy Not Found"
+msgstr ""
+
+#: ../java/build/Proxy.java:109
+msgid "The HTTP Outproxy was not found."
+msgstr "HTTP代理未启动"
+
+#: ../java/build/Proxy.java:110
+msgid ""
+"It is offline, there is network congestion, or your router is not yet well-"
+"integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:111
+#, java-format
+msgid ""
+"You may want to {0}retry{1} as this will randomly reselect an outproxy from "
+"the pool you have defined {2}here{3} (if you have more than one configured)."
+msgstr ""
+
+#: ../java/build/Proxy.java:112
+#, java-format
+msgid ""
+"If you continue to have trouble you may want to edit your outproxy list {0}"
+"here{1}."
+msgstr ""
+
+#: ../java/build/Proxy.java:114 ../java/build/Proxy.java:120
+msgid "Warning: Eepsite Unreachable"
+msgstr ""
+
+#: ../java/build/Proxy.java:121
+msgid "The eepsite was not reachable."
+msgstr ""
+
+#: ../java/build/Proxy.java:122
+msgid ""
+"The eepsite is offline, there is network congestion, or your router is not "
+"yet well-integrated with peers."
+msgstr ""
+
+#: ../java/build/Proxy.java:125 ../java/build/Proxy.java:126
+msgid "Warning: Destination Key Conflict"
+msgstr ""
+
+#: ../java/build/Proxy.java:127
+msgid ""
+"The addresshelper link you followed specifies a different destination key "
+"than a host entry in your host database."
+msgstr ""
+
+#: ../java/build/Proxy.java:128
+msgid ""
+"Someone could be trying to impersonate another eepsite, or people have given "
+"two eepsites identical names."
+msgstr ""
+
+#: ../java/build/Proxy.java:129
+msgid ""
+"You can resolve the conflict by considering which key you trust, and either "
+"discarding the addresshelper link, discarding the host entry from your host "
+"database, or naming one of them differently."
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "This seems to be a bad destination:"
+msgstr "隧道目标"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:572
+msgid "i2paddresshelper cannot help you with a destination like that!"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:639
+#, java-format
+msgid ""
+"To visit the destination in your host database, click here"
+"a>. To visit the conflicting addresshelper destination, click here."
+msgstr ""
+"域名冲突:要访问您本地【地址簿】中设置的目标主机(相当与IP),请点击这里。要访问【地址助手】返回的目标主机请点这里"
+"a>。"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1043
+msgid "Host"
+msgstr "主机"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1047
+msgid "Base 32"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1051
+msgid "Destination"
+msgstr "目标"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1057
+#, java-format
+msgid "Continue to {0} without saving"
+msgstr "继续{0},而不保存"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1062
+#, java-format
+msgid "Save {0} to router address book and continue to eepsite"
+msgstr "保存{0}至“路由器地址簿”并访问该eepsite。"
+
+#. only blockfile supports multiple books
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1065
+#, java-format
+msgid "Save {0} to master address book and continue to eepsite"
+msgstr "保存{0}至“主地址簿”并访问该eepsite。"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1066
+#, java-format
+msgid "Save {0} to private address book and continue to eepsite"
+msgstr "保存{0}至“私有地址簿”并访问该eepsite。"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1180
+msgid "HTTP Outproxy"
+msgstr "出口代理"
+
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1185
+msgid ""
+"Click a link below to look for an address helper by using a \"jump\" service:"
+msgstr ""
+"请点击下面的链接通过【跳转(Jump)】服务提供的【地址助手】链接跳转至域名对应的"
+"主机:"
+
+#. Translators: parameter is a host name
+#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:1221
+#, java-format
+msgid "{0} jump service"
+msgstr ""
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:163
+#, java-format
+msgid "Added via address helper from {0}"
+msgstr "已通过地址助手添加 {0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:165
+msgid "Added via address helper"
+msgstr "已通过地址助手添加"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:190
+#, java-format
+msgid "Redirecting to {0}"
+msgstr "正在重定向至{0}"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:202
+#, java-format
+msgid "Saved {0} to the {1} addressbook, redirecting now."
+msgstr "目标{0}已保存至{1}地址簿中,正在重定向至此目标。"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:203
+#, java-format
+msgid "Failed to save {0} to the {1} addressbook, redirecting now."
+msgstr "目标{0}在{1}地址簿中保存失败,,正在重定向至此目标。"
+
+#: ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java:205
+msgid "Click here if you are not redirected automatically."
+msgstr "如果未能自动重定向,请点击这里。"
diff --git a/apps/jetty/.classpath b/apps/jetty/.classpath
index fc1168715..e53376d8c 100644
--- a/apps/jetty/.classpath
+++ b/apps/jetty/.classpath
@@ -4,7 +4,10 @@
+
+
+
diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
index e7990f28f..71199960c 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
@@ -26,7 +26,6 @@ import net.i2p.util.Log;
public class I2PSocketManagerFactory {
public static final String PROP_MANAGER = "i2p.streaming.manager";
- //public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
/**
@@ -47,7 +46,7 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
- * @param opts I2CP options
+ * @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(Properties opts) {
@@ -60,8 +59,8 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
- * @param host I2CP host
- * @param port I2CP port
+ * @param host I2CP host null to use default
+ * @param port I2CP port <= 0 to use default
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String host, int port) {
@@ -74,9 +73,9 @@ public class I2PSocketManagerFactory {
*
* Blocks for a long time while the router builds tunnels.
*
- * @param i2cpHost I2CP host
- * @param i2cpPort I2CP port
- * @param opts I2CP options
+ * @param i2cpHost I2CP host null to use default
+ * @param i2cpPort I2CP port <= 0 to use default
+ * @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
@@ -102,6 +101,7 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+ * or null for a transient destination. Caller must close.
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream) {
@@ -115,7 +115,8 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
- * @param opts I2CP options
+ * or null for a transient destination. Caller must close.
+ * @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, Properties opts) {
@@ -130,13 +131,73 @@ public class I2PSocketManagerFactory {
* Blocks for a long time while the router builds tunnels.
*
* @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
- * @param i2cpHost I2CP host
- * @param i2cpPort I2CP port
- * @param opts I2CP options
+ * or null for a transient destination. Caller must close.
+ * @param i2cpHost I2CP host null to use default
+ * @param i2cpPort I2CP port <= 0 to use default
+ * @param opts Streaming and I2CP options, may be null
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
Properties opts) {
+ try {
+ return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, true);
+ } catch (I2PSessionException ise) {
+ getLog().error("Error creating session for socket manager", ise);
+ return null;
+ }
+ }
+
+ /**
+ * Create a disconnected socket manager using the destination loaded from the given private key
+ * stream, or null for a transient destination.
+ *
+ * Non-blocking. Does not connect to the router or build tunnels.
+ * For servers, caller MUST call getSession().connect() to build tunnels and start listening.
+ * For clients, caller may do that to build tunnels in advance;
+ * otherwise, the first call to connect() will initiate a connection to the router,
+ * with significant delay for tunnel building.
+ *
+ * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+ * or null for a transient destination. Caller must close.
+ * @param i2cpHost I2CP host null to use default
+ * @param i2cpPort I2CP port <= 0 to use default
+ * @param opts Streaming and I2CP options, may be null
+ * @return the newly created socket manager, non-null (throws on error)
+ * @since 0.9.8
+ */
+ public static I2PSocketManager createDisconnectedManager(InputStream myPrivateKeyStream, String i2cpHost,
+ int i2cpPort, Properties opts) throws I2PSessionException {
+ if (myPrivateKeyStream == null) {
+ I2PClient client = I2PClientFactory.createClient();
+ ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
+ try {
+ client.createDestination(keyStream);
+ } catch (Exception e) {
+ throw new I2PSessionException("Error creating keys", e);
+ }
+ myPrivateKeyStream = new ByteArrayInputStream(keyStream.toByteArray());
+ }
+ return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, false);
+ }
+
+ /**
+ * Create a socket manager using the destination loaded from the given private key
+ * stream and connected to the I2CP router on the specified machine on the given
+ * port.
+ *
+ * Blocks for a long time while the router builds tunnels if connect is true.
+ *
+ * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+ * non-null. Caller must close.
+ * @param i2cpHost I2CP host null to use default
+ * @param i2cpPort I2CP port <= 0 to use default
+ * @param opts Streaming and I2CP options, may be null
+ * @param connect true to connect (blocking)
+ * @return the newly created socket manager, non-null (throws on error)
+ * @since 0.9.7
+ */
+ private static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
+ Properties opts, boolean connect) throws I2PSessionException {
I2PClient client = I2PClientFactory.createClient();
if (opts == null)
opts = new Properties();
@@ -146,34 +207,20 @@ public class I2PSocketManagerFactory {
if (!opts.containsKey(name))
opts.setProperty(name, (String) e.getValue());
}
- //boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
- //if (oldLib && false) {
- // for the old streaming lib
- // opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
- //opts.setProperty("tunnels.depthInbound", "0");
- //} else {
- // for new streaming lib:
- //opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
- // as of 0.8.1 (I2CP default is BestEffort)
- if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
- opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
- //p.setProperty("tunnels.depthInbound", "0");
- //}
+ // as of 0.8.1 (I2CP default is BestEffort)
+ if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
+ opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
if (i2cpHost != null)
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
if (i2cpPort > 0)
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
- try {
- I2PSession session = client.createSession(myPrivateKeyStream, opts);
+ I2PSession session = client.createSession(myPrivateKeyStream, opts);
+ if (connect)
session.connect();
- I2PSocketManager sockMgr = createManager(session, opts, "manager");
- return sockMgr;
- } catch (I2PSessionException ise) {
- getLog().error("Error creating session for socket manager", ise);
- return null;
- }
+ I2PSocketManager sockMgr = createManager(session, opts, "manager");
+ return sockMgr;
}
private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {
diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml
index a5f451167..aedb8fc9a 100644
--- a/apps/routerconsole/java/build.xml
+++ b/apps/routerconsole/java/build.xml
@@ -97,7 +97,7 @@
-
+
@@ -134,7 +134,6 @@
-
@@ -166,6 +165,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/routerconsole/java/bundle-messages-news.sh b/apps/routerconsole/java/bundle-messages-news.sh
new file mode 100755
index 000000000..64a5b2631
--- /dev/null
+++ b/apps/routerconsole/java/bundle-messages-news.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+# Update messages_xx.po and messages_xx.class files,
+# from both java and jsp sources.
+# Requires installed programs xgettext, msgfmt, msgmerge, and find.
+#
+# usage:
+# bundle-messages.sh (generates the resource bundle from the .po file)
+# bundle-messages.sh -p (updates the .po file from the source tags, then generates the resource bundle)
+#
+# zzz - public domain
+#
+CLASS=net.i2p.router.news.messages
+TMPFILE=build/javafiles-news.txt
+export TZ=UTC
+RC=0
+
+if [ "$1" = "-p" ]
+then
+ POUPDATE=1
+fi
+
+# on windows, one must specify the path of commnad find
+# since windows has its own retarded version of find.
+if which find|grep -q -i windows ; then
+ export PATH=.:/bin:/usr/local/bin:$PATH
+fi
+# Fast mode - update ondemond
+# set LG2 to the language you need in envrionment varibales to enable this
+
+# add ../java/ so the refs will work in the po file
+JPATHS="../java/build/News.java"
+for i in ../locale-news/messages_*.po
+do
+ # get language
+ LG=${i#../locale-news/messages_}
+ LG=${LG%.po}
+
+ # skip, if specified
+ if [ $LG2 ]; then
+ [ $LG != $LG2 ] && continue || echo INFO: Language update is set to [$LG2] only.
+ fi
+
+ if [ "$POUPDATE" = "1" ]
+ then
+ # make list of java files newer than the .po file
+ find $JPATHS -name *.java -newer $i > $TMPFILE
+ fi
+ if [ -s build/obj/net/i2p/router/news/messages_$LG.class -a \
+ build/obj/net/i2p/router/news/messages_$LG.class -nt $i -a \
+ ! -s $TMPFILE ]
+ then
+ continue
+ fi
+
+ if [ "$POUPDATE" = "1" ]
+ then
+ echo "Updating the $i file from the tags..."
+ # extract strings from java and jsp files, and update messages.po files
+ # translate calls must be one of the forms:
+ # _("foo")
+ # _x("foo")
+ # intl._("foo")
+ # intl.title("foo")
+ # In a jsp, you must use a helper or handler that has the context set.
+ # To start a new translation, copy the header from an old translation to the new .po file,
+ # then ant distclean updater.
+ find $JPATHS -name *.java > $TMPFILE
+ xgettext -f $TMPFILE -L java --from-code=UTF-8 --no-location\
+ --keyword=_ \
+ -o ${i}t
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - xgettext failed on ${i}, not updating translations"
+ rm -f ${i}t
+ RC=1
+ break
+ fi
+ msgmerge -U --backup=none $i ${i}t
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - msgmerge failed on ${i}, not updating translations"
+ rm -f ${i}t
+ RC=1
+ break
+ fi
+ rm -f ${i}t
+ # so we don't do this again
+ touch $i
+ fi
+
+ if [ "$LG" != "en" ]
+ then
+ # only generate for non-source language
+ echo "Generating ${CLASS}_$LG ResourceBundle..."
+
+ # convert to class files in build/obj
+ msgfmt --java --statistics -r $CLASS -l $LG -d build/obj $i
+ if [ $? -ne 0 ]
+ then
+ echo "ERROR - msgfmt failed on ${i}, not updating translations"
+ # msgfmt leaves the class file there so the build would work the next time
+ find build/obj -name messages_${LG}.class -exec rm -f {} \;
+ RC=1
+ break
+ fi
+ fi
+done
+rm -f $TMPFILE
+exit $RC
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
index 67c47fbb7..cece33751 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
@@ -7,8 +7,8 @@ import java.util.Map;
import net.i2p.router.Router;
import net.i2p.router.transport.FIFOBandwidthRefiller;
-import net.i2p.router.transport.TransportImpl;
import net.i2p.router.transport.TransportManager;
+import net.i2p.router.transport.TransportUtil;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.router.web.ConfigServiceHandler;
import net.i2p.util.Addresses;
@@ -17,6 +17,7 @@ import net.i2p.util.Addresses;
* Handler to deal with form submissions from the main config form and act
* upon the values.
*
+ * Used for both /config and /confignet
*/
public class ConfigNetHandler extends FormHandler {
private String _hostname;
@@ -45,6 +46,7 @@ public class ConfigNetHandler extends FormHandler {
private String _sharePct;
private boolean _ratesOnly;
private boolean _udpDisabled;
+ private String _ipv6Mode;
private final Map changes = new HashMap();
private static final String PROP_HIDDEN = Router.PROP_HIDDEN_HIDDEN; // see Router for other choice
@@ -130,6 +132,11 @@ public class ConfigNetHandler extends FormHandler {
_udpDisabled = true;
}
+ /** @since IPv6 */
+ public void setIpv6(String mode) {
+ _ipv6Mode = mode;
+ }
+
private void recheckReachability() {
_context.commSystem().recheckReachability();
addFormNotice(_("Rechecking router reachability..."));
@@ -177,6 +184,26 @@ public class ConfigNetHandler extends FormHandler {
restartRequired = true;
}
}
+ if (_ipv6Mode != null) {
+ // take care not to set default, as it will change
+ String tcp6 = _context.getProperty(TransportUtil.NTCP_IPV6_CONFIG);
+ if (tcp6 == null)
+ tcp6 = TransportUtil.DEFAULT_IPV6_CONFIG.toConfigString();
+ String udp6 = _context.getProperty(TransportUtil.SSU_IPV6_CONFIG);
+ if (udp6 == null)
+ udp6 = TransportUtil.DEFAULT_IPV6_CONFIG.toConfigString();
+ boolean ch = false;
+ if (!_ipv6Mode.equals(tcp6)) {
+ changes.put(TransportUtil.NTCP_IPV6_CONFIG, _ipv6Mode);
+ ch = true;
+ }
+ if (!_ipv6Mode.equals(udp6)) {
+ changes.put(TransportUtil.SSU_IPV6_CONFIG, _ipv6Mode);
+ ch = true;
+ }
+ if (ch)
+ addFormNotice(_("Updating IPv6 setting"));
+ }
// NTCP Settings
// Normalize some things to make the following code a little easier...
@@ -370,7 +397,8 @@ public class ConfigNetHandler extends FormHandler {
addFormError(_("Invalid address") + ": " + addr);
return false;
}
- boolean rv = TransportImpl.isPubliclyRoutable(iab);
+ // TODO set IPv6 arg based on configuration?
+ boolean rv = TransportUtil.isPubliclyRoutable(iab, true);
if (!rv)
addFormError(_("The hostname or IP {0} is not publicly routable", addr));
return rv;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
index cbf8c859c..1d90ba66f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
@@ -7,12 +7,15 @@ import net.i2p.data.RouterAddress;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.transport.TransportManager;
-import net.i2p.router.transport.udp.UDPAddress;
+import net.i2p.router.transport.TransportUtil;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.Addresses;
+/**
+ *
+ * Used for both /config and /confignet
+ */
public class ConfigNetHelper extends HelperBase {
- public ConfigNetHelper() {}
/** copied from various private components */
public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
@@ -20,6 +23,7 @@ public class ConfigNetHelper extends HelperBase {
public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
private final static String CHECKED = " checked=\"checked\" ";
+
public String getUdphostname() {
return _context.getProperty(UDPTransport.PROP_EXTERNAL_HOST, "");
}
@@ -32,22 +36,15 @@ public class ConfigNetHelper extends HelperBase {
return _context.getProperty(PROP_I2NP_NTCP_PORT, "");
}
- public String getUdpAddress() {
- RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU");
- if (addr == null)
- return _("unknown");
- UDPAddress ua = new UDPAddress(addr);
- return ua.toString();
- }
-
+ /** @return host or "unknown" */
public String getUdpIP() {
RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU");
if (addr == null)
return _("unknown");
- UDPAddress ua = new UDPAddress(addr);
- if (ua.getHost() == null)
+ String rv = addr.getHost();
+ if (rv == null)
return _("unknown");
- return ua.getHost();
+ return rv;
}
/**
@@ -160,6 +157,23 @@ public class ConfigNetHelper extends HelperBase {
return CHECKED;
}
}
+
+ /**
+ * Combined SSU/NTCP
+ * Use SSU setting, then NTCP setting, then default
+ * @since IPv6
+ */
+ public String getIPv6Checked(String mode) {
+ String s = _context.getProperty(TransportUtil.SSU_IPV6_CONFIG);
+ if (s == null) {
+ s = _context.getProperty(TransportUtil.NTCP_IPV6_CONFIG);
+ if (s == null)
+ s = TransportUtil.DEFAULT_IPV6_CONFIG.toConfigString();
+ }
+ if (s.equals(mode))
+ return CHECKED;
+ return "";
+ }
public String[] getAddresses() {
ArrayList al = new ArrayList(Addresses.getAddresses());
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java
index de5824bd3..c1fbf2b05 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java
@@ -1,6 +1,9 @@
package net.i2p.router.web;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -8,6 +11,7 @@ import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.router.update.ConsoleUpdateManager;
import static net.i2p.update.UpdateType.*;
+import net.i2p.util.TranslateReader;
/**
* If news file does not exist, use file from the initialNews directory
@@ -138,11 +142,36 @@ public class NewsHelper extends ContentHelper {
return mgr.getStatus();
}
+ private static final String BUNDLE_NAME = "net.i2p.router.news.messages";
+
+ /**
+ * If we haven't downloaded news yet, use the translated initial news file
+ */
@Override
public String getContent() {
File news = new File(_page);
- if (!news.exists())
+ if (!news.exists()) {
_page = (new File(_context.getBaseDir(), "docs/initialNews/initialNews.xml")).getAbsolutePath();
+ // don't use super, translate on-the-fly
+ Reader reader = null;
+ try {
+ char[] buf = new char[512];
+ StringBuilder out = new StringBuilder(2048);
+ reader = new TranslateReader(_context, BUNDLE_NAME, new FileInputStream(_page));
+ int len;
+ while((len = reader.read(buf)) > 0) {
+ out.append(buf, 0, len);
+ }
+ return out.toString();
+ } catch (IOException ioe) {
+ return "";
+ } finally {
+ try {
+ if (reader != null)
+ reader.close();
+ } catch (IOException foo) {}
+ }
+ }
return super.getContent();
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index bff1abdd5..7d75790ae 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -24,7 +24,7 @@ import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
-import net.i2p.router.transport.ntcp.NTCPAddress;
+import net.i2p.router.transport.TransportUtil;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.util.PortMapper;
@@ -159,7 +159,8 @@ public class SummaryHelper extends HelperBase {
switch (status) {
case CommSystemFacade.STATUS_OK:
RouterAddress ra = routerInfo.getTargetAddress("NTCP");
- if (ra == null || (new NTCPAddress(ra)).isPubliclyRoutable())
+ // TODO set IPv6 arg based on configuration?
+ if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
return _("OK");
return _("ERR-Private TCP Address");
case CommSystemFacade.STATUS_DIFFERENT:
diff --git a/apps/routerconsole/jsp/confignet.jsp b/apps/routerconsole/jsp/confignet.jsp
index cd1ad65f7..a97b2d49b 100644
--- a/apps/routerconsole/jsp/confignet.jsp
+++ b/apps/routerconsole/jsp/confignet.jsp
@@ -64,6 +64,18 @@
>
<%=intl._("Laptop mode - Change router identity and UDP port when IP changes for enhanced anonymity")%>
(<%=intl._("Experimental")%>)
+
<%=intl._("UDP Configuration:")%>
<%=intl._("UDP port:")%>
" >
diff --git a/apps/routerconsole/locale-news/messages_ar.po b/apps/routerconsole/locale-news/messages_ar.po
new file mode 100644
index 000000000..fcfb9c867
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_ar.po
@@ -0,0 +1,64 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P routerconsole\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2011-03-04 14:02-0000\n"
+"Last-Translator: hamada \n"
+"Language-Team: hamada@mail.i2p\n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
+"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
+"X-Generator: Virtaal 0.6.1\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr ""
+
+msgid "Welcome to I2P!"
+msgstr "!I2P مرحبا بك في شبكة"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "انتظر قليلا بينما يتم ربط الإتصال بباقي أعضاء الشبكة.{0}{1}"
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "بسنما يتم الإتصال {0}بتغير بيانات سرعة الشبكة في{1} قم {2}صفحة الخيارات{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "عندما عندما تظهر عبارة \"shared clients\" على اليسار يمكنك حينها {0}زيارة{1} {2}الأسئلة الشائعة{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "IRC اتصل بخادم الدردشة الفورية على {0}localhost:6668{1} {2}#i2p-help{3} or {4}#i2p{5}."
diff --git a/apps/routerconsole/locale-news/messages_de.po b/apps/routerconsole/locale-news/messages_de.po
new file mode 100644
index 000000000..0aee78a0d
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_de.po
@@ -0,0 +1,64 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2013-05-08 15:06+0000\n"
+"Last-Translator: zeroflag \n"
+"Language-Team: German (http://www.transifex.com/projects/p/I2P/language/"
+"de/)\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "Wir gratulieren zur erfolgreichen Installation von I2P!"
+
+msgid "Welcome to I2P!"
+msgstr "Willkommen im I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "Hab noch {0}etwas Geduld{1}, während I2P startet und weitere I2P-Router findet."
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Passe bitte in der Zwischenzeit {0}deine Einstellungen zur Bandbreite{1} auf der"
+"{2}Einstellungsseite{3} an!"
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr "Auch kannst Du gleich deinen Browser für die Nutzung von I2P einrichten."
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr "Setze in den Einstellungen zum Browser ein HTTP Proxy ein mit 127.0.0.1 (oder localhost) mit Port 4444 als Ziel."
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr "Mehr Informationen gibt es auf der {0}I2P browser proxy Setup Seite{1}."
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "Sobald auf der linken Seite eine Verbindung namens \"versch. Klienten\" aufgelistet ist, kannst Du unsere {0}{1} {2}FAQ{3} besuchen."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Verbinde deinen IRC-Klienten mit dem Server auf {0}localhost:6668{1} und schau bei uns im Kanal #i2p-de, {2}#i2p-help{3} oder {4}#i2p{5} vorbei!"
diff --git a/apps/routerconsole/locale-news/messages_en.po b/apps/routerconsole/locale-news/messages_en.po
new file mode 100644
index 000000000..732a76c54
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_en.po
@@ -0,0 +1,63 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo , 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P routerconsole\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2010-06-15 14:09+0100\n"
+"Last-Translator: duck \n"
+"Language-Team: duck \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr ""
+
+msgid "Welcome to I2P!"
+msgstr ""
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr ""
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr ""
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr ""
diff --git a/apps/routerconsole/locale-news/messages_es.po b/apps/routerconsole/locale-news/messages_es.po
new file mode 100644
index 000000000..daa3d7f7b
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_es.po
@@ -0,0 +1,63 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2013-04-15 11:10+0000\n"
+"Last-Translator: trolly \n"
+"Language-Team: Spanish (http://www.transifex.com/projects/p/I2P/language/"
+"es/)\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "¡Felicidades!, has instalado el enrutador I2P con éxito."
+
+msgid "Welcome to I2P!"
+msgstr "¡Bienvenido a I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "¡Ten todavía {0}paciencia{1} mientras I2P esté arrancando y encontrando otros enrutadores I2P!"
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Este es el momento ideal para adaptar tu {0}configuración del ancho de banda{1} en la {2}página de configuración{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "En cuanto veas a la izquierda una conexión llamada \"clientes diferentes\", puedes visitar nuestros {0}{1} {2}FAQ{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "¡Conécta tu cliente IRC con el servidor {0}localhost:6668{1} y ven a saludarnos en los canales #i2p-es, {2}#i2p-help{3} o {4}#i2p{5}!"
diff --git a/apps/routerconsole/locale-news/messages_fr.po b/apps/routerconsole/locale-news/messages_fr.po
new file mode 100644
index 000000000..fa755ad0c
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_fr.po
@@ -0,0 +1,63 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo , 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P routerconsole\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2010-06-15 14:09+0100\n"
+"Last-Translator: duck \n"
+"Language-Team: duck \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "Merci d'utiliser I2P !"
+
+msgid "Welcome to I2P!"
+msgstr "Bienvenue sur I2P !"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "Merci de {0}patienter{1} le temps qu''I2P démarre et trouve des pairs."
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Pendant ce temps, merci d''{0}adjuster vos réglages de bande passante{1} sur la page de {2}configuration{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "Une fois que vous avez une indication \"clients partagés…\" listée sur la gauche, merci de {0}jeter un œil{1} à la {2}FAQ{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Pointez votre client IRC sur {0}localhost:6668{1} et venez nous faire coucou sur {2}#i2p-help{3}, #i2p-fr ou {4}#i2p{5}."
diff --git a/apps/routerconsole/locale-news/messages_nl.po b/apps/routerconsole/locale-news/messages_nl.po
new file mode 100644
index 000000000..4980bfbb8
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_nl.po
@@ -0,0 +1,62 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2012-08-25 19:21+0000\n"
+"Last-Translator: attesor \n"
+"Language-Team: Dutch (http://www.transifex.com/projects/p/I2P/language/nl/)\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "Gefeliciteerd met de installatie van I2P!"
+
+msgid "Welcome to I2P!"
+msgstr "Welkom bij I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "Heb {0}wat geduld{1} terwijl I2P opstart en peers zoekt."
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Terwijl je wacht, {0}pas je bandbreedte instellingen aan{1} op de {2}configuratie pagina{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "Wanneer je een \"gedeelde clients\" destination in de linker lijst hebt, {0}lees dan aub{1} onze {2}FAQ{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Verbind je IRC client met {0}localhost:6668{1} en zeg Hallo in {2}#i2p-help{3} of {4}#i2p{5}."
diff --git a/apps/routerconsole/locale-news/messages_pt.po b/apps/routerconsole/locale-news/messages_pt.po
new file mode 100644
index 000000000..d48d08107
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_pt.po
@@ -0,0 +1,63 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2013-07-04 19:08+0000\n"
+"Last-Translator: tuliouel\n"
+"Language-Team: Portuguese (http://www.transifex.com/projects/p/I2P/language/"
+"pt/)\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "Parabéns, você instalou o roteador I2P com êxito!"
+
+msgid "Welcome to I2P!"
+msgstr "Bem-vindo ao I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "Seja {0}paciente{1} enquanto I2P ainda está iniciando-se e enquanto continuam sendo encontrados outros roteadores I2P!"
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Este é o momento ideal para personalizar a {0}configuração de largura de banda{1} na {2}página de configuração{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "Quando você vê uma conexão no lado esquerdo chamada \"shared clients\", você pode visitar os nossos {0}{1} {2}FAQ{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Conecte seu cliente de IRC para o servidor {0}localhost:6668{1} e vem para nos cumprimentar aos canais #i2p-br, {2}#i2p-help{3} ou {4}#i2p{5}!"
diff --git a/apps/routerconsole/locale-news/messages_ru.po b/apps/routerconsole/locale-news/messages_ru.po
new file mode 100644
index 000000000..e1c257361
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_ru.po
@@ -0,0 +1,64 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2013-07-05 16:26+0000\n"
+"Last-Translator: Roman Azarenko \n"
+"Language-Team: Russian (Russia) (http://www.transifex.com/projects/p/I2P/"
+"language/ru_RU/)\n"
+"Language: ru_RU\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr "Поздравляем с успешным завершением установки I2P!"
+
+msgid "Welcome to I2P!"
+msgstr "Добро пожаловать в I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "Немного терпения! {0}{1} I2P-маршрутизатору потребуется пара минут для запуска модулей и первого подключения к сети I2P."
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Пока Вы ждете, самое время зайти в {2}сетевые настройки{3} и {0}выставить ограничение скорости{1} в соответствии со скоростью Вашего подключения к интернету. "
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "{0}Не забудьте заглянуть{1} в наш {2}{3} FAQ."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Как только в панели слева в разделе «Локальные туннели» появится запись «коллективные клиенты» — I2P готов к работе. Подключайте Ваш IRC-клиент к серверу {0}localhost:6668{1} и заходите сказать нам привет на канал {2}#i2p-help{3} и {4}#i2p{5}."
diff --git a/apps/routerconsole/locale-news/messages_sv.po b/apps/routerconsole/locale-news/messages_sv.po
new file mode 100644
index 000000000..7fb7c36f9
--- /dev/null
+++ b/apps/routerconsole/locale-news/messages_sv.po
@@ -0,0 +1,63 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-07-13 21:46+0000\n"
+"PO-Revision-Date: 2013-01-03 09:01+0000\n"
+"Last-Translator: mon \n"
+"Language-Team: Swedish (Sweden) (http://www.transifex.com/projects/p/I2P/"
+"language/sv_SE/)\n"
+"Language: sv_SE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Congratulations on getting I2P installed!"
+msgstr ""
+
+msgid "Welcome to I2P!"
+msgstr "Välkommen till I2P!"
+
+#, java-format
+msgid "Please {0}have patience{1} as I2P boots up and finds peers."
+msgstr "{0}Ha tålamod{1} medan I2P startar upp och letar upp peers."
+
+#, java-format
+msgid ""
+"While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}"
+"configuration page{3}."
+msgstr "Medan du väntar, var god {0}ställ in dina bandbreddd och port inställningar{1} på {2}konfigurations-sidan{3}."
+
+msgid "Also you can setup your browser to use the I2P proxy to reach eepsites."
+msgstr ""
+
+msgid ""
+"Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your "
+"browser settings."
+msgstr ""
+
+msgid "Do not use SOCKS for this."
+msgstr ""
+
+#, java-format
+msgid ""
+"More information can be found on the {0}I2P browser proxy setup page{1}."
+msgstr ""
+
+#, java-format
+msgid ""
+"Once you have a \"shared clients\" destination listed on the left, please {0}"
+"check out{1} our {2}FAQ{3}."
+msgstr "När du har en \"delade klienter\" distination listad på vänster sida, {0}kika på{1} vår {2}FAQ{3}."
+
+#, java-format
+msgid ""
+"Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-"
+"help{3} or {4}#i2p{5}."
+msgstr "Rikta din IRC-klient till {0}localhost:6668{1} och säg hej till oss på {2}#i2p-help{3} eller {4}#i2p{5}."
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/LogUtil.java b/apps/streaming/java/src/net/i2p/client/streaming/LogUtil.java
index 27d475270..1b55bd1a1 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/LogUtil.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/LogUtil.java
@@ -2,16 +2,20 @@ package net.i2p.client.streaming;
import net.i2p.util.Log;
+/**
+ * Debug logging utility
+ * @since 0.9.7
+ */
class LogUtil {
private LogUtil() {}
/**
- * logs a loop when closing a resource with level WARN
+ * logs a loop when closing a resource with level INFO
* @param desc vararg description
* @param log logger for the class we're intersted in
*/
static void logCloseLoop(Log log, Object... desc) {
- logCloseLoop(log, Log.WARN, desc);
+ logCloseLoop(log, Log.INFO, desc);
}
/**
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
index 5a723b3d4..0a641526b 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
@@ -61,6 +61,8 @@ import net.i2p.util.Log;
*
If the sequenceNum is 0 and the SYN is not set, this is a plain ACK
* packet that should not be ACKed
*
+ * NOTE: All setters unsynchronized.
+ *
*/
class Packet {
private long _sendStreamId;
@@ -209,7 +211,7 @@ class Packet {
* connection packet (where receiveStreamId is the unknown id) or
* if FLAG_NO_ACK is set.
*
- * @return The highest packet sequence number received on receiveStreamId
+ * @return The highest packet sequence number received on receiveStreamId, or -1 if FLAG_NO_ACK
*/
public long getAckThrough() {
if (isFlagSet(FLAG_NO_ACK))
@@ -217,6 +219,10 @@ class Packet {
else
return _ackThrough;
}
+
+ /**
+ * @param id if < 0, sets FLAG_NO_ACK
+ */
public void setAckThrough(long id) {
if (id < 0)
setFlag(FLAG_NO_ACK);
@@ -672,7 +678,7 @@ class Packet {
return size;
}
- @Override
+ @Override
public String toString() {
StringBuilder str = formatAsString();
return str.toString();
@@ -689,7 +695,10 @@ class Packet {
// buf.append('\t');
buf.append(' ');
buf.append(toFlagString());
- buf.append(" ACK ").append(getAckThrough());
+ if (isFlagSet(FLAG_NO_ACK))
+ buf.append(" NO_ACK ");
+ else
+ buf.append(" ACK ").append(getAckThrough());
if (_nacks != null) {
buf.append(" NACK");
for (int i = 0; i < _nacks.length; i++) {
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
index 31c008c48..7a9d78f68 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
@@ -32,7 +32,7 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
private long _cancelledOn;
private final AtomicInteger _nackCount = new AtomicInteger(0);
private volatile boolean _retransmitted;
- private SimpleTimer2.TimedEvent _resendEvent;
+ private volatile SimpleTimer2.TimedEvent _resendEvent;
/** not bound to a connection */
public PacketLocal(I2PAppContext ctx, Destination to) {
@@ -112,6 +112,13 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
_numSends++;
_lastSend = _context.clock().now();
}
+
+ private void cancelResend() {
+ SimpleTimer2.TimedEvent ev = _resendEvent;
+ if (ev != null)
+ ev.cancel();
+ }
+
public void ackReceived() {
final long now = _context.clock().now();
synchronized (this) {
@@ -120,15 +127,16 @@ class PacketLocal extends Packet implements MessageOutputStream.WriteStatus {
releasePayload();
notifyAll();
}
- _resendEvent.cancel();
+ cancelResend();
}
+
public void cancelled() {
synchronized (this) {
_cancelledOn = _context.clock().now();
releasePayload();
notifyAll();
}
- _resendEvent.cancel();
+ cancelResend();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Cancelled! " + toString(), new Exception("cancelled"));
}
diff --git a/build.xml b/build.xml
index c5fb124c9..9b984740b 100644
--- a/build.xml
+++ b/build.xml
@@ -432,9 +432,15 @@
-
+
+
+
+
-
+
+
+
+
@@ -1121,7 +1127,9 @@
-
+
+
+
@@ -1137,6 +1145,7 @@
+
@@ -1292,11 +1301,18 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
@@ -1304,9 +1320,11 @@
+
+
diff --git a/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java
index ba68e3d14..148c2467c 100644
--- a/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java
+++ b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java
@@ -139,13 +139,18 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
long before = System.currentTimeMillis();
doFill(aBuff.buffer);
long after = System.currentTimeMillis();
+ boolean shouldWait = _fullBuffers.size() > 1;
_fullBuffers.offer(aBuff);
_context.statManager().addRateData("prng.bufferFillTime", after - before, 0);
- Thread.yield();
- long waitTime = (after-before)*5;
- if (waitTime <= 0) // somehow postman saw waitTime show up as negative
- waitTime = 50;
- try { Thread.sleep(waitTime); } catch (InterruptedException ie) {}
+ if (shouldWait) {
+ Thread.yield();
+ long waitTime = (after-before)*5;
+ if (waitTime <= 0) // somehow postman saw waitTime show up as negative
+ waitTime = 50;
+ else if (waitTime > 5000)
+ waitTime = 5000;
+ try { Thread.sleep(waitTime); } catch (InterruptedException ie) {}
+ }
}
}
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index e43be08f8..29389bdea 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -80,12 +80,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected I2CPMessageReader _reader;
/** writer message queue */
protected ClientWriterRunner _writer;
- /** where we pipe our messages */
- protected /* FIXME final FIXME */OutputStream _out;
/**
* Used for internal connections to the router.
- * If this is set, _socket, _writer, and _out will be null.
+ * If this is set, _socket and _writer will be null.
* @since 0.8.3
*/
protected I2CPMessageQueue _queue;
@@ -94,7 +92,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected I2PSessionListener _sessionListener;
/** class that generates new messages */
- protected I2CPMessageProducer _producer;
+ protected final I2CPMessageProducer _producer;
/** map of Long --> MessagePayloadMessage */
protected Map _availableMessages;
@@ -103,7 +101,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected final Object _bwReceivedLock = new Object();
protected volatile int[] _bwLimits;
- protected I2PClientMessageHandlerMap _handlerMap;
+ protected final I2PClientMessageHandlerMap _handlerMap;
/** used to seperate things out so we can get rid of singletons */
protected final I2PAppContext _context;
@@ -111,22 +109,24 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** monitor for waiting until a lease set has been granted */
private final Object _leaseSetWait = new Object();
- /** whether the session connection has already been closed (or not yet opened) */
- protected volatile boolean _closed;
+ /**
+ * @since 0.9.8
+ */
+ protected enum State {
+ OPENING,
+ OPEN,
+ CLOSING,
+ CLOSED
+ }
- /** whether the session connection is in the process of being closed */
- protected volatile boolean _closing;
+ private State _state = State.CLOSED;
+ protected final Object _stateLock = new Object();
/** have we received the current date from the router yet? */
private volatile boolean _dateReceived;
/** lock that we wait upon, that the SetDateMessageHandler notifies */
private final Object _dateReceivedLock = new Object();
- /** whether the session connection is in the process of being opened */
- protected volatile boolean _opening;
-
- /** monitor for waiting until opened */
- private final Object _openingWait = new Object();
/**
* thread that we tell when new messages are available who then tells us
* to fetch them. The point of this is so that the fetch doesn't block the
@@ -168,22 +168,24 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public static final int LISTEN_PORT = 7654;
private static final int BUF_SIZE = 32*1024;
-
+
/**
* for extension by SimpleSession (no dest)
*/
- protected I2PSessionImpl(I2PAppContext context, Properties options) {
- this(context, options, false);
+ protected I2PSessionImpl(I2PAppContext context, Properties options,
+ I2PClientMessageHandlerMap handlerMap) {
+ this(context, options, handlerMap, false);
}
/**
* Basic setup of finals
* @since 0.9.7
*/
- private I2PSessionImpl(I2PAppContext context, Properties options, boolean hasDest) {
+ private I2PSessionImpl(I2PAppContext context, Properties options,
+ I2PClientMessageHandlerMap handlerMap, boolean hasDest) {
_context = context;
+ _handlerMap = handlerMap;
_log = context.logManager().getLog(getClass());
- _closed = true;
if (options == null)
options = (Properties) System.getProperties().clone();
_options = loadConfig(options);
@@ -191,10 +193,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_portNum = getPort();
_fastReceive = Boolean.parseBoolean(_options.getProperty(I2PClient.PROP_FAST_RECEIVE));
if (hasDest) {
+ _producer = new I2CPMessageProducer(context);
+ _availableMessages = new ConcurrentHashMap();
_myDestination = new Destination();
_privateKey = new PrivateKey();
_signingPrivateKey = new SigningPrivateKey();
} else {
+ _producer = null;
+ _availableMessages = null;
_myDestination = null;
_privateKey = null;
_signingPrivateKey = null;
@@ -211,11 +217,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* @throws I2PSessionException if there is a problem loading the private keys or
*/
public I2PSessionImpl(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException {
- this(context, options, true);
- _handlerMap = new I2PClientMessageHandlerMap(context);
- _producer = new I2CPMessageProducer(context);
+ this(context, options, new I2PClientMessageHandlerMap(context), true);
_availabilityNotifier = new AvailabilityNotifier();
- _availableMessages = new ConcurrentHashMap();
try {
readDestination(destKeyStream);
} catch (DataFormatException dfe) {
@@ -306,11 +309,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
continue;
}
String val = options.getProperty(key);
- if ((key.length() > 255) || (val.length() > 255)) {
+ // Long strings MUST be removed, even in router context,
+ // as the session config properties must be serialized to be signed.
+ // fixme, bytes could still be over 255 (unlikely)
+ if (key.length() > 255 || val.length() > 255) {
if (_log.shouldLog(Log.WARN))
- _log.warn(getPrefix() + "Not passing on property ["
+ _log.warn("Not passing on property ["
+ key
- + "] in the session configuration as the value is too long (max = 255): "
+ + "] in the session config, key or value is too long (max = 255): "
+ val);
} else {
rv.setProperty(key, val);
@@ -351,17 +357,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
return _leaseSet;
}
- void setOpening(boolean ls) {
- _opening = ls;
- synchronized (_openingWait) {
- _openingWait.notifyAll();
+ protected void changeState(State state) {
+ synchronized (_stateLock) {
+ _state = state;
+ _stateLock.notifyAll();
}
}
- boolean getOpening() {
- return _opening;
- }
-
/**
* Load up the destKeyFile for our Destination, PrivateKey, and SigningPrivateKey
*
@@ -378,12 +380,41 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Connect to the router and establish a session. This call blocks until
* a session is granted.
*
+ * Should be threadsafe, other threads will block until complete.
+ * Disconnect / destroy from another thread may be called simultaneously and
+ * will (should?) interrupt the connect.
+ *
* @throws I2PSessionException if there is a configuration error or the router is
* not reachable
*/
public void connect() throws I2PSessionException {
- setOpening(true);
- _closed = false;
+ synchronized(_stateLock) {
+ boolean wasOpening = false;
+ boolean loop = true;
+ while (loop) {
+ switch (_state) {
+ case CLOSED:
+ if (wasOpening)
+ throw new I2PSessionException("connect by other thread failed");
+ loop = false;
+ break;
+ case OPENING:
+ wasOpening = true;
+ try {
+ _stateLock.wait(10*1000);
+ } catch (InterruptedException ie) {
+ throw new I2PSessionException("Interrupted", ie);
+ }
+ break;
+ case CLOSING:
+ throw new I2PSessionException("close in progress");
+ case OPEN:
+ return;
+ }
+ }
+ changeState(State.OPENING);
+ }
+
_availabilityNotifier.stopNotifying();
if ( (_options != null) &&
@@ -392,32 +423,34 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_log.error("I2CP guaranteed delivery mode has been removed, using best effort.");
}
+ boolean success = false;
long startConnect = _context.clock().now();
try {
- // If we are in the router JVM, connect using the interal queue
- if (_context.isRouterContext()) {
- // _socket, _out, and _writer remain null
- InternalClientManager mgr = _context.internalClientManager();
- if (mgr == null)
- throw new I2PSessionException("Router is not ready for connections");
- // the following may throw an I2PSessionException
- _queue = mgr.connect();
- _reader = new QueuedI2CPMessageReader(_queue, this);
- } else {
- if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL)))
- _socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
- else
- _socket = new Socket(_hostname, _portNum);
- // _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
- _out = _socket.getOutputStream();
- _out.write(I2PClient.PROTOCOL_BYTE);
- _out.flush();
- _writer = new ClientWriterRunner(_out, this);
- InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
- _reader = new I2CPMessageReader(in, this);
+ // protect w/ closeSocket()
+ synchronized(_stateLock) {
+ // If we are in the router JVM, connect using the interal queue
+ if (_context.isRouterContext()) {
+ // _socket and _writer remain null
+ InternalClientManager mgr = _context.internalClientManager();
+ if (mgr == null)
+ throw new I2PSessionException("Router is not ready for connections");
+ // the following may throw an I2PSessionException
+ _queue = mgr.connect();
+ _reader = new QueuedI2CPMessageReader(_queue, this);
+ } else {
+ if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL)))
+ _socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
+ else
+ _socket = new Socket(_hostname, _portNum);
+ // _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
+ OutputStream out = _socket.getOutputStream();
+ out.write(I2PClient.PROTOCOL_BYTE);
+ out.flush();
+ _writer = new ClientWriterRunner(out, this);
+ InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
+ _reader = new I2CPMessageReader(in, this);
+ }
}
- Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
- notifier.start();
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "before startReading");
_reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before getDate");
@@ -426,55 +459,60 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
int waitcount = 0;
while (!_dateReceived) {
if (waitcount++ > 30) {
- closeSocket();
throw new IOException("No handshake received from the router");
}
- try {
- synchronized (_dateReceivedLock) {
- _dateReceivedLock.wait(1000);
- }
- } catch (InterruptedException ie) { // nop
+ synchronized (_dateReceivedLock) {
+ // InterruptedException caught below
+ _dateReceivedLock.wait(1000);
}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before producer.connect()");
_producer.connect(this);
- if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After producer.connect()");
+ if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After producer.connect()");
// wait until we have created a lease set
waitcount = 0;
while (_leaseSet == null) {
if (waitcount++ > 5*60) {
+ throw new IOException("No tunnels built after waiting 5 minutes. Your network connection may be down, or there is severe network congestion.");
+ }
+ synchronized (_leaseSetWait) {
+ // InterruptedException caught below
+ _leaseSetWait.wait(1000);
+ }
+ }
+ if (_log.shouldLog(Log.INFO)) {
+ long connected = _context.clock().now();
+ _log.info(getPrefix() + "Lease set created with inbound tunnels after "
+ + (connected - startConnect)
+ + "ms - ready to participate in the network!");
+ }
+ Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
+ notifier.start();
+ startIdleMonitor();
+ startVerifyUsage();
+ success = true;
+ } catch (InterruptedException ie) {
+ throw new I2PSessionException("Interrupted", ie);
+ } catch (UnknownHostException uhe) {
+ throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, uhe);
+ } catch (IOException ioe) {
+ throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, ioe);
+ } finally {
+ if (success) {
+ changeState(State.OPEN);
+ } else {
+ _availabilityNotifier.stopNotifying();
+ synchronized(_stateLock) {
+ changeState(State.CLOSING);
try {
_producer.disconnect(this);
} catch (I2PSessionException ipe) {}
closeSocket();
- throw new IOException("No tunnels built after waiting 5 minutes. Your network connection may be down, or there is severe network congestion.");
- }
- synchronized (_leaseSetWait) {
- try {
- _leaseSetWait.wait(1000);
- } catch (InterruptedException ie) { // nop
- }
}
}
- long connected = _context.clock().now();
- if (_log.shouldLog(Log.INFO))
- _log.info(getPrefix() + "Lease set created with inbound tunnels after "
- + (connected - startConnect)
- + "ms - ready to participate in the network!");
- startIdleMonitor();
- startVerifyUsage();
- setOpening(false);
- } catch (UnknownHostException uhe) {
- _closed = true;
- setOpening(false);
- throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, uhe);
- } catch (IOException ioe) {
- _closed = true;
- setOpening(false);
- throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, ioe);
}
}
@@ -570,8 +608,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Needs work.
*/
protected class AvailabilityNotifier implements Runnable {
- private final List _pendingIds;
- private final List _pendingSizes;
+ private final List _pendingIds;
+ private final List _pendingSizes;
private volatile boolean _alive;
public AvailabilityNotifier() {
@@ -606,8 +644,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
}
if (!_pendingIds.isEmpty()) {
- msgId = (Long)_pendingIds.remove(0);
- size = (Integer)_pendingSizes.remove(0);
+ msgId = _pendingIds.remove(0);
+ size = _pendingSizes.remove(0);
}
}
if ( (msgId != null) && (size != null) ) {
@@ -695,8 +733,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** configure the listener */
public void setSessionListener(I2PSessionListener lsnr) { _sessionListener = lsnr; }
- /** has the session been closed (or not yet connected)? */
- public boolean isClosed() { return _closed; }
+ /**
+ * Has the session been closed (or not yet connected)?
+ * False when open and during transitions. Unsynchronized.
+ */
+ public boolean isClosed() {
+ synchronized (_stateLock) {
+ return _state == State.CLOSED;
+ }
+ }
/**
* Deliver an I2CP message to the router
@@ -713,7 +758,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
if (!_queue.offer(message, MAX_SEND_WAIT))
throw new I2PSessionException("Timed out waiting while write queue was full");
} catch (InterruptedException ie) {
- throw new I2PSessionException("Interrupted while write queue was full", ie);
+ throw new I2PSessionException("Interrupted", ie);
}
} else if (_writer == null) {
throw new I2PSessionException("Already closed");
@@ -756,21 +801,16 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/**
* Tear down the session, and do NOT reconnect.
*
- * Blocks if session has not been fully started.
+ * Will interrupt an open in progress.
*/
public void destroySession(boolean sendDisconnect) {
- while (_opening) {
- synchronized (_openingWait) {
- try {
- _openingWait.wait(1000);
- } catch (InterruptedException ie) { // nop
- }
- }
+ synchronized(_stateLock) {
+ if (_state == State.CLOSING || _state == State.CLOSED)
+ return;
+ changeState(State.CLOSING);
}
- if (_closed) return;
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Destroy the session", new Exception("DestroySession()"));
- _closing = true; // we use this to prevent a race
if (sendDisconnect && _producer != null) { // only null if overridden by I2PSimpleSession
try {
_producer.disconnect(this);
@@ -783,19 +823,27 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
// SimpleSession does not initialize
if (_availabilityNotifier != null)
_availabilityNotifier.stopNotifying();
- _closed = true;
- _closing = false;
closeSocket();
if (_sessionListener != null) _sessionListener.disconnected(this);
}
/**
- * Close the socket carefully
- *
+ * Close the socket carefully.
*/
private void closeSocket() {
+ synchronized(_stateLock) {
+ changeState(State.CLOSING);
+ locked_closeSocket();
+ changeState(State.CLOSED);
+ }
+ }
+
+ /**
+ * Close the socket carefully.
+ * Caller must change state.
+ */
+ private void locked_closeSocket() {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Closing the socket", new Exception("closeSocket"));
- _closed = true;
if (_reader != null) {
_reader.stopReading();
_reader = null;
@@ -830,8 +878,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
disconnect();
}
+ /**
+ * Will interrupt a connect in progress.
+ */
protected void disconnect() {
- if (_closed || _closing) return;
+ synchronized(_stateLock) {
+ if (_state == State.CLOSING || _state == State.CLOSED)
+ return;
+ changeState(State.CLOSING);
+ }
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
@@ -842,11 +897,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
if (_log.shouldLog(Log.ERROR))
- _log.error(getPrefix() + "Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
+ _log.error(getPrefix() + "Disconned from the router, and not trying to reconnect");
if (_sessionListener != null) _sessionListener.disconnected(this);
- _closed = true;
closeSocket();
+ changeState(State.CLOSED);
}
private final static int MAX_RECONNECT_DELAY = 320*1000;
@@ -865,7 +920,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
i++;
if ( (delay > MAX_RECONNECT_DELAY) || (delay <= 0) )
delay = MAX_RECONNECT_DELAY;
- try { Thread.sleep(delay); } catch (InterruptedException ie) {}
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException ie) {
+ return false;
+ }
try {
connect();
@@ -970,7 +1029,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
if (rv != null)
return rv;
}
- if (_closed)
+ if (isClosed())
return null;
LookupWaiter waiter = new LookupWaiter(h);
_pendingLookups.offer(waiter);
@@ -980,7 +1039,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
synchronized (waiter) {
waiter.wait(maxWait);
}
- } catch (InterruptedException ie) {}
+ } catch (InterruptedException ie) {
+ throw new I2PSessionException("Interrupted", ie);
+ }
} finally {
_pendingLookups.remove(waiter);
}
@@ -996,14 +1057,16 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* @return null on failure
*/
public int[] bandwidthLimits() throws I2PSessionException {
- if (_closed)
+ if (isClosed())
return null;
sendMessage(new GetBandwidthLimitsMessage());
try {
synchronized (_bwReceivedLock) {
_bwReceivedLock.wait(5*1000);
}
- } catch (InterruptedException ie) {}
+ } catch (InterruptedException ie) {
+ throw new I2PSessionException("Interrupted", ie);
+ }
return _bwLimits;
}
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java
index c369f3a62..eb537815e 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl2.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java
@@ -44,9 +44,12 @@ class I2PSessionImpl2 extends I2PSessionImpl {
/** Don't expect any MSMs from the router for outbound traffic @since 0.8.1 */
protected boolean _noEffort;
- /** for extension */
- protected I2PSessionImpl2(I2PAppContext context, Properties options) {
- super(context, options);
+ /**
+ * for extension by SimpleSession (no dest)
+ */
+ protected I2PSessionImpl2(I2PAppContext context, Properties options,
+ I2PClientMessageHandlerMap handlerMap) {
+ super(context, options, handlerMap);
}
/**
diff --git a/core/java/src/net/i2p/client/I2PSimpleSession.java b/core/java/src/net/i2p/client/I2PSimpleSession.java
index 9962c325d..35b6422e2 100644
--- a/core/java/src/net/i2p/client/I2PSimpleSession.java
+++ b/core/java/src/net/i2p/client/I2PSimpleSession.java
@@ -8,6 +8,7 @@ package net.i2p.client;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Properties;
@@ -37,53 +38,57 @@ class I2PSimpleSession extends I2PSessionImpl2 {
* @throws I2PSessionException if there is a problem
*/
public I2PSimpleSession(I2PAppContext context, Properties options) throws I2PSessionException {
- super(context, options);
- _handlerMap = new SimpleMessageHandlerMap(context);
+ super(context, options, new SimpleMessageHandlerMap(context));
}
/**
* Connect to the router and establish a session. This call blocks until
* a session is granted.
*
+ * NOT threadsafe, do not call from multiple threads.
+ *
* @throws I2PSessionException if there is a configuration error or the router is
* not reachable
*/
@Override
public void connect() throws I2PSessionException {
- _closed = false;
-
+ changeState(State.OPENING);
+ boolean success = false;
try {
- // If we are in the router JVM, connect using the interal queue
- if (_context.isRouterContext()) {
- // _socket, _out, and _writer remain null
- InternalClientManager mgr = _context.internalClientManager();
- if (mgr == null)
- throw new I2PSessionException("Router is not ready for connections");
- // the following may throw an I2PSessionException
- _queue = mgr.connect();
- _reader = new QueuedI2CPMessageReader(_queue, this);
- } else {
- if (Boolean.parseBoolean(getOptions().getProperty(PROP_ENABLE_SSL)))
- _socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
- else
- _socket = new Socket(_hostname, _portNum);
- _out = _socket.getOutputStream();
- _out.write(I2PClient.PROTOCOL_BYTE);
- _out.flush();
- _writer = new ClientWriterRunner(_out, this);
- InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
- _reader = new I2CPMessageReader(in, this);
+ // protect w/ closeSocket()
+ synchronized(_stateLock) {
+ // If we are in the router JVM, connect using the interal queue
+ if (_context.isRouterContext()) {
+ // _socket and _writer remain null
+ InternalClientManager mgr = _context.internalClientManager();
+ if (mgr == null)
+ throw new I2PSessionException("Router is not ready for connections");
+ // the following may throw an I2PSessionException
+ _queue = mgr.connect();
+ _reader = new QueuedI2CPMessageReader(_queue, this);
+ } else {
+ if (Boolean.parseBoolean(getOptions().getProperty(PROP_ENABLE_SSL)))
+ _socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
+ else
+ _socket = new Socket(_hostname, _portNum);
+ OutputStream out = _socket.getOutputStream();
+ out.write(I2PClient.PROTOCOL_BYTE);
+ out.flush();
+ _writer = new ClientWriterRunner(out, this);
+ InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
+ _reader = new I2CPMessageReader(in, this);
+ }
}
// we do not receive payload messages, so we do not need an AvailabilityNotifier
// ... or an Idle timer, or a VerifyUsage
_reader.startReading();
-
+ success = true;
} catch (UnknownHostException uhe) {
- _closed = true;
throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, uhe);
} catch (IOException ioe) {
- _closed = true;
throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, ioe);
+ } finally {
+ changeState(success ? State.OPEN : State.CLOSED);
}
}
diff --git a/core/java/src/net/i2p/crypto/Hash384.java b/core/java/src/net/i2p/crypto/Hash384.java
new file mode 100644
index 000000000..bdf60d00a
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/Hash384.java
@@ -0,0 +1,31 @@
+package net.i2p.crypto;
+
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * No warranty of any kind, either expressed or implied.
+ */
+
+import net.i2p.data.SimpleDataStructure;
+
+/**
+ * 48 byte hash
+ *
+ * @since 0.9.8
+ */
+public class Hash384 extends SimpleDataStructure {
+
+ public final static int HASH_LENGTH = 48;
+
+ public Hash384() {
+ super();
+ }
+
+ /** @throws IllegalArgumentException if data is not correct length (null is ok) */
+ public Hash384(byte data[]) {
+ super(data);
+ }
+
+ public int length() {
+ return HASH_LENGTH;
+ }
+}
diff --git a/core/java/src/net/i2p/crypto/Hash512.java b/core/java/src/net/i2p/crypto/Hash512.java
new file mode 100644
index 000000000..5916d2c53
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/Hash512.java
@@ -0,0 +1,31 @@
+package net.i2p.crypto;
+
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * No warranty of any kind, either expressed or implied.
+ */
+
+import net.i2p.data.SimpleDataStructure;
+
+/**
+ * 64 byte hash
+ *
+ * @since 0.9.8
+ */
+public class Hash512 extends SimpleDataStructure {
+
+ public final static int HASH_LENGTH = 64;
+
+ public Hash512() {
+ super();
+ }
+
+ /** @throws IllegalArgumentException if data is not correct length (null is ok) */
+ public Hash512(byte data[]) {
+ super(data);
+ }
+
+ public int length() {
+ return HASH_LENGTH;
+ }
+}
diff --git a/core/java/src/net/i2p/crypto/SU3File.java b/core/java/src/net/i2p/crypto/SU3File.java
new file mode 100644
index 000000000..3c797be25
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/SU3File.java
@@ -0,0 +1,425 @@
+package net.i2p.crypto;
+
+import java.io.EOFException;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.DigestInputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.i2p.I2PAppContext;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.data.Signature;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.data.SigningPublicKey;
+
+/**
+ * Succesor to the ".sud" format used in TrustedUpdate.
+ * Format specified in http://www.i2p2.de/updates
+ *
+ * @since 0.9.8
+ */
+public class SU3File {
+
+ private final I2PAppContext _context;
+ private final Map _trustedKeys;
+
+ private final File _file;
+ private String _version;
+ private int _versionLength;
+ private String _signer;
+ private int _signerLength;
+ private int _contentType;
+ private long _contentLength;
+ private SigningPublicKey _signerPubkey;
+ private boolean _headerVerified;
+
+ private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
+ private static final int FILE_VERSION = 0;
+ private static final int MIN_VERSION_BYTES = 16;
+ private static final int VERSION_OFFSET = Signature.SIGNATURE_BYTES;
+
+ private static final int TYPE_ZIP = 0;
+
+ private static final int CONTENT_ROUTER = 0;
+ private static final int CONTENT_ROUTER_P200 = 1;
+ private static final int CONTENT_PLUGIN = 2;
+ private static final int CONTENT_RESEED = 3;
+
+ private static final int SIG_DSA_160 = SigType.DSA_SHA1.getCode();
+
+ /**
+ * Uses TrustedUpdate's default keys for verification.
+ */
+ public SU3File(String file) {
+ this(new File(file));
+ }
+
+ /**
+ * Uses TrustedUpdate's default keys for verification.
+ */
+ public SU3File(File file) {
+ this(file, (new TrustedUpdate()).getKeys());
+ }
+
+ /**
+ * @param trustedKeys map of pubkey to signer name, null ok if not verifying
+ */
+ public SU3File(File file, Map trustedKeys) {
+ this(I2PAppContext.getGlobalContext(), file, trustedKeys);
+ }
+
+ /**
+ * @param trustedKeys map of pubkey to signer name, null ok if not verifying
+ */
+ public SU3File(I2PAppContext context, File file, Map trustedKeys) {
+ _context = context;
+ _file = file;
+ _trustedKeys = trustedKeys;
+ }
+
+ public String getVersionString() throws IOException {
+ verifyHeader();
+ return _version;
+ }
+
+ public String getSignerString() throws IOException {
+ verifyHeader();
+ return _signer;
+ }
+
+ /**
+ * Throws IOE if verify vails.
+ */
+ public void verifyHeader() throws IOException {
+ if (_headerVerified)
+ return;
+ InputStream in = null;
+ try {
+ in = new FileInputStream(_file);
+ verifyHeader(in);
+ } catch (DataFormatException dfe) {
+ IOException ioe = new IOException("foo");
+ ioe.initCause(dfe);
+ throw ioe;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+ }
+
+ /**
+ * Throws if verify vails.
+ */
+ private void verifyHeader(InputStream in) throws IOException, DataFormatException {
+ byte[] magic = new byte[MAGIC.length];
+ DataHelper.read(in, magic);
+ if (!DataHelper.eq(magic, MAGIC))
+ throw new IOException("Not an su3 file");
+ skip(in, 1);
+ int foo = in.read();
+ if (foo != FILE_VERSION)
+ throw new IOException("bad file version");
+ skip(in, 1);
+ int sigType = in.read();
+ // TODO, for other known algos we must start over with a new MessageDigest
+ // (rewind 10 bytes)
+ if (sigType != SIG_DSA_160)
+ throw new IOException("bad sig type");
+ _signerLength = (int) DataHelper.readLong(in, 2);
+ if (_signerLength != Signature.SIGNATURE_BYTES)
+ throw new IOException("bad sig length");
+ skip(in, 1);
+ int _versionLength = in.read();
+ if (_versionLength < MIN_VERSION_BYTES)
+ throw new IOException("bad version length");
+ skip(in, 1);
+ int signerLen = in.read();
+ if (signerLen <= 0)
+ throw new IOException("bad signer length");
+ _contentLength = DataHelper.readLong(in, 8);
+ if (_contentLength <= 0)
+ throw new IOException("bad content length");
+ skip(in, 1);
+ foo = in.read();
+ if (foo != TYPE_ZIP)
+ throw new IOException("bad type");
+ skip(in, 1);
+ _contentType = in.read();
+ if (_contentType < CONTENT_ROUTER || _contentType > CONTENT_RESEED)
+ throw new IOException("bad content type");
+ skip(in, 12);
+
+ byte[] data = new byte[_versionLength];
+ int bytesRead = DataHelper.read(in, data);
+ if (bytesRead != _versionLength)
+ throw new EOFException();
+ int zbyte;
+ for (zbyte = 0; zbyte < _versionLength; zbyte++) {
+ if (data[zbyte] == 0x00)
+ break;
+ }
+ _version = new String(data, 0, zbyte, "UTF-8");
+
+ data = new byte[signerLen];
+ bytesRead = DataHelper.read(in, data);
+ if (bytesRead != signerLen)
+ throw new EOFException();
+ _signer = DataHelper.getUTF8(data);
+ if (_trustedKeys != null) {
+ for (Map.Entry e : _trustedKeys.entrySet()) {
+ if (e.getValue().equals(_signer)) {
+ _signerPubkey = e.getKey();
+ break;
+ }
+ }
+ if (_signerPubkey == null)
+ throw new IOException("unknown signer: " + _signer);
+ }
+ _headerVerified = true;
+ }
+
+ /** skip but update digest */
+ private static void skip(InputStream in, int cnt) throws IOException {
+ for (int i = 0; i < cnt; i++) {
+ if (in.read() < 0)
+ throw new EOFException();
+ }
+ }
+
+ private int getContentOffset() throws IOException {
+ verifyHeader();
+ return VERSION_OFFSET + _versionLength + _signerLength;
+ }
+
+ /**
+ * One-pass verify and extract the content.
+ * Recommend extracting to a temp location as the sig is not checked until
+ * after extraction. This will delete the file if the sig does not verify.
+ * Throws IOE on all format errors.
+ *
+ * @param migrateTo the output file, probably in zip format
+ * @return true if signature is good
+ */
+ public boolean verifyAndMigrate(File migrateTo) throws IOException {
+ DigestInputStream in = null;
+ OutputStream out = null;
+ boolean rv = false;
+ try {
+ MessageDigest md = SHA1.getInstance();
+ in = new DigestInputStream(new BufferedInputStream(new FileInputStream(_file)), md);
+ if (!_headerVerified)
+ verifyHeader(in);
+ else
+ skip(in, getContentOffset());
+ if (_signerPubkey == null)
+ throw new IOException("unknown signer: " + _signer);
+ out = new FileOutputStream(migrateTo);
+ byte[] buf = new byte[16*1024];
+ long tot = 0;
+ while (tot < _contentLength) {
+ int read = in.read(buf, 0, (int) Math.min(buf.length, _contentLength - tot));
+ if (read < 0)
+ throw new EOFException();
+ out.write(buf, 0, read);
+ tot += read;
+ }
+ byte[] sha = md.digest();
+ in.on(false);
+ Signature signature = new Signature();
+ signature.readBytes(in);
+ SHA1Hash hash = new SHA1Hash(sha);
+ rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
+ } catch (DataFormatException dfe) {
+ IOException ioe = new IOException("foo");
+ ioe.initCause(dfe);
+ throw ioe;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ if (out != null) try { out.close(); } catch (IOException ioe) {}
+ if (!rv)
+ migrateTo.delete();
+ }
+ return rv;
+ }
+
+ /**
+ * One-pass wrap and sign the content.
+ * Writes to the file specified in the constructor.
+ * Throws on all errors.
+ *
+ * @param content the input file, probably in zip format
+ * @param contentType 0-255, 0 for zip
+ * @param version 1-255 bytes when converted to UTF-8
+ * @param signer ID of the public key, 1-255 bytes when converted to UTF-8
+ */
+ public void write(File content, int contentType, String version,
+ String signer, SigningPrivateKey privkey) throws IOException {
+ InputStream in = null;
+ DigestOutputStream out = null;
+ boolean ok = false;
+ try {
+ in = new BufferedInputStream(new FileInputStream(content));
+ MessageDigest md = SHA1.getInstance();
+ out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
+ out.write(MAGIC);
+ out.write((byte) 0);
+ out.write((byte) FILE_VERSION);
+ out.write((byte) 0);
+ out.write((byte) SIG_DSA_160);
+ DataHelper.writeLong(out, 2, Signature.SIGNATURE_BYTES);
+ out.write((byte) 0);
+ byte[] verBytes = DataHelper.getUTF8(version);
+ if (verBytes.length == 0 || verBytes.length > 255)
+ throw new IllegalArgumentException("bad version length");
+ int verLen = Math.max(verBytes.length, MIN_VERSION_BYTES);
+ out.write((byte) verLen);
+ out.write((byte) 0);
+ byte[] signerBytes = DataHelper.getUTF8(signer);
+ if (signerBytes.length == 0 || signerBytes.length > 255)
+ throw new IllegalArgumentException("bad signer length");
+ out.write((byte) signerBytes.length);
+ long contentLength = content.length();
+ if (contentLength <= 0)
+ throw new IllegalArgumentException("No content");
+ DataHelper.writeLong(out, 8, contentLength);
+ out.write((byte) 0);
+ out.write((byte) TYPE_ZIP);
+ out.write((byte) 0);
+ if (contentType < 0 || contentType > 255)
+ throw new IllegalArgumentException("bad content type");
+ out.write((byte) contentType);
+ out.write(new byte[12]);
+ out.write(verBytes);
+ if (verBytes.length < MIN_VERSION_BYTES)
+ out.write(new byte[MIN_VERSION_BYTES - verBytes.length]);
+ out.write(signerBytes);
+
+ byte[] buf = new byte[16*1024];
+ long tot = 0;
+ while (tot < contentLength) {
+ int read = in.read(buf, 0, (int) Math.min(buf.length, contentLength - tot));
+ if (read < 0)
+ throw new EOFException();
+ out.write(buf, 0, read);
+ tot += read;
+ }
+
+ byte[] sha = md.digest();
+ out.on(false);
+ SHA1Hash hash = new SHA1Hash(sha);
+ Signature signature = _context.dsa().sign(hash, privkey);
+ signature.writeBytes(out);
+ ok = true;
+ } catch (DataFormatException dfe) {
+ IOException ioe = new IOException("foo");
+ ioe.initCause(dfe);
+ throw ioe;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ if (out != null) try { out.close(); } catch (IOException ioe) {}
+ if (!ok)
+ _file.delete();
+ }
+ }
+
+ /**
+ * Parses command line arguments when this class is used from the command
+ * line.
+ * Exits 1 on failure so this can be used in scripts.
+ *
+ * @param args Command line parameters.
+ */
+ public static void main(String[] args) {
+ boolean ok = false;
+ try {
+ if ("showversion".equals(args[0])) {
+ ok = showVersionCLI(args[1]);
+ } else if ("sign".equals(args[0])) {
+ ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
+ } else if ("verifysig".equals(args[0])) {
+ ok = verifySigCLI(args[1]);
+ } else {
+ showUsageCLI();
+ }
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ showUsageCLI();
+ }
+ if (!ok)
+ System.exit(1);
+ }
+
+ private static final void showUsageCLI() {
+ System.err.println("Usage: SU3File showversion signedFile.su3");
+ System.err.println(" SU3File sign inputFile.zip signedFile.su3 privateKeyFile version signerName@mail.i2p");
+ System.err.println(" SU3File verifysig signedFile.su3");
+ }
+
+ /** @return success */
+ private static final boolean showVersionCLI(String signedFile) {
+ try {
+ SU3File file = new SU3File(new File(signedFile), null);
+ String versionString = file.getVersionString();
+ if (versionString.equals(""))
+ System.out.println("No version string found in file '" + signedFile + "'");
+ else
+ System.out.println("Version: " + versionString);
+ return !versionString.equals("");
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return false;
+ }
+ }
+
+ /** @return success */
+ private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile,
+ String version, String signerName) {
+ InputStream in = null;
+ try {
+ in = new FileInputStream(privateKeyFile);
+ SigningPrivateKey spk = new SigningPrivateKey();
+ spk.readBytes(in);
+ in.close();
+ SU3File file = new SU3File(signedFile);
+ file.write(new File(inputFile), CONTENT_ROUTER, version, signerName, spk);
+ System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
+ return true;
+ } catch (DataFormatException dfe) {
+ System.out.println("Error signing input file '" + inputFile + "'");
+ dfe.printStackTrace();
+ return false;
+ } catch (IOException ioe) {
+ System.out.println("Error signing input file '" + inputFile + "'");
+ ioe.printStackTrace();
+ return false;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+ }
+
+ /** @return valid */
+ private static final boolean verifySigCLI(String signedFile) {
+ InputStream in = null;
+ try {
+ SU3File file = new SU3File(signedFile);
+ boolean isValidSignature = file.verifyAndMigrate(new File("/dev/null"));
+ if (isValidSignature)
+ System.out.println("Signature VALID (signed by " + file.getSignerString() + ')');
+ else
+ System.out.println("Signature INVALID (signed by " + file.getSignerString() + ')');
+ return isValidSignature;
+ } catch (IOException ioe) {
+ System.out.println("Error verifying input file '" + signedFile + "'");
+ ioe.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/core/java/src/net/i2p/crypto/SigType.java b/core/java/src/net/i2p/crypto/SigType.java
new file mode 100644
index 000000000..245711153
--- /dev/null
+++ b/core/java/src/net/i2p/crypto/SigType.java
@@ -0,0 +1,91 @@
+package net.i2p.crypto;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Defines the properties for various signature types
+ * that I2P supports or may someday support.
+ *
+ * @since 0.9.8
+ */
+public enum SigType {
+ /**
+ * DSA_SHA1 is the default.
+ * Pubkey 128 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes
+ * @since 0.9.8
+ */
+ DSA_SHA1(0, 128, 20, 20, 40, "SHA-1", "SHA1withDSA"),
+ /** Pubkey 40 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes */
+ ECDSA_SHA1(1, 40, 20, 20, 40, "SHA-1", "SHA1withECDSA"),
+ /** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
+ ECDSA_SHA256(2, 64, 32, 32, 64, "SHA-256", "SHA256withECDSA"),
+ /** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
+ ECDSA_SHA384(3, 96, 48, 48, 96, "SHA-384", "SHA384withECDSA"),
+ /** Pubkey 128 bytes; privkey 64 bytes; hash 64 bytes; sig 128 bytes */
+ ECDSA_SHA512(4, 128, 64, 64, 128, "SHA-512", "SHA512withECDSA")
+
+ //MD5
+ //ELGAMAL_SHA256
+ //RSA_SHA1
+ //RSA_SHA256
+ //RSA_SHA384
+ //RSA_SHA512
+ //DSA_2048_224(2, 256, 28, 32, 56, "SHA-256"),
+ // Nonstandard, used by Syndie.
+ // Pubkey 128 bytes; privkey 20 bytes; hash 32 bytes; sig 40 bytes
+ //DSA_1024_160_SHA256(1, 128, 20, 32, 40, "SHA-256", "?"),
+ // Pubkey 256 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
+ //DSA_2048_256(2, 256, 32, 32, 64, "SHA-256", "?"),
+ // Pubkey 384 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
+ //DSA_3072_256(3, 384, 32, 32, 64, "SHA-256", "?"),
+ ;
+
+ private final int code, pubkeyLen, privkeyLen, hashLen, sigLen;
+ private final String digestName, algoName;
+
+ SigType(int cod, int pubLen, int privLen, int hLen, int sLen, String mdName, String aName) {
+ code = cod;
+ pubkeyLen = pubLen;
+ privkeyLen = privLen;
+ hashLen = hLen;
+ sigLen = sLen;
+ digestName = mdName;
+ algoName = aName;
+ }
+
+ public int getCode() { return code; }
+ public int getPubkeyLen() { return pubkeyLen; }
+ public int getPrivkeyLen() { return privkeyLen; }
+ public int getHashLen() { return hashLen; }
+ public int getSigLen() { return sigLen; }
+ public String getAlgorithmName() { return algoName; }
+
+ /** @throws UnsupportedOperationException if not supported */
+ public MessageDigest getDigestInstance() {
+ if (digestName.equals("SHA-1"))
+ return SHA1.getInstance();
+ if (digestName.equals("SHA-256"))
+ return SHA256Generator.getDigestInstance();
+ try {
+ return MessageDigest.getInstance(digestName);
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ private static final Map BY_CODE = new HashMap();
+
+ static {
+ for (SigType type : SigType.values()) {
+ BY_CODE.put(Integer.valueOf(type.getCode()), type);
+ }
+ }
+
+ /** @return null if not supported */
+ public static SigType getByCode(int code) {
+ return BY_CODE.get(Integer.valueOf(code));
+ }
+}
diff --git a/core/java/src/net/i2p/crypto/TrustedUpdate.java b/core/java/src/net/i2p/crypto/TrustedUpdate.java
index 8ad2d14ed..51c19e4ea 100644
--- a/core/java/src/net/i2p/crypto/TrustedUpdate.java
+++ b/core/java/src/net/i2p/crypto/TrustedUpdate.java
@@ -8,6 +8,7 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.io.UnsupportedEncodingException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
@@ -209,6 +210,13 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");
}
+ /**
+ * @since 0.9.8
+ */
+ Map getKeys() {
+ return Collections.unmodifiableMap(_trustedKeys);
+ }
+
/**
* Duplicate keys or names rejected,
* except that duplicate empty names are allowed
diff --git a/core/java/src/net/i2p/data/Destination.java b/core/java/src/net/i2p/data/Destination.java
index 3c823a52b..6a1764805 100644
--- a/core/java/src/net/i2p/data/Destination.java
+++ b/core/java/src/net/i2p/data/Destination.java
@@ -13,6 +13,13 @@ package net.i2p.data;
* Defines an end point in the I2P network. The Destination may move around
* in the network, but messages sent to the Destination will find it
*
+ * Note that the public (encryption) key is essentially unused, since
+ * "end-to-end" encryption was removed in 0.6. The public key in the
+ * LeaseSet is used instead.
+ *
+ * The first bytes of the public key are used for the IV for leaseset encryption,
+ * but that encryption is poorly designed and should be deprecated.
+ *
* @author jrandom
*/
public class Destination extends KeysAndCert {
diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java
index bf7ca7636..a339ed694 100644
--- a/core/java/src/net/i2p/data/LeaseSet.java
+++ b/core/java/src/net/i2p/data/LeaseSet.java
@@ -31,6 +31,10 @@ import net.i2p.util.RandomSource;
* Only the gateways and tunnel IDs in the individual
* leases are encrypted.
*
+ * WARNING:
+ * Encryption is poorly designed and probably insecure.
+ * Not recommended.
+ *
* Encrypted leases are not indicated as such.
* The only way to tell a lease is encrypted is to
* determine that the listed gateways do not exist.
diff --git a/core/java/src/net/i2p/data/RouterAddress.java b/core/java/src/net/i2p/data/RouterAddress.java
index c21f0f5bf..0b6fde6e0 100644
--- a/core/java/src/net/i2p/data/RouterAddress.java
+++ b/core/java/src/net/i2p/data/RouterAddress.java
@@ -38,7 +38,7 @@ import net.i2p.util.OrderedProperties;
* @author jrandom
*/
public class RouterAddress extends DataStructureImpl {
- private int _cost;
+ private short _cost;
//private Date _expiration;
private String _transportStyle;
private final Properties _options;
@@ -50,16 +50,30 @@ public class RouterAddress extends DataStructureImpl {
public static final String PROP_PORT = "port";
public RouterAddress() {
- _cost = -1;
_options = new OrderedProperties();
}
+ /**
+ * For efficiency when created by a Transport.
+ * @param options not copied; do not reuse or modify
+ * @param cost 0-255
+ * @since IPv6
+ */
+ public RouterAddress(String style, OrderedProperties options, int cost) {
+ _transportStyle = style;
+ _options = options;
+ if (cost < 0 || cost > 255)
+ throw new IllegalArgumentException();
+ _cost = (short) cost;
+ }
+
/**
* Retrieve the weighted cost of this address, relative to other methods of
* contacting this router. The value 0 means free and 255 means really expensive.
* No value above 255 is allowed.
*
* Unused before 0.7.12
+ * @return 0-255
*/
public int getCost() {
return _cost;
@@ -67,12 +81,18 @@ public class RouterAddress extends DataStructureImpl {
/**
* Configure the weighted cost of using the address.
- * No value above 255 is allowed.
+ * No value negative or above 255 is allowed.
+ *
+ * WARNING - do not change cost on a published address or it will break the RI sig.
+ * There is no check here.
+ * Rarely used, use 3-arg constructor.
*
* NTCP is set to 10 and SSU to 5 by default, unused before 0.7.12
*/
public void setCost(int cost) {
- _cost = cost;
+ if (cost < 0 || cost > 255)
+ throw new IllegalArgumentException();
+ _cost = (short) cost;
}
/**
@@ -113,6 +133,7 @@ public class RouterAddress extends DataStructureImpl {
* Configure the type of transport that must be used to communicate on this address
*
* @throws IllegalStateException if was already set
+ * @deprecated unused, use 3-arg constructor
*/
public void setTransportStyle(String transportStyle) {
if (_transportStyle != null)
@@ -152,6 +173,7 @@ public class RouterAddress extends DataStructureImpl {
* Makes a copy.
* @param options non-null
* @throws IllegalStateException if was already set
+ * @deprecated unused, use 3-arg constructor
*/
public void setOptions(Properties options) {
if (!_options.isEmpty())
@@ -171,7 +193,7 @@ public class RouterAddress extends DataStructureImpl {
if (_ip != null)
return _ip;
byte[] rv = null;
- String host = _options.getProperty(PROP_HOST);
+ String host = getHost();
if (host != null) {
rv = Addresses.getIP(host);
if (rv != null &&
@@ -183,6 +205,17 @@ public class RouterAddress extends DataStructureImpl {
return rv;
}
+ /**
+ * Convenience, same as getOption("host").
+ * Does no parsing, so faster than getIP().
+ *
+ * @return host string or null
+ * @since IPv6
+ */
+ public String getHost() {
+ return _options.getProperty(PROP_HOST);
+ }
+
/**
* Caching version of Integer.parseInt(getOption("port"))
* Caches valid ports 1-65535 only.
@@ -212,7 +245,7 @@ public class RouterAddress extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_transportStyle != null)
throw new IllegalStateException();
- _cost = (int) DataHelper.readLong(in, 1);
+ _cost = (short) DataHelper.readLong(in, 1);
//_expiration = DataHelper.readDate(in);
DataHelper.readDate(in);
_transportStyle = DataHelper.readString(in);
@@ -229,8 +262,8 @@ public class RouterAddress extends DataStructureImpl {
* readin and the signature will fail.
*/
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
- if ((_cost < 0) || (_transportStyle == null))
- throw new DataFormatException("Not enough data to write a router address");
+ if (_transportStyle == null)
+ throw new DataFormatException("uninitialized");
DataHelper.writeLong(out, 1, _cost);
//DataHelper.writeDate(out, _expiration);
DataHelper.writeDate(out, null);
@@ -238,28 +271,44 @@ public class RouterAddress extends DataStructureImpl {
DataHelper.writeProperties(out, _options);
}
+ /**
+ * Transport, host, and port only.
+ * Never look at cost or other properties.
+ */
@Override
public boolean equals(Object object) {
if (object == this) return true;
if ((object == null) || !(object instanceof RouterAddress)) return false;
RouterAddress addr = (RouterAddress) object;
- // let's keep this fast as we are putting an address into the RouterInfo set frequently
return
- _cost == addr._cost &&
+ getPort() == addr.getPort() &&
+ DataHelper.eq(getHost(), addr.getHost()) &&
DataHelper.eq(_transportStyle, addr._transportStyle);
//DataHelper.eq(_options, addr._options) &&
//DataHelper.eq(_expiration, addr._expiration);
}
+ /**
+ * Everything, including Transport, host, port, options, and cost
+ * @param addr may be null
+ * @since IPv6
+ */
+ public boolean deepEquals(RouterAddress addr) {
+ return
+ equals(addr) &&
+ _cost == addr._cost &&
+ _options.equals(addr._options);
+ }
+
/**
* Just use a few items for speed (expiration is always null).
+ * Never look at cost or other properties.
*/
@Override
public int hashCode() {
return DataHelper.hashCode(_transportStyle) ^
DataHelper.hashCode(getIP()) ^
- getPort() ^
- _cost;
+ getPort();
}
/**
@@ -271,10 +320,10 @@ public class RouterAddress extends DataStructureImpl {
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("[RouterAddress: ");
- buf.append("\n\tTransportStyle: ").append(_transportStyle);
+ buf.append("\n\tType: ").append(_transportStyle);
buf.append("\n\tCost: ").append(_cost);
//buf.append("\n\tExpiration: ").append(_expiration);
- buf.append("\n\tOptions: #: ").append(_options.size());
+ buf.append("\n\tOptions (").append(_options.size()).append("):");
for (Map.Entry e : _options.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
diff --git a/core/java/src/net/i2p/data/RouterInfo.java b/core/java/src/net/i2p/data/RouterInfo.java
index 264495fed..b6234d7ad 100644
--- a/core/java/src/net/i2p/data/RouterInfo.java
+++ b/core/java/src/net/i2p/data/RouterInfo.java
@@ -61,7 +61,7 @@ public class RouterInfo extends DatabaseEntry {
private final Properties _options;
private volatile boolean _validated;
private volatile boolean _isValid;
- private volatile String _stringified;
+ //private volatile String _stringified;
private volatile byte _byteified[];
private volatile int _hashCode;
private volatile boolean _hashCodeInitialized;
@@ -613,30 +613,34 @@ public class RouterInfo extends DatabaseEntry {
@Override
public String toString() {
- if (_stringified != null) return _stringified;
- StringBuilder buf = new StringBuilder(5*1024);
+ //if (_stringified != null) return _stringified;
+ StringBuilder buf = new StringBuilder(1024);
buf.append("[RouterInfo: ");
buf.append("\n\tIdentity: ").append(_identity);
buf.append("\n\tSignature: ").append(_signature);
- buf.append("\n\tPublished on: ").append(new Date(_published));
- buf.append("\n\tAddresses: #: ").append(_addresses.size());
- for (RouterAddress addr : _addresses) {
- buf.append("\n\t\tAddress: ").append(addr);
+ buf.append("\n\tPublished: ").append(new Date(_published));
+ if (_peers != null) {
+ buf.append("\n\tPeers (").append(_peers.size()).append("):");
+ for (Hash hash : _peers) {
+ buf.append("\n\t\tPeer hash: ").append(hash);
+ }
}
- Set peers = getPeers();
- buf.append("\n\tPeers: #: ").append(peers.size());
- for (Hash hash : peers) {
- buf.append("\n\t\tPeer hash: ").append(hash);
- }
- buf.append("\n\tOptions: #: ").append(_options.size());
+ buf.append("\n\tOptions (").append(_options.size()).append("):");
for (Map.Entry e : _options.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
}
+ if (!_addresses.isEmpty()) {
+ buf.append("\n\tAddresses (").append(_addresses.size()).append("):");
+ for (RouterAddress addr : _addresses) {
+ buf.append("\n\t").append(addr);
+ }
+ }
buf.append("]");
- _stringified = buf.toString();
- return _stringified;
+ String rv = buf.toString();
+ //_stringified = rv;
+ return rv;
}
/**
diff --git a/core/java/src/net/i2p/data/Signature.java b/core/java/src/net/i2p/data/Signature.java
index 0e6af4c04..412706302 100644
--- a/core/java/src/net/i2p/data/Signature.java
+++ b/core/java/src/net/i2p/data/Signature.java
@@ -9,6 +9,8 @@ package net.i2p.data;
*
*/
+import net.i2p.crypto.SigType;
+
/**
* Defines the signature as defined by the I2P data structure spec.
* A signature is a 40-byte array verifying the authenticity of some data
@@ -20,19 +22,47 @@ package net.i2p.data;
* @author jrandom
*/
public class Signature extends SimpleDataStructure {
- public final static int SIGNATURE_BYTES = 40;
+ private static final SigType DEF_TYPE = SigType.DSA_SHA1;
+ /** 40 */
+ public final static int SIGNATURE_BYTES = DEF_TYPE.getSigLen();
/** all zeros */
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
+ private final SigType _type;
+
public Signature() {
+ this(DEF_TYPE);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public Signature(SigType type) {
super();
+ _type = type;
}
public Signature(byte data[]) {
- super(data);
+ this(DEF_TYPE, data);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public Signature(SigType type, byte data[]) {
+ super();
+ _type = type;
+ setData(data);
}
public int length() {
- return SIGNATURE_BYTES;
+ return _type.getSigLen();
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigType getType() {
+ return _type;
}
}
diff --git a/core/java/src/net/i2p/data/SigningPrivateKey.java b/core/java/src/net/i2p/data/SigningPrivateKey.java
index 4d804d080..818509b07 100644
--- a/core/java/src/net/i2p/data/SigningPrivateKey.java
+++ b/core/java/src/net/i2p/data/SigningPrivateKey.java
@@ -10,6 +10,7 @@ package net.i2p.data;
*/
import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
/**
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
@@ -20,14 +21,34 @@ import net.i2p.crypto.KeyGenerator;
* @author jrandom
*/
public class SigningPrivateKey extends SimpleDataStructure {
- public final static int KEYSIZE_BYTES = 20;
+ private static final SigType DEF_TYPE = SigType.DSA_SHA1;
+ public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
+
+ private final SigType _type;
public SigningPrivateKey() {
+ this(DEF_TYPE);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigningPrivateKey(SigType type) {
super();
+ _type = type;
}
public SigningPrivateKey(byte data[]) {
- super(data);
+ this(DEF_TYPE, data);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigningPrivateKey(SigType type, byte data[]) {
+ super();
+ _type = type;
+ setData(data);
}
/** constructs from base64
@@ -35,12 +56,20 @@ public class SigningPrivateKey extends SimpleDataStructure {
* on a prior instance of SigningPrivateKey
*/
public SigningPrivateKey(String base64Data) throws DataFormatException {
- super();
+ this();
fromBase64(base64Data);
}
+
public int length() {
- return KEYSIZE_BYTES;
+ return _type.getPrivkeyLen();
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigType getType() {
+ return _type;
}
/** converts this signing private key to its public equivalent
diff --git a/core/java/src/net/i2p/data/SigningPublicKey.java b/core/java/src/net/i2p/data/SigningPublicKey.java
index 5839cf7e8..c1c9445d9 100644
--- a/core/java/src/net/i2p/data/SigningPublicKey.java
+++ b/core/java/src/net/i2p/data/SigningPublicKey.java
@@ -12,6 +12,8 @@ package net.i2p.data;
import java.io.InputStream;
import java.io.IOException;
+import net.i2p.crypto.SigType;
+
/**
* Defines the SigningPublicKey as defined by the I2P data structure spec.
* A signing public key is 128 byte Integer. The public key represents only the
@@ -21,11 +23,14 @@ import java.io.IOException;
* @author jrandom
*/
public class SigningPublicKey extends SimpleDataStructure {
- public final static int KEYSIZE_BYTES = 128;
+ private static final SigType DEF_TYPE = SigType.DSA_SHA1;
+ public final static int KEYSIZE_BYTES = DEF_TYPE.getPubkeyLen();
private static final int CACHE_SIZE = 1024;
private static final SDSCache _cache = new SDSCache(SigningPublicKey.class, KEYSIZE_BYTES, CACHE_SIZE);
+ private final SigType _type;
+
/**
* Pull from cache or return new
* @throws AIOOBE if not enough bytes
@@ -44,11 +49,28 @@ public class SigningPublicKey extends SimpleDataStructure {
}
public SigningPublicKey() {
+ this(DEF_TYPE);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigningPublicKey(SigType type) {
super();
+ _type = type;
}
public SigningPublicKey(byte data[]) {
- super(data);
+ this(DEF_TYPE, data);
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigningPublicKey(SigType type, byte data[]) {
+ super();
+ _type = type;
+ setData(data);
}
/** constructs from base64
@@ -56,11 +78,18 @@ public class SigningPublicKey extends SimpleDataStructure {
* on a prior instance of SigningPublicKey
*/
public SigningPublicKey(String base64Data) throws DataFormatException {
- super();
+ this();
fromBase64(base64Data);
}
public int length() {
- return KEYSIZE_BYTES;
+ return _type.getPubkeyLen();
+ }
+
+ /**
+ * @since 0.9.8
+ */
+ public SigType getType() {
+ return _type;
}
}
diff --git a/core/java/src/net/i2p/data/SimpleDataStructure.java b/core/java/src/net/i2p/data/SimpleDataStructure.java
index 2084955b1..06fe7fb94 100644
--- a/core/java/src/net/i2p/data/SimpleDataStructure.java
+++ b/core/java/src/net/i2p/data/SimpleDataStructure.java
@@ -31,17 +31,13 @@ import net.i2p.crypto.SHA256Generator;
*/
public abstract class SimpleDataStructure extends DataStructureImpl {
protected byte[] _data;
- /** this is just to avoid lots of calls to length() */
- protected final int _length;
/** A new instance with the data set to null. Call readBytes(), setData(), or fromByteArray() after this to set the data */
public SimpleDataStructure() {
- _length = length();
}
/** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */
public SimpleDataStructure(byte data[]) {
- _length = length();
setData(data);
}
@@ -68,8 +64,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public void setData(byte[] data) {
if (_data != null)
throw new RuntimeException("Data already set");
- if (data != null && data.length != _length)
- throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + _length);
+ if (data != null && data.length != length())
+ throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + length());
_data = data;
}
@@ -81,9 +77,10 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_data != null)
throw new RuntimeException("Data already set");
- _data = new byte[_length];
+ int length = length();
+ _data = new byte[length];
int read = read(in, _data);
- if (read != _length) throw new DataFormatException("Not enough bytes to read the data");
+ if (read != length) throw new DataFormatException("Not enough bytes to read the data");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
@@ -109,8 +106,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
byte[] d = Base64.decode(data);
if (d == null)
throw new DataFormatException("Bad Base64 encoded data");
- if (d.length != _length)
- throw new DataFormatException("Bad decoded data length, expected " + _length + " got " + d.length);
+ if (d.length != length())
+ throw new DataFormatException("Bad decoded data length, expected " + length() + " got " + d.length);
// call setData() instead of _data = data in case overridden
setData(d);
}
@@ -141,8 +138,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
@Override
public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in");
- if (data.length != _length)
- throw new DataFormatException("Bad data length: " + data.length + "; required: " + _length);
+ if (data.length != length())
+ throw new DataFormatException("Bad data length: " + data.length + "; required: " + length());
// call setData() instead of _data = data in case overridden
setData(data);
}
@@ -151,12 +148,13 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append('[').append(getClass().getSimpleName()).append(": ");
+ int length = length();
if (_data == null) {
buf.append("null");
- } else if (_length <= 32) {
+ } else if (length <= 32) {
buf.append(toBase64());
} else {
- buf.append("size: ").append(Integer.toString(_length));
+ buf.append("size: ").append(Integer.toString(length));
}
buf.append(']');
return buf.toString();
diff --git a/core/java/src/net/i2p/util/Addresses.java b/core/java/src/net/i2p/util/Addresses.java
index e54f93a02..77e4759fa 100644
--- a/core/java/src/net/i2p/util/Addresses.java
+++ b/core/java/src/net/i2p/util/Addresses.java
@@ -34,7 +34,7 @@ public abstract class Addresses {
return !getAddresses(true, false, false).isEmpty();
}
- /** @return the first non-local address it finds, or null */
+ /** @return the first non-local address IPv4 address it finds, or null */
public static String getAnyAddress() {
SortedSet a = getAddresses();
if (!a.isEmpty())
@@ -95,7 +95,7 @@ public abstract class Addresses {
haveIPv6 = true;
if (shouldInclude(allMyIps[i], includeSiteLocal,
includeLoopbackAndWildcard, includeIPv6))
- rv.add(allMyIps[i].getHostAddress());
+ rv.add(stripScope(allMyIps[i].getHostAddress()));
}
}
} catch (UnknownHostException e) {}
@@ -113,7 +113,7 @@ public abstract class Addresses {
haveIPv6 = true;
if (shouldInclude(addr, includeSiteLocal,
includeLoopbackAndWildcard, includeIPv6))
- rv.add(addr.getHostAddress());
+ rv.add(stripScope(addr.getHostAddress()));
}
}
}
@@ -128,6 +128,17 @@ public abstract class Addresses {
return rv;
}
+ /**
+ * Strip the trailing "%nn" from Inet6Address.getHostAddress()
+ * @since IPv6
+ */
+ private static String stripScope(String ip) {
+ int pct = ip.indexOf("%");
+ if (pct > 0)
+ ip = ip.substring(0, pct);
+ return ip;
+ }
+
private static boolean shouldInclude(InetAddress ia, boolean includeSiteLocal,
boolean includeLoopbackAndWildcard, boolean includeIPv6) {
return
@@ -137,7 +148,10 @@ public abstract class Addresses {
((!ia.isAnyLocalAddress()) &&
(!ia.isLoopbackAddress()))) &&
(includeSiteLocal ||
- !ia.isSiteLocalAddress()) &&
+ ((!ia.isSiteLocalAddress()) &&
+ // disallow fc00::/8 and fd00::/8 (Unique local addresses RFC 4193)
+ // not recognized as local by InetAddress
+ (ia.getAddress().length != 16 || (ia.getAddress()[0] & 0xfe) != 0xfc))) &&
// Hamachi 5/8 allocated to RIPE (30 November 2010)
// Removed from TransportImpl.isPubliclyRoutable()
// Check moved to here, for now, but will eventually need to
diff --git a/core/java/src/net/i2p/util/FortunaRandomSource.java b/core/java/src/net/i2p/util/FortunaRandomSource.java
index 2209785cf..54a93b2a0 100644
--- a/core/java/src/net/i2p/util/FortunaRandomSource.java
+++ b/core/java/src/net/i2p/util/FortunaRandomSource.java
@@ -262,27 +262,23 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
}
}
-/*****
+ /**
+ * Outputs to stdout for dieharder:
+ *
+ * java -cp build/i2p.jar net.i2p.util.FortunaRandomSource | dieharder -a -g 200
+ *
+ */
public static void main(String args[]) {
try {
- RandomSource rand = I2PAppContext.getGlobalContext().random();
- if (true) {
- for (int i = 0; i < 1000; i++)
- if (rand.nextFloat() < 0)
- throw new RuntimeException("negative!");
- System.out.println("All positive");
- return;
+ java.util.Properties props = new java.util.Properties();
+ props.setProperty("prng.buffers", "12");
+ I2PAppContext ctx = new I2PAppContext(props);
+ RandomSource rand = ctx.random();
+ byte[] buf = new byte[65536];
+ while (true) {
+ rand.nextBytes(buf);
+ System.out.write(buf);
}
- java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
- java.util.zip.GZIPOutputStream gos = new java.util.zip.GZIPOutputStream(baos);
- for (int i = 0; i < 1024*1024; i++) {
- int c = rand.nextInt(256);
- gos.write((byte)c);
- }
- gos.finish();
- byte compressed[] = baos.toByteArray();
- System.out.println("Compressed size of 1MB: " + compressed.length);
} catch (Exception e) { e.printStackTrace(); }
}
-*****/
}
diff --git a/core/java/src/net/i2p/util/I2PThread.java b/core/java/src/net/i2p/util/I2PThread.java
index 477568f69..ac52b96d6 100644
--- a/core/java/src/net/i2p/util/I2PThread.java
+++ b/core/java/src/net/i2p/util/I2PThread.java
@@ -92,8 +92,12 @@ public class I2PThread extends Thread {
t.printStackTrace();
}
****/
- if (t instanceof OutOfMemoryError)
+ if (t instanceof OutOfMemoryError) {
fireOOM((OutOfMemoryError)t);
+ } else {
+ System.out.println ("Thread terminated unexpectedly: " + getName());
+ t.printStackTrace();
+ }
}
// This creates a new I2PAppContext after it was deleted
// in Router.finalShutdown() via RouterContext.killGlobalContext()
diff --git a/core/java/src/net/i2p/util/RandomSource.java b/core/java/src/net/i2p/util/RandomSource.java
index 408af8f7b..4f97c7d47 100644
--- a/core/java/src/net/i2p/util/RandomSource.java
+++ b/core/java/src/net/i2p/util/RandomSource.java
@@ -176,8 +176,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
SecureRandom.getInstance("SHA1PRNG").nextBytes(buf);
ok = true;
} catch (NoSuchAlgorithmException e) {}
- // why urandom? because /dev/random blocks, and there are arguments
- // suggesting such blockages are largely meaningless
+ // why urandom? because /dev/random blocks
ok = seedFromFile(new File("/dev/urandom"), buf) || ok;
// we merge (XOR) in the data from /dev/urandom with our own seedfile
File localFile = new File(_context.getConfigDir(), SEEDFILE);
@@ -186,6 +185,8 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
}
/**
+ * XORs the seed into buf
+ *
* @param f absolute path
* @return success
*/
diff --git a/core/java/src/net/i2p/util/Translate.java b/core/java/src/net/i2p/util/Translate.java
index 05d345c95..cd57be8e5 100644
--- a/core/java/src/net/i2p/util/Translate.java
+++ b/core/java/src/net/i2p/util/Translate.java
@@ -1,6 +1,7 @@
package net.i2p.util;
import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
@@ -64,43 +65,33 @@ public abstract class Translate {
* Use autoboxing to call with ints, longs, floats, etc.
*/
public static String getString(String s, Object o, I2PAppContext ctx, String bun) {
- String lang = getLanguage(ctx);
- if (lang.equals(TEST_LANG))
- return TEST_STRING + '(' + o + ')' + TEST_STRING;
- String x = getString(s, ctx, bun);
- Object[] oArray = new Object[1];
- oArray[0] = o;
- try {
- MessageFormat fmt = new MessageFormat(x, new Locale(lang));
- return fmt.format(oArray, new StringBuffer(), null).toString();
- } catch (IllegalArgumentException iae) {
- System.err.println("Bad format: orig: \"" + s +
- "\" trans: \"" + x +
- "\" param: \"" + o +
- "\" lang: " + lang);
- return "FIXME: " + x + ' ' + o;
- }
+ return getString(s, ctx, bun, o);
}
/** for {0} and {1} */
public static String getString(String s, Object o, Object o2, I2PAppContext ctx, String bun) {
+ return getString(s, ctx, bun, o, o2);
+ }
+
+ /**
+ * Varargs
+ * @param oArray parameters
+ * @since 0.9.8
+ */
+ public static String getString(String s, I2PAppContext ctx, String bun, Object... oArray) {
String lang = getLanguage(ctx);
if (lang.equals(TEST_LANG))
- return TEST_STRING + '(' + o + ',' + o2 + ')' + TEST_STRING;
+ return TEST_STRING + Arrays.toString(oArray) + TEST_STRING;
String x = getString(s, ctx, bun);
- Object[] oArray = new Object[2];
- oArray[0] = o;
- oArray[1] = o2;
try {
MessageFormat fmt = new MessageFormat(x, new Locale(lang));
return fmt.format(oArray, new StringBuffer(), null).toString();
} catch (IllegalArgumentException iae) {
System.err.println("Bad format: orig: \"" + s +
"\" trans: \"" + x +
- "\" param1: \"" + o +
- "\" param2: \"" + o2 +
- "\" lang: " + lang);
- return "FIXME: " + x + ' ' + o + ',' + o2;
+ "\" params: " + Arrays.toString(oArray) +
+ " lang: " + lang);
+ return "FIXME: " + x + ' ' + Arrays.toString(oArray);
}
}
diff --git a/core/java/src/net/i2p/util/TranslateReader.java b/core/java/src/net/i2p/util/TranslateReader.java
new file mode 100644
index 000000000..f9b89429f
--- /dev/null
+++ b/core/java/src/net/i2p/util/TranslateReader.java
@@ -0,0 +1,449 @@
+package net.i2p.util;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.i2p.I2PAppContext;
+
+/**
+ * Translate.
+ *
+ * Strings are tagged with _("translateme")
+ * or _("translate {0} me", "foo")
+ *
+ * Max two parameters.
+ * String and parameters must be double-quoted (no ngettext, no tagged parameters).
+ * Escape quotes inside quote with \".
+ * Commas and spaces between args are optional.
+ * Entire tag (from '_' to ')') must be on one line.
+ * Multiple tags allowed on one line.
+ *
+ * Also will extract strings to a dummy java file for postprocessing by xgettext - see main().
+ *
+ * @since 0.9.8
+ */
+public class TranslateReader extends FilterReader {
+
+ /** all states may transition to START */
+ private enum S {
+ START,
+ /** next state LPAREN */
+ UNDER,
+ /** next state QUOTE */
+ LPAREN,
+ /** next state LPAREN or BACK */
+ QUOTE,
+ /** next state QUOTE */
+ BACK
+ }
+
+ private final String _bundle;
+ private final I2PAppContext _ctx;
+ /** parse in progress */
+ private final StringBuilder _inBuf;
+ /** parsed and translated */
+ private final StringBuilder _outBuf;
+ /** pending string or parameter for translation */
+ private final StringBuilder _argBuf;
+ /** parsed string and parameters */
+ private final List _args;
+ private S _state = S.START;
+ private TagHook _hook;
+
+ private static final int MAX_ARGS = 9;
+
+ /**
+ * @param bundle may be null for tagging only
+ * @param in UTF-8
+ */
+ public TranslateReader(I2PAppContext ctx, String bundle, InputStream in) throws IOException {
+ super(new BufferedReader(new InputStreamReader(in, "UTF-8")));
+ _ctx = ctx;
+ _bundle = bundle;
+ _args = new ArrayList(4);
+ _inBuf = new StringBuilder(64);
+ _outBuf = new StringBuilder(64);
+ _argBuf = new StringBuilder(64);
+ }
+
+ @Override
+ public int read() throws IOException {
+ int rv = popit();
+ if (rv > 0)
+ return rv;
+ return parse();
+ }
+
+ private int parse() throws IOException {
+ while (true) {
+ int c = in.read();
+ if (c >= 0)
+ pushit((char) c);
+ //System.err.println("State: " + _state + " char: '" + ((char)c) + "'");
+
+ switch (c) {
+ case -1:
+ case '\r':
+ case '\n':
+ return flushit();
+
+ case '_':
+ switch (_state) {
+ case START:
+ _state = S.UNDER;
+ break;
+ case BACK:
+ _state = S.QUOTE;
+ // fall thru
+ case QUOTE:
+ _argBuf.append((char) c);
+ break;
+ default:
+ return flushit();
+ }
+ break;
+
+ case '(':
+ switch (_state) {
+ case UNDER:
+ _args.clear();
+ _state = S.LPAREN;
+ break;
+ case BACK:
+ _state = S.QUOTE;
+ // fall thru
+ case QUOTE:
+ _argBuf.append((char) c);
+ break;
+ default:
+ return flushit();
+ }
+ break;
+
+ case '"':
+ switch (_state) {
+ case LPAREN:
+ // got an opening quote for a parameter
+ if (_args.size() >= MAX_ARGS)
+ return flushit();
+ _argBuf.setLength(0);
+ _state = S.QUOTE;
+ break;
+ case BACK:
+ _argBuf.append((char) c);
+ _state = S.QUOTE;
+ break;
+ case QUOTE:
+ // got a closing quote for a parameter
+ _args.add(_argBuf.toString());
+ _state = S.LPAREN;
+ break;
+ default:
+ return flushit();
+ }
+ break;
+
+ case '\\':
+ switch (_state) {
+ case QUOTE:
+ _state = S.BACK;
+ break;
+ case BACK:
+ _argBuf.append((char) c);
+ _state = S.QUOTE;
+ break;
+ default:
+ return flushit();
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ case ',':
+ switch (_state) {
+ case BACK:
+ _state = S.QUOTE;
+ // fall thru
+ case QUOTE:
+ _argBuf.append((char) c);
+ break;
+ case LPAREN:
+ // ignore whitespace and commas between args
+ break;
+ default:
+ return flushit();
+ }
+ break;
+
+ case ')':
+ switch (_state) {
+ case BACK:
+ _state = S.QUOTE;
+ // fall thru
+ case QUOTE:
+ _argBuf.append((char) c);
+ break;
+ case LPAREN:
+ // Finally, we have something to translate!
+ translate();
+ return popit();
+ default:
+ return flushit();
+ }
+ break;
+
+ default:
+ switch (_state) {
+ case BACK:
+ _state = S.QUOTE;
+ // fall thru
+ case QUOTE:
+ _argBuf.append((char) c);
+ break;
+ default:
+ return flushit();
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ 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;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ for (long i = 0; i < n; i++) {
+ int c = read();
+ if (c < 0) {
+ if (i == 0)
+ return -1;
+ return i;
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public boolean ready() throws IOException {
+ return _outBuf.length() > 0 || _inBuf.length() > 0 ||in.ready();
+ }
+
+ @Override
+ public void close() throws IOException {
+ _inBuf.setLength(0);
+ _outBuf.setLength(0);
+ _state = S.START;
+ in.close();
+ }
+
+ @Override
+ public void mark(int readLimit) {}
+
+ @Override
+ public void reset() throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ /**
+ * put in the pending parse buf
+ */
+ private void pushit(char c) {
+ _inBuf.append(c);
+ }
+
+ /**
+ * flush _inBuf to _outBuf,
+ * reset state,
+ * and return next char or -1
+ */
+ private int flushit() {
+ _state = S.START;
+ if (_inBuf.length() > 0) {
+ _outBuf.append(_inBuf);
+ _inBuf.setLength(0);
+ }
+ return popit();
+ }
+
+ /**
+ * return next char from _outBuf or -1
+ */
+ private int popit() {
+ if (_outBuf.length() > 0) {
+ int rv = _outBuf.charAt(0) & 0xffff;
+ _outBuf.deleteCharAt(0);
+ return rv;
+ }
+ return -1;
+ }
+
+ /**
+ * clear _inBuf, translate _args to _outBuf,
+ * reset state
+ */
+ private void translate() {
+ //System.err.println("Translating: " + _args.toString());
+ int argCount = _args.size();
+ if (argCount <= 0 || argCount > MAX_ARGS) {
+ flushit();
+ return;
+ }
+ _state = S.START;
+ _inBuf.setLength(0);
+ if (_hook != null) {
+ _hook.tag(_args);
+ return;
+ }
+ String tx = null;
+ if (argCount == 1)
+ tx = Translate.getString(_args.get(0), _ctx, _bundle);
+ else
+ tx = Translate.getString(_args.get(0), _ctx, _bundle, _args.subList(1, _args.size()).toArray());
+ _outBuf.append(tx);
+ }
+
+ private interface TagHook extends Closeable {
+ public void tag(List args);
+ }
+
+ private static class Tagger implements TagHook {
+ private final PrintStream _out;
+ private final String _name;
+ private int _count;
+
+ public Tagger(String file) throws IOException {
+ _name = file;
+ _out = new PrintStream(file, "UTF-8");
+ _out.println("// Automatically generated, do not edit");
+ _out.println("package dummy;");
+ _out.println("class Dummy {");
+ _out.println(" void dummy() {");
+ }
+
+ public void tag(List args) {
+ if (args.size() <= 0)
+ return;
+ _out.print("\t_(");
+ for (int i = 0; i < args.size(); i++) {
+ if (i > 0)
+ _out.print(", ");
+ _out.print('"');
+ _out.print(args.get(i).replace("\"", "\\\""));
+ _out.print('"');
+ }
+ _out.println(");");
+ _count++;
+ }
+
+ public void close() throws IOException {
+ _out.println(" }");
+ _out.println("}");
+ if (_out.checkError())
+ throw new IOException();
+ _out.close();
+ System.out.println(_count + " strings written to " + _name);
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ if (args.length >= 2 && args[0].equals("test"))
+ test(args[1]);
+ else if (args.length >= 2 && args[0].equals("tag"))
+ tag(args);
+ else
+ System.err.println("Usage:\n" +
+ "\ttest file (output to stdout)\n" +
+ "\ttag file (output to file.java)\n" +
+ "\ttag dir outfile\n" +
+ "\ttag file1 [file2...] outfile");
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ private static void test(String file) throws IOException {
+ TranslateReader r = new TranslateReader(I2PAppContext.getGlobalContext(),
+ "net.i2p.router.web.messages",
+ new FileInputStream(file));
+ int c;
+ while ((c = r.read()) >= 0) {
+ System.out.print((char)c);
+ }
+ System.out.flush();
+ }
+
+ /** @param files ignore 0 */
+ private static void tag(String[] files) throws IOException {
+ char[] buf = new char[256];
+ String outfile;
+ List filelist;
+ if (files.length == 2) {
+ outfile = files[1] + ".java";
+ filelist = Collections.singletonList(files[1]);
+ } else if (files.length == 3 && (new File(files[1])).isDirectory()) {
+ outfile = files[2];
+ File dir = new File(files[1]);
+ File[] listing = dir.listFiles();
+ if (listing == null)
+ throw new IOException();
+ filelist = new ArrayList(listing.length);
+ for (int i = 0; i < listing.length; i++) {
+ File f = listing[i];
+ if (!f.isDirectory())
+ filelist.add(f.getAbsolutePath());
+ }
+ } else {
+ outfile = files[files.length - 1];
+ filelist = Arrays.asList(files).subList(1, files.length - 1);
+ }
+ TagHook tagger = null;
+ try {
+ tagger = new Tagger(outfile);
+ for (String file : filelist) {
+ TranslateReader r = null;
+ try {
+ r = new TranslateReader(I2PAppContext.getGlobalContext(),
+ null,
+ new FileInputStream(file));
+ r._hook = tagger;
+ while (r.read(buf, 0, buf.length) >= 0) {
+ // throw away output
+ }
+ } finally {
+ if (r != null) r.close();
+ }
+ }
+ } finally {
+ if (tagger != null) tagger.close();
+ }
+ }
+}
diff --git a/core/java/test/junit/net/i2p/data/RouterAddressTest.java b/core/java/test/junit/net/i2p/data/RouterAddressTest.java
index 16f7f1118..1dd4896fd 100644
--- a/core/java/test/junit/net/i2p/data/RouterAddressTest.java
+++ b/core/java/test/junit/net/i2p/data/RouterAddressTest.java
@@ -102,6 +102,6 @@ public class RouterAddressTest extends StructureTest {
addr.setTransportStyle("Blah");
String ret = addr.toString();
//assertEquals("[RouterAddress: \n\tTransportStyle: Blah\n\tCost: 42\n\tExpiration: Fri Jan 02 00:00:00 UTC 1970\n\tOptions: #: 2\n\t\t[hostname] = [localhost]\n\t\t[portnum] = [1234]]", ret);
- assertEquals("[RouterAddress: \n\tTransportStyle: Blah\n\tCost: 42\n\tOptions: #: 2\n\t\t[hostname] = [localhost]\n\t\t[portnum] = [1234]]", ret);
+ assertEquals("[RouterAddress: \n\tType: Blah\n\tCost: 42\n\tOptions (2):\n\t\t[hostname] = [localhost]\n\t\t[portnum] = [1234]]", ret);
}
}
diff --git a/debian/changelog b/debian/changelog
index 2ed9b0a33..98cbaab8c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,24 @@
-i2p (0.9.7-0) unstable; urgency=low
+i2p (0.9.7-3) UNRELEASED; urgency=low
+
+ * debian/rules: compress with xz
+
+ -- Kill Your TV Thu, 01 Aug 2013 23:26:52 +0000
+
+i2p (0.9.7-2) unstable; urgency=low
+
+ * Bugfix: Add missing ; to i2prouter script.
+ * Test scripts with "sh -n" in the build target
+
+ -- Kill Your TV Tue, 16 Jul 2013 11:08:48 +0000
+
+i2p (0.9.7-1) unstable; urgency=low
* New upstream release
+ * debconf:
+ - New Chinese, Portuguese, and Turkish translations
+ - Russian translation updates
- -- Kill Your TV Mon, 15 Jul 2013 00:00:00 +0000
+ -- Kill Your TV Mon, 15 Jul 2013 19:35:09 +0000
i2p (0.9.6-1) unstable; urgency=low
diff --git a/debian/patches/0001-path-substitution.patch b/debian/patches/0001-path-substitution.patch
index 68d4be8cb..81a336fa1 100644
--- a/debian/patches/0001-path-substitution.patch
+++ b/debian/patches/0001-path-substitution.patch
@@ -158,7 +158,7 @@ Debian wrapper.config to try to prevent confusion.
exit 1
fi
}
-@@ -1866,24 +1809,9 @@
+@@ -1866,24 +1809,10 @@
status
;;
@@ -183,6 +183,7 @@ Debian wrapper.config to try to prevent confusion.
+ 'install' | 'remove' | 'uninstall')
+ echo "Use \"dpkg-reconfigure i2p\" to configure the initscript."
+ exit 1
++ ;;
'dump')
checkUser "" "$COMMAND"
diff --git a/debian/rules b/debian/rules
index d5f9b339f..9cab2018c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -62,6 +62,8 @@ ifeq "$(findstring deb,$(shell grep 'deb' $(ROUTERVERSION)))" ""
else
@echo "Already found deb in version, not doing anything"
endif
+ sh -n installer/resources/i2prouter || exit 1
+ sh -n installer/resources/runplain.sh || exit 1
TZ=UTC ant preppkg-unix javadoc
echo router.updateDisabled=true > $(I2P)/router.config
mv $(I2P)/runplain.sh $(I2P)/i2prouter-nowrapper
@@ -104,7 +106,7 @@ override_dh_dhlibdeps:
# dh_installinit -r
override_dh_builddeb:
- dh_builddeb -- -Zbzip2
+ dh_builddeb -- -Zxz
get-orig-source:
uscan --verbose --rename --destdir $(CURDIR)/.. --watchfile debian/watch --force-download
diff --git a/history.txt b/history.txt
index 8f3cc96b0..1151e0422 100644
--- a/history.txt
+++ b/history.txt
@@ -1,16 +1,230 @@
-* 2013-08-10 0.9.7.1 released
+2012-07-30 zzz
+ * i2psnark: Increase max piece size to 4 MB (ticket #993)
+ * Tunnels: Extend local expiration of IB tunnels, to allow for
+ more clock skew and not drop valid messages at their destination
-2013-08-08 zzz
- * New update hosts, thx Meeh and dg
- * Updater: Increase update-via-torrent to 30%
- * Plucks from trunk for 0.9.7.1:
- - Disable RI verifies
- - Extend IB tunnel expiration
- - i2prouter: bashism fix
- - i2psnark: increase max piece size, mime type updates
- - New reseed host
- - Streaming: RTO changes
- - UPnP fix for some hardware
+2012-07-29 zzz
+ * Signatures:
+ - Prep for new signature algorithms; new SigType enum;
+ Signature, SigningPublicKey, SigningPrivateKey store type
+ - New Hash384 and Hash512 classes
+ - Remove length field in SimpleDataStructure
+ - New SU3File generator/verifier/extractor
+ * GeoIPv6: Fix NPE (ticket #992)
+
+2012-07-28 zzz
+ * Addresses: Treat RFC 4193 addresses fc00::/7 as local
+ * NetDB: Disable RI verifies for now
+
+2012-07-26 zzz
+ * Transports:
+ - Fix IPv6-only option 2nd try
+ - Treat RFC 4193 addresses fc00::/7 as local
+
+2012-07-25 zzz
+ * Transports:
+ - Prefer IPv6 by default
+ - Fix IPv6-only option
+ - Don't try NTCP IPv6 addresses unless we have one
+ - Fix non-%16 SSU padding; enable by default
+ * Tunnels:
+ - Make expl. default 3 hops (ticket #966)
+ - Allow expl. fallback up to -2 hops
+
+2012-07-24 zzz
+ * GeoIP: Fix lookups, broken in IPv6 branch
+ * UPnP:
+ - main() tweaks for debugging
+ - Fix for devices that return base URLs with trailing '/' (ticket #986)
+
+2012-07-22 zzz
+ * BuildReplyHandler:
+ - Make non-static
+ - Don't pollute Hash cache with build response record hashes
+ - Use SimpleByteCache
+ - cleanups
+ * HashComparator:
+ - Don't pollute Hash cache with hashes
+ - cleanups
+
+2012-07-21 zzz
+ * I2CP:
+ - Don't NPE when loglevel=warn
+ - Don't enforce property length limits when inside router JVM
+ * I2PSnark: Log uncaught error in ThreadedStarter to router log
+ * I2PThread: Log uncaught error to wrapper log
+
+2012-07-20 zzz
+ Prop from i2p.i2p.zzz.ipv6:
+ * Addresses:
+ - blocklist 192.88.88.0/24 6to4 anycast
+ - invalidate 2002::/16
+ - Strip scope from returned IPv6 addresses
+ * Blocklist:
+ - Add IPv6 in-memory single list
+ - Limit in-memory single list size
+ - Fix dup check in getAddresses()
+ * CSFI: Pass TransportManager instead of CSFI to GetBidsJob; remove unused methods
+ * Console:
+ - Add IPv6 column on /peers
+ - Other minor /peers cleanup
+ * FloodfillPeerSelector, ProfileOrganizer: Use 8 bytes for IPv6 check
+ * GeoIP:
+ - Use cached IP in RouterAddresses
+ - Use both NTCP and SSU addresses
+ * GeoIPv6:
+ - Handle gzipped input data in merge tool
+ - Add script to generate compressed data
+ - Add local additions
+ - Add compressed data file, generated from Maxmind data fetched 2013-05-24
+ - Include data in installer and updater
+ - Update Maxmind license info, now CC-SA 3.0
+ - Enable GeoIPv6 lookup in CSFI
+ * NetDb:
+ - Add floodfillEnabled() to NetworkDatabaseFacade so callers don't
+ have to cast to FNDF, and Dummy NDF will work
+ - Remove static FNDF.floodfillEnabled()
+ * NTCP:
+ - Move SSU address notification handling from CSFI to NTCPTransport
+ - Drop NTCPAddress, just use RouterAddress
+ - Drop _myAddress field, use super's currentAddress
+ - Bad bind config not fatal
+ - Fix publishing address when host specified but interface is not
+ - Republish even if only changing cost
+ * PacketPusher: Minor optimization
+ * Peer tests:
+ - Use only IPv4 peer for Alice and Bob in peer tests; Charlie may be an IPv6 peer.
+ - Enforce IPv4 (Alice's) address inside PeerTest packet
+ * RouterAddress and RouterInfo toString() cleanups; don't cache RI in _stringified
+ * RouterAddress:
+ - hashCode/equals tweaks
+ - Add new constructor
+ - Add add getHost() and deepEquals()
+ - Compare host string, not IP, in equals()
+ - Deprecate some setters
+ - Add warning about setCost()
+ - Change cost storage from int to short
+ - Cost range checks
+ * RouterContext: Fix disabling client manager with i2p.dummyClientFacade=true
+ * SSU:
+ - Change from single UDPEndpoint to a List of UDPEndpoints
+ - Move (single) receive queue from UDPReceiver to PacketHandler
+ - Multiple transmit queues (one for each UDPEndpoint/UDPSender),
+ select queue in PacketPusher
+ - Throw exception on UDPEndpoint.startup() failure
+ - Remove all _external* fields; use _currentAddresses in super
+ - Big rework of externalAddressReceived(), rebuildExternalAddress(),
+ needsRebuild(), and replaceAddress() for multiple addresses and IPv6
+ - Add caching in UDPAddress
+ - More IPv6 flavors of utility methods
+ - Remove two-arg replaceAddress()
+ - Fixes for i2np.udp.allowLocal, log tweaks, sender/receiver thread name tweaks
+ - Handle IPv6 in too-close checks
+ - IPv6 SSU MTU adjustments
+ - Hash IPv6 addresses in IPThrottler
+ * SSU Introduction:
+ - Document that Alice-Bob RelayRequest/RelayResponse may be IPv4 or IPv6,
+ but don't implement IPv6 yet.
+ Changes required in IntroductionManager and PacketBuilder to send Alice's
+ IPv4 address in the RelayRequest packet over IPv6, and to publish
+ IPv6 introducer IPs.
+ - Bob-Charlie RelayIntro must be IPv4
+ - Only offer/accept relay tags as Bob or Charlie if the Bob-Charlie session is IPv4
+ - Alice-Charlie communication must be IPv4
+ * SSUDemo:
+ - Configuration fixes, delete RI on exit, log tweaks, cleanups
+ - Use IPv6; don't use System properties
+ - Adapt for NTCP testing too
+ * Tests:
+ - New buildTest and prepTest targets
+ - Fix UDPEndpoint usage in unit tests: Restore receive(); Handle null UDPTransport
+ - Fix UDPEndpointTestStandalone: init context; Add unit test buildPacket() method to PacketBuilder;
+ Fix NPE in MessageHistory
+ * Transports:
+ - Add new TransportUtil for getting/setting IPv6 config
+ - Prep for supporting multiple RouterAddresses per-transport
+ - Prep for multiple address discovery
+ - Fix multiple-detection code in externalAddressReceived()
+ - Synchronize tracking of last IP/port
+ - Don't accept IPv6 address changes from peers
+ - Remove unused getLocalAddress()
+ - Pkg private getLocalPort()
+ - Start transports in a standard order to make testing easier
+ - When transports learn of interface addresses before being started, save them
+ and use them at startup
+ - Pick SSU random port before startListening() and have the TransportManager
+ pass it to NTCP before starting
+ - Only restart NTCP after changing addresses when necessary;
+ prevent thrashing at startup (ticket #459)
+ - Always bind NTCP if we have port; not just when not firewalled
+ - Only call rebuildRouterInfo() once at startup
+ - More checking of min/max SSU port config
+ - Invalid SSU bind config no longer fatal
+ - Allow "true" for ipv6 config
+ - SSU fixes for per-address and IPv6 MTU
+ - MTU.main() print all interfaces
+ - Add i2np.disable property for testing
+ * Transports:
+ - Sort multiple peer addresses by cost, with adjustment for local IPv6 preference
+ - Add default IPv6Config for ease of changing later
+ - Only treat IPv6 addresses as valid if we have a public IPv6 address
+ * Tunnel GW pumper: Limit threads when testing
+ * UDPAddress:
+ - Reduce object churn, check intro key length,
+ don't look for ihost3, reject ports < 1024, cleanups
+ - Remove dependency in console; make package private
+ * UPnP:
+ - Fix UPnP address received before startListening(), broken by isAlive() check
+ - Pass device IP back in forward port callback
+ - Only declare success if forwarded IP is public
+
+2012-07-20 zzz
+ Prop from i2p.i2p.zzz.test2:
+ * DHSessionKeyBuilder: Fix for session and mac keys if DH key is between 32 and 63 bytes.
+ Was: NPE. Now: mac key is hash of session key. Won't ever happen. (Ticket #963)
+ * I2CP/I2PTunnel locking fixes (partial fixes for tickets 650. 722, 815, 946, 947, 953):
+ - I2PSocketManagerFactory: New createDisconnectedManager(), javadocs
+ - I2PSessionImpl: Rewrite state management and locking, prevent multiple
+ connect() calls, but allow disconnect() to interrupt connect()
+ - I2PSimpleSession: Changes to match I2PSessionImpl
+ - I2PTunnelServer: Don't connect in constructor, use createDisconnectedManager()
+ for a final manager, finals and cleanups
+ * ClientManager:
+ - Make classes extensible for router-side test stubs
+ - Add router-side local-only test implementation, no full router required.
+ Only tested with external clients, probably doesn't work in-JVM.
+ - Don't start threads in ClientManager constructor
+ - Remove unused Reader param in ClientMessageEventListener methods
+ - Cleanups, volatiles, finals, javadocs
+ * Translations:
+ - New varargs method in Translate
+ - New TranslateReader to translate static files on-the-fly using _("") tagging
+ - Tag proxy error pages for TranslateReader
+ - Drop static translated proxy error pages
+ - Add new i2ptunnel bundle for the proxy alone, shipped in the jar.
+ - Use TranslateReader in HTTP proxy for error pages
+ - Move HTTP proxy strings from the web (war) to proxy (jar) bundle so they will be loaded
+ Fixes translations for the lower half of the proxy pages that weren't accessible in the war.
+ - Initial translations created by msgmerge from i2ptunnel and routerconsole po files, un-fuzzied
+ - Tag initialNews.xml for TranslateReader
+ - Drop static translated initialNews pages
+ - Add new routerconsole bundle for the news alone, shipped in the jar
+ - Use TranslateReader in HTTP proxy for initial news
+ - Initial translations created manually from translated xml filesx
+ - Add translated proxy error pages and initial news to deletelist.txt
+ * Transports: Consolidate translation methods in TransportImpl;
+ ngettext fix for "peers" string
+
+
+2012-07-19 zzz
+ * PRNG:
+ - Don't delay the refiller if we need more (don't limit max output)
+ - Add FortunaRandomSource.main() to output to stdout for testing e.g. with dieharder
+
+2012-07-19 zab
+ * Streaming:
+ - initialize streaming RTT from sample, trac #979, RFC 6298
+ - store rttDev in TCBCache
* 2013-07-15 0.9.7 released
diff --git a/installer/resources/blocklist.txt b/installer/resources/blocklist.txt
index 9f9dde18f..02440c58e 100644
--- a/installer/resources/blocklist.txt
+++ b/installer/resources/blocklist.txt
@@ -46,6 +46,7 @@ Friend of the Chinese Floodfill Flooder:159.226.40.3
The Team Cymru Bogon List v6.8 03 FEB 2011:172.16.0.0/12
The Team Cymru Bogon List v6.8 03 FEB 2011:192.0.0.0/24
The Team Cymru Bogon List v6.8 03 FEB 2011:192.0.2.0/24
+6to4 Anycast:192.88.99.0/24
The Team Cymru Bogon List v6.8 03 FEB 2011:192.168.0.0/16
The Team Cymru Bogon List v6.8 03 FEB 2011:198.18.0.0/15
The Team Cymru Bogon List v6.8 03 FEB 2011:198.51.100.0/24
diff --git a/installer/resources/deletelist.txt b/installer/resources/deletelist.txt
index 52975081f..8ff0ead3b 100644
--- a/installer/resources/deletelist.txt
+++ b/installer/resources/deletelist.txt
@@ -4,3 +4,67 @@ certificates/r31453.ovh.net
certificates/75.145.125.59.crt
certificates/forum.i2p2.de.crt
certificates/cowpuncher.drollette.com.crt
+# old translated proxy error pages
+docs/ahelper-conflict-header_ar.ht
+docs/ahelper-conflict-header_de.ht
+docs/ahelper-conflict-header_fr.ht
+docs/ahelper-conflict-header_nl.ht
+docs/ahelper-conflict-header_ru.ht
+docs/ahelper-conflict-header_zh.ht
+docs/auth-header_de.ht
+docs/auth-header_fr.ht
+docs/denied-header_ar.ht
+docs/denied-header_de.ht
+docs/denied-header_fr.ht
+docs/denied-header_nl.ht
+docs/denied-header_ru.ht
+docs/denied-header_zh.ht
+docs/dnf-header_de.ht
+docs/dnf-header_fr.ht
+docs/dnf-header_nl.ht
+docs/dnf-header_ru.ht
+docs/dnf-header_zh.ht
+docs/dnfb-header_ar.ht
+docs/dnfb-header_de.ht
+docs/dnfb-header_fr.ht
+docs/dnfb-header_nl.ht
+docs/dnfb-header_ru.ht
+docs/dnfb-header_zh.ht
+docs/dnfh-header_de.ht
+docs/dnfh-header_fr.ht
+docs/dnfh-header_nl.ht
+docs/dnfh-header_ru.ht
+docs/dnfh-header_zh.ht
+docs/dnfp-header_ar.ht
+docs/dnfp-header_de.ht
+docs/dnfp-header_fr.ht
+docs/dnfp-header_nl.ht
+docs/dnfp-header_ru.ht
+docs/dnfp-header_zh.ht
+docs/localhost-header_ar.ht
+docs/localhost-header_de.ht
+docs/localhost-header_fr.ht
+docs/localhost-header_nl.ht
+docs/localhost-header_ru.ht
+docs/localhost-header_zh.ht
+docs/noproxy-header_ar.ht
+docs/noproxy-header_de.ht
+docs/noproxy-header_fr.ht
+docs/noproxy-header_nl.ht
+docs/noproxy-header_ru.ht
+docs/noproxy-header_zh.ht
+docs/protocol-header_ar.ht
+docs/protocol-header_de.ht
+docs/protocol-header_fr.ht
+docs/protocol-header_nl.ht
+docs/protocol-header_ru.ht
+docs/protocol-header_zh.ht
+# old translated initial news
+docs/initialNews/initialNews_ar.xml
+docs/initialNews/initialNews_de.xml
+docs/initialNews/initialNews_es.xml
+docs/initialNews/initialNews_fr.xml
+docs/initialNews/initialNews_nl.xml
+docs/initialNews/initialNews_pt.xml
+docs/initialNews/initialNews_ru.xml
+docs/initialNews/initialNews_sv.xml
diff --git a/installer/resources/geoipv6-extras.csv b/installer/resources/geoipv6-extras.csv
new file mode 100644
index 000000000..414882684
--- /dev/null
+++ b/installer/resources/geoipv6-extras.csv
@@ -0,0 +1,9 @@
+# Local geoIPv6 additions
+# Format: from IP,to IP,,,country code[,"country name"]
+####
+# common tunnel brokers
+2001:5c0:1000:a::,2001:5c0:1000:a::,,,X1,"Freenet6 anonymous"
+2001:5c0:1000:b::,2001:5c0:1000:b::,,,X2,"Freenet6 authenticated"
+2001:5c0:1100::,2001:5c0:11ff:ffff:ffff:ffff:ffff:ffff,,,X3,"Freenet6 delegated"
+# other interesting addresses
+2002::,2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff,,,X4,"IPv4 compatibility"
diff --git a/installer/resources/geoipv6.dat.gz b/installer/resources/geoipv6.dat.gz
new file mode 100644
index 000000000..fe9273dd3
Binary files /dev/null and b/installer/resources/geoipv6.dat.gz differ
diff --git a/installer/resources/initialNews/initialNews.xml b/installer/resources/initialNews/initialNews.xml
index ab4afe73a..63199b999 100644
--- a/installer/resources/initialNews/initialNews.xml
+++ b/installer/resources/initialNews/initialNews.xml
@@ -1,25 +1,22 @@
-
-
Congratulations on getting I2P installed!
+
+
_("Congratulations on getting I2P installed!")
-Welcome to I2P!
-Please have patience as I2P boots up and finds peers.
+_("Welcome to I2P!")
+_("Please {0}have patience{1} as I2P boots up and finds peers.", "", "")
-While you are waiting, please adjust your bandwidth settings on the
-configuration page.
+_("While you are waiting, please {0}adjust your bandwidth settings{1} on the {2}configuration page{3}.", "", "", "", "")
-Also you can setup your browser to use the I2P proxy to reach eepsites. Just enter 127.0.0.1 (or localhost)
-port 4444 as a http proxy into your browser settings. Do not use SOCKS for this. More information
-can be found on I2P browser proxy setup page.
+_("Also you can setup your browser to use the I2P proxy to reach eepsites.")
+_("Just enter 127.0.0.1 (or localhost) port 4444 as a http proxy into your browser settings.")
+_("Do not use SOCKS for this.")
+_("More information can be found on the {0}I2P browser proxy setup page{1}.", "", "")
-Once you have a "shared clients" destination listed on the left,
-please check out our
-FAQ.
+_("Once you have a \"shared clients\" destination listed on the left, please {0}check out{1} our {2}FAQ{3}.", "", "", "", "")
-Point your IRC client to localhost:6668 and say hi to us on
-#i2p-help or #i2p.
+_("Point your IRC client to {0}localhost:6668{1} and say hi to us on {2}#i2p-help{3} or {4}#i2p{5}.", "", "", "", "", "", "")
Wir gratulieren zur erfolgreichen Installation von I2P!
-
-Willkommen im I2P!
-Hab noch etwas Geduld, während I2P startet und weitere I2P-Router findet.
-
-
-Passe bitte in der Zwischenzeit deine Einstellungen zur Bandbreite auf der
-Einstellungsseite an!
-
-
-Auch kannst Du gleich deinen Browser für die Nutzung von I2P einrichten. Setze in den
-Einstellungen zum Browser ein HTTP Proxy ein mit 127.0.0.1 (oder localhost) mit Port 4444 als Ziel.
-Mehr Informationen gibt es auf der I2P browser proxy Setup Seite.
-
-
-
-Sobald auf der linken Seite eine Verbindung namens "versch. Klienten" aufgelistet ist, kannst Du unsere FAQ besuchen.
-
-
-Verbinde deinen IRC-Klienten mit dem Server auf localhost:6668 und schau bei uns im Kanal
-#i2p-de, #i2p-help oder #i2p vorbei!
-
-
-Wir wünschen Dir viel Freude an unserem Netz und hoffen, Deine Erwartungen zu erfüllen.
-
-Добро пожаловать в I2P! Немного терпения! I2P-маршрутизатору потребуется пара минут для запуска модулей и первого подключения к сети I2P.
-
-
-Пока Вы ждете, самое время зайти в сетевые настройки и выставить ограничение скорости в соответствии со скоростью Вашего подключения к интернету.
-
-
-Как только в панели слева в разделе «Локальные туннели» появится запись «коллективные клиенты» — I2P готов к работе. Подключайте Ваш IRC-клиент к серверу localhost:6668 и заходите сказать нам привет на канал
-#i2p-help и #i2p.
-
-The addresshelper link you followed specifies a different destination key
-than a host entry in your host database.
-Someone could be trying to impersonate another eepsite,
-or people have given two eepsites identical names.
+_("The addresshelper link you followed specifies a different destination key than a host entry in your host database.")
+_("Someone could be trying to impersonate another eepsite, or people have given two eepsites identical names.")
-You can resolve the conflict by considering which key you trust,
-and either discarding the addresshelper link,
-discarding the host entry from your host database,
-or naming one of them differently.
+_("You can resolve the conflict by considering which key you trust, and either discarding the addresshelper link, discarding the host entry from your host database, or naming one of them differently.")
-The addresshelper link you followed specifies a different destination key
-than a host entry in your host database.
-Someone could be trying to impersonate another eepsite,
-or people have given two eepsites identical names.
-
-
-You can resolve the conflict by considering which key you trust,
-and either discarding the addresshelper link,
-discarding the host entry from your host database,
-or naming one of them differently.
-
-Der Adresshelferverweis, dem Sie folgen wollen, verwendet für diesen Hostnamen ein anderes kryptographisches Ziel
-als im zugehörigen Eintrag in Ihrem Routeradressbuch angegeben.
-Jemand könnte versuchen, fälschlicherweise eine andere als die gesuchte Eepseite auszugeben.
-Möglicherweise haben aber die betreiber beider Eepseiten nur zufällig denselben Namen ausgewählt.
-
-
-Diesen Konflikt können Sie lösen, indem Sie entscheiden, welchem Ziel Sie trauen, das gesuchte zu sein,
-und dann entweder den Verweis des Adresshelfers verwerfen, den Eintrag in Ihrem Routeradressbuch löschen,
-oder beiden Zielen unterschiedliche Namen zuweisen.
-
diff --git a/installer/resources/proxy/ahelper-conflict-header_fr.ht b/installer/resources/proxy/ahelper-conflict-header_fr.ht
deleted file mode 100644
index 7a32386ba..000000000
--- a/installer/resources/proxy/ahelper-conflict-header_fr.ht
+++ /dev/null
@@ -1,27 +0,0 @@
-HTTP/1.1 409 Conflict
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: conflit de clef de destination
-
-
-
-
-
-Le lien d'adresse que vous avez suivi indique une clef de destination différente de celle de votre carnet d'adresses.
-Quelqu'un est peut-être en train d'usurper un autre site eep, ou des gens ont donné le même nom à des sites eep différents.
-
-
-Vous pouvez résoudre le conflit en décidant à quelle clé accorder votre confiance,
-et soit écarter le lien d'adresse, supprimer l'entrée de votre carnet d'adresses, ou renommer un des deux.
-
-De adreshelper link die je probeert te volgen gebruikt een andere destination
-dan een host item in je host database.
-Mogelijk probeert iemand een andere eepsite na te apen,
-of mensen hebben twee eepsites een identieke naam gegeven.
-
-
-Je kan dit conflict oplossen door te bepalen welke destination je vertrouwt,
-en dan of de adreshelper link te verwerpen,
-de host item in je host database te verwijderen,
-of één van de twee een andere naam te geven.
-
diff --git a/installer/resources/proxy/ahelper-conflict-header_ru.ht b/installer/resources/proxy/ahelper-conflict-header_ru.ht
deleted file mode 100644
index e2e427b18..000000000
--- a/installer/resources/proxy/ahelper-conflict-header_ru.ht
+++ /dev/null
@@ -1,31 +0,0 @@
-HTTP/1.1 409 Conflict
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Предупреждение I2P: Конфликт ключей адреса назначения
-
-
-
-
-
Предупреждение: Конфликт ключей для адреса назначения
-
-Addresshelper-ссылка, по которой вы перешли, задает ключ адреса назначения,
-отличающийся от ключа в Вашей адресной книге. Возможно это чья-то попытка
-выдать себя за чужой сайт. Или же это случайное совпадение, в результате
-которого два сайта получили идентичные названия.
-
-
-Решите для себя, которому из ключей Вы доверяете. Затем устраните конфликт:
-либо проигнорируйте новую addresshelper-ссылку, либо переименуйте её при
-добавлении в адресную книгу.
-
_("Information: New Host Name with Address Helper")
-The address helper link you followed is for a new host name that is not in your address book.
-You may save this host name to your local address book.
-If you save it to your address book, you will not see this message again.
-If you do not save it, the host name will be forgotten after the next router restart.
-If you do not wish to visit this host, click the "back" button on your browser.
+_("The address helper link you followed is for a new host name that is not in your address book.")
+_("You may save this host name to your local address book.")
+_("If you save it to your address book, you will not see this message again.")
+_("If you do not save it, the host name will be forgotten after the next router restart.")
+_("If you do not wish to visit this host, click the \"back\" button on your browser.")
diff --git a/installer/resources/proxy/ahelper-notfound-header.ht b/installer/resources/proxy/ahelper-notfound-header.ht
index d87f83094..f907ce3f3 100644
--- a/installer/resources/proxy/ahelper-notfound-header.ht
+++ b/installer/resources/proxy/ahelper-notfound-header.ht
@@ -6,19 +6,19 @@ Proxy-Connection: close
-I2P Warning: Bad Address Helper
+_("Warning: Bad Address Helper")
-The helper key you put for i2paddresshelper= is not resolvable.
-It seems to be garbage data, or a mistyped b32. Check your URL
-to try and fix the helper key to be a valid Base 32 hostname or Base 64 key.
+_("The helper key in the URL ({0}i2paddresshelper={1}) is not resolvable.", "", "")
+_("It seems to be garbage data, or a mistyped Base 32 address.")
+_("Check your URL to try and fix the helper key to be a valid Base 32 hostname or Base 64 key.")
-This proxy is configured to require a username and password for access.
-Please enter your username and password, or check your
-router configuration
-or
-I2PTunnel configuration.
-To disable authorization, remove the configuration
-i2ptunnel.proxy.auth=basic, then stop and restart the HTTP Proxy tunnel.
+_("This proxy is configured to require a username and password for access.")
+_("Please enter your username and password, or check your {0}router configuration{1} or {2}I2PTunnel configuration{3}.", "", "/a>", "", "")
+_("To disable authorization, remove the configuration {0}i2ptunnel.proxy.auth=basic{1}, then stop and restart the HTTP Proxy tunnel.", "", ""))
-Dieser Proxy ist so eingestellt, dass er Zugang nur für gültige Kombinationen
-aus einem Benutzernamen und einem Passwort zulässt. Bitte geben Sie Ihren
-Benutzernamen und Passwort an bzw. überprüfen Sie Ihre
-Routereinstellungen oder
-Ihre I2PTunnel-Einstellungen!
-Um das Autorsierungserfordernis aufzuheben, entfernen Sie
-i2ptunnel.proxy.auth=basic aus der betreffenden Tunneleinstellung,
-stoppen Sie den HTTP-Proxy-Tunnel dann und starten Sie ihn neu!
-
-Ce mandataire est configuré pour demander un nom d'utilisateur et un mot de passe pour les accès.
-Merci d'entrer votre identifiant / mot de passe, ou vérifier la
-configuration de votre routeur,
-ou du I2PTunnel.
-Pour désactiver le contrôle d'accès, supprimez la configuration
-i2ptunnel.proxy.auth=basic, puis redémarrez le tunnel proxy HTTP.
-
-
diff --git a/installer/resources/proxy/baduri-header.ht b/installer/resources/proxy/baduri-header.ht
index 49fb39a83..0f8e53221 100644
--- a/installer/resources/proxy/baduri-header.ht
+++ b/installer/resources/proxy/baduri-header.ht
@@ -6,19 +6,19 @@ Proxy-Connection: close
-I2P Warning: Invalid Request URI
+_("Warning: Invalid Request URI")
-The request URI is invalid, and probably contains illegal characters.
-If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.
+_("The request URI is invalid, and probably contains illegal characters.")
+_("If you clicked a link, check the end of the URI for any characters the browser has mistakenly added on.")
-The eepsite was not reachable.
-The eepsite is offline, there is network congestion,
-or your router is not yet well-integrated with peers.
-You may want to
-retry.
+_("The eepsite was not reachable.")
+_("The eepsite is offline, there is network congestion, or your router is not yet well-integrated with peers.")
+_("You may want to {0}retry{1}.", "", "")
-
-Die Eepseite war nicht erreichbar.
-Sie ist vielleicht offline, es gibt eine Überlast oder Verstopfung im Netz,
-oder Ihr Router ist noch nicht gut ins Netzwerk integriert.
-Versuchen Sie, die Seite erneut zu laden!
-
-
-
-Konnte das folgende Ziel nicht finden:
-
diff --git a/installer/resources/proxy/dnf-header_fr.ht b/installer/resources/proxy/dnf-header_fr.ht
deleted file mode 100644
index becbc0cc5..000000000
--- a/installer/resources/proxy/dnf-header_fr.ht
+++ /dev/null
@@ -1,28 +0,0 @@
-HTTP/1.1 504 Gateway Timeout
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: site eep non joignable
-
-
-
-
-
-Le site eep n'a pas pu être atteint. Il est hors service,
-le réseau est surchargé, ou votre routeur n'est pas bien intégré aux pairs.
-Vous pouvez réessayer.
-
-De eepsite was niet bereikbaar.
-De eepsite is offline, er is een netwerk verstopping,
-of je router is niet goed geïntegreerd met peers.
-Probeer opnieuw.
-
-Не удалось открыть I2P-сайт. Он либо в оффлайне, либо сеть сейчас перегружена,
-либо ваш I2P-маршрутизатор ещё недостаточно интегрирован с другими пирами.
-
-The eepsite destination specified was not valid, or was
-otherwise unreachable. Perhaps you pasted in the
-wrong BASE64 string or the link you are following is
-bad. The I2P host could also be offline. You may want to
-retry.
-
Could not find the following destination:
+_("The eepsite destination specified was not valid, or was otherwise unreachable.")
+_("Perhaps you pasted in the wrong Base 64 string or the link you are following is bad.")
+_("The I2P host could also be offline.")
+_("You may want to {0}retry{1}.", "", "")
+_("Could not find the following destination:")
diff --git a/installer/resources/proxy/dnfb-header_ar.ht b/installer/resources/proxy/dnfb-header_ar.ht
deleted file mode 100644
index bfbdefc26..000000000
--- a/installer/resources/proxy/dnfb-header_ar.ht
+++ /dev/null
@@ -1,29 +0,0 @@
-HTTP/1.1 400 Destination Not Found
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-
-تحذير: عنوان eepsite خاطئ
-
-
-
-
-
-
-Das angegebene Eepseiten-Ziel ist nicht gültig,
-oder auf andere Art nicht erreichbar. Vielleicht haben Sie eine falsche BASIS64-Zeichenkette kopiert
-oder Sie folgten einem ungültigen Verweis. Möglicherweise ist auch der I2P-Host offline.
-Versuchen Sie, die Seite erneut zu laden!
-
-
-
-Konnte das folgende Ziel nicht finden:
diff --git a/installer/resources/proxy/dnfb-header_fr.ht b/installer/resources/proxy/dnfb-header_fr.ht
deleted file mode 100644
index 40ab55cde..000000000
--- a/installer/resources/proxy/dnfb-header_fr.ht
+++ /dev/null
@@ -1,28 +0,0 @@
-HTTP/1.1 400 Destination non trouvée
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: destination eep site incorrecte
-
-
-
-
-
Avertissement I2P: destination eep site incorrecte
-
-La destination du site eep indiquée est incorrecte, ou injoignable.
-Vous avez peut-être collé une chaîne BASE64 incorrecte le lien que vous suivez est incorrect.
-L'hôte I2P est peut-être aussi hors-servicehost.
-Vous pouvez réessayer.
-
-
-
Destination non trouvée:
-
diff --git a/installer/resources/proxy/dnfb-header_nl.ht b/installer/resources/proxy/dnfb-header_nl.ht
deleted file mode 100644
index 79193b917..000000000
--- a/installer/resources/proxy/dnfb-header_nl.ht
+++ /dev/null
@@ -1,30 +0,0 @@
-HTTP/1.1 400 Destination Not Found
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-I2P Waarschuwing: Ongeldige eepsite destination
-
-
-
-
-
-De gespecificeerde eepsite destination is ongeldig,
-of anderzijds onbereikbaar. Mogelijk heb je de
-BASE64 string niet goed gekopieerd of is de link
-die je probeert te volgen ongeldig. De I2P host kan
-ook offline zijn. Probeer
-opnieuw.
-
-
-
Kon de volgende destination niet vinden:
-
diff --git a/installer/resources/proxy/dnfb-header_ru.ht b/installer/resources/proxy/dnfb-header_ru.ht
deleted file mode 100644
index bdc5b15a4..000000000
--- a/installer/resources/proxy/dnfb-header_ru.ht
+++ /dev/null
@@ -1,32 +0,0 @@
-HTTP/1.1 400 Destination Not Found
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Предупреждение I2P: Неверный адрес назначения
-
-
-
-
-
Предупреждение: Неверный адрес назначения для I2P-сайта
-
-Указанный I2P-адрес либо неверен, либо недоступен. Возможно Вы скопировали
-поврежденную BASE64-строчку или перешли по битой ссылке. Также возможно, что
-I2P-хост в офлайне.
-
-The eepsite was not found in your router's addressbook.
-Check the link or find a Base 32 or Base 64 address.
-If you have the Base 64 address,
-add it to your addressbook.
-Otherwise, find a Base 32 or address helper link, or use a jump service link below.
+_("The eepsite was not found in your router's addressbook.")
+_("Check the link or find a Base 32 or Base 64 address.")
+_("If you have the Base 64 address, {0}add it to your addressbook{1}.", "", "")
+_("Otherwise, find a Base 32 or address helper link, or use a jump service link below.")
-Die Eepseite konnte nicht im Adressbuch des Routers gefunden werden.
-Überprüfen Sie den Link oder finden Sie eine BASIS64-Adresse!
-Wenn Sie eine BASIS64-Adresse haben, fügen Sie diese unter folgendem Link zu Ihrer hosts.txt hinzu:
-SusiDNS,
-benutzen Sie einen BASIS64-Adresshelfer oder einen der folgenden Sprungdienste:
-
-
-(Sehen Sie diese Seite öfters? Dann lesen Sie die FAQ
-für Hilfe beim Hinzufügen von Abonnements
-zu Ihrem Adressbuch.)
-
-
-Konnte das folgende Ziel nicht finden:
-
diff --git a/installer/resources/proxy/dnfh-header_fr.ht b/installer/resources/proxy/dnfh-header_fr.ht
deleted file mode 100644
index 0c03e0f97..000000000
--- a/installer/resources/proxy/dnfh-header_fr.ht
+++ /dev/null
@@ -1,34 +0,0 @@
-HTTP/1.1 500 Domaine non trouvé
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: site eep inconnu
-
-
-
-
-
Avertissement I2P: site eep non trouvé dans le carnet d'adresses
-
-Le site eep n'était pas dans le carnet d'adresses de votre routeur.
-Vérifiez le lien ou trouvez l'adresse Base 64.
-Si vous avez l'adresse Base 64, reportez-la dans votre fichier hosts.txt en passant par
-SusiDNS,
-utilisez un lien d'adresse Base 64, ou un service de saut (jump) en lien ci-dessous.
-
-De eepsite was niet gevonden in je router's adresboek.
-Controlleer de link of zoek een Base 64 adres.
-Indien je een Base 64 adres hebt, plak het in je hosts.txt via
-SusiDNS,
-gebruikt een Base 64 adres helper, of gebruik een jump service link hieronder.
-
Предупреждение: I2P-сайт не найден в адресной книге
-
-Вы попытались открыть I2P-сайт, которого нет в адресной книге Вашего I2P-маршрутизатора. Проверьте, что ссылка введена без ошибок.
-
-
-Попробуйте воспользоваться jump-сервисами (блок ссылок в конце этой страницы). Если кто-то уже добавил этот сайт в jump-сервис, Вы получите addresshelper-ссылку, по которой сможете перейти на сайт.
-
-
-Если ни один из jump-сервисов не помог, попробуйте найти Base 64-адрес этого сайта. Добавьте Base 64-адрес в Ваш файл hosts.txt используя SusiDNS.
-
-The HTTP Outproxy was not found.
-It is offline, there is network congestion,
-or your router is not yet well-integrated with peers.
-You may want to
-retry
-as this will randomly reselect an outproxy from the pool you have defined
-here
-(if you have more than one configured).
-If you continue to have trouble you may want to edit your outproxy list
-here.
+_("The HTTP Outproxy was not found.")
+_("It is offline, there is network congestion, or your router is not yet well-integrated with peers.")
+_("You may want to {0}retry{1} as this will randomly reselect an outproxy from the pool you have defined {2}here{3} (if you have more than one configured).", "", "", "", "")
+_("If you continue to have trouble you may want to edit your outproxy list {0}here{1}.", "", "")
-لم يتم ايجاد بروكسي خارجي www
-خارج الشبكة أو غير متصل جيدا بباقي المستخدمين.
-يمكنك
-اعادة المحاولة
-لإختيار بروكسي خارجي جديد
-هنا
-(اذا كان لديك اكثر من واحد).
-اذا استمر المشكل قم بتحرير قائمة البروكسي الخارجية
-هنا.
-
-Der HTTP-Outproxy wurde nicht gefunden. Er ist offline, das Netz überlastet
-oder verstopft, oder Ihr Router ist noch nicht gut ins Netzwerk integriert.
-Laden Sie die Seite neu!
-Dies trifft eine erneute zufällige Auswahl des Outproxys aus der von Ihnen
-hier vorgegebenen Liste
-(falls mehr als einer angegeben ist). Sollte das Problem weiterhin bestehen,
-passen Sie die Liste Ihrer Outproxys hier an!
-
-
-
-Konnte das folgende Ziel nicht finden:
-
diff --git a/installer/resources/proxy/dnfp-header_fr.ht b/installer/resources/proxy/dnfp-header_fr.ht
deleted file mode 100644
index 292f945a1..000000000
--- a/installer/resources/proxy/dnfp-header_fr.ht
+++ /dev/null
@@ -1,35 +0,0 @@
-HTTP/1.1 504 Délai d'attente de passerelle dépassé
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: Proxy sortant non trouvé
-
-
-
-
-
-Le serveur mandataire sortant est introuvable.
-Il est hors service, le réseau est surchargé,
-ou votre routeur n'est pas encore bien intégré avec des pairs.
-Vous pouvez essayer de
-retenter
-car il se produira une sélection aléatoire de mandataire dans le groupe que vous avez défini
-ici
-(si vous en avez plus d'un de défini).
-Si vous avez toujours des problèmes vous pouvez modifier votre liste de
-mandataires ici.
-
-De HTTP Outproxy was niet gevonden.
-Het is offline, er is een netwerk verstopping,
-of je router is niet goed geïntegreerd met peers.
-Probeer opnieuw
-aangezien dit willekeurig een outproxy van de groep zal selecteren die
-hier gedefinieerd
-(indien er meer van één geconfigureerd is).
-Wanneer de problemen blijven volhouden dan kan je de outproxy lijst
-hier bewerken.
-
-Outproxy для www не найден. Он либо в офлайне, либо сеть сейчас перегружена,
-либо ваш I2P-маршрутизатор ещё недостаточно интегрирован с другими пирами.
-
-
-Вы можете попробовать повторить запрос, при этом outproxy будет
-случайным образом выбран из списка, который Вы настроили (если в списке
-более одного прокси). Если проблемы будут продолжаться, Вы можете отредактировать с
-писок outproxy на этой странице.
-
diff --git a/installer/resources/proxy/localhost-header.ht b/installer/resources/proxy/localhost-header.ht
index bb6d22d17..5b634f036 100644
--- a/installer/resources/proxy/localhost-header.ht
+++ b/installer/resources/proxy/localhost-header.ht
@@ -6,7 +6,7 @@ Proxy-Connection: close
-I2P Error: Request Denied
+_("Error: Request Denied")
@@ -14,14 +14,14 @@ Proxy-Connection: close
-
Error: Local Access
+
_("Error: Local Access")
-Your browser is misconfigured. Do not use the proxy to access the router console,
-localhost, or local LAN destinations.
+_("Your browser is misconfigured.")
+_("Do not use the proxy to access the router console, localhost, or local LAN destinations.")
diff --git a/installer/resources/proxy/localhost-header_ar.ht b/installer/resources/proxy/localhost-header_ar.ht
deleted file mode 100644
index 876dc3e0e..000000000
--- a/installer/resources/proxy/localhost-header_ar.ht
+++ /dev/null
@@ -1,26 +0,0 @@
-HTTP/1.1 403 Access Denied
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-خطأ: تم رفض الطلب
-
-
-
-
-
-
-
خطـأ: عنوان محلي
-
-خطأ في اعدادات المتصفح. لا تستعمل بروكسي للوصول الى شاشة التحكم، localhost او الشبكة المحلية LAN.
-
-Ihr Browser ist fehlkonfiguriert: Benutzen Sie diesen Proxy nicht, um auf
-lokale Ziele wie die I2P-Routerkonsole, localhost oder LAN-Ziele zuzugreifen!
-
-Votre navigateur est mal configuré. N'utilisez pas de mandataire pour accéder à la console du routeur I2P,
-à l'hôte local (localhost), ou des destinations LAN: définissez une exception.
-
-Your request was for a site outside of I2P, but you have no
-HTTP outproxy configured. Please configure an outproxy in I2PTunnel.
+_("Your request was for a site outside of I2P, but you have no HTTP outproxy configured.")
+_("Please configure an outproxy in I2PTunnel.")
diff --git a/installer/resources/proxy/noproxy-header_ar.ht b/installer/resources/proxy/noproxy-header_ar.ht
deleted file mode 100644
index 708e12f10..000000000
--- a/installer/resources/proxy/noproxy-header_ar.ht
+++ /dev/null
@@ -1,23 +0,0 @@
-HTTP/1.1 503 Service Unavailable
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-تحذير: لم يحدد بروكسي خارجي
-
-
-
-
-
-Sie haben eine Seite außerhalb des I2P-Netzes angefordert, jedoch ist kein
-HTTP-Outproxy eingerichtet. Bitte richten Sie einen Outproxy in der Sektion
-I2PTunnel ein!
-
-
diff --git a/installer/resources/proxy/noproxy-header_fr.ht b/installer/resources/proxy/noproxy-header_fr.ht
deleted file mode 100644
index 38a3673f3..000000000
--- a/installer/resources/proxy/noproxy-header_fr.ht
+++ /dev/null
@@ -1,24 +0,0 @@
-HTTP/1.1 503 Service indisponible
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-Avertissement I2P: Mandataire non configuré
-
-
-
-
-
-Votre requête était pour un site hors du réseau I2P, mais vous n'avez pas de mandataire
-configuré. Merci de configurer un mandataire dans I2PTunnel.
-
-
diff --git a/installer/resources/proxy/noproxy-header_nl.ht b/installer/resources/proxy/noproxy-header_nl.ht
deleted file mode 100644
index 06da8ca61..000000000
--- a/installer/resources/proxy/noproxy-header_nl.ht
+++ /dev/null
@@ -1,24 +0,0 @@
-HTTP/1.1 503 Service Unavailable
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-I2P Waarschuwing: Geen outproxy geconfigureerd
-
-
-
-
-
-The request uses a bad protocol.
-The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.
+_("The request uses a bad protocol.")
+_("The I2P HTTP Proxy supports {0}http://{1} requests ONLY.", "", "")
+_("Other protocols such as {0}https://{1} and {0}ftp://{1} are not allowed.", "", "")
diff --git a/installer/resources/proxy/protocol-header_ar.ht b/installer/resources/proxy/protocol-header_ar.ht
deleted file mode 100644
index 2769a8d90..000000000
--- a/installer/resources/proxy/protocol-header_ar.ht
+++ /dev/null
@@ -1,24 +0,0 @@
-HTTP/1.1 403 Bad Protocol
-Content-Type: text/html; charset=UTF-8
-Cache-control: no-cache
-Connection: close
-Proxy-Connection: close
-
-
-
-HTTP تحذير: استعمال بروتوكول غير متوافق مع
-
-
-
-
-
-Die Anfrage verwendet ein falsches Protokoll. I2Ps HTTP-Proxy unterstützt
-AUSSCHLIEßLICH Anfragen des HTTP-Protokolls ( http:// ). Andere
-Protokolle wie https:// und ftp:// sind nicht
-erlaubt.
-
-La requête a utilisé un protocole inadpté.
-Le mandataire HTTP I2P prend en charge les requêtes http://
-seulement. Les autre protocoles tels que https:// and
-ftp:// ne sont pas permis.
-
-De aanvraag gebruikt een verkeerd protocol.
-De I2P HTTP Proxy ondersteunt ALLEEN http:// requests. Andere
-protocollen zoals https:// en ftp:// zijn niet
-toegestaan.
-
-В запросе использован неподдерживаемый протокол. I2P-HTTP прокси поддерживает
-ТОЛЬКО http:// запросы. Другие протоколы, такие как
-https:// или ftp://, не допускаются.
-
diff --git a/licenses/LICENSE-GeoIP.txt b/licenses/LICENSE-GeoIP.txt
index 6f5b0713d..0c91833ef 100644
--- a/licenses/LICENSE-GeoIP.txt
+++ b/licenses/LICENSE-GeoIP.txt
@@ -1,32 +1,8 @@
-OPEN DATA LICENSE (GeoLite Country and GeoLite City databases)
+The GeoLite databases are distributed under the
+Creative Commons Attribution-ShareAlike 3.0 Unported License
+http://creativecommons.org/licenses/by-sa/3.0/ .
+The attribution requirement may be met by including the following in
+all advertising and documentation mentioning features of or use of this database:
-Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
-
-All advertising materials and documentation mentioning features or use of
-this database must display the following acknowledgment:
"This product includes GeoLite data created by MaxMind, available from
-http://maxmind.com/"
-
-Redistribution and use with or without modification, are permitted provided
-that the following conditions are met:
-1. Redistributions must retain the above copyright notice, this list of
-conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-2. All advertising materials and documentation mentioning features or use of
-this database must display the following acknowledgement:
-"This product includes GeoLite data created by MaxMind, available from
-http://maxmind.com/"
-3. "MaxMind" may not be used to endorse or promote products derived from this
-database without specific prior written permission.
-
-THIS DATABASE IS PROVIDED BY MAXMIND, INC ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL MAXMIND BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+http://www.maxmind.com/"
diff --git a/router/java/build.xml b/router/java/build.xml
index f473abf0c..46ec22e49 100644
--- a/router/java/build.xml
+++ b/router/java/build.xml
@@ -258,7 +258,10 @@
+
+
+
diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java
index 5f8112cc0..3a28bd85a 100644
--- a/router/java/src/net/i2p/router/Blocklist.java
+++ b/router/java/src/net/i2p/router/Blocklist.java
@@ -11,10 +11,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Writer;
+import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -31,6 +33,7 @@ import net.i2p.data.RouterInfo;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.util.Addresses;
import net.i2p.util.ConcurrentHashSet;
+import net.i2p.util.LHMCache;
import net.i2p.util.Log;
import net.i2p.util.Translate;
@@ -62,6 +65,8 @@ import net.i2p.util.Translate;
* banlist it forever, then go back to the file to get the original
* entry so we can add the reason to the banlist text.
*
+ * On-disk blocklist supports IPv4 only.
+ * In-memory supports both IPv4 and IPv6.
*/
public class Blocklist {
private final Log _log;
@@ -72,8 +77,20 @@ public class Blocklist {
private Entry _wrapSave;
private final Set _inProcess = new HashSet(4);
private Map _peerBlocklist = new HashMap(4);
+
+ /**
+ * Limits of transient (in-memory) blocklists.
+ * Note that it's impossible to prevent clogging up
+ * the tables by a determined attacker, esp. on IPv6
+ */
+ private static final int MAX_IPV4_SINGLES = 256;
+ private static final int MAX_IPV6_SINGLES = 512;
+
private final Set _singleIPBlocklist = new ConcurrentHashSet(4);
-
+ private final Map _singleIPv6Blocklist = new LHMCache(MAX_IPV6_SINGLES);
+
+ private static final Object DUMMY = Integer.valueOf(0);
+
public Blocklist(RouterContext context) {
_context = context;
_log = context.logManager().getLog(Blocklist.class);
@@ -437,6 +454,8 @@ public class Blocklist {
* Maintain a simple in-memory single-IP blocklist
* This is used for new additions, NOT for the main list
* of IP ranges read in from the file.
+ *
+ * @param ip IPv4 or IPv6
*/
public void add(String ip) {
byte[] pib = Addresses.getIP(ip);
@@ -448,16 +467,24 @@ public class Blocklist {
* Maintain a simple in-memory single-IP blocklist
* This is used for new additions, NOT for the main list
* of IP ranges read in from the file.
+ *
+ * @param ip IPv4 or IPv6
*/
public void add(byte ip[]) {
- if (ip.length != 4)
- return;
- if (add(toInt(ip)))
- if (_log.shouldLog(Log.WARN))
- _log.warn("Adding IP to blocklist: " + Addresses.toString(ip));
+ boolean rv;
+ if (ip.length == 4)
+ rv = add(toInt(ip));
+ else if (ip.length == 16)
+ rv = add(new BigInteger(1, ip));
+ else
+ rv = false;
+ if (rv && _log.shouldLog(Log.WARN))
+ _log.warn("Adding IP to blocklist: " + Addresses.toString(ip));
}
private boolean add(int ip) {
+ if (_singleIPBlocklist.size() >= MAX_IPV4_SINGLES)
+ return false;
return _singleIPBlocklist.add(Integer.valueOf(ip));
}
@@ -466,20 +493,41 @@ public class Blocklist {
}
/**
- * this tries to not return duplicates
- * but I suppose it could.
+ * @param ip IPv6 non-negative
+ * @since IPv6
+ */
+ private boolean add(BigInteger ip) {
+ synchronized(_singleIPv6Blocklist) {
+ return _singleIPv6Blocklist.put(ip, DUMMY) == null;
+ }
+ }
+
+ /**
+ * @param ip IPv6 non-negative
+ * @since IPv6
+ */
+ private boolean isOnSingleList(BigInteger ip) {
+ synchronized(_singleIPv6Blocklist) {
+ return _singleIPv6Blocklist.get(ip) != null;
+ }
+ }
+
+ /**
+ * Will not contain duplicates.
*/
private List getAddresses(Hash peer) {
- List rv = new ArrayList(1);
RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
- if (pinfo == null) return rv;
- byte[] oldpib = null;
+ if (pinfo == null)
+ return Collections.EMPTY_LIST;
+ List rv = new ArrayList(4);
// for each peer address
for (RouterAddress pa : pinfo.getAddresses()) {
byte[] pib = pa.getIP();
if (pib == null) continue;
- if (DataHelper.eq(oldpib, pib)) continue;
- oldpib = pib;
+ // O(n**2)
+ for (int i = 0; i < rv.size(); i++) {
+ if (DataHelper.eq(rv.get(i), pib)) continue;
+ }
rv.add(pib);
}
return rv;
@@ -491,8 +539,9 @@ public class Blocklist {
*/
public boolean isBlocklisted(Hash peer) {
List ips = getAddresses(peer);
- for (Iterator iter = ips.iterator(); iter.hasNext(); ) {
- byte ip[] = iter.next();
+ if (ips.isEmpty())
+ return false;
+ for (byte[] ip : ips) {
if (isBlocklisted(ip)) {
if (! _context.banlist().isBanlisted(peer))
// nice knowing you...
@@ -505,6 +554,8 @@ public class Blocklist {
/**
* calling this externally won't banlist the peer, this is just an IP check
+ *
+ * @param ip IPv4 or IPv6
*/
public boolean isBlocklisted(String ip) {
byte[] pib = Addresses.getIP(ip);
@@ -514,11 +565,15 @@ public class Blocklist {
/**
* calling this externally won't banlist the peer, this is just an IP check
+ *
+ * @param ip IPv4 or IPv6
*/
public boolean isBlocklisted(byte ip[]) {
- if (ip.length != 4)
- return false;
- return isBlocklisted(toInt(ip));
+ if (ip.length == 4)
+ return isBlocklisted(toInt(ip));
+ if (ip.length == 16)
+ return isOnSingleList(new BigInteger(1, ip));
+ return false;
}
/**
@@ -760,7 +815,7 @@ public class Blocklist {
//out.write("
Banned IPs
");
Set singles = new TreeSet();
singles.addAll(_singleIPBlocklist);
- if (!singles.isEmpty()) {
+ if (!(singles.isEmpty() && _singleIPv6Blocklist.isEmpty())) {
out.write("
");
out.write(_("IPs Banned Until Restart"));
out.write("
");
@@ -782,6 +837,19 @@ public class Blocklist {
out.write(toStr(ip));
out.write("
\n");
}
+ // then IPv6
+ if (!_singleIPv6Blocklist.isEmpty()) {
+ List s6;
+ synchronized(_singleIPv6Blocklist) {
+ s6 = new ArrayList(_singleIPv6Blocklist.keySet());
+ }
+ Collections.sort(s6);
+ for (BigInteger bi : s6) {
+ out.write("
");
}
if (_blocklistSize > 0) {
@@ -832,6 +900,23 @@ public class Blocklist {
out.flush();
}
+ /**
+ * Convert a (non-negative) two's complement IP to exactly 16 bytes
+ * @since IPv6
+ */
+ private static byte[] toIPBytes(BigInteger bi) {
+ byte[] ba = bi.toByteArray();
+ int len = ba.length;
+ if (len == 16)
+ return ba;
+ byte[] rv = new byte[16];
+ if (len < 16)
+ System.arraycopy(ba, 0, rv, 16 - len, len);
+ else
+ System.arraycopy(ba, len - 16, rv, 0, 16);
+ return rv;
+ }
+
/**
* Mark a string for extraction by xgettext and translation.
* Use this only in static initializers.
diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java
index 82e73c2e1..1386b8727 100644
--- a/router/java/src/net/i2p/router/CommSystemFacade.java
+++ b/router/java/src/net/i2p/router/CommSystemFacade.java
@@ -28,8 +28,8 @@ public abstract class CommSystemFacade implements Service {
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { }
public void renderStatusHTML(Writer out) throws IOException { renderStatusHTML(out, null, 0); }
- /** Create the set of RouterAddress structures based on the router's config */
- public Set createAddresses() { return Collections.EMPTY_SET; }
+ /** Create the list of RouterAddress structures based on the router's config */
+ public List createAddresses() { return Collections.EMPTY_LIST; }
public int countActivePeers() { return 0; }
public int countActiveSendPeers() { return 0; }
diff --git a/router/java/src/net/i2p/router/MessageHistory.java b/router/java/src/net/i2p/router/MessageHistory.java
index 2ded9f03b..66e13952f 100644
--- a/router/java/src/net/i2p/router/MessageHistory.java
+++ b/router/java/src/net/i2p/router/MessageHistory.java
@@ -91,8 +91,13 @@ public class MessageHistory {
*/
public synchronized void initialize(boolean forceReinitialize) {
if (!forceReinitialize) return;
+ Router router = _context.router();
+ if (router == null) {
+ // unit testing, presumably
+ return;
+ }
- if (_context.router().getRouterInfo() == null) {
+ if (router.getRouterInfo() == null) {
_reinitializeJob.getTiming().setStartAfter(_context.clock().now() + 15*1000);
_context.jobQueue().addJob(_reinitializeJob);
} else {
diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
index f1fbd1b9f..4ed10ebe8 100644
--- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
@@ -77,4 +77,13 @@ public abstract class NetworkDatabaseFacade implements Service {
/** @since 0.9 */
public ReseedChecker reseedChecker() { return null; };
+
+ /**
+ * For convenience, so users don't have to cast to FNDF, and unit tests using
+ * Dummy NDF will work.
+ *
+ * @return false; FNDF overrides to return actual setting
+ * @since IPv6
+ */
+ public boolean floodfillEnabled() { return false; };
}
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 125d2e075..1ae539ab8 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -601,7 +601,7 @@ public class Router implements RouterClock.ClockShiftListener {
}
// if prop set to true, don't tell people we are ff even if we are
- if (FloodfillNetworkDatabaseFacade.floodfillEnabled(_context) &&
+ if (_context.netDb().floodfillEnabled() &&
!_context.getBooleanProperty("router.hideFloodfillParticipant"))
ri.addCapability(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL);
diff --git a/router/java/src/net/i2p/router/RouterClock.java b/router/java/src/net/i2p/router/RouterClock.java
index a5025259e..8ec8db80e 100644
--- a/router/java/src/net/i2p/router/RouterClock.java
+++ b/router/java/src/net/i2p/router/RouterClock.java
@@ -156,7 +156,7 @@ public class RouterClock extends Clock {
(stratum >= _lastStratum || _startedOn - System.currentTimeMillis() > 60*1000)) {
// Update the target offset, slewing will take care of the rest
if (delta > 15*1000)
- getLog().error("Warning - Updating target clock offset to " + offsetMs + "ms from " + _offset + "ms, Stratum " + stratum);
+ getLog().logAlways(Log.WARN, "Warning - Updating target clock offset to " + offsetMs + "ms from " + _offset + "ms, Stratum " + stratum);
else if (getLog().shouldLog(Log.INFO))
getLog().info("Updating target clock offset to " + offsetMs + "ms from " + _offset + "ms, Stratum " + stratum);
diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java
index 990f087da..5101076a8 100644
--- a/router/java/src/net/i2p/router/RouterContext.java
+++ b/router/java/src/net/i2p/router/RouterContext.java
@@ -38,7 +38,8 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
*/
public class RouterContext extends I2PAppContext {
private final Router _router;
- private ClientManagerFacadeImpl _clientManagerFacade;
+ private ClientManagerFacade _clientManagerFacade;
+ private InternalClientManager _internalClientManager;
private ClientMessagePool _clientMessagePool;
private JobQueue _jobQueue;
private InNetMessagePool _inNetMessagePool;
@@ -152,15 +153,29 @@ public class RouterContext extends I2PAppContext {
}
+ /**
+ * The following properties may be used to replace various parts
+ * of the context with dummy implementations for testing, by setting
+ * the property to "true":
+ *
+ */
public synchronized void initAll() {
if (_initialized)
throw new IllegalStateException();
- if (getBooleanProperty("i2p.dummyClientFacade"))
- System.err.println("i2p.dummyClientFacade currently unsupported");
- _clientManagerFacade = new ClientManagerFacadeImpl(this);
- // removed since it doesn't implement InternalClientManager for now
- //else
- // _clientManagerFacade = new DummyClientManagerFacade(this);
+ if (!getBooleanProperty("i2p.dummyClientFacade")) {
+ ClientManagerFacadeImpl cmfi = new ClientManagerFacadeImpl(this);
+ _clientManagerFacade = cmfi;
+ _internalClientManager = cmfi;
+ } else {
+ _clientManagerFacade = new DummyClientManagerFacade(this);
+ // internal client manager is null
+ }
_clientMessagePool = new ClientMessagePool(this);
_jobQueue = new JobQueue(this);
_inNetMessagePool = new InNetMessagePool(this);
@@ -168,23 +183,23 @@ public class RouterContext extends I2PAppContext {
_messageHistory = new MessageHistory(this);
_messageRegistry = new OutboundMessageRegistry(this);
//_messageStateMonitor = new MessageStateMonitor(this);
- if ("false".equals(getProperty("i2p.dummyNetDb", "false")))
+ if (!getBooleanProperty("i2p.dummyNetDb"))
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
else
_netDb = new DummyNetworkDatabaseFacade(this);
_keyManager = new KeyManager(this);
- if ("false".equals(getProperty("i2p.vmCommSystem", "false")))
+ if (!getBooleanProperty("i2p.vmCommSystem"))
_commSystem = new CommSystemFacadeImpl(this);
else
_commSystem = new VMCommSystem(this);
_profileOrganizer = new ProfileOrganizer(this);
- if ("false".equals(getProperty("i2p.dummyPeerManager", "false")))
+ if (!getBooleanProperty("i2p.dummyPeerManager"))
_peerManagerFacade = new PeerManagerFacadeImpl(this);
else
_peerManagerFacade = new DummyPeerManagerFacade();
_profileManager = new ProfileManagerImpl(this);
_bandwidthLimiter = new FIFOBandwidthLimiter(this);
- if ("false".equals(getProperty("i2p.dummyTunnelManager", "false")))
+ if (!getBooleanProperty("i2p.dummyTunnelManager"))
_tunnelManager = new TunnelPoolManager(this);
else
_tunnelManager = new DummyTunnelManagerFacade();
@@ -529,7 +544,7 @@ public class RouterContext extends I2PAppContext {
*/
@Override
public InternalClientManager internalClientManager() {
- return _clientManagerFacade;
+ return _internalClientManager;
}
/**
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index a411b0099..e1f87e077 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 0;
+ public final static long BUILD = 16;
/** for example "-test" */
public final static String EXTRA = "";
diff --git a/router/java/src/net/i2p/router/TunnelPoolSettings.java b/router/java/src/net/i2p/router/TunnelPoolSettings.java
index 841a22e03..d5669e4a6 100644
--- a/router/java/src/net/i2p/router/TunnelPoolSettings.java
+++ b/router/java/src/net/i2p/router/TunnelPoolSettings.java
@@ -53,7 +53,7 @@ public class TunnelPoolSettings {
public static final int DEFAULT_BACKUP_QUANTITY = 0;
// public static final int DEFAULT_REBUILD_PERIOD = 60*1000;
public static final int DEFAULT_DURATION = 10*60*1000;
- public static final int DEFAULT_LENGTH = 2;
+ public static final int DEFAULT_LENGTH = 3;
public static final int DEFAULT_LENGTH_VARIANCE = 0;
public static final boolean DEFAULT_ALLOW_ZERO_HOP = true;
public static final int DEFAULT_IP_RESTRICTION = 2; // class B (/16)
diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
index 052a06e3b..94d26378f 100644
--- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
+++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
@@ -58,7 +58,7 @@ import net.i2p.util.SimpleTimer;
class ClientConnectionRunner {
protected final Log _log;
protected final RouterContext _context;
- private final ClientManager _manager;
+ protected final ClientManager _manager;
/** socket for this particular peer connection */
private final Socket _socket;
/** output stream of the socket that I2CP messages bound to the client should be written to */
@@ -137,7 +137,7 @@ class ClientConnectionRunner {
if (_dead || _reader != null)
throw new IllegalStateException();
_reader = new I2CPMessageReader(new BufferedInputStream(_socket.getInputStream(), BUF_SIZE),
- new ClientMessageEventListener(_context, this, true));
+ createListener());
_writer = new ClientWriterRunner(_context, this);
I2PThread t = new I2PThread(_writer);
t.setName("I2CP Writer " + __id.incrementAndGet());
@@ -148,6 +148,14 @@ class ClientConnectionRunner {
// TODO need a cleaner for unclaimed items in _messages, but we have no timestamps...
}
+ /**
+ * Allow override for testing
+ * @since 0.9.8
+ */
+ protected I2CPMessageReader.I2CPMessageEventListener createListener() {
+ return new ClientMessageEventListener(_context, this, true);
+ }
+
/**
* Die a horrible death. Cannot be restarted.
*/
@@ -460,8 +468,8 @@ class ClientConnectionRunner {
* @param set LeaseSet with requested leases - this object must be updated to contain the
* signed version (as well as any changed/added/removed Leases)
* @param expirationTime ms to wait before failing
- * @param onCreateJob Job to run after the LeaseSet is authorized
- * @param onFailedJob Job to run after the timeout passes without receiving authorization
+ * @param onCreateJob Job to run after the LeaseSet is authorized, null OK
+ * @param onFailedJob Job to run after the timeout passes without receiving authorization, null OK
*/
void requestLeaseSet(LeaseSet set, long expirationTime, Job onCreateJob, Job onFailedJob) {
if (_dead) {
diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java
index e82ef49d9..21cac726a 100644
--- a/router/java/src/net/i2p/router/client/ClientManager.java
+++ b/router/java/src/net/i2p/router/client/ClientManager.java
@@ -10,6 +10,7 @@ package net.i2p.router.client;
import java.io.IOException;
import java.io.Writer;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -44,7 +45,7 @@ import net.i2p.util.Log;
*/
class ClientManager {
private final Log _log;
- private ClientListenerRunner _listener;
+ protected ClientListenerRunner _listener;
// Destination --> ClientConnectionRunner
// Locked for adds/removes but not lookups
private final Map _runners;
@@ -53,8 +54,9 @@ class ClientManager {
private final Map _runnersByHash;
// ClientConnectionRunner for clients w/out a Dest yet
private final Set _pendingRunners;
- private final RouterContext _ctx;
- private volatile boolean _isStarted;
+ protected final RouterContext _ctx;
+ protected final int _port;
+ protected volatile boolean _isStarted;
/** Disable external interface, allow internal clients only @since 0.8.3 */
private static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface";
@@ -65,6 +67,10 @@ class ClientManager {
private static final long REQUEST_LEASESET_TIMEOUT = 60*1000;
+ /**
+ * Does not start the listeners.
+ * Caller must call start()
+ */
public ClientManager(RouterContext context, int port) {
_ctx = context;
_log = context.logManager().getLog(ClientManager.class);
@@ -75,22 +81,27 @@ class ClientManager {
_runners = new ConcurrentHashMap();
_runnersByHash = new ConcurrentHashMap();
_pendingRunners = new HashSet();
- startListeners(port);
+ _port = port;
// following are for RequestLeaseSetJob
_ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 60*60*1000 });
_ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 60*60*1000 });
_ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 60*60*1000 });
}
+ /** @since 0.9.8 */
+ public synchronized void start() {
+ startListeners();
+ }
+
/** Todo: Start a 3rd listener for IPV6? */
- private void startListeners(int port) {
+ protected void startListeners() {
if (!_ctx.getBooleanProperty(PROP_DISABLE_EXTERNAL)) {
// there's no option to start both an SSL and non-SSL listener
if (_ctx.getBooleanProperty(PROP_ENABLE_SSL))
- _listener = new SSLClientListenerRunner(_ctx, this, port);
+ _listener = new SSLClientListenerRunner(_ctx, this, _port);
else
- _listener = new ClientListenerRunner(_ctx, this, port);
- Thread t = new I2PThread(_listener, "ClientListener:" + port, true);
+ _listener = new ClientListenerRunner(_ctx, this, _port);
+ Thread t = new I2PThread(_listener, "ClientListener:" + _port, true);
t.start();
}
_isStarted = true;
@@ -102,9 +113,7 @@ class ClientManager {
// to let the old listener die
try { Thread.sleep(2*1000); } catch (InterruptedException ie) {}
- int port = _ctx.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT,
- ClientManagerFacadeImpl.DEFAULT_PORT);
- startListeners(port);
+ startListeners();
}
/**
@@ -404,12 +413,18 @@ class ClientManager {
}
}
+ /**
+ * @return unmodifiable, not a copy
+ */
Set getRunnerDestinations() {
- Set dests = new HashSet();
- dests.addAll(_runners.keySet());
- return dests;
+ return Collections.unmodifiableSet(_runners.keySet());
}
+ /**
+ * Unused
+ *
+ * @param dest null for all local destinations
+ */
public void reportAbuse(Destination dest, String reason, int severity) {
if (dest != null) {
ClientConnectionRunner runner = getRunner(dest);
@@ -417,9 +432,7 @@ class ClientManager {
runner.reportAbuse(reason, severity);
}
} else {
- Set dests = getRunnerDestinations();
- for (Iterator iter = dests.iterator(); iter.hasNext(); ) {
- Destination d = (Destination)iter.next();
+ for (Destination d : _runners.keySet()) {
reportAbuse(d, reason, severity);
}
}
diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
index a9133182d..edb92147a 100644
--- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
+++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
@@ -56,6 +56,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade implements Inte
_log.info("Starting up the client subsystem");
int port = _context.getProperty(PROP_CLIENT_PORT, DEFAULT_PORT);
_manager = new ClientManager(_context, port);
+ _manager.start();
}
public synchronized void shutdown() {
@@ -82,12 +83,12 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade implements Inte
public boolean isAlive() { return _manager != null && _manager.isAlive(); }
private static final long MAX_TIME_TO_REBUILD = 10*60*1000;
+
@Override
public boolean verifyClientLiveliness() {
if (_manager == null) return true;
boolean lively = true;
- for (Iterator iter = _manager.getRunnerDestinations().iterator(); iter.hasNext(); ) {
- Destination dest = (Destination)iter.next();
+ for (Destination dest : _manager.getRunnerDestinations()) {
ClientConnectionRunner runner = _manager.getRunner(dest);
if ( (runner == null) || (runner.getIsDead())) continue;
LeaseSet ls = runner.getLeaseSet();
diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
index de71f26a7..4107de39b 100644
--- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
+++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
@@ -46,8 +46,8 @@ import net.i2p.util.RandomSource;
*/
class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventListener {
private final Log _log;
- private final RouterContext _context;
- private final ClientConnectionRunner _runner;
+ protected final RouterContext _context;
+ protected final ClientConnectionRunner _runner;
private final boolean _enforceAuth;
private static final String PROP_AUTH = "i2cp.auth";
@@ -73,40 +73,40 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_log.debug("Message received: \n" + message);
switch (message.getType()) {
case GetDateMessage.MESSAGE_TYPE:
- handleGetDate(reader, (GetDateMessage)message);
+ handleGetDate((GetDateMessage)message);
break;
case SetDateMessage.MESSAGE_TYPE:
- handleSetDate(reader, (SetDateMessage)message);
+ handleSetDate((SetDateMessage)message);
break;
case CreateSessionMessage.MESSAGE_TYPE:
- handleCreateSession(reader, (CreateSessionMessage)message);
+ handleCreateSession((CreateSessionMessage)message);
break;
case SendMessageMessage.MESSAGE_TYPE:
- handleSendMessage(reader, (SendMessageMessage)message);
+ handleSendMessage((SendMessageMessage)message);
break;
case SendMessageExpiresMessage.MESSAGE_TYPE:
- handleSendMessage(reader, (SendMessageExpiresMessage)message);
+ handleSendMessage((SendMessageExpiresMessage)message);
break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
- handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
+ handleReceiveBegin((ReceiveMessageBeginMessage)message);
break;
case ReceiveMessageEndMessage.MESSAGE_TYPE:
- handleReceiveEnd(reader, (ReceiveMessageEndMessage)message);
+ handleReceiveEnd((ReceiveMessageEndMessage)message);
break;
case CreateLeaseSetMessage.MESSAGE_TYPE:
- handleCreateLeaseSet(reader, (CreateLeaseSetMessage)message);
+ handleCreateLeaseSet((CreateLeaseSetMessage)message);
break;
case DestroySessionMessage.MESSAGE_TYPE:
- handleDestroySession(reader, (DestroySessionMessage)message);
+ handleDestroySession((DestroySessionMessage)message);
break;
case DestLookupMessage.MESSAGE_TYPE:
- handleDestLookup(reader, (DestLookupMessage)message);
+ handleDestLookup((DestLookupMessage)message);
break;
case ReconfigureSessionMessage.MESSAGE_TYPE:
- handleReconfigureSession(reader, (ReconfigureSessionMessage)message);
+ handleReconfigureSession((ReconfigureSessionMessage)message);
break;
case GetBandwidthLimitsMessage.MESSAGE_TYPE:
- handleGetBWLimits(reader, (GetBandwidthLimitsMessage)message);
+ handleGetBWLimits((GetBandwidthLimitsMessage)message);
break;
default:
if (_log.shouldLog(Log.ERROR))
@@ -131,7 +131,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_runner.disconnected();
}
- private void handleGetDate(I2CPMessageReader reader, GetDateMessage message) {
+ private void handleGetDate(GetDateMessage message) {
// sent by clients >= 0.8.7
String clientVersion = message.getVersion();
if (clientVersion != null)
@@ -148,7 +148,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
/**
* As of 0.8.7, does nothing. Do not allow a client to set the router's clock.
*/
- private void handleSetDate(I2CPMessageReader reader, SetDateMessage message) {
+ private void handleSetDate(SetDateMessage message) {
//_context.clock().setNow(message.getDate().getTime());
}
@@ -160,7 +160,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
* DisconnectMessage in return, and not wait around for our DisconnectMessage.
* So keep it simple.
*/
- private void handleCreateSession(I2CPMessageReader reader, CreateSessionMessage message) {
+ private void handleCreateSession(CreateSessionMessage message) {
SessionConfig in = message.getSessionConfig();
if (in.verifySignature()) {
if (_log.shouldLog(Log.DEBUG))
@@ -209,17 +209,24 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
if (_log.shouldLog(Log.DEBUG))
_log.debug("after sessionEstablished for " + message.getSessionConfig().getDestination().calculateHash().toBase64());
-
- _context.jobQueue().addJob(new CreateSessionJob(_context, _runner));
+ startCreateSessionJob();
}
+ /**
+ * Override for testing
+ * @since 0.9.8
+ *
+ */
+ protected void startCreateSessionJob() {
+ _context.jobQueue().addJob(new CreateSessionJob(_context, _runner));
+ }
/**
* Handle a SendMessageMessage: give it a message Id, have the ClientManager distribute
* it, and send the client an ACCEPTED message
*
*/
- private void handleSendMessage(I2CPMessageReader reader, SendMessageMessage message) {
+ private void handleSendMessage(SendMessageMessage message) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("handleSendMessage called");
long beforeDistribute = _context.clock().now();
@@ -236,7 +243,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
* The client asked for a message, so we send it to them.
*
*/
- private void handleReceiveBegin(I2CPMessageReader reader, ReceiveMessageBeginMessage message) {
+ private void handleReceiveBegin(ReceiveMessageBeginMessage message) {
if (_runner.isDead()) return;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handling recieve begin: id = " + message.getMessageId());
@@ -266,17 +273,18 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
* pending queue, though it should.
*
*/
- private void handleReceiveEnd(I2CPMessageReader reader, ReceiveMessageEndMessage message) {
+ private void handleReceiveEnd(ReceiveMessageEndMessage message) {
_runner.removePayload(new MessageId(message.getMessageId()));
}
- private void handleDestroySession(I2CPMessageReader reader, DestroySessionMessage message) {
+ private void handleDestroySession(DestroySessionMessage message) {
if (_log.shouldLog(Log.INFO))
_log.info("Destroying client session " + _runner.getSessionId());
_runner.stopRunning();
}
- private void handleCreateLeaseSet(I2CPMessageReader reader, CreateLeaseSetMessage message) {
+ /** override for testing */
+ protected void handleCreateLeaseSet(CreateLeaseSetMessage message) {
if ( (message.getLeaseSet() == null) || (message.getPrivateKey() == null) || (message.getSigningPrivateKey() == null) ) {
if (_log.shouldLog(Log.ERROR))
_log.error("Null lease set granted: " + message);
@@ -293,7 +301,8 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_runner.leaseSetCreated(message.getLeaseSet());
}
- private void handleDestLookup(I2CPMessageReader reader, DestLookupMessage message) {
+ /** override for testing */
+ protected void handleDestLookup(DestLookupMessage message) {
_context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getHash()));
}
@@ -305,7 +314,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
* Note that this does NOT update the few options handled in
* ClientConnectionRunner.sessionEstablished(). Those can't be changed later.
*/
- private void handleReconfigureSession(I2CPMessageReader reader, ReconfigureSessionMessage message) {
+ private void handleReconfigureSession(ReconfigureSessionMessage message) {
if (_log.shouldLog(Log.INFO))
_log.info("Updating options - old: " + _runner.getConfig() + " new: " + message.getSessionConfig());
if (!message.getSessionConfig().getDestination().equals(_runner.getConfig().getDestination())) {
@@ -343,7 +352,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
* This could someday give a different answer to each client.
* But it's not enforced anywhere.
*/
- private void handleGetBWLimits(I2CPMessageReader reader, GetBandwidthLimitsMessage message) {
+ protected void handleGetBWLimits(GetBandwidthLimitsMessage message) {
if (_log.shouldLog(Log.INFO))
_log.info("Got BW Limits request");
int in = _context.bandwidthLimiter().getInboundKBytesPerSecond() * 4 / 7;
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreKeySelectorJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreKeySelectorJob.java
index 6fa041660..f11937571 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreKeySelectorJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreKeySelectorJob.java
@@ -38,7 +38,7 @@ class ExploreKeySelectorJob extends JobImpl {
public String getName() { return "Explore Key Selector Job"; }
public void runJob() {
- if (((FloodfillNetworkDatabaseFacade)_facade).floodfillEnabled()) {
+ if (_facade.floodfillEnabled()) {
requeue(30*RERUN_DELAY_MS);
return;
}
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
index 909beb914..3a058fc78 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
@@ -261,10 +261,8 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
}
}
+ @Override
public boolean floodfillEnabled() { return _floodfillEnabled; }
- public static boolean floodfillEnabled(RouterContext ctx) {
- return ((FloodfillNetworkDatabaseFacade)ctx.netDb()).floodfillEnabled();
- }
/**
* @param peer may be null, returns false if null
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
index 7567e79b3..86170d237 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
@@ -303,7 +303,7 @@ class FloodfillPeerSelector extends PeerSelector {
* @since 0.9.5 modified from ProfileOrganizer
*/
private Set maskedIPSet(Hash peer, RouterInfo pinfo, int mask) {
- Set rv = new HashSet(2);
+ Set rv = new HashSet(4);
byte[] commIP = _context.commSystem().getIP(peer);
if (commIP != null)
rv.add(maskedIP(commIP, mask));
@@ -322,12 +322,22 @@ class FloodfillPeerSelector extends PeerSelector {
/**
* generate an arbitrary unique value for this ip/mask (mask = 1-4)
+ * If IPv6, force mask = 8.
* @since 0.9.5 copied from ProfileOrganizer
*/
private static Integer maskedIP(byte[] ip, int mask) {
- int rv = 0;
- for (int i = 0; i < mask; i++)
- rv = (rv << 8) | (ip[i] & 0xff);
+ int rv = ip[0];
+ if (ip.length == 16) {
+ for (int i = 1; i < 8; i++) {
+ rv <<= i * 4;
+ rv ^= ip[i];
+ }
+ } else {
+ for (int i = 1; i < mask; i++) {
+ rv <<= 8;
+ rv ^= ip[i];
+ }
+ }
return Integer.valueOf(rv);
}
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
index 1409ecc2a..0d03d7d4f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
@@ -38,7 +38,7 @@ public class HandleFloodfillDatabaseLookupMessageJob extends HandleDatabaseLooku
*/
@Override
protected boolean answerAllQueries() {
- if (!FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext())) return false;
+ if (!getContext().netDb().floodfillEnabled()) return false;
return FloodfillNetworkDatabaseFacade.isFloodfill(getContext().router().getRouterInfo());
}
@@ -52,7 +52,7 @@ public class HandleFloodfillDatabaseLookupMessageJob extends HandleDatabaseLooku
super.sendClosest(key, routerInfoSet, toPeer, replyTunnel);
// go away, you got the wrong guy, send our RI back unsolicited
- if (!FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext())) {
+ if (!getContext().netDb().floodfillEnabled()) {
// We could just call sendData(myhash, myri, toPeer, replyTunnel) but
// that would increment the netDb.lookupsHandled and netDb.lookupsMatched stats
DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext());
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
index 98a41874b..a56f23098 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
@@ -187,7 +187,7 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl {
// flood it
if (invalidMessage == null &&
- FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()) &&
+ getContext().netDb().floodfillEnabled() &&
_message.getReplyToken() > 0) {
if (wasNew) {
// DOS prevention
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index 553c734cf..dc21ff943 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -763,7 +763,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
// As the net grows this won't be sufficient, and we'll have to implement
// flushing some from memory, while keeping all on disk.
long adjustedExpiration;
- if (FloodfillNetworkDatabaseFacade.floodfillEnabled(_context))
+ if (floodfillEnabled())
adjustedExpiration = ROUTER_INFO_EXPIRATION_FLOODFILL;
else
// _kb.size() includes leasesets but that's ok
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
index 7df231726..80815ea82 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
@@ -134,7 +134,7 @@ class SearchJob extends JobImpl {
// The other two places this was called (one below and one in FNDF)
// have been commented out.
// Returning false essentially enables kademlia as a backup to floodfill for search responses.
- if (FloodfillNetworkDatabaseFacade.floodfillEnabled(ctx))
+ if (ctx.netDb().floodfillEnabled())
return false;
return ctx.getProperty("netDb.floodfillOnly", DEFAULT_FLOODFILL_ONLY);
}
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StartExplorersJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StartExplorersJob.java
index 0f9f4557b..49198ad9f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StartExplorersJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StartExplorersJob.java
@@ -51,7 +51,7 @@ class StartExplorersJob extends JobImpl {
public String getName() { return "Start Explorers Job"; }
public void runJob() {
- if (! (((FloodfillNetworkDatabaseFacade)_facade).floodfillEnabled() ||
+ if (! (_facade.floodfillEnabled() ||
getContext().router().gracefulShutdownInProgress())) {
int num = MAX_PER_RUN;
if (_facade.getDataStore().size() < LOW_ROUTERS)
@@ -93,7 +93,7 @@ class StartExplorersJob extends JobImpl {
*/
private long getNextRunDelay() {
// we don't explore if floodfill
- if (((FloodfillNetworkDatabaseFacade)_facade).floodfillEnabled())
+ if (_facade.floodfillEnabled())
return MAX_RERUN_DELAY_MS;
// If we don't know too many peers, or just started, explore aggressively
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index 55b57bae9..4ded9f81c 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -1264,7 +1264,7 @@ public class ProfileOrganizer {
* @return an opaque set of masked IPs for this peer
*/
private Set maskedIPSet(Hash peer, int mask) {
- Set rv = new HashSet(2);
+ Set rv = new HashSet(4);
byte[] commIP = _context.commSystem().getIP(peer);
if (commIP != null)
rv.add(maskedIP(commIP, mask));
@@ -1282,11 +1282,23 @@ public class ProfileOrganizer {
return rv;
}
- /** generate an arbitrary unique value for this ip/mask (mask = 1-4) */
+ /**
+ * generate an arbitrary unique value for this ip/mask (mask = 1-4)
+ * If IPv6, force mask = 8.
+ */
private static Integer maskedIP(byte[] ip, int mask) {
- int rv = 0;
- for (int i = 0; i < mask; i++)
- rv = (rv << 8) | (ip[i] & 0xff);
+ int rv = ip[0];
+ if (ip.length == 16) {
+ for (int i = 1; i < 8; i++) {
+ rv <<= i * 4;
+ rv ^= ip[i];
+ }
+ } else {
+ for (int i = 1; i < mask; i++) {
+ rv <<= 8;
+ rv ^= ip[i];
+ }
+ }
return Integer.valueOf(rv);
}
diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
index 6bed29d73..bb60b978a 100644
--- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
+++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
@@ -10,15 +10,14 @@ package net.i2p.router.transport;
import java.io.IOException;
import java.io.Writer;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.Vector;
import net.i2p.data.Hash;
@@ -27,9 +26,6 @@ import net.i2p.data.RouterInfo;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
-import net.i2p.router.transport.ntcp.NTCPAddress;
-import net.i2p.router.transport.ntcp.NTCPTransport;
-import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
@@ -45,6 +41,12 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
private final GeoIP _geoIP;
private volatile boolean _netMonitorStatus;
private boolean _wasStarted;
+
+ /**
+ * Disable connections for testing
+ * @since IPv6
+ */
+ private static final String PROP_DISABLED = "i2np.disable";
public CommSystemFacadeImpl(RouterContext context) {
_context = context;
@@ -125,23 +127,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
return sum * 1000 / frameSize;
}
- public List getBids(OutNetMessage msg) {
- return _manager.getBids(msg);
- }
- public TransportBid getBid(OutNetMessage msg) {
- return _manager.getBid(msg);
- }
- public TransportBid getNextBid(OutNetMessage msg) {
- return _manager.getNextBid(msg);
- }
- int getTransportCount() { return _manager.getTransportCount(); }
-
/** Send the message out */
public void processMessage(OutNetMessage msg) {
+ if (isDummy()) {
+ // testing
+ GetBidsJob.fail(_context, msg);
+ return;
+ }
//GetBidsJob j = new GetBidsJob(_context, this, msg);
//j.runJob();
//long before = _context.clock().now();
- GetBidsJob.getBids(_context, this, msg);
+ GetBidsJob.getBids(_context, _manager, msg);
// < 0.4 ms
//_context.statManager().addRateData("transport.getBidsJobTime", _context.clock().now() - before);
}
@@ -167,7 +163,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
}
@Override
- public List getMostRecentErrorMessages() {
+ public List getMostRecentErrorMessages() {
return _manager.getMostRecentErrorMessages();
}
@@ -190,240 +186,33 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
/** @return non-null, possibly empty */
@Override
- public Set createAddresses() {
+ public List createAddresses() {
// No, don't do this, it makes it almost impossible to build inbound tunnels
//if (_context.router().isHidden())
// return Collections.EMPTY_SET;
- Map addresses = _manager.getAddresses();
- boolean newCreated = false;
-
- if (!addresses.containsKey(NTCPTransport.STYLE)) {
- RouterAddress addr = createNTCPAddress(_context);
- if (_log.shouldLog(Log.INFO))
- _log.info("NTCP address: " + addr);
- if (addr != null) {
- addresses.put(NTCPTransport.STYLE, addr);
- newCreated = true;
- }
- }
-
+ List addresses = new ArrayList(_manager.getAddresses());
if (_log.shouldLog(Log.INFO))
- _log.info("Creating addresses: " + addresses + " isNew? " + newCreated, new Exception("creator"));
- return new HashSet(addresses.values());
+ _log.info("Creating addresses: " + addresses, new Exception("creator"));
+ return addresses;
}
- public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
- public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
- public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
- public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
-
- /**
- * This only creates an address if the hostname AND port are set in router.config,
- * which should be rare.
- * Otherwise, notifyReplaceAddress() below takes care of it.
- * Note this is called both from above and from NTCPTransport.startListening()
- *
- * This should really be moved to ntcp/NTCPTransport.java, why is it here?
- */
- public static RouterAddress createNTCPAddress(RouterContext ctx) {
- if (!TransportManager.isNTCPEnabled(ctx)) return null;
- String name = ctx.router().getConfigSetting(PROP_I2NP_NTCP_HOSTNAME);
- String port = ctx.router().getConfigSetting(PROP_I2NP_NTCP_PORT);
- /*
- boolean isNew = false;
- if (name == null) {
- name = "localhost";
- isNew = true;
- }
- if (port == null) {
- port = String.valueOf(ctx.random().nextInt(10240)+1024);
- isNew = true;
- }
- */
- if ( (name == null) || (port == null) || (name.trim().length() <= 0) || ("null".equals(name)) )
- return null;
- try {
- int p = Integer.parseInt(port);
- if ( (p <= 0) || (p > 64*1024) )
- return null;
- } catch (NumberFormatException nfe) {
- return null;
- }
- Properties props = new Properties();
- props.setProperty(NTCPAddress.PROP_HOST, name);
- props.setProperty(NTCPAddress.PROP_PORT, port);
- RouterAddress addr = new RouterAddress();
- addr.setCost(NTCPAddress.DEFAULT_COST);
- //addr.setExpiration(null);
- addr.setOptions(props);
- addr.setTransportStyle(NTCPTransport.STYLE);
- //if (isNew) {
- // why save the same thing?
- Map changes = new HashMap();
- changes.put(PROP_I2NP_NTCP_HOSTNAME, name);
- changes.put(PROP_I2NP_NTCP_PORT, port);
- ctx.router().saveConfig(changes, null);
- //}
- return addr;
- }
-
/**
* UDP changed addresses, tell NTCP and restart
- * This should really be moved to ntcp/NTCPTransport.java, why is it here?
+ *
+ * All the work moved to NTCPTransport.externalAddressReceived()
+ *
+ * @param udpAddr may be null; or udpAddr's host/IP may be null
*/
@Override
- public synchronized void notifyReplaceAddress(RouterAddress udpAddr) {
- if (udpAddr == null)
- return;
- NTCPTransport t = (NTCPTransport) _manager.getTransport(NTCPTransport.STYLE);
- if (t == null)
- return;
- RouterAddress oldAddr = t.getCurrentAddress();
- if (_log.shouldLog(Log.INFO))
- _log.info("Changing NTCP Address? was " + oldAddr);
- RouterAddress newAddr = new RouterAddress();
- newAddr.setTransportStyle(NTCPTransport.STYLE);
- Properties newProps = new Properties();
- if (oldAddr == null) {
- newAddr.setCost(NTCPAddress.DEFAULT_COST);
- } else {
- newAddr.setCost(oldAddr.getCost());
- newProps.putAll(oldAddr.getOptionsMap());
- }
-
- boolean changed = false;
-
- // Auto Port Setting
- // old behavior (<= 0.7.3): auto-port defaults to false, and true trumps explicit setting
- // new behavior (>= 0.7.4): auto-port defaults to true, but explicit setting trumps auto
- // TODO rewrite this to operate on ints instead of strings
- String oport = newProps.getProperty(NTCPAddress.PROP_PORT);
- String nport = null;
- String cport = _context.getProperty(PROP_I2NP_NTCP_PORT);
- if (cport != null && cport.length() > 0) {
- nport = cport;
- } else if (_context.getBooleanPropertyDefaultTrue(PROP_I2NP_NTCP_AUTO_PORT)) {
- // 0.9.6 change
- // This wasn't quite right, as udpAddr is the EXTERNAL port and we really
- // want NTCP to bind to the INTERNAL port the first time,
- // because if they are different, the NAT is changing them, and
- // it probably isn't mapping UDP and TCP the same.
+ public void notifyReplaceAddress(RouterAddress udpAddr) {
+ byte[] ip = udpAddr != null ? udpAddr.getIP() : null;
+ int port = udpAddr != null ? udpAddr.getPort() : 0;
+ if (port < 0) {
Transport udp = _manager.getTransport(UDPTransport.STYLE);
- if (udp != null) {
- int udpIntPort = udp.getRequestedPort();
- if (udpIntPort > 0)
- // should always be true
- nport = Integer.toString(udpIntPort);
- }
- if (nport == null)
- // fallback
- nport = udpAddr.getOption(UDPAddress.PROP_PORT);
+ if (udp != null)
+ port = udp.getRequestedPort();
}
- if (_log.shouldLog(Log.INFO))
- _log.info("old: " + oport + " config: " + cport + " new: " + nport);
- if (nport == null || nport.length() <= 0)
- return;
- // 0.9.6 change
- // Don't have NTCP "chase" SSU's external port,
- // as it may change, possibly frequently.
- //if (oport == null || ! oport.equals(nport)) {
- if (oport == null) {
- newProps.setProperty(NTCPAddress.PROP_PORT, nport);
- changed = true;
- }
-
- // Auto IP Setting
- // old behavior (<= 0.7.3): auto-ip defaults to false, and trumps configured hostname,
- // and ignores reachability status - leading to
- // "firewalled with inbound TCP enabled" warnings.
- // new behavior (>= 0.7.4): auto-ip defaults to true, and explicit setting trumps auto,
- // and only takes effect if reachability is OK.
- // And new "always" setting ignores reachability status, like
- // "true" was in 0.7.3
- String ohost = newProps.getProperty(NTCPAddress.PROP_HOST);
- String enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "true").toLowerCase(Locale.US);
- String name = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME);
- // hostname config trumps auto config
- if (name != null && name.length() > 0)
- enabled = "false";
- Transport udp = _manager.getTransport(UDPTransport.STYLE);
- short status = STATUS_UNKNOWN;
- if (udp != null)
- status = udp.getReachabilityStatus();
- if (_log.shouldLog(Log.INFO))
- _log.info("old: " + ohost + " config: " + name + " auto: " + enabled + " status: " + status);
- if (enabled.equals("always") ||
- (Boolean.parseBoolean(enabled) && status == STATUS_OK)) {
- String nhost = udpAddr.getOption(UDPAddress.PROP_HOST);
- if (_log.shouldLog(Log.INFO))
- _log.info("old: " + ohost + " config: " + name + " new: " + nhost);
- if (nhost == null || nhost.length() <= 0)
- return;
- if (ohost == null || ! ohost.equalsIgnoreCase(nhost)) {
- newProps.setProperty(NTCPAddress.PROP_HOST, nhost);
- changed = true;
- }
- } else if (enabled.equals("false") &&
- name != null && name.length() > 0 &&
- !name.equals(ohost) &&
- nport != null) {
- // Host name is configured, and we have a port (either auto or configured)
- // but we probably only get here if the port is auto,
- // otherwise createNTCPAddress() would have done it already
- if (_log.shouldLog(Log.INFO))
- _log.info("old: " + ohost + " config: " + name + " new: " + name);
- newProps.setProperty(NTCPAddress.PROP_HOST, name);
- changed = true;
- } else if (ohost == null || ohost.length() <= 0) {
- return;
- } else if (Boolean.parseBoolean(enabled) && status != STATUS_OK) {
- // UDP transitioned to not-OK, turn off NTCP address
- // This will commonly happen at startup if we were initially OK
- // because UPnP was successful, but a subsequent SSU Peer Test determines
- // we are still firewalled (SW firewall, bad UPnP indication, etc.)
- if (_log.shouldLog(Log.INFO))
- _log.info("old: " + ohost + " config: " + name + " new: null");
- newAddr = null;
- changed = true;
- }
-
- if (!changed) {
- if (oldAddr != null) {
- int oldCost = oldAddr.getCost();
- int newCost = NTCPAddress.DEFAULT_COST;
- if (TransportImpl.ADJUST_COST && !t.haveCapacity())
- newCost++;
- if (newCost != oldCost) {
- oldAddr.setCost(newCost);
- if (_log.shouldLog(Log.WARN))
- _log.warn("Changing NTCP cost from " + oldCost + " to " + newCost);
- } else {
- _log.info("No change to NTCP Address");
- }
- } else {
- _log.info("No change to NTCP Address");
- }
- return;
- }
-
- // stopListening stops the pumper, readers, and writers, so required even if
- // oldAddr == null since startListening starts them all again
- //
- // really need to fix this so that we can change or create an inbound address
- // without tearing down everything
- // Especially on disabling the address, we shouldn't tear everything down.
- //
- _log.warn("Halting NTCP to change address");
- t.stopListening();
- if (newAddr != null)
- newAddr.setOptions(newProps);
- // Wait for NTCP Pumper to stop so we don't end up with two...
- while (t.isAlive()) {
- try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
- }
- t.restartListening(newAddr);
- _log.warn("Changed NTCP Address and started up, address is now " + newAddr);
- return;
+ _manager.externalAddressReceived(Transport.AddressSource.SOURCE_SSU, ip, port);
}
/*
@@ -451,10 +240,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(iter.next());
if (ri == null)
continue;
- String host = getIPString(ri);
- if (host == null)
+ byte[] ip = getIP(ri);
+ if (ip == null)
continue;
- _geoIP.add(host);
+ _geoIP.add(ip);
}
_context.simpleScheduler().addPeriodicEvent(new Lookup(), 5000, LOOKUP_TIME);
}
@@ -491,31 +280,35 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
/**
* Uses the transport IP first because that lookup is fast,
- * then the SSU IP from the netDb.
+ * then the IP from the netDb.
*
* @return two-letter lower-case country code or null
*/
@Override
public String getCountry(Hash peer) {
byte[] ip = TransportImpl.getIP(peer);
+ //if (ip != null && ip.length == 4)
if (ip != null)
return _geoIP.get(ip);
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(peer);
if (ri == null)
return null;
- String s = getIPString(ri);
- if (s != null)
- return _geoIP.get(s);
+ ip = getIP(ri);
+ if (ip != null)
+ return _geoIP.get(ip);
return null;
}
- private String getIPString(RouterInfo ri) {
- // use SSU only, it is likely to be an IP not a hostname,
- // we don't want to generate a lot of DNS queries at startup
- RouterAddress ra = ri.getTargetAddress("SSU");
- if (ra == null)
- return null;
- return ra.getOption("host");
+ private static byte[] getIP(RouterInfo ri) {
+ // Return first IP (v4 or v6) we find, any transport
+ // Assume IPv6 doesn't have geoIP for now
+ for (RouterAddress ra : ri.getAddresses()) {
+ byte[] rv = ra.getIP();
+ //if (rv != null && rv.length == 4)
+ if (rv != null)
+ return rv;
+ }
+ return null;
}
/** full name for a country code, or the code if we don't know the name */
@@ -556,9 +349,14 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
return buf.toString();
}
- /** @since 0.8.13 */
+ /**
+ * Is everything disabled for testing?
+ * @since 0.8.13
+ */
@Override
- public boolean isDummy() { return false; }
+ public boolean isDummy() {
+ return _context.getBooleanProperty(PROP_DISABLED);
+ }
/**
* Translate
diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java
index 5b6cc813f..c4b6f606c 100644
--- a/router/java/src/net/i2p/router/transport/GeoIP.java
+++ b/router/java/src/net/i2p/router/transport/GeoIP.java
@@ -48,8 +48,12 @@ class GeoIP {
private final Map _codeToName;
/** code to itself to prevent String proliferation */
private final Map _codeCache;
+
+ // In the following structures, an IPv4 IP is stored as a non-negative long, 0 to 2**32 - 1,
+ // and the first 8 bytes of an IPv6 IP are stored as a signed long.
private final Map _IPToCountry;
private final Set _pendingSearch;
+ private final Set _pendingIPv6Search;
private final Set _notFound;
private final AtomicBoolean _lock;
private int _lookupRunCount;
@@ -58,10 +62,11 @@ class GeoIP {
public GeoIP(RouterContext context) {
_context = context;
_log = context.logManager().getLog(GeoIP.class);
- _codeToName = new ConcurrentHashMap(256);
- _codeCache = new ConcurrentHashMap(256);
+ _codeToName = new ConcurrentHashMap(512);
+ _codeCache = new ConcurrentHashMap(512);
_IPToCountry = new ConcurrentHashMap();
_pendingSearch = new ConcurrentHashSet();
+ _pendingIPv6Search = new ConcurrentHashSet();
_notFound = new ConcurrentHashSet();
_lock = new AtomicBoolean();
readCountryFile();
@@ -81,6 +86,7 @@ class GeoIP {
_codeCache.clear();
_IPToCountry.clear();
_pendingSearch.clear();
+ _pendingIPv6Search.clear();
_notFound.clear();
}
@@ -107,6 +113,7 @@ class GeoIP {
public void blockingLookup() {
if (! _context.getBooleanPropertyDefaultTrue(PROP_GEOIP_ENABLED)) {
_pendingSearch.clear();
+ _pendingIPv6Search.clear();
return;
}
int pri = Thread.currentThread().getPriority();
@@ -132,18 +139,31 @@ class GeoIP {
// clear the negative cache every few runs, to prevent it from getting too big
if (((++_lookupRunCount) % CLEAR) == 0)
_notFound.clear();
+ // IPv4
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
- if (search.length <= 0)
- return;
_pendingSearch.clear();
- Arrays.sort(search);
- String[] countries = readGeoIPFile(search);
-
- for (int i = 0; i < countries.length; i++) {
- if (countries[i] != null)
- _IPToCountry.put(search[i], countries[i]);
- else
- _notFound.add(search[i]);
+ if (search.length > 0) {
+ Arrays.sort(search);
+ String[] countries = readGeoIPFile(search);
+ for (int i = 0; i < countries.length; i++) {
+ if (countries[i] != null)
+ _IPToCountry.put(search[i], countries[i]);
+ else
+ _notFound.add(search[i]);
+ }
+ }
+ // IPv6
+ search = _pendingIPv6Search.toArray(new Long[_pendingIPv6Search.size()]);
+ _pendingIPv6Search.clear();
+ if (search.length > 0) {
+ Arrays.sort(search);
+ String[] countries = GeoIPv6.readGeoIPFile(_context, search, _codeCache);
+ for (int i = 0; i < countries.length; i++) {
+ if (countries[i] != null)
+ _IPToCountry.put(search[i], countries[i]);
+ else
+ _notFound.add(search[i]);
+ }
}
} finally {
_lock.set(false);
@@ -169,16 +189,16 @@ class GeoIP {
*
*/
private void readCountryFile() {
- File GeoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT);
- GeoFile = new File(GeoFile, COUNTRY_FILE_DEFAULT);
- if (!GeoFile.exists()) {
+ File geoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT);
+ geoFile = new File(geoFile, COUNTRY_FILE_DEFAULT);
+ if (!geoFile.exists()) {
if (_log.shouldLog(Log.WARN))
- _log.warn("Country file not found: " + GeoFile.getAbsolutePath());
+ _log.warn("Country file not found: " + geoFile.getAbsolutePath());
return;
}
FileInputStream in = null;
try {
- in = new FileInputStream(GeoFile);
+ in = new FileInputStream(geoFile);
BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
String line = null;
while ( (line = br.readLine()) != null) {
@@ -228,11 +248,11 @@ class GeoIP {
*
*/
private String[] readGeoIPFile(Long[] search) {
- File GeoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT);
- GeoFile = new File(GeoFile, GEOIP_FILE_DEFAULT);
- if (!GeoFile.exists()) {
+ File geoFile = new File(_context.getBaseDir(), GEOIP_DIR_DEFAULT);
+ geoFile = new File(geoFile, GEOIP_FILE_DEFAULT);
+ if (!geoFile.exists()) {
if (_log.shouldLog(Log.WARN))
- _log.warn("GeoIP file not found: " + GeoFile.getAbsolutePath());
+ _log.warn("GeoIP file not found: " + geoFile.getAbsolutePath());
return new String[0];
}
String[] rv = new String[search.length];
@@ -240,7 +260,7 @@ class GeoIP {
long start = _context.clock().now();
FileInputStream in = null;
try {
- in = new FileInputStream(GeoFile);
+ in = new FileInputStream(geoFile);
String buf = null;
BufferedReader br = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
while ((buf = br.readLine()) != null && idx < search.length) {
@@ -268,7 +288,7 @@ class GeoIP {
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
- _log.error("Error reading the GeoFile", ioe);
+ _log.error("Error reading the geoFile", ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
@@ -307,6 +327,7 @@ class GeoIP {
/**
* Add to the list needing lookup
+ * @param ip IPv4 or IPv6
*/
public void add(String ip) {
byte[] pib = Addresses.getIP(ip);
@@ -314,20 +335,28 @@ class GeoIP {
add(pib);
}
+ /**
+ * Add to the list needing lookup
+ * @param ip IPv4 or IPv6
+ */
public void add(byte ip[]) {
- if (ip.length != 4)
- return;
add(toLong(ip));
}
+ /** see above for ip-to-long mapping */
private void add(long ip) {
Long li = Long.valueOf(ip);
- if (!(_IPToCountry.containsKey(li) || _notFound.contains(li)))
- _pendingSearch.add(li);
+ if (!(_IPToCountry.containsKey(li) || _notFound.contains(li))) {
+ if (ip >= 0 && ip < (1L << 32))
+ _pendingSearch.add(li);
+ else
+ _pendingIPv6Search.add(li);
+ }
}
/**
* Get the country for an IP from the cache.
+ * @param ip IPv4 or IPv6
* @return lower-case code, generally two letters, or null.
*/
public String get(String ip) {
@@ -338,23 +367,30 @@ class GeoIP {
/**
* Get the country for an IP from the cache.
+ * @param ip IPv4 or IPv6
* @return lower-case code, generally two letters, or null.
*/
public String get(byte ip[]) {
- if (ip.length != 4)
- return null;
return get(toLong(ip));
}
+ /** see above for ip-to-long mapping */
private String get(long ip) {
return _IPToCountry.get(Long.valueOf(ip));
}
+ /** see above for ip-to-long mapping */
private static long toLong(byte ip[]) {
int rv = 0;
- for (int i = 0; i < 4; i++)
- rv |= (ip[i] & 0xff) << ((3-i)*8);
- return rv & 0xffffffffl;
+ if (ip.length == 16) {
+ for (int i = 0; i < 8; i++)
+ rv |= (ip[i] & 0xffL) << ((7-i)*8);
+ return rv;
+ } else {
+ for (int i = 0; i < 4; i++)
+ rv |= (ip[i] & 0xff) << ((3-i)*8);
+ return rv & 0xffffffffl;
+ }
}
/**
diff --git a/router/java/src/net/i2p/router/transport/GeoIPv6.java b/router/java/src/net/i2p/router/transport/GeoIPv6.java
new file mode 100644
index 000000000..58a0fb290
--- /dev/null
+++ b/router/java/src/net/i2p/router/transport/GeoIPv6.java
@@ -0,0 +1,352 @@
+package net.i2p.router.transport;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Use at your own risk.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import net.i2p.I2PAppContext;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Generate compressed geoipv6.dat.gz file, and
+ * lookup entries in it.
+ *
+ * @since IPv6
+ */
+class GeoIPv6 {
+
+ private static final String GEOIP_DIR_DEFAULT = "geoip";
+ private static final String GEOIP_FILE_DEFAULT = "geoipv6.dat.gz";
+ private static final String MAGIC = "I2PGeoIPv6\0\001\0\0\0\0";
+ private static final String COMMENT = "I2P compressed geoipv6 file. See GeoIPv6.java for format.";
+ /** includes magic */
+ private static final int HEADER_LEN = 256;
+
+ /**
+ * Lookup search items in the geoip file.
+ * See below for format.
+ *
+ * @param search a sorted array of IPs to search
+ * @return an array of country codes, same order as the search param,
+ * or a zero-length array on failure
+ */
+ public static String[] readGeoIPFile(I2PAppContext context, Long[] search, Map codeCache) {
+ Log log = context.logManager().getLog(GeoIPv6.class);
+ File geoFile = new File(context.getBaseDir(), GEOIP_DIR_DEFAULT);
+ geoFile = new File(geoFile, GEOIP_FILE_DEFAULT);
+ if (!geoFile.exists()) {
+ if (log.shouldLog(Log.WARN))
+ log.warn("GeoIP file not found: " + geoFile.getAbsolutePath());
+ return new String[0];
+ }
+ return readGeoIPFile(geoFile, search, codeCache, log);
+ }
+
+ /**
+ * Lookup search items in the geoip file.
+ * See below for format.
+ *
+ * @param search a sorted array of IPs to search
+ * @return an array of country codes, same order as the search param,
+ * or a zero-length array on failure
+ */
+ private static String[] readGeoIPFile(File geoFile, Long[] search, Map codeCache, Log log) {
+ String[] rv = new String[search.length];
+ int idx = 0;
+ long start = System.currentTimeMillis();
+ InputStream in = null;
+ try {
+ in = new GZIPInputStream(new BufferedInputStream(new FileInputStream(geoFile)));
+ byte[] magic = new byte[MAGIC.length()];
+ DataHelper.read(in, magic);
+ if (!DataHelper.eq(magic, DataHelper.getASCII(MAGIC)))
+ throw new IOException("Not a IPv6 geoip data file");
+ // skip timestamp and comments
+ in.skip(HEADER_LEN - MAGIC.length());
+ byte[] buf = new byte[18];
+ while (DataHelper.read(in, buf) == 18 && idx < search.length) {
+ long ip1 = readLong(buf, 0);
+ long ip2 = readLong(buf, 8);
+ while (idx < search.length && search[idx].longValue() < ip1) {
+ idx++;
+ }
+ while (idx < search.length && search[idx].longValue() >= ip1 && search[idx].longValue() <= ip2) {
+ // written in lower case
+ String lc = new String(buf, 16, 2, "ISO-8859-1");
+ // replace the new string with the identical one from the cache
+ String cached = codeCache.get(lc);
+ if (cached == null)
+ cached = lc;
+ rv[idx++] = cached;
+ }
+ }
+ } catch (IOException ioe) {
+ if (log.shouldLog(Log.ERROR))
+ log.error("Error reading the geoFile", ioe);
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+
+ if (log.shouldLog(Log.INFO))
+ log.info("GeoIPv6 processing finished, time: " + (System.currentTimeMillis() - start));
+ return rv;
+ }
+
+
+ /**
+ * Read in and parse multiple IPv6 geoip CSV files,
+ * merge them, and write out a gzipped binary IPv6 geoip file.
+ *
+ * Acceptable input formats (IPv6 only):
+ *
+ * #comment (# must be in column 1)
+ * "text IP", "text IP", "bigint IP", "bigint IP", "country code", "country name"
+ *
+ * Quotes and spaces optional. Sorting not required.
+ * Country code case-insensitive.
+ * Fields 1, 2, and 5 are used; fields 3, 4, and 6 are ignored.
+ * This is identical to the format of the MaxMind GeoLite IPv6 file.
+ *
+ * Example:
+ *
+ * Output format:
+ * Bytes 0-9: Magic number "I2PGeoIPv6"
+ * Bytes 10-11: version (0x0001)
+ * Bytes 12-15 flags (0)
+ * Bytes 16-23: Date (long)
+ * Bytes 24-xx: Comment (UTF-8)
+ * Bytes xx-255: null padding
+ * Bytes 256-: 18 byte records:
+ * 8 byte from (/64)
+ * 8 byte to (/64)
+ * 2 byte country code LOWER case (ASCII)
+ * Data must be sorted (SIGNED twos complement), no overlap
+ *
+ *
+ * SLOW. For preprocessing only!
+ *
+ * @return success
+ */
+ private static boolean compressGeoIPv6CSVFiles(List inFiles, File outFile) {
+ boolean DEBUG = false;
+ List entries = new ArrayList(20000);
+ for (File geoFile : inFiles) {
+ int count = 0;
+ InputStream in = null;
+ try {
+ in = new BufferedInputStream(new FileInputStream(geoFile));
+ if (geoFile.getName().endsWith(".gz"))
+ in = new GZIPInputStream(in);
+ String buf = null;
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
+ while ((buf = br.readLine()) != null) {
+ try {
+ if (buf.charAt(0) == '#') {
+ continue;
+ }
+ String[] s = buf.split(",");
+ String ips1 = s[0].replace("\"", "").trim();
+ String ips2 = s[1].replace("\"", "").trim();
+ byte[] ip1 = InetAddress.getByName(ips1).getAddress();
+ byte[] ip2 = InetAddress.getByName(ips2).getAddress();
+ String country = s[4].replace("\"", "").trim().toLowerCase(Locale.US);
+ entries.add(new V6Entry(ip1, ip2, country));
+ count++;
+ } catch (UnknownHostException uhe) {
+ uhe.printStackTrace();
+ } catch (RuntimeException re) {
+ re.printStackTrace();
+ }
+ }
+ System.err.println("Read " + count + " entries from " + geoFile);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ //if (_log.shouldLog(Log.ERROR))
+ // _log.error("Error reading the geoFile", ioe);
+ return false;
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+ }
+ Collections.sort(entries);
+ // merge
+ V6Entry old = null;
+ for (int i = 0; i < entries.size(); i++) {
+ V6Entry e = entries.get(i);
+ if (DEBUG)
+ System.out.println("proc " + e.toString());
+ if (old != null) {
+ if (e.from == old.from && e.to == old.to) {
+ // dup
+ if (DEBUG)
+ System.out.println("remove dup " + e);
+ entries.remove(i);
+ i--;
+ continue;
+ }
+ if (e.from <= old.to) {
+ // overlap
+ // truncate old
+ if (e.from < old.to) {
+ V6Entry rewrite = new V6Entry(old.from, e.from - 1, old.cc);
+ if (DEBUG)
+ System.out.println("rewrite old to " + rewrite);
+ entries.set(i - 1, rewrite);
+ }
+ if (e.to < old.to) {
+ // e inside old, add new after e
+ V6Entry insert = new V6Entry(e.to + 1, old.to, old.cc);
+ if (DEBUG)
+ System.out.println("insert " + insert);
+ int j = i + 1;
+ while (j < entries.size() && insert.compareTo(entries.get(j)) > 0) {
+ j++;
+ }
+ entries.add(j, insert);
+ }
+ }
+ }
+ old = e;
+ }
+ OutputStream out = null;
+ try {
+ out = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
+ out.write(DataHelper.getASCII(MAGIC));
+ writeLong(out, System.currentTimeMillis());
+ byte[] comment = DataHelper.getUTF8(COMMENT);
+ out.write(comment);
+ out.write(new byte[256 - (16 + 8 + comment.length)]);
+ for (V6Entry e : entries) {
+ writeLong(out, e.from);
+ writeLong(out, e.to);
+ out.write(DataHelper.getASCII(e.cc));
+ }
+ System.err.println("Wrote " + entries.size() + " entries to " + outFile);
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ //if (_log.shouldLog(Log.ERROR))
+ // _log.error("Error reading the geoFile", ioe);
+ return false;
+ } finally {
+ if (out != null) try { out.close(); } catch (IOException ioe) {}
+ }
+ return true;
+ }
+
+ /**
+ * Used to temporarily hold, sort, and merge entries before compressing
+ */
+ private static class V6Entry implements Comparable {
+ public final long from, to;
+ public final String cc;
+
+ public V6Entry(byte[] f, byte[] t, String c) {
+ if (f.length != 16 || t.length != 16 || c.length() != 2)
+ throw new IllegalArgumentException();
+ from = toLong(f);
+ to = toLong(t);
+ cc = c;
+ if (to < from)
+ throw new IllegalArgumentException(toString());
+ }
+
+ public V6Entry(long f, long t, String c) {
+ from = f;
+ to = t;
+ cc = c;
+ if (t < f)
+ throw new IllegalArgumentException(toString());
+ }
+
+ /** twos complement */
+ public int compareTo(V6Entry r) {
+ if (from < r.from) return -1;
+ if (r.from < from) return 1;
+ if (to < r.to) return -1;
+ if (r.to < to) return 1;
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(from) + " -> 0x" + Long.toHexString(to) + " : " + cc;
+ }
+ }
+
+ private static long toLong(byte ip[]) {
+ long rv = 0;
+ for (int i = 0; i < 8; i++)
+ rv |= (ip[i] & 0xffL) << ((7-i)*8);
+ return rv;
+ }
+
+ /** like DataHelper.writeLong(rawStream, 8, value) but allows negative values */
+ private static void writeLong(OutputStream rawStream, long value) throws IOException {
+ for (int i = 56; i >= 0; i -= 8) {
+ byte cur = (byte) (value >> i);
+ rawStream.write(cur);
+ }
+ }
+
+ /** like DataHelper.readLong(src, offset, 8) but allows negative values */
+ private static long readLong(byte[] src, int offset) throws IOException {
+ long rv = 0;
+ int limit = offset + 8;
+ for (int i = offset; i < limit; i++) {
+ rv <<= 8;
+ rv |= src[i] & 0xFF;
+ }
+ return rv;
+ }
+
+ /**
+ * Merge and compress CSV files to I2P compressed format
+ *
+ * GeoIP infile1.csv[.gz] [infile2.csv[.gz]...] outfile.dat.gz
+ *
+ * Used to create the file for distribution, do not comment out
+ */
+ public static void main(String args[]) {
+ if (args.length < 2) {
+ System.err.println("Usage: GeoIP infile1.csv [infile2.csv...] outfile.dat.gz");
+ System.exit(1);
+ }
+ List infiles = new ArrayList();
+ for (int i = 0; i < args.length - 1; i++) {
+ infiles.add(new File(args[i]));
+ }
+ File outfile = new File(args[args.length - 1]);
+ boolean success = compressGeoIPv6CSVFiles(infiles, outfile);
+ if (!success) {
+ System.err.println("Failed");
+ System.exit(1);
+ }
+ // readback for testing
+ readGeoIPFile(outfile, new Long[] { Long.MAX_VALUE }, Collections.EMPTY_MAP, new Log(GeoIPv6.class));
+ }
+}
diff --git a/router/java/src/net/i2p/router/transport/GetBidsJob.java b/router/java/src/net/i2p/router/transport/GetBidsJob.java
index 85da18c13..676f72e16 100644
--- a/router/java/src/net/i2p/router/transport/GetBidsJob.java
+++ b/router/java/src/net/i2p/router/transport/GetBidsJob.java
@@ -23,22 +23,25 @@ import net.i2p.util.Log;
*/
class GetBidsJob extends JobImpl {
private final Log _log;
- private final CommSystemFacadeImpl _facade;
+ private final TransportManager _tmgr;
private final OutNetMessage _msg;
- public GetBidsJob(RouterContext ctx, CommSystemFacadeImpl facade, OutNetMessage msg) {
+ /**
+ * @deprecated unused, see static getBids()
+ */
+ public GetBidsJob(RouterContext ctx, TransportManager tmgr, OutNetMessage msg) {
super(ctx);
_log = ctx.logManager().getLog(GetBidsJob.class);
- _facade = facade;
+ _tmgr = tmgr;
_msg = msg;
}
public String getName() { return "Fetch bids for a message to be delivered"; }
public void runJob() {
- getBids(getContext(), _facade, _msg);
+ getBids(getContext(), _tmgr, _msg);
}
- static void getBids(RouterContext context, CommSystemFacadeImpl facade, OutNetMessage msg) {
+ static void getBids(RouterContext context, TransportManager tmgr, OutNetMessage msg) {
Log log = context.logManager().getLog(GetBidsJob.class);
Hash to = msg.getTarget().getIdentity().getHash();
msg.timestamp("bid");
@@ -61,14 +64,14 @@ class GetBidsJob extends JobImpl {
return;
}
- TransportBid bid = facade.getNextBid(msg);
+ TransportBid bid = tmgr.getNextBid(msg);
if (bid == null) {
int failedCount = msg.getFailedTransports().size();
if (failedCount == 0) {
context.statManager().addRateData("transport.bidFailNoTransports", msg.getLifetime());
// This used to be "no common transports" but it is almost always no transports at all
context.banlist().banlistRouter(to, _x("No transports (hidden or starting up?)"));
- } else if (failedCount >= facade.getTransportCount()) {
+ } else if (failedCount >= tmgr.getTransportCount()) {
context.statManager().addRateData("transport.bidFailAllTransports", msg.getLifetime());
// fail after all transports were unsuccessful
context.netDb().fail(to);
@@ -82,7 +85,7 @@ class GetBidsJob extends JobImpl {
}
- private static void fail(RouterContext context, OutNetMessage msg) {
+ static void fail(RouterContext context, OutNetMessage msg) {
if (msg.getOnFailedSendJob() != null) {
context.jobQueue().addJob(msg.getOnFailedSendJob());
}
diff --git a/router/java/src/net/i2p/router/transport/Transport.java b/router/java/src/net/i2p/router/transport/Transport.java
index 4bece170d..1fbc7a6d4 100644
--- a/router/java/src/net/i2p/router/transport/Transport.java
+++ b/router/java/src/net/i2p/router/transport/Transport.java
@@ -19,7 +19,10 @@ import net.i2p.data.RouterInfo;
import net.i2p.router.OutNetMessage;
/**
- * Defines a way to send a message to another peer and start listening for messages
+ * Defines a way to send a message to another peer and start listening for messages.
+ *
+ * To implement a new or pluggable I2P transport, implement this interface,
+ * and add it to TransportManager.startListening().
*
*/
public interface Transport {
@@ -32,17 +35,91 @@ public interface Transport {
*
*/
public void send(OutNetMessage msg);
- public RouterAddress startListening();
+ public void startListening();
public void stopListening();
- public RouterAddress getCurrentAddress();
- public RouterAddress updateAddress();
- public static final String SOURCE_UPNP = "upnp";
- public static final String SOURCE_INTERFACE = "local";
- public static final String SOURCE_CONFIG = "config"; // unused
- public void externalAddressReceived(String source, byte[] ip, int port);
- public void forwardPortStatus(int port, int externalPort, boolean success, String reason);
+
+ /**
+ * What addresses are we currently listening to?
+ * Replaces getCurrentAddress()
+ * @return all addresses, non-null
+ * @since IPv6
+ */
+ public List getCurrentAddresses();
+
+ /**
+ * Do we have any current address?
+ * @since IPv6
+ */
+ public boolean hasCurrentAddress();
+
+ /**
+ * Ask the transport to update its addresses based on current information and return them
+ * @return all addresses, non-null
+ */
+ public List updateAddress();
+
+ /**
+ * @since IPv6
+ */
+ public enum AddressSource {
+ SOURCE_UPNP("upnp"),
+ SOURCE_INTERFACE("local"),
+ /** unused */
+ SOURCE_CONFIG("config"),
+ SOURCE_SSU("ssu");
+
+ private final String cfgstr;
+
+ AddressSource(String cfgstr) {
+ this.cfgstr = cfgstr;
+ }
+
+ public String toConfigString() {
+ return cfgstr;
+ }
+ }
+
+ /**
+ * Notify a transport of an external address change.
+ * This may be from a local interface, UPnP, a config change, etc.
+ * This should not be called if the ip didn't change
+ * (from that source's point of view), or is a local address.
+ * May be called multiple times for IPv4 or IPv6.
+ * The transport should also do its own checking on whether to accept
+ * notifications from this source.
+ *
+ * This can be called before startListening() to set an initial address,
+ * or after the transport is running.
+ *
+ * @param source defined in Transport.java
+ * @param ip typ. IPv4 or IPv6 non-local; may be null to indicate IPv4 failure or port info only
+ * @param port 0 for unknown or unchanged
+ */
+ public void externalAddressReceived(AddressSource source, byte[] ip, int port);
+
+ /**
+ * Notify a transport of the results of trying to forward a port.
+ *
+ * @param ip may be null
+ * @param port the internal port
+ * @param externalPort the external port, which for now should always be the same as
+ * the internal port if the forwarding was successful.
+ */
+ public void forwardPortStatus(byte[] ip, int port, int externalPort, boolean success, String reason);
+
+ /**
+ * What INTERNAL port would the transport like to have forwarded by UPnP.
+ * This can't be passed via getCurrentAddress(), as we have to open the port
+ * before we can publish the address, and that's the external port anyway.
+ *
+ * @return port or -1 for none or 0 for any
+ */
public int getRequestedPort();
+
+ /** Who to notify on message availability */
public void setListener(TransportEventListener listener);
+
+ /** The unique identity of this Transport */
public String getStyle();
public int countPeers();
@@ -51,13 +128,18 @@ public interface Transport {
public boolean haveCapacity();
public boolean haveCapacity(int pct);
public Vector getClockSkews();
- public List getMostRecentErrorMessages();
+ public List getMostRecentErrorMessages();
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException;
public short getReachabilityStatus();
public void recheckReachability();
- public boolean isBacklogged(Hash dest);
- public boolean wasUnreachable(Hash dest);
+ public boolean isBacklogged(Hash peer);
+
+ /**
+ * Was the peer UNreachable (outbound only) the last time we tried it?
+ * This is NOT reset if the peer contacts us and it is never expired.
+ */
+ public boolean wasUnreachable(Hash peer);
public boolean isUnreachable(Hash peer);
public boolean isEstablished(Hash peer);
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index fc24856be..251200f34 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -10,8 +10,11 @@ package net.i2p.router.transport;
import java.io.IOException;
import java.io.Writer;
+import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -23,6 +26,7 @@ import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CopyOnWriteArrayList;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -36,13 +40,13 @@ import net.i2p.router.MessageSelector;
import net.i2p.router.OutNetMessage;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
-import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.LHMCache;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
import net.i2p.util.SystemVersion;
+import net.i2p.util.Translate;
/**
* Defines a way to send a message to another peer and start listening for messages
@@ -51,13 +55,14 @@ import net.i2p.util.SystemVersion;
public abstract class TransportImpl implements Transport {
private final Log _log;
private TransportEventListener _listener;
- private RouterAddress _currentAddress;
+ protected final List _currentAddresses;
// Only used by NTCP. SSU does not use. See send() below.
private final BlockingQueue _sendPool;
protected final RouterContext _context;
/** map from routerIdentHash to timestamp (Long) that the peer was last unreachable */
private final Map _unreachableEntries;
private final Set _wasUnreachableEntries;
+ private final Set _localAddresses;
/** global router ident -> IP */
private static final Map _IPMap;
@@ -88,12 +93,15 @@ public abstract class TransportImpl implements Transport {
_context.statManager().createRequiredRateStat("transport.sendProcessingTime", "Time to process and send a message (ms)", "Transport", new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
//_context.statManager().createRateStat("transport.sendProcessingTime." + getStyle(), "Time to process and send a message (ms)", "Transport", new long[] { 60*1000l });
_context.statManager().createRateStat("transport.expiredOnQueueLifetime", "How long a message that expires on our outbound queue is processed", "Transport", new long[] { 60*1000l, 10*60*1000l, 60*60*1000l, 24*60*60*1000l } );
+
+ _currentAddresses = new CopyOnWriteArrayList();
if (getStyle().equals("NTCP"))
_sendPool = new ArrayBlockingQueue(8);
else
_sendPool = null;
_unreachableEntries = new HashMap(16);
_wasUnreachableEntries = new ConcurrentHashSet(16);
+ _localAddresses = new ConcurrentHashSet(4);
_context.simpleScheduler().addPeriodicEvent(new CleanupUnreachable(), 2 * UNREACHABLE_PERIOD, UNREACHABLE_PERIOD / 2);
}
@@ -119,6 +127,9 @@ public abstract class TransportImpl implements Transport {
/** Per-transport connection limit */
public int getMaxConnections() {
+ if (_context.commSystem().isDummy())
+ // testing
+ return 0;
String style = getStyle();
// object churn
String maxProp;
@@ -135,7 +146,7 @@ public abstract class TransportImpl implements Transport {
if (bw > Router.CAPABILITY_BW12 && bw <= Router.CAPABILITY_BW256)
def *= (1 + bw - Router.CAPABILITY_BW12);
}
- if (((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled()) {
+ if (_context.netDb().floodfillEnabled()) {
// && !SystemVersion.isWindows()) {
def *= 17; def /= 10; // 425 for Class O ff
}
@@ -167,7 +178,7 @@ public abstract class TransportImpl implements Transport {
*/
public Vector getClockSkews() { return new Vector(); }
- public List getMostRecentErrorMessages() { return Collections.EMPTY_LIST; }
+ public List getMostRecentErrorMessages() { return Collections.EMPTY_LIST; }
/**
* Nonblocking call to pull the next outbound message
@@ -232,8 +243,8 @@ public abstract class TransportImpl implements Transport {
if (!sendSuccessful)
msg.transportFailed(getStyle());
- if (msToSend > 1000) {
- if (_log.shouldLog(Log.WARN))
+ if (msToSend > 1500) {
+ if (_log.shouldLog(Log.INFO))
_log.warn(getStyle() + " afterSend slow: " + (sendSuccessful ? "success " : "FAIL ")
+ msg.getMessageSize() + " byte "
+ msg.getMessageType() + ' ' + msg.getMessageId() + " to "
@@ -330,7 +341,7 @@ public abstract class TransportImpl implements Transport {
long allTime = now - msg.getCreated();
if (allTime > 5*1000) {
if (_log.shouldLog(Log.INFO))
- _log.info("Took too long from preperation to afterSend(ok? " + sendSuccessful
+ _log.info("Took too long from preparation to afterSend(ok? " + sendSuccessful
+ "): " + allTime + "ms/" + sendTime + "ms after failing on: "
+ msg.getFailedTransports() + " and succeeding on " + getStyle());
if ( (allTime > 60*1000) && (sendSuccessful) ) {
@@ -463,63 +474,219 @@ public abstract class TransportImpl implements Transport {
}
/** Do we increase the advertised cost when approaching conn limits? */
- public static final boolean ADJUST_COST = true;
+ protected static final boolean ADJUST_COST = true;
+ /** TODO change to 2 */
+ protected static final int CONGESTION_COST_ADJUSTMENT = 1;
- /** What addresses are we currently listening to? */
- public RouterAddress getCurrentAddress() {
- return _currentAddress;
+ /**
+ * What addresses are we currently listening to?
+ * Replaces getCurrentAddress()
+ * @return all addresses, non-null
+ * @since IPv6
+ */
+ public List getCurrentAddresses() {
+ return _currentAddresses;
+ }
+
+ /**
+ * What address are we currently listening to?
+ * Replaces getCurrentAddress()
+ * @param ipv6 true for IPv6 only; false for IPv4 only
+ * @return first matching address or null
+ * @since IPv6
+ */
+ public RouterAddress getCurrentAddress(boolean ipv6) {
+ for (RouterAddress ra : _currentAddresses) {
+ if (ipv6 == TransportUtil.isIPv6(ra))
+ return ra;
+ }
+ return null;
+ }
+
+ /**
+ * Do we have any current address?
+ * @since IPv6
+ */
+ public boolean hasCurrentAddress() {
+ return !_currentAddresses.isEmpty();
}
/**
* Ask the transport to update its address based on current information and return it
* Transports should override.
+ * @return all addresses, non-null
* @since 0.7.12
*/
- public RouterAddress updateAddress() {
- return _currentAddress;
+ public List updateAddress() {
+ return _currentAddresses;
}
/**
- * Replace any existing addresses for the current transport with the given
- * one.
+ * Replace any existing addresses for the current transport
+ * with the same IP length (4 or 16) with the given one.
+ * TODO: Allow multiple addresses of the same length.
+ * Calls listener.transportAddressChanged()
+ *
+ * @param address null to remove all
*/
protected void replaceAddress(RouterAddress address) {
- // _log.error("Replacing address for " + getStyle() + " was " + _currentAddress + " now " + address);
- _currentAddress = address;
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Replacing address with " + address, new Exception());
+ if (address == null) {
+ _currentAddresses.clear();
+ } else {
+ boolean isIPv6 = TransportUtil.isIPv6(address);
+ for (RouterAddress ra : _currentAddresses) {
+ if (isIPv6 == TransportUtil.isIPv6(ra))
+ _currentAddresses.remove(ra);
+ }
+ _currentAddresses.add(address);
+ }
+ if (_log.shouldLog(Log.WARN))
+ _log.warn(getStyle() + " now has " + _currentAddresses.size() + " addresses");
if (_listener != null)
_listener.transportAddressChanged();
}
+ /**
+ * Save a local address we were notified about before we started.
+ *
+ * @since IPv6
+ */
+ protected void saveLocalAddress(InetAddress address) {
+ _localAddresses.add(address);
+ }
+
+ /**
+ * Return and then clear all saved local addresses.
+ *
+ * @since IPv6
+ */
+ protected Collection getSavedLocalAddresses() {
+ List rv = new ArrayList(_localAddresses);
+ _localAddresses.clear();
+ return rv;
+ }
+
+ /**
+ * Get all available address we can use,
+ * shuffled and then sorted by cost/preference.
+ * Lowest cost (most preferred) first.
+ * @return non-null, possibly empty
+ * @since IPv6
+ */
+ protected List getTargetAddresses(RouterInfo target) {
+ List rv = target.getTargetAddresses(getStyle());
+ if (rv.isEmpty())
+ return rv;
+ // Shuffle so everybody doesn't use the first one
+ if (rv.size() > 1)
+ Collections.shuffle(rv, _context.random());
+ TransportUtil.IPv6Config config = getIPv6Config();
+ int adj;
+ switch (config) {
+ case IPV6_DISABLED:
+ adj = 10;
+ /**** IPv6 addresses will be rejected in isPubliclyRoutable()
+ for (Iterator iter = rv.iterator(); iter.hasNext(); ) {
+ byte[] ip = iter.next().getIP();
+ if (ip != null && ip.length == 16)
+ iter.remove();
+ }
+ ****/
+ break;
+ case IPV6_NOT_PREFERRED:
+ adj = 1; break;
+ default:
+ case IPV6_ENABLED:
+ adj = 0; break;
+ case IPV6_PREFERRED:
+ adj = -1; break;
+ case IPV6_ONLY:
+ adj = -10;
+ /**** IPv6 addresses will be rejected in isPubliclyRoutable()
+ for (Iterator iter = rv.iterator(); iter.hasNext(); ) {
+ byte[] ip = iter.next().getIP();
+ if (ip != null && ip.length == 4)
+ iter.remove();
+ }
+ ****/
+ break;
+ }
+ if (rv.size() > 1)
+ Collections.sort(rv, new AddrComparator(adj));
+ return rv;
+ }
+
+ /**
+ * Compare based on published cost, adjusting for our IPv6 preference.
+ * Lowest cost (most preferred) first.
+ * @since IPv6
+ */
+ private static class AddrComparator implements Comparator {
+ private final int adj;
+
+ public AddrComparator(int ipv6Adjustment) {
+ adj = ipv6Adjustment;
+ }
+
+ public int compare(RouterAddress l, RouterAddress r) {
+ int lc = l.getCost();
+ int rc = r.getCost();
+ byte[] lip = l.getIP();
+ byte[] rip = r.getIP();
+ if (lip == null)
+ lc += 20;
+ else if (lip.length == 16)
+ lc += adj;
+ if (rip == null)
+ rc += 20;
+ else if (rip.length == 16)
+ rc += adj;
+ if (lc > rc)
+ return 1;
+ if (lc < rc)
+ return -1;
+ return 0;
+ }
+ }
+
/**
* Notify a transport of an external address change.
* This may be from a local interface, UPnP, a config change, etc.
* This should not be called if the ip didn't change
- * (from that source's point of view), or is a local address,
- * or if the ip is IPv6, but the transport should check anyway.
+ * (from that source's point of view), or is a local address.
+ * May be called multiple times for IPv4 or IPv6.
* The transport should also do its own checking on whether to accept
* notifications from this source.
*
* This can be called before startListening() to set an initial address,
* or after the transport is running.
*
+ * This implementation does nothing. Transports should override if they want notification.
+ *
* @param source defined in Transport.java
- * @param ip typ. IPv4 non-local
+ * @param ip typ. IPv4 or IPv6 non-local; may be null to indicate IPv4 failure or port info only
* @param port 0 for unknown or unchanged
*/
- public void externalAddressReceived(String source, byte[] ip, int port) {}
+ public void externalAddressReceived(AddressSource source, byte[] ip, int port) {}
/**
* Notify a transport of the results of trying to forward a port.
+ *
+ * This implementation does nothing. Transports should override if they want notification.
+ *
+ * @param ip may be null
* @param port the internal port
* @param externalPort the external port, which for now should always be the same as
* the internal port if the forwarding was successful.
*/
- public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {}
+ public void forwardPortStatus(byte[] ip, int port, int externalPort, boolean success, String reason) {}
/**
- * What port would the transport like to have forwarded by UPnP.
+ * What INTERNAL port would the transport like to have forwarded by UPnP.
* This can't be passed via getCurrentAddress(), as we have to open the port
- * before we can publish the address.
+ * before we can publish the address, and that's the external port anyway.
*
* @return port or -1 for none or 0 for any
*/
@@ -531,13 +698,13 @@ public abstract class TransportImpl implements Transport {
public void renderStatusHTML(Writer out) throws IOException {}
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { renderStatusHTML(out); }
- public RouterContext getContext() { return _context; }
public short getReachabilityStatus() { return CommSystemFacade.STATUS_UNKNOWN; }
public void recheckReachability() {}
public boolean isBacklogged(Hash dest) { return false; }
public boolean isEstablished(Hash dest) { return false; }
private static final long UNREACHABLE_PERIOD = 5*60*1000;
+
public boolean isUnreachable(Hash peer) {
long now = _context.clock().now();
synchronized (_unreachableEntries) {
@@ -551,6 +718,7 @@ public abstract class TransportImpl implements Transport {
}
}
}
+
/** called when we can't reach a peer */
/** This isn't very useful since it is cleared when they contact us */
public void markUnreachable(Hash peer) {
@@ -560,6 +728,7 @@ public abstract class TransportImpl implements Transport {
}
markWasUnreachable(peer, true);
}
+
/** called when we establish a peer connection (outbound or inbound) */
public void markReachable(Hash peer, boolean isInbound) {
// if *some* transport can reach them, then we shouldn't banlist 'em
@@ -605,9 +774,15 @@ public abstract class TransportImpl implements Transport {
else
_wasUnreachableEntries.remove(peer);
if (_log.shouldLog(Log.INFO))
- _log.info(this.getStyle() + " setting wasUnreachable to " + yes + " for " + peer);
+ _log.info(this.getStyle() + " setting wasUnreachable to " + yes + " for " + peer,
+ yes ? new Exception() : null);
}
+ /**
+ * IP of the peer from the last connection (in or out, any transport).
+ *
+ * @param ip IPv4 or IPv6, non-null
+ */
public void setIP(Hash peer, byte[] ip) {
byte[] old;
synchronized (_IPMap) {
@@ -617,6 +792,11 @@ public abstract class TransportImpl implements Transport {
_context.commSystem().queueLookup(ip);
}
+ /**
+ * IP of the peer from the last connection (in or out, any transport).
+ *
+ * @return IPv4 or IPv6 or null
+ */
public static byte[] getIP(Hash peer) {
synchronized (_IPMap) {
return _IPMap.get(peer);
@@ -632,26 +812,48 @@ public abstract class TransportImpl implements Transport {
}
}
- /** @param addr non-null */
- public static boolean isPubliclyRoutable(byte addr[]) {
- if (addr.length == 4) {
- int a0 = addr[0] & 0xFF;
- if (a0 == 127) return false;
- if (a0 == 10) return false;
- int a1 = addr[1] & 0xFF;
- if (a0 == 172 && a1 >= 16 && a1 <= 31) return false;
- if (a0 == 192 && a1 == 168) return false;
- if (a0 >= 224) return false; // no multicast
- if (a0 == 0) return false;
- if (a0 == 169 && a1 == 254) return false;
- // 5/8 allocated to RIPE (30 November 2010)
- //if ((addr[0]&0xFF) == 5) return false; // Hamachi
- return true; // or at least possible to be true
- } else if (addr.length == 16) {
- return false;
- } else {
- // ipv?
- return false;
- }
+ /**
+ * @since IPv6
+ */
+ protected TransportUtil.IPv6Config getIPv6Config() {
+ return TransportUtil.getIPv6Config(_context, getStyle());
+ }
+
+ /**
+ * Allows IPv6 only if the transport is configured for it.
+ * Caller must check if we actually have a public IPv6 address.
+ * @param addr non-null
+ */
+ protected boolean isPubliclyRoutable(byte addr[]) {
+ TransportUtil.IPv6Config cfg = getIPv6Config();
+ return TransportUtil.isPubliclyRoutable(addr,
+ cfg != TransportUtil.IPv6Config.IPV6_ONLY,
+ cfg != TransportUtil.IPv6Config.IPV6_DISABLED);
+ }
+
+ private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
+
+ /**
+ * Translate
+ * @since 0.9.8 moved from transports
+ */
+ protected String _(String s) {
+ return Translate.getString(s, _context, BUNDLE_NAME);
+ }
+
+ /**
+ * Translate
+ * @since 0.9.8 moved from transports
+ */
+ protected String _(String s, Object o) {
+ return Translate.getString(s, o, _context, BUNDLE_NAME);
+ }
+
+ /**
+ * Translate
+ * @since 0.9.8
+ */
+ protected String ngettext(String s, String p, int n) {
+ return Translate.getString(n, s, p, _context, BUNDLE_NAME);
}
}
diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java
index f6a7c71f8..ce33b5b89 100644
--- a/router/java/src/net/i2p/router/transport/TransportManager.java
+++ b/router/java/src/net/i2p/router/transport/TransportManager.java
@@ -14,6 +14,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -29,6 +30,7 @@ import net.i2p.data.i2np.I2NPMessage;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
+import static net.i2p.router.transport.Transport.AddressSource.*;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.transport.ntcp.NTCPTransport;
import net.i2p.router.transport.udp.UDPTransport;
@@ -85,13 +87,24 @@ public class TransportManager implements TransportEventListener {
private void configTransports() {
boolean enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP);
+ Transport udp = null;
if (enableUDP) {
- UDPTransport udp = new UDPTransport(_context, _dhThread);
+ udp = new UDPTransport(_context, _dhThread);
addTransport(udp);
initializeAddress(udp);
}
- if (isNTCPEnabled(_context))
- addTransport(new NTCPTransport(_context, _dhThread));
+ if (isNTCPEnabled(_context)) {
+ Transport ntcp = new NTCPTransport(_context, _dhThread);
+ addTransport(ntcp);
+ initializeAddress(ntcp);
+ if (udp != null) {
+ // pass along the port SSU is probably going to use
+ // so that NTCP may bind early
+ int port = udp.getRequestedPort();
+ if (port > 0)
+ ntcp.externalAddressReceived(SOURCE_CONFIG, null, port);
+ }
+ }
if (_transports.isEmpty())
_log.log(Log.CRIT, "No transports are enabled");
}
@@ -100,40 +113,44 @@ public class TransportManager implements TransportEventListener {
return ctx.getBooleanPropertyDefaultTrue(PROP_ENABLE_NTCP);
}
+ /**
+ * Notify transport of ALL routable interface addresses, including IPv6.
+ * It's the transport's job to ignore what it can't handle.
+ */
private void initializeAddress(Transport t) {
- String ips = Addresses.getAnyAddress();
- if (ips == null)
- return;
- InetAddress ia;
- try {
- ia = InetAddress.getByName(ips);
- } catch (UnknownHostException e) {
- _log.error("UDP failed to bind to local address", e);
- return;
+ Set ipset = Addresses.getAddresses(false, true); // non-local, include IPv6
+ for (String ips : ipset) {
+ try {
+ InetAddress ia = InetAddress.getByName(ips);
+ byte[] ip = ia.getAddress();
+ t.externalAddressReceived(SOURCE_INTERFACE, ip, 0);
+ } catch (UnknownHostException e) {
+ _log.error("UDP failed to bind to local address", e);
+ }
}
- byte[] ip = ia.getAddress();
- t.externalAddressReceived(Transport.SOURCE_INTERFACE, ip, 0);
}
/**
- * callback from UPnP
- * Only tell SSU, it will tell NTCP
+ * Initialize from interfaces, and callback from UPnP or SSU.
+ * Tell all transports... but don't loop
*
*/
- public void externalAddressReceived(String source, byte[] ip, int port) {
- Transport t = getTransport(UDPTransport.STYLE);
- if (t != null)
- t.externalAddressReceived(source, ip, port);
+ public void externalAddressReceived(Transport.AddressSource source, byte[] ip, int port) {
+ for (Transport t : _transports.values()) {
+ // don't loop
+ if (!(source == SOURCE_SSU && t.getStyle().equals(UDPTransport.STYLE)))
+ t.externalAddressReceived(source, ip, port);
+ }
}
/**
* callback from UPnP
*
*/
- public void forwardPortStatus(String style, int port, int externalPort, boolean success, String reason) {
+ public void forwardPortStatus(String style, byte[] ip, int port, int externalPort, boolean success, String reason) {
Transport t = getTransport(style);
if (t != null)
- t.forwardPortStatus(port, externalPort, success, reason);
+ t.forwardPortStatus(ip, port, externalPort, success, reason);
}
public synchronized void startListening() {
@@ -148,7 +165,17 @@ public class TransportManager implements TransportEventListener {
_upnpManager.start();
configTransports();
_log.debug("Starting up the transport manager");
- for (Transport t : _transports.values()) {
+ // Let's do this in a predictable order to make testing easier
+ // Start NTCP first so it can get notified from SSU
+ List tps = new ArrayList();
+ Transport tp = getTransport(NTCPTransport.STYLE);
+ if (tp != null)
+ tps.add(tp);
+ tp = getTransport(UDPTransport.STYLE);
+ if (tp != null)
+ tps.add(tp);
+ //for (Transport t : _transports.values()) {
+ for (Transport t : tps) {
t.startListening();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Transport " + t.getStyle() + " started");
@@ -248,7 +275,7 @@ public class TransportManager implements TransportEventListener {
*/
public boolean haveInboundCapacity(int pct) {
for (Transport t : _transports.values()) {
- if (t.getCurrentAddress() != null && t.haveCapacity(pct))
+ if (t.hasCurrentAddress() && t.haveCapacity(pct))
return true;
}
return false;
@@ -266,8 +293,8 @@ public class TransportManager implements TransportEventListener {
if ((tempSkews == null) || (tempSkews.isEmpty())) continue;
skews.addAll(tempSkews);
}
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("Transport manager returning " + skews.size() + " peer clock skews.");
+ //if (_log.shouldLog(Log.DEBUG))
+ // _log.debug("Transport manager returning " + skews.size() + " peer clock skews.");
return skews;
}
@@ -324,6 +351,8 @@ public class TransportManager implements TransportEventListener {
*
* For blocking purposes, etc. it's worth checking both
* the netDb addresses and this address.
+ *
+ * @return IPv4 or IPv6 or null
*/
public byte[] getIP(Hash dest) {
return TransportImpl.getIP(dest);
@@ -332,35 +361,62 @@ public class TransportManager implements TransportEventListener {
/**
* This forces a rebuild
*/
- public Map getAddresses() {
- Map rv = new HashMap(_transports.size());
+ public List getAddresses() {
+ List rv = new ArrayList(4);
// do this first since SSU may force a NTCP change
for (Transport t : _transports.values())
t.updateAddress();
for (Transport t : _transports.values()) {
- if (t.getCurrentAddress() != null)
- rv.put(t.getStyle(), t.getCurrentAddress());
+ rv.addAll(t.getCurrentAddresses());
}
return rv;
}
/**
- * The actual or requested INTERNAL ports, for each transport,
- * which we will pass along to UPnP to be forwarded.
+ * @since IPv6
*/
- private Map getPorts() {
- Map rv = new HashMap(_transports.size());
+ static class Port {
+ public final String style;
+ public final int port;
+
+ public Port(String style, int port) {
+ this.style = style;
+ this.port = port;
+ }
+
+ @Override
+ public int hashCode() {
+ return style.hashCode() ^ port;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (! (o instanceof Port))
+ return false;
+ Port p = (Port) o;
+ return port == p.port && style.equals(p.style);
+ }
+ }
+
+ /**
+ * Include the published port, or the requested port, for each transport
+ * which we will pass along to UPnP
+ */
+ private Set getPorts() {
+ Set rv = new HashSet(4);
for (Transport t : _transports.values()) {
int port = t.getRequestedPort();
// Use UDP port for NTCP too - see comment in NTCPTransport.getRequestedPort() for why this is here
if (t.getStyle().equals(NTCPTransport.STYLE) && port <= 0 &&
- _context.getBooleanProperty(CommSystemFacadeImpl.PROP_I2NP_NTCP_AUTO_PORT)) {
+ _context.getBooleanProperty(NTCPTransport.PROP_I2NP_NTCP_AUTO_PORT)) {
Transport udp = getTransport(UDPTransport.STYLE);
if (udp != null)
port = t.getRequestedPort();
}
if (port > 0)
- rv.put(t.getStyle(), Integer.valueOf(port));
+ rv.add(new Port(t.getStyle(), port));
}
return rv;
}
@@ -461,11 +517,11 @@ public class TransportManager implements TransportEventListener {
*/
public void messageReceived(I2NPMessage message, RouterIdentity fromRouter, Hash fromRouterHash) {
if (_log.shouldLog(Log.DEBUG))
- _log.debug("I2NPMessage received: " + message.getClass().getName(), new Exception("Where did I come from again?"));
+ _log.debug("I2NPMessage received: " + message.getClass().getSimpleName() /*, new Exception("Where did I come from again?") */ );
try {
_context.inNetMessagePool().add(message, fromRouter, fromRouterHash);
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("Added to in pool");
+ //if (_log.shouldLog(Log.DEBUG))
+ // _log.debug("Added to in pool");
} catch (IllegalArgumentException iae) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error receiving message", iae);
@@ -477,8 +533,8 @@ public class TransportManager implements TransportEventListener {
_upnpManager.update(getPorts());
}
- public List getMostRecentErrorMessages() {
- List rv = new ArrayList(16);
+ public List getMostRecentErrorMessages() {
+ List rv = new ArrayList(16);
for (Transport t : _transports.values()) {
rv.addAll(t.getMostRecentErrorMessages());
}
@@ -486,12 +542,11 @@ public class TransportManager implements TransportEventListener {
}
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException {
- TreeMap transports = new TreeMap();
+ TreeMap transports = new TreeMap();
for (Transport t : _transports.values()) {
transports.put(t.getStyle(), t);
}
- for (Iterator iter = transports.values().iterator(); iter.hasNext(); ) {
- Transport t= (Transport)iter.next();
+ for (Transport t : transports.values()) {
t.renderStatusHTML(out, urlBase, sortFlags);
}
@@ -502,11 +557,15 @@ public class TransportManager implements TransportEventListener {
StringBuilder buf = new StringBuilder(4*1024);
buf.append("
").append(_("Router Transport Addresses")).append("
\n");
for (Transport t : _transports.values()) {
- if (t.getCurrentAddress() != null)
- buf.append(t.getCurrentAddress());
- else
+ if (t.hasCurrentAddress()) {
+ for (RouterAddress ra : t.getCurrentAddresses()) {
+ buf.append(ra.toString());
+ buf.append("\n\n");
+ }
+ } else {
buf.append(_("{0} is used for outbound connections only", t.getStyle()));
- buf.append("\n\n");
+ buf.append("\n\n");
+ }
}
buf.append("
\n");
out.write(buf.toString());
diff --git a/router/java/src/net/i2p/router/transport/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java
new file mode 100644
index 000000000..a37e2d542
--- /dev/null
+++ b/router/java/src/net/i2p/router/transport/TransportUtil.java
@@ -0,0 +1,146 @@
+package net.i2p.router.transport;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain
+ * with no warranty of any kind { either expressed or implied.
+ * It probably won't make your computer catch on fire { or eat
+ * your children { but it might. Use at your own risk.
+ *
+ */
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.i2p.data.RouterAddress;
+import net.i2p.router.RouterContext;
+
+/**
+ * @since IPv6
+ */
+public abstract class TransportUtil {
+
+ public static final String NTCP_IPV6_CONFIG = "i2np.ntcp.ipv6";
+ public static final String SSU_IPV6_CONFIG = "i2np.udp.ipv6";
+
+ public enum IPv6Config {
+ /** IPv6 disabled */
+ IPV6_DISABLED("false"),
+
+ /** lower priority than IPv4 */
+ IPV6_NOT_PREFERRED("preferIPv4"),
+
+ /** equal priority to IPv4 */
+ IPV6_ENABLED("enable"),
+
+ /** higher priority than IPv4 */
+ IPV6_PREFERRED("preferIPv6"),
+
+ /** IPv4 disabled */
+ IPV6_ONLY("only");
+
+ private final String cfgstr;
+
+ IPv6Config(String cfgstr) {
+ this.cfgstr = cfgstr;
+ }
+
+ public String toConfigString() {
+ return cfgstr;
+ }
+ }
+
+ private static final Map BY_NAME = new HashMap();
+ public static final IPv6Config DEFAULT_IPV6_CONFIG = IPv6Config.IPV6_PREFERRED;
+
+ static {
+ for (IPv6Config cfg : IPv6Config.values()) {
+ BY_NAME.put(cfg.toConfigString(), cfg);
+ }
+ // alias
+ BY_NAME.put("true", IPv6Config.IPV6_ENABLED);
+ BY_NAME.put("disable", IPv6Config.IPV6_DISABLED);
+ }
+
+ public static IPv6Config getIPv6Config(RouterContext ctx, String transportStyle) {
+ String cfg;
+ if (transportStyle.equals("NTCP"))
+ cfg = ctx.getProperty(NTCP_IPV6_CONFIG);
+ else if (transportStyle.equals("SSU"))
+ cfg = ctx.getProperty(SSU_IPV6_CONFIG);
+ else
+ return DEFAULT_IPV6_CONFIG;
+ return getIPv6Config(cfg);
+ }
+
+ public static IPv6Config getIPv6Config(String cfg) {
+ if (cfg == null)
+ return DEFAULT_IPV6_CONFIG;
+ IPv6Config c = BY_NAME.get(cfg);
+ if (c != null)
+ return c;
+ return DEFAULT_IPV6_CONFIG;
+ }
+
+ /**
+ * Addresses without a host (i.e. w/introducers)
+ * are assumed to be IPv4
+ */
+ public static boolean isIPv6(RouterAddress addr) {
+ // do this the fast way, without calling getIP() to parse the host string
+ String host = addr.getOption(RouterAddress.PROP_HOST);
+ return host != null && host.contains(":");
+ }
+
+ /**
+ * @param addr non-null
+ * @since IPv6 moved from TransportImpl
+ */
+ public static boolean isPubliclyRoutable(byte addr[], boolean allowIPv6) {
+ return isPubliclyRoutable(addr, true, allowIPv6);
+ }
+
+ /**
+ * @param addr non-null
+ * @since IPv6
+ */
+ public static boolean isPubliclyRoutable(byte addr[], boolean allowIPv4, boolean allowIPv6) {
+ if (addr.length == 4) {
+ if (!allowIPv4)
+ return false;
+ int a0 = addr[0] & 0xFF;
+ if (a0 == 127) return false;
+ if (a0 == 10) return false;
+ int a1 = addr[1] & 0xFF;
+ if (a0 == 172 && a1 >= 16 && a1 <= 31) return false;
+ if (a0 == 192 && a1 == 168) return false;
+ if (a0 >= 224) return false; // no multicast
+ if (a0 == 0) return false;
+ if (a0 == 169 && a1 == 254) return false;
+ // 5/8 allocated to RIPE (30 November 2010)
+ //if ((addr[0]&0xFF) == 5) return false; // Hamachi
+ return true; // or at least possible to be true
+ } else if (addr.length == 16) {
+ if (allowIPv6) {
+ // disallow 2002::/16 (6to4 RFC 3056)
+ if (addr[0] == 0x20 && addr[1] == 0x02)
+ return false;
+ // disallow fc00::/8 and fd00::/8 (Unique local addresses RFC 4193)
+ // not recognized as local by InetAddress
+ if ((addr[0] & 0xfe) == 0xfc)
+ return false;
+ try {
+ InetAddress ia = InetAddress.getByAddress(addr);
+ return
+ (!ia.isLinkLocalAddress()) &&
+ (!ia.isMulticastAddress()) &&
+ (!ia.isAnyLocalAddress()) &&
+ (!ia.isLoopbackAddress()) &&
+ (!ia.isSiteLocalAddress());
+ } catch (UnknownHostException uhe) {}
+ }
+ }
+ return false;
+ }
+}
diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java
index 8d4dd1a6b..9d38230e1 100644
--- a/router/java/src/net/i2p/router/transport/UPnP.java
+++ b/router/java/src/net/i2p/router/transport/UPnP.java
@@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
@@ -20,12 +21,16 @@ import net.i2p.util.Log;
import net.i2p.util.Translate;
import org.cybergarage.upnp.Action;
+import org.cybergarage.upnp.ActionList;
+import org.cybergarage.upnp.Argument;
import org.cybergarage.upnp.ArgumentList;
import org.cybergarage.upnp.ControlPoint;
import org.cybergarage.upnp.Device;
import org.cybergarage.upnp.DeviceList;
import org.cybergarage.upnp.Service;
import org.cybergarage.upnp.ServiceList;
+import org.cybergarage.upnp.ServiceStateTable;
+import org.cybergarage.upnp.StateVariable;
import org.cybergarage.upnp.UPnPStatus;
import org.cybergarage.upnp.device.DeviceChangeListener;
import org.cybergarage.upnp.event.EventListener;
@@ -52,6 +57,7 @@ import org.freenetproject.ForwardPortStatus;
*
* @see "http://www.upnp.org/"
* @see "http://en.wikipedia.org/wiki/Universal_Plug_and_Play"
+ * @since 0.7.4
*/
/*
@@ -84,6 +90,8 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
private final Set portsForwarded;
/** Callback to call when a forward fails or succeeds */
private ForwardPortCallback forwardCallback;
+
+ private static final String PROP_ADVANCED = "routerconsole.advanced";
public UPnP(I2PAppContext context) {
super();
@@ -147,7 +155,7 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
InetAddress detectedIP = InetAddress.getByName(natAddress);
short status = DetectedIP.NOT_SUPPORTED;
- thinksWeAreDoubleNatted = !TransportImpl.isPubliclyRoutable(detectedIP.getAddress());
+ thinksWeAreDoubleNatted = !TransportUtil.isPubliclyRoutable(detectedIP.getAddress(), false);
// If we have forwarded a port AND we don't have a private address
if (_log.shouldLog(Log.WARN))
_log.warn("NATAddress: \"" + natAddress + "\" detectedIP: " + detectedIP + " double? " + thinksWeAreDoubleNatted);
@@ -384,37 +392,50 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
return Integer.valueOf(getIP.getOutputArgumentList().getArgument("NewDownstreamMaxBitRate").getValue());
}
-/***
- private void listStateTable(Service serv, StringBuilder sb) {
- ServiceStateTable table = serv.getServiceStateTable();
- sb.append("
" +
@@ -783,8 +1135,7 @@ public class NTCPTransport extends TransportImpl {
"
\n");
out.write(buf.toString());
buf.setLength(0);
- for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
- NTCPConnection con = (NTCPConnection)iter.next();
+ for (NTCPConnection con : peers) {
buf.append("
");
buf.append(_context.commSystem().renderPeerHTML(con.getRemotePeer().calculateHash()));
//byte[] ip = getIP(con.getRemotePeer().calculateHash());
@@ -795,6 +1146,11 @@ public class NTCPTransport extends TransportImpl {
buf.append("");
else
buf.append("");
+ buf.append("
").append(totalRecv);
@@ -888,15 +1245,6 @@ public class NTCPTransport extends TransportImpl {
}
}
- private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
-
- /**
- * Translate
- */
- private final String _(String s) {
- return Translate.getString(s, _context, BUNDLE_NAME);
- }
-
/**
* Cache the bid to reduce object churn
*/
diff --git a/router/java/src/net/i2p/router/transport/ntcp/Reader.java b/router/java/src/net/i2p/router/transport/ntcp/Reader.java
index 11dcd07b6..87f332f51 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/Reader.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/Reader.java
@@ -132,30 +132,24 @@ class Reader {
* Return read buffers back to the pool as we process them.
*/
private void processRead(NTCPConnection con) {
- if (con.isClosed())
- return;
ByteBuffer buf = null;
- while (!con.isClosed() && !con.isEstablished() && ( (buf = con.getNextReadBuf()) != null) ) {
+ while(true) {
+ synchronized(con) {
+ if (con.isClosed())
+ return;
+ if (con.isEstablished())
+ break;
+ }
+ if ((buf = con.getNextReadBuf()) == null)
+ return;
EstablishState est = con.getEstablishState();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Processing read buffer as an establishment for " + con + " with [" + est + "]");
- if (est == null) {
- EventPumper.releaseBuf(buf);
- if (!con.isEstablished()) {
- // establish state is only removed when the connection is fully established,
- // yet if that happens, con.isEstablished() should return true...
- throw new RuntimeException("connection was not established, yet the establish state is null for " + con);
- } else {
- // hmm, there shouldn't be a race here - only one reader should
- // be running on a con at a time...
- _log.error("no establishment state but " + con + " is established... race?");
- break;
- }
- }
+
if (est.isComplete()) {
// why is it complete yet !con.isEstablished?
- _log.error("establishment state [" + est + "] is complete, yet the connection isn't established? "
- + con.isEstablished() + " (inbound? " + con.isInbound() + " " + con + ")");
+ _log.error("establishment state [" + est + "] is complete, yet the connection isn't established? "
+ + con.isEstablished() + " (inbound? " + con.isInbound() + " " + con + ")");
EventPumper.releaseBuf(buf);
break;
}
@@ -172,9 +166,6 @@ class Reader {
if (est.isComplete() && est.getExtraBytes() != null)
con.recvEncryptedI2NP(ByteBuffer.wrap(est.getExtraBytes()));
}
- // catch race?
- if (!con.isEstablished())
- return;
while (!con.isClosed() && (buf = con.getNextReadBuf()) != null) {
// decrypt the data and push it into an i2np message
if (_log.shouldLog(Log.DEBUG))
diff --git a/router/java/src/net/i2p/router/transport/udp/ACKSender.java b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
index f1aedda5a..8fef9c434 100644
--- a/router/java/src/net/i2p/router/transport/udp/ACKSender.java
+++ b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
@@ -62,7 +62,7 @@ class ACKSender implements Runnable {
public synchronized void shutdown() {
_alive = false;
- PeerState poison = new PeerState(_context, _transport, null, 0, null, false);
+ PeerState poison = new PeerState(_context, _transport, new byte[4], 0, null, false);
poison.setTheyRelayToUsAs(POISON_PS);
_peersToACK.offer(poison);
for (int i = 1; i <= 5 && !_peersToACK.isEmpty(); i++) {
diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
index 4987314a3..95250aa95 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -249,7 +249,7 @@ class EstablishmentManager {
maybeTo = new RemoteHostId(remAddr.getAddress(), port);
if ((!_transport.isValid(maybeTo.getIP())) ||
- Arrays.equals(maybeTo.getIP(), _transport.getExternalIP())) {
+ (Arrays.equals(maybeTo.getIP(), _transport.getExternalIP()) && !_transport.allowLocal())) {
_transport.failed(msg, "Remote peer's IP isn't valid");
_transport.markUnreachable(toHash);
//_context.banlist().banlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address", UDPTransport.STYLE);
@@ -447,7 +447,9 @@ class EstablishmentManager {
}
if (!_transport.allowConnection())
return; // drop the packet
- state = new InboundEstablishState(_context, from.getIP(), from.getPort(), _transport.getExternalPort(),
+ byte[] fromIP = from.getIP();
+ state = new InboundEstablishState(_context, fromIP, from.getPort(),
+ _transport.getExternalPort(fromIP.length == 16),
_transport.getDHBuilder());
state.receiveSessionRequest(reader.getSessionRequestReader());
InboundEstablishState oldState = _inboundStates.putIfAbsent(from, state);
@@ -459,8 +461,10 @@ class EstablishmentManager {
if (isNew) {
// Don't offer to relay to privileged ports.
+ // Only offer for an IPv4 session.
// TODO if already we have their RI, only offer if they need it (no 'C' cap)
- if (_transport.canIntroduce() && state.getSentPort() >= 1024) {
+ if (_transport.canIntroduce() && state.getSentPort() >= 1024 &&
+ state.getSentIP().length == 4) {
// ensure > 0
long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE);
state.setSentRelayTag(tag);
@@ -673,7 +677,8 @@ class EstablishmentManager {
String smtu = addr.getOption(UDPAddress.PROP_MTU);
if (smtu != null) {
try {
- int mtu = MTU.rectify(Integer.parseInt(smtu));
+ boolean isIPv6 = state.getSentIP().length == 16;
+ int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
peer.setHisMTU(mtu);
} catch (NumberFormatException nfe) {}
}
@@ -833,7 +838,9 @@ class EstablishmentManager {
_inboundStates.remove(state.getRemoteHostId());
return;
}
- _transport.send(_builder.buildSessionCreatedPacket(state, _transport.getExternalPort(), _transport.getIntroKey()));
+ _transport.send(_builder.buildSessionCreatedPacket(state,
+ _transport.getExternalPort(state.getSentIP().length == 16),
+ _transport.getIntroKey()));
state.createdPacketSent();
}
@@ -884,6 +891,9 @@ class EstablishmentManager {
state.introSent();
}
+ /**
+ * We are Alice, we sent a RelayRequest to Bob and got a response back.
+ */
void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) {
long nonce = reader.getRelayResponseReader().readNonce();
OutboundEstablishState state = _liveIntroductions.remove(Long.valueOf(nonce));
@@ -893,6 +903,7 @@ class EstablishmentManager {
return; // already established
}
+ // Note that we ignore the Alice (us) IP/Port in the RelayResponse
int sz = reader.getRelayResponseReader().readCharlieIPSize();
byte ip[] = new byte[sz];
reader.getRelayResponseReader().readCharlieIP(ip, 0);
@@ -940,15 +951,17 @@ class EstablishmentManager {
}
/**
- * Are IP and port valid?
+ * Are IP and port valid? This is only for checking the relay response.
+ * Reject all IPv6, for now, even if we are configured for it.
* Refuse anybody in the same /16
* @since 0.9.3
*/
private boolean isValid(byte[] ip, int port) {
- return port >= 1024 &&
+ return port >= UDPTransport.MIN_PEER_PORT &&
port <= 65535 &&
+ ip != null && ip.length == 4 &&
_transport.isValid(ip) &&
- (!DataHelper.eq(ip, 0, _transport.getExternalIP(), 0, 2)) &&
+ (!_transport.isTooClose(ip)) &&
(!_context.blocklist().isBlocklisted(ip));
}
diff --git a/router/java/src/net/i2p/router/transport/udp/IPThrottler.java b/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
index 5f9462062..1308c2e70 100644
--- a/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
+++ b/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
@@ -3,6 +3,7 @@ package net.i2p.router.transport.udp;
import net.i2p.util.ObjectCounter;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
+import net.i2p.util.SipHash;
/**
* Count IPs
@@ -21,12 +22,16 @@ class IPThrottler {
/**
* Increments before checking
- * @return true if ip.length != 4
*/
public boolean shouldThrottle(byte[] ip) {
- if (ip.length != 4)
- return true;
- return _counter.increment(toInt(ip)) > _max;
+ // for IPv4 we simply use the IP;
+ // for IPv6 we use a secure hash as an attacker could select the lower bytes
+ Integer key;
+ if (ip.length == 4)
+ key = toInt(ip);
+ else
+ key = Integer.valueOf(SipHash.hashCode(ip));
+ return _counter.increment(key) > _max;
}
private static Integer toInt(byte ip[]) {
diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java
index 2d551fda9..33b766d6c 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java
@@ -66,6 +66,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{
_ackSender.shutdown();
_messageReceiver.shutdown();
}
+
public boolean isAlive() { return _alive; }
/**
diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index 0d49d2b5b..03b70e2a7 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -23,6 +23,52 @@ import net.i2p.util.Log;
/**
* Keep track of inbound and outbound introductions.
+ *
+ * IPv6 info: Alice-Bob communication may be via IPv4 or IPv6.
+ * Bob-Charlie communication must be via established IPv4 session as that's the only way
+ * that Bob knows Charlie's IPv4 address to give it to Alice.
+ * Alice-Charlie communication is via IPv4.
+ * If Alice-Bob is over IPv6, Alice must include her IPv4 address in
+ * the RelayRequest message.
+ *
+ * From udp.html on the website:
+
+
Indirect session establishment by means of a third party introduction
+is necessary for efficient NAT traversal. Charlie, a router behind a
+NAT or firewall which does not allow unsolicited inbound UDP packets,
+first contacts a few peers, choosing some to serve as introducers. Each
+of these peers (Bob, Bill, Betty, etc) provide Charlie with an introduction
+tag - a 4 byte random number - which he then makes available to the public
+as methods of contacting him. Alice, a router who has Charlie's published
+contact methods, first sends a RelayRequest packet to one or more of the
+introducers, asking each to introduce her to Charlie (offering the
+introduction tag to identify Charlie). Bob then forwards a RelayIntro
+packet to Charlie including Alice's public IP and port number, then sends
+Alice back a RelayResponse packet containing Charlie's public IP and port
+number. When Charlie receives the RelayIntro packet, he sends off a small
+random packet to Alice's IP and port (poking a hole in his NAT/firewall),
+and when Alice receives Bob's RelayResponse packet, she begins a new
+full direction session establishment with the specified IP and port.
+
+Alice first connects to introducer Bob, who relays the request to Charlie.
+
+
+ Alice Bob Charlie
+ RelayRequest ---------------------->
+ <-------------- RelayResponse RelayIntro ----------->
+ <-------------------------------------------- HolePunch (data ignored)
+ SessionRequest -------------------------------------------->
+ <-------------------------------------------- SessionCreated
+ SessionConfirmed ------------------------------------------>
+ <-------------------------------------------- DeliveryStatusMessage
+ <-------------------------------------------- DatabaseStoreMessage
+ DatabaseStoreMessage -------------------------------------->
+ Data <--------------------------------------------------> Data
+
+
+
+After the hole punch, the session is established between Alice and Charlie as in a direct establishment.
+
*/
class IntroductionManager {
private final RouterContext _context;
@@ -77,6 +123,9 @@ class IntroductionManager {
// let's not use an introducer on a privileged port, sounds like trouble
if (peer.getRemotePort() < 1024)
return;
+ // Only allow relay as Bob or Charlie if the Bob-Charlie session is IPv4
+ if (peer.getRemoteIP().length != 4)
+ return;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding peer " + peer.getRemoteHostId() + ", weRelayToThemAs "
+ peer.getWeRelayToThemAs() + ", theyRelayToUsAs " + peer.getTheyRelayToUsAs());
@@ -136,6 +185,8 @@ class IntroductionManager {
_log.info("Picked peer has no local routerInfo: " + cur);
continue;
}
+ // FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
+ // but requires RelayRequest support, see below
RouterAddress ra = _transport.getTargetAddress(ri);
if (ra == null) {
if (_log.shouldLog(Log.INFO))
@@ -159,6 +210,8 @@ class IntroductionManager {
_log.info("Peer is idle too long: " + cur);
continue;
}
+ // FIXME we can include all his addresses including IPv6 even if we don't support IPv6 (isValid() is false)
+ // but requires RelayRequest support, see below
byte[] ip = cur.getRemoteIP();
int port = cur.getRemotePort();
if (!isValid(ip, port))
@@ -331,6 +384,9 @@ class IntroductionManager {
// ip/port inside message should be 0:0, as it's unimplemented on send -
// see PacketBuilder.buildRelayRequest()
+ // and we don't read it here.
+ // FIXME implement for getting Alice's IPv4 in RelayRequest sent over IPv6?
+ // or is that just too easy to spoof?
if (!isValid(alice.getIP(), alice.getPort()) || ipSize != 0 || port != 0) {
if (_log.shouldLog(Log.WARN)) {
byte ip[] = new byte[ipSize];
@@ -368,14 +424,16 @@ class IntroductionManager {
/**
* Are IP and port valid?
+ * Reject all IPv6, for now, even if we are configured for it.
* Refuse anybody in the same /16
* @since 0.9.3
*/
private boolean isValid(byte[] ip, int port) {
- return port >= 1024 &&
+ return port >= UDPTransport.MIN_PEER_PORT &&
port <= 65535 &&
+ ip != null && ip.length == 4 &&
_transport.isValid(ip) &&
- (!DataHelper.eq(ip, 0, _transport.getExternalIP(), 0, 2)) &&
+ (!_transport.isTooClose(ip)) &&
(!_context.blocklist().isBlocklisted(ip));
}
}
diff --git a/router/java/src/net/i2p/router/transport/udp/MTU.java b/router/java/src/net/i2p/router/transport/udp/MTU.java
index 9258043f6..641c12163 100644
--- a/router/java/src/net/i2p/router/transport/udp/MTU.java
+++ b/router/java/src/net/i2p/router/transport/udp/MTU.java
@@ -1,6 +1,7 @@
package net.i2p.router.transport.udp;
import java.net.InetAddress;
+import java.net.Inet6Address;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
@@ -41,7 +42,8 @@ abstract class MTU {
try {
// testing
//return ifc.getMTU();
- return rectify(ifc.getMTU());
+ boolean isIPv6 = addr instanceof Inet6Address;
+ return rectify(isIPv6, ifc.getMTU());
} catch (SocketException se) {
// ignore
} catch (Throwable t) {
@@ -58,11 +60,16 @@ abstract class MTU {
/**
* @return min of PeerState.MIN_MTU, max of PeerState.LARGE_MTU,
- * rectified so rv % 16 == 12
+ * rectified so rv % 16 == 12 (IPv4)
+ * or rv % 16 == 0 (IPv6)
*/
- public static int rectify(int mtu) {
+ public static int rectify(boolean isIPv6, int mtu) {
int rv = mtu;
int mod = rv % 16;
+ if (isIPv6) {
+ rv -= mod;
+ return Math.max(PeerState.MIN_IPV6_MTU, Math.min(PeerState.MAX_IPV6_MTU, rv));
+ }
if (mod > 12)
rv -= mod - 12;
else if (mod < 12)
@@ -72,11 +79,29 @@ abstract class MTU {
/****
public static void main(String args[]) {
+ System.out.println("Cmd line interfaces:");
+ for (int i = 0; i < args.length; i++) {
+ try {
+ InetAddress test = InetAddress.getByName(args[i]);
+ System.out.println("MTU of " + args[i] + " is " + getMTU(test));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ System.out.println("All interfaces:");
try {
- InetAddress test = InetAddress.getByName(args[0]);
- System.out.println("MTU is " + getMTU(test));
- } catch (Exception e) {
- e.printStackTrace();
+ Enumeration ifcs = NetworkInterface.getNetworkInterfaces();
+ if (ifcs != null) {
+ while (ifcs.hasMoreElements()) {
+ NetworkInterface ifc = ifcs.nextElement();
+ for(Enumeration addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) {
+ InetAddress addr = addrs.nextElement();
+ System.out.println("MTU of " + addr.getHostAddress() + " is " + getMTU(addr));
+ }
+ }
+ }
+ } catch (SocketException se) {
+ System.out.println("no interfaces");
}
}
****/
diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java
index d4c8be833..96e9a6686 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java
@@ -309,8 +309,8 @@ class OutboundMessageFragments {
// use max of 1 second so finishMessages() and/or PeerState.finishMessages()
// gets called regularly
int toWait = Math.min(Math.max(nextSendDelay, 10), MAX_WAIT);
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("wait for " + toWait);
+ //if (_log.shouldLog(Log.DEBUG))
+ // _log.debug("wait for " + toWait);
// wait.. or somethin'
synchronized (_activePeers) {
try {
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
index 0b835e204..e16b34daa 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
@@ -141,6 +141,10 @@ class PacketBuilder {
/** 74 */
public static final int MIN_DATA_PACKET_OVERHEAD = IP_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE;
+ public static final int IPV6_HEADER_SIZE = 40;
+ /** 94 */
+ public static final int MIN_IPV6_DATA_PACKET_OVERHEAD = IPV6_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE;
+
/** one byte field */
public static final int ABSOLUTE_MAX_ACKS = 255;
@@ -157,7 +161,11 @@ class PacketBuilder {
private static final int MAX_RESEND_ACKS_SMALL = 4;
private static final String PROP_PADDING = "i2np.udp.padding";
+ private static final boolean DEFAULT_ENABLE_PADDING = true;
+ /**
+ * @param transport may be null for unit testing only
+ */
public PacketBuilder(I2PAppContext ctx, UDPTransport transport) {
_context = ctx;
_transport = transport;
@@ -244,7 +252,15 @@ class PacketBuilder {
}
int currentMTU = peer.getMTU();
- int availableForAcks = currentMTU - MIN_DATA_PACKET_OVERHEAD - dataSize;
+ int availableForAcks = currentMTU - dataSize;
+ int ipHeaderSize;
+ if (peer.getRemoteIP().length == 4) {
+ availableForAcks -= MIN_DATA_PACKET_OVERHEAD;
+ ipHeaderSize = IP_HEADER_SIZE;
+ } else {
+ availableForAcks -= MIN_IPV6_DATA_PACKET_OVERHEAD;
+ ipHeaderSize = IPV6_HEADER_SIZE;
+ }
int availableForExplicitAcks = availableForAcks;
// ok, now for the body...
@@ -393,23 +409,23 @@ class PacketBuilder {
// pad up so we're on the encryption boundary
off = pad1(data, off);
- off = pad2(data, off, currentMTU);
+ off = pad2(data, off, currentMTU - (ipHeaderSize + UDP_HEADER_SIZE));
pkt.setLength(off);
authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey());
setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort());
if (_log.shouldLog(Log.INFO)) {
- msg.append(" pkt size ").append(off + (IP_HEADER_SIZE + UDP_HEADER_SIZE));
+ msg.append(" pkt size ").append(off + (ipHeaderSize + UDP_HEADER_SIZE));
_log.info(msg.toString());
}
// the packet could have been built before the current mtu got lowered, so
// compare to LARGE_MTU
- if (off + (IP_HEADER_SIZE + UDP_HEADER_SIZE) > PeerState.LARGE_MTU) {
+ if (off + (ipHeaderSize + UDP_HEADER_SIZE) > PeerState.LARGE_MTU) {
_log.error("Size is " + off + " for " + packet +
" fragment " + fragment +
" data size " + dataSize +
- " pkt size " + (off + (IP_HEADER_SIZE + UDP_HEADER_SIZE)) +
+ " pkt size " + (off + (ipHeaderSize + UDP_HEADER_SIZE)) +
" MTU " + currentMTU +
' ' + availableForAcks + " for all acks " +
availableForExplicitAcks + " for full acks " +
@@ -561,7 +577,7 @@ class PacketBuilder {
state.prepareSessionCreated();
byte sentIP[] = state.getSentIP();
- if ( (sentIP == null) || (sentIP.length <= 0) || ( (_transport != null) && (!_transport.isValid(sentIP)) ) ) {
+ if ( (sentIP == null) || (sentIP.length <= 0) || (!_transport.isValid(sentIP))) {
if (_log.shouldLog(Log.ERROR))
_log.error("How did our sent IP become invalid? " + state);
state.fail();
@@ -642,7 +658,7 @@ class PacketBuilder {
int off = HEADER_SIZE;
byte toIP[] = state.getSentIP();
- if ( (_transport !=null) && (!_transport.isValid(toIP)) ) {
+ if (!_transport.isValid(toIP)) {
packet.release();
return null;
}
@@ -656,7 +672,7 @@ class PacketBuilder {
return null;
}
if (_log.shouldLog(Log.DEBUG))
- _log.debug("Sending request");
+ _log.debug("Sending request to " + Addresses.toString(toIP));
// now for the body
byte[] x = state.getSentX();
@@ -767,6 +783,9 @@ class PacketBuilder {
_context.random().nextBytes(data, off, paddingRequired);
off += paddingRequired;
}
+ // We cannot have non-mod16 (pad2) padding here, since the signature
+ // is at the end. As of 0.9.7 we won't decrypt past the end of the packet
+ // so trailing non-mod-16 data is ignored. That truncates the sig.
// BUG: NPE here if null signature
System.arraycopy(state.getSentSignature().getData(), 0, data, off, Signature.SIGNATURE_BYTES);
@@ -777,8 +796,9 @@ class PacketBuilder {
// nothing more to add beyond the identity fragment
// pad up so we're on the encryption boundary
off = pad1(data, off);
+ // allowed but untested
+ //off = pad2(data, off);
}
- off = pad2(data, off);
pkt.setLength(off);
authenticate(packet, state.getCipherKey(), state.getMACKey());
setTo(packet, to, state.getSentPort());
@@ -1051,6 +1071,7 @@ class PacketBuilder {
// specify these if we know what our external receive ip/port is and if its different
// from what bob is going to think
+ // FIXME IPv4 addr must be specified when sent over IPv6
private byte[] getOurExplicitIP() { return null; }
private int getOurExplicitPort() { return 0; }
@@ -1070,8 +1091,10 @@ class PacketBuilder {
// let's not use an introducer on a privileged port, sounds like trouble
if (ikey == null || iport < 1024 || iport > 65535 ||
iaddr == null || tag <= 0 ||
+ // must be IPv4 for now as we don't send Alice IP/port, see below
+ iaddr.getAddress().length != 4 ||
(!_transport.isValid(iaddr.getAddress())) ||
- Arrays.equals(iaddr.getAddress(), _transport.getExternalIP())) {
+ (Arrays.equals(iaddr.getAddress(), _transport.getExternalIP()) && !_transport.allowLocal())) {
if (_log.shouldLog(_log.WARN))
_log.warn("Cannot build a relay request to " + state.getRemoteIdentity().calculateHash()
+ ", as their UDP address is invalid: addr=" + addr + " index=" + i);
@@ -1083,6 +1106,11 @@ class PacketBuilder {
return rv;
}
+ /**
+ * TODO Alice IP/port in packet will always be null/0, must be fixed to
+ * send a RelayRequest over IPv6
+ *
+ */
private UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[],
long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) {
UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE);
@@ -1090,6 +1118,7 @@ class PacketBuilder {
byte data[] = pkt.getData();
int off = HEADER_SIZE;
+ // FIXME must specify these if request is going over IPv6
byte ourIP[] = getOurExplicitIP();
int ourPort = getOurExplicitPort();
@@ -1211,6 +1240,7 @@ class PacketBuilder {
DataHelper.toLong(data, off, 2, charlie.getRemotePort());
off += 2;
+ // Alice IP/Port currently ignored on receive - see UDPPacketReader
byte aliceIP[] = alice.getIP();
DataHelper.toLong(data, off, 1, aliceIP.length);
off++;
@@ -1233,7 +1263,7 @@ class PacketBuilder {
}
/**
- * Sends an empty unauthenticated packet for hole punching.
+ * Creates an empty unauthenticated packet for hole punching.
* Parameters must be validated previously.
*/
public UDPPacket buildHolePunch(InetAddress to, int port) {
@@ -1250,6 +1280,22 @@ class PacketBuilder {
return packet;
}
+ /**
+ * TESTING ONLY.
+ * Creates an arbitrary packet for unit testing.
+ * Null transport in constructor OK.
+ *
+ * @since IPv6
+ */
+ public UDPPacket buildPacket(byte[] data, InetAddress to, int port) {
+ UDPPacket packet = UDPPacket.acquire(_context, false);
+ byte d[] = packet.getPacket().getData();
+ System.arraycopy(data, 0, d, 0, data.length);
+ packet.getPacket().setLength(data.length);
+ setTo(packet, to, port);
+ return packet;
+ }
+
/**
* Create a new packet and add the flag byte and the time stamp.
* Caller should add data starting at HEADER_SIZE.
@@ -1310,7 +1356,7 @@ class PacketBuilder {
* @since 0.9.7
*/
private int pad2(byte[] data, int off) {
- if (!_context.getBooleanProperty(PROP_PADDING))
+ if (!_context.getProperty(PROP_PADDING, DEFAULT_ENABLE_PADDING))
return off;
int padSize = _context.random().nextInt(MAX_PAD2);
if (padSize == 0)
@@ -1329,7 +1375,7 @@ class PacketBuilder {
* @since 0.9.7
*/
private int pad2(byte[] data, int off, int maxLen) {
- if (!_context.getBooleanProperty(PROP_PADDING))
+ if (!_context.getProperty(PROP_PADDING, DEFAULT_ENABLE_PADDING))
return off;
if (off >= maxLen)
return off;
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
index 96ceec9c0..e901ac859 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
@@ -3,9 +3,11 @@ package net.i2p.router.transport.udp;
import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.BlockingQueue;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
+import net.i2p.router.util.CoDelBlockingQueue;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PThread;
import net.i2p.util.LHMCache;
@@ -26,7 +28,6 @@ class PacketHandler {
private final RouterContext _context;
private final Log _log;
private final UDPTransport _transport;
- private final UDPEndpoint _endpoint;
private final EstablishmentManager _establisher;
private final InboundMessageFragments _inbound;
private final PeerTestManager _testManager;
@@ -34,19 +35,22 @@ class PacketHandler {
private volatile boolean _keepReading;
private final Handler[] _handlers;
private final Map _failCache;
+ private final BlockingQueue _inboundQueue;
private static final Object DUMMY = new Object();
+ private static final int TYPE_POISON = -99999;
+ private static final int MIN_QUEUE_SIZE = 16;
+ private static final int MAX_QUEUE_SIZE = 192;
private static final int MIN_NUM_HANDLERS = 1; // unless < 32MB
private static final int MAX_NUM_HANDLERS = 1;
/** let packets be up to 30s slow */
private static final long GRACE_PERIOD = Router.CLOCK_FUDGE_FACTOR + 30*1000;
- PacketHandler(RouterContext ctx, UDPTransport transport, UDPEndpoint endpoint, EstablishmentManager establisher,
+ PacketHandler(RouterContext ctx, UDPTransport transport, EstablishmentManager establisher,
InboundMessageFragments inbound, PeerTestManager testManager, IntroductionManager introManager) {
_context = ctx;
_log = ctx.logManager().getLog(PacketHandler.class);
_transport = transport;
- _endpoint = endpoint;
_establisher = establisher;
_inbound = inbound;
_testManager = testManager;
@@ -56,6 +60,8 @@ class PacketHandler {
long maxMemory = Runtime.getRuntime().maxMemory();
if (maxMemory == Long.MAX_VALUE)
maxMemory = 96*1024*1024l;
+ int qsize = (int) Math.max(MIN_QUEUE_SIZE, Math.min(MAX_QUEUE_SIZE, maxMemory / (2*1024*1024)));
+ _inboundQueue = new CoDelBlockingQueue(ctx, "UDP-Receiver", qsize);
int num_handlers;
if (maxMemory < 32*1024*1024)
num_handlers = 1;
@@ -107,6 +113,7 @@ class PacketHandler {
public synchronized void shutdown() {
_keepReading = false;
+ stopQueue();
}
String getHandlerStatus() {
@@ -119,9 +126,55 @@ class PacketHandler {
return rv.toString();
}
- /** @since 0.8.8 */
- int getHandlerCount() {
- return _handlers.length;
+ /**
+ * Blocking call to retrieve the next inbound packet, or null if we have
+ * shut down.
+ *
+ * @since IPv6 moved from UDPReceiver
+ */
+ public void queueReceived(UDPPacket packet) throws InterruptedException {
+ _inboundQueue.put(packet);
+ }
+
+
+ /**
+ * Blocking for a while
+ *
+ * @since IPv6 moved from UDPReceiver
+ */
+ private void stopQueue() {
+ _inboundQueue.clear();
+ for (int i = 0; i < _handlers.length; i++) {
+ UDPPacket poison = UDPPacket.acquire(_context, false);
+ poison.setMessageType(TYPE_POISON);
+ _inboundQueue.offer(poison);
+ }
+ for (int i = 1; i <= 5 && !_inboundQueue.isEmpty(); i++) {
+ try {
+ Thread.sleep(i * 50);
+ } catch (InterruptedException ie) {}
+ }
+ _inboundQueue.clear();
+ }
+
+ /**
+ * Blocking call to retrieve the next inbound packet, or null if we have
+ * shut down.
+ *
+ * @since IPv6 moved from UDPReceiver
+ */
+ public UDPPacket receiveNext() {
+ UDPPacket rv = null;
+ //int remaining = 0;
+ while (_keepReading && rv == null) {
+ try {
+ rv = _inboundQueue.take();
+ } catch (InterruptedException ie) {}
+ if (rv != null && rv.getMessageType() == TYPE_POISON)
+ return null;
+ }
+ //_context.statManager().addRateData("udp.receiveRemaining", remaining, 0);
+ return rv;
}
/** the packet is from a peer we are establishing an outbound con to, but failed validation, so fallback */
@@ -144,7 +197,7 @@ class PacketHandler {
_state = 1;
while (_keepReading) {
_state = 2;
- UDPPacket packet = _endpoint.receive();
+ UDPPacket packet = receiveNext();
_state = 3;
if (packet == null) break; // keepReading is probably false, or bind failed...
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java
index cf7ce2192..db7fb4ea3 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java
@@ -1,26 +1,29 @@
package net.i2p.router.transport.udp;
+import java.util.List;
+
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Blocking thread to grab new packets off the outbound fragment
- * pool and toss 'em onto the outbound packet queue
+ * pool and toss 'em onto the outbound packet queues.
*
+ * Here we select which UDPEndpoint/UDPSender to send it out.
*/
class PacketPusher implements Runnable {
// private RouterContext _context;
private final Log _log;
private final OutboundMessageFragments _fragments;
- private final UDPSender _sender;
+ private final List _endpoints;
private volatile boolean _alive;
- public PacketPusher(RouterContext ctx, OutboundMessageFragments fragments, UDPSender sender) {
+ public PacketPusher(RouterContext ctx, OutboundMessageFragments fragments, List endpoints) {
// _context = ctx;
_log = ctx.logManager().getLog(PacketPusher.class);
_fragments = fragments;
- _sender = sender;
+ _endpoints = endpoints;
}
public synchronized void startup() {
@@ -38,8 +41,7 @@ class PacketPusher implements Runnable {
if (packets != null) {
for (int i = 0; i < packets.length; i++) {
if (packets[i] != null) // null for ACKed fragments
- // BLOCKING if queue is full
- _sender.add(packets[i]);
+ send(packets[i]);
}
}
} catch (Exception e) {
@@ -47,4 +49,38 @@ class PacketPusher implements Runnable {
}
}
}
+
+ /**
+ * This sends it directly out, bypassing OutboundMessageFragments
+ * and the PacketPusher. The only queueing is for the bandwidth limiter.
+ * BLOCKING if OB queue is full.
+ *
+ * @param packet non-null
+ * @since IPv6
+ */
+ public void send(UDPPacket packet) {
+ boolean isIPv4 = packet.getPacket().getAddress().getAddress().length == 4;
+ for (int j = 0; j < _endpoints.size(); j++) {
+ // Find the best endpoint (socket) to send this out.
+ // TODO if we have multiple IPv4, or multiple IPv6 endpoints,
+ // we have to track which one we're using in the PeerState and
+ // somehow set that in the UDPPacket so we're consistent
+ UDPEndpoint ep;
+ try {
+ ep = _endpoints.get(j);
+ } catch (IndexOutOfBoundsException ioobe) {
+ // whups, list changed
+ break;
+ }
+ if ((isIPv4 && ep.isIPv4()) ||
+ ((!isIPv4) && ep.isIPv6())) {
+ // BLOCKING if queue is full
+ ep.getSender().add(packet);
+ return;
+ }
+ }
+ // not handled
+ _log.error("No endpoint to send " + packet);
+ packet.release();
+ }
}
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java
index 31b2dd7bd..65e236019 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -267,6 +267,12 @@ class PeerState {
* and so PacketBuilder.buildPacket() works correctly.
*/
public static final int MIN_MTU = 620;
+
+ /**
+ * IPv6/UDP header is 48 bytes, so we want MTU % 16 == 0.
+ */
+ public static final int MIN_IPV6_MTU = 1280;
+ public static final int MAX_IPV6_MTU = 1472; // TODO 1488
private static final int DEFAULT_MTU = MIN_MTU;
/*
@@ -315,9 +321,15 @@ class PeerState {
_receivePeriodBegin = now;
_lastCongestionOccurred = -1;
_remotePort = remotePort;
- _mtu = DEFAULT_MTU;
- _mtuReceive = DEFAULT_MTU;
- _largeMTU = transport.getMTU();
+ if (remoteIP.length == 4) {
+ _mtu = DEFAULT_MTU;
+ _mtuReceive = DEFAULT_MTU;
+ _largeMTU = transport.getMTU(false);
+ } else {
+ _mtu = MIN_IPV6_MTU;
+ _mtuReceive = MIN_IPV6_MTU;
+ _largeMTU = transport.getMTU(true);
+ }
//_mtuLastChecked = -1;
_lastACKSend = -1;
_rto = INIT_RTO;
@@ -686,6 +698,12 @@ class PeerState {
public int getConcurrentSendWindow() { return _concurrentMessagesAllowed; }
public int getConsecutiveSendRejections() { return _consecutiveRejections; }
public boolean isInbound() { return _isInbound; }
+
+ /** @since IPv6 */
+ public boolean isIPv6() {
+ return _remoteIP.length == 16;
+ }
+
public long getIntroducerTime() { return _lastIntroducerTime; }
public void setIntroducerTime() { _lastIntroducerTime = _context.clock().now(); }
@@ -1145,12 +1163,12 @@ class PeerState {
_context.statManager().addRateData("udp.mtuIncrease", _mtuIncreases);
}
} else if (!wantLarge && _mtu == _largeMTU) {
- _mtu = MIN_MTU;
+ _mtu = _remoteIP.length == 4 ? MIN_MTU : MIN_IPV6_MTU;
_mtuDecreases++;
_context.statManager().addRateData("udp.mtuDecrease", _mtuDecreases);
}
} else {
- _mtu = DEFAULT_MTU;
+ _mtu = _remoteIP.length == 4 ? DEFAULT_MTU : MIN_IPV6_MTU;
}
}
@@ -1158,7 +1176,8 @@ class PeerState {
* @since 0.9.2
*/
public synchronized void setHisMTU(int mtu) {
- if (mtu <= MIN_MTU || mtu >= _largeMTU)
+ if (mtu <= MIN_MTU || mtu >= _largeMTU ||
+ (_remoteIP.length == 16 && mtu <= MIN_IPV6_MTU))
return;
_largeMTU = mtu;
if (mtu < _mtu)
@@ -1225,17 +1244,27 @@ class PeerState {
/** 60 */
private static final int OVERHEAD_SIZE = PacketBuilder.IP_HEADER_SIZE + PacketBuilder.UDP_HEADER_SIZE +
UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
+ /** 80 */
+ private static final int IPV6_OVERHEAD_SIZE = PacketBuilder.IPV6_HEADER_SIZE + PacketBuilder.UDP_HEADER_SIZE +
+ UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
/**
* @param size not including IP header, UDP header, MAC or IV
*/
public void packetReceived(int size) {
_packetsReceived++;
- size += OVERHEAD_SIZE;
- if (size <= MIN_MTU) {
+ int minMTU;
+ if (_remoteIP.length == 4) {
+ size += OVERHEAD_SIZE;
+ minMTU = MIN_MTU;
+ } else {
+ size += IPV6_OVERHEAD_SIZE;
+ minMTU = MIN_IPV6_MTU;
+ }
+ if (size <= minMTU) {
_consecutiveSmall++;
if (_consecutiveSmall >= MTU_RCV_DISPLAY_THRESHOLD)
- _mtuReceive = MIN_MTU;
+ _mtuReceive = minMTU;
} else {
_consecutiveSmall = 0;
if (size > _mtuReceive)
@@ -1289,7 +1318,7 @@ class PeerState {
private int countMaxACKData() {
return Math.min(PacketBuilder.ABSOLUTE_MAX_ACKS * 4,
_mtu
- - PacketBuilder.IP_HEADER_SIZE
+ - (_remoteIP.length == 4 ? PacketBuilder.IP_HEADER_SIZE : PacketBuilder.IPV6_HEADER_SIZE)
- PacketBuilder.UDP_HEADER_SIZE
- UDPPacket.IV_SIZE
- UDPPacket.MAC_SIZE
@@ -1630,11 +1659,14 @@ class PeerState {
/**
* how much payload data can we shove in there?
- * @return MTU - 87, i.e. 533 or 1397
+ * @return MTU - 87, i.e. 533 or 1397 (IPv4), MTU - 107 (IPv6)
*/
- private static final int fragmentSize(int mtu) {
- // 46 + 20 + 8 + 13 = 74 + 13 = 87
- return mtu - (PacketBuilder.MIN_DATA_PACKET_OVERHEAD + MIN_ACK_SIZE);
+ private int fragmentSize() {
+ // 46 + 20 + 8 + 13 = 74 + 13 = 87 (IPv4)
+ // 46 + 40 + 8 + 13 = 74 + 13 = 107 (IPv6)
+ return _mtu -
+ (_remoteIP.length == 4 ? PacketBuilder.MIN_DATA_PACKET_OVERHEAD : PacketBuilder.MIN_IPV6_DATA_PACKET_OVERHEAD) -
+ MIN_ACK_SIZE;
}
private enum ShouldSend { YES, NO, NO_BW };
@@ -1647,7 +1679,7 @@ class PeerState {
long now = _context.clock().now();
if (state.getNextSendTime() <= now) {
if (!state.isFragmented()) {
- state.fragment(fragmentSize(_mtu));
+ state.fragment(fragmentSize());
if (state.getMessage() != null)
state.getMessage().timestamp("fragment into " + state.getFragmentCount());
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 2adecf521..ffc512654 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -24,6 +24,13 @@ import net.i2p.util.SimpleTimer;
* Entry points are runTest() to start a new test as Alice,
* and receiveTest() for all received test packets.
*
+ * IPv6 info: All Alice-Bob and Alice-Charlie communication is via IPv4.
+ * The Bob-Charlie communication may be via IPv6, however Charlie must
+ * be IPv4-capable.
+ * The IP address (of Alice) in the message must be IPv4 if present,
+ * as we only support testing of IPv4.
+ * Testing of IPv6 could be added in the future.
+ *
* From udp.html on the website:
The automation of collaborative reachability testing for peers is
@@ -166,6 +173,8 @@ class PeerTestManager {
/**
* The next few methods are for when we are Alice
+ *
+ * @param bobIP IPv4 only
*/
public synchronized void runTest(InetAddress bobIP, int bobPort, SessionKey bobCipherKey, SessionKey bobMACKey) {
if (_currentTest != null) {
@@ -173,7 +182,7 @@ class PeerTestManager {
_log.warn("We are already running a test: " + _currentTest + ", aborting test with bob = " + bobIP);
return;
}
- if (DataHelper.eq(bobIP.getAddress(), 0, _transport.getExternalIP(), 0, 2)) {
+ if (_transport.isTooClose(bobIP.getAddress())) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not running test with Bob too close to us " + bobIP);
return;
@@ -304,7 +313,7 @@ class PeerTestManager {
// The reply is from Bob
int ipSize = testInfo.readIPSize();
- if (ipSize != 4 && ipSize != 16) {
+ if (ipSize != 4) {
// There appears to be a bug where Bob is sending us a zero-length IP.
// We could proceed without setting the IP, but then when Charlie
// sends us his message, we will think we are behind a symmetric NAT
@@ -366,6 +375,8 @@ class PeerTestManager {
throw new UnknownHostException("port 0");
test.setAlicePortFromCharlie(testPort);
byte ip[] = new byte[testInfo.readIPSize()];
+ if (ip.length != 4)
+ throw new UnknownHostException("not IPv4");
testInfo.readIP(ip, 0);
InetAddress addr = InetAddress.getByAddress(ip);
test.setAliceIPFromCharlie(addr);
@@ -485,7 +496,7 @@ class PeerTestManager {
int fromPort = from.getPort();
if (fromPort < 1024 || fromPort > 65535 ||
(!_transport.isValid(fromIP)) ||
- DataHelper.eq(fromIP, 0, _transport.getExternalIP(), 0, 2) ||
+ _transport.isTooClose(fromIP) ||
_context.blocklist().isBlocklisted(fromIP)) {
// spoof check, and don't respond to privileged ports
if (_log.shouldLog(Log.WARN))
@@ -505,6 +516,7 @@ class PeerTestManager {
if ((testPort > 0 && (testPort < 1024 || testPort > 65535)) ||
(testIP != null &&
((!_transport.isValid(testIP)) ||
+ testIP.length != 4 ||
_context.blocklist().isBlocklisted(testIP)))) {
// spoof check, and don't respond to privileged ports
if (_log.shouldLog(Log.WARN))
@@ -544,7 +556,7 @@ class PeerTestManager {
Long lNonce = Long.valueOf(nonce);
PeerTestState state = _activeTests.get(lNonce);
- if (testIP != null && DataHelper.eq(testIP, 0, _transport.getExternalIP(), 0, 2)) {
+ if (testIP != null && _transport.isTooClose(testIP)) {
// spoof check - have to do this after receiveTestReply(), since
// the field should be us there.
// Let's also eliminate anybody in the same /16
@@ -641,6 +653,8 @@ class PeerTestManager {
byte aliceIPData[] = new byte[sz];
try {
testInfo.readIP(aliceIPData, 0);
+ if (sz != 4)
+ throw new UnknownHostException("not IPv4");
int alicePort = testInfo.readPort();
if (alicePort == 0)
throw new UnknownHostException("port 0");
@@ -706,7 +720,12 @@ class PeerTestManager {
PeerState charlie;
RouterInfo charlieInfo = null;
if (state == null) { // pick a new charlie
- charlie = _transport.pickTestPeer(from);
+ if (from.getIP().length != 4) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("PeerTest over IPv6 from Alice as Bob? " + from);
+ return;
+ }
+ charlie = _transport.pickTestPeer(CHARLIE, from);
} else {
charlie = _transport.getPeerState(new RemoteHostId(state.getCharlieIP().getAddress(), state.getCharliePort()));
}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
index 5381bfaea..55fa9788d 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
@@ -2,16 +2,17 @@ package net.i2p.router.transport.udp;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Map;
import net.i2p.data.Base64;
import net.i2p.data.RouterAddress;
import net.i2p.data.SessionKey;
+import net.i2p.util.LHMCache;
/**
* basic helper to parse out peer info from a udp address
- * FIXME public for ConfigNetHelper
*/
-public class UDPAddress {
+class UDPAddress {
private final String _host;
private InetAddress _hostAddress;
private final int _port;
@@ -37,6 +38,23 @@ public class UDPAddress {
public static final String PROP_INTRO_KEY_PREFIX = "ikey";
public static final String PROP_INTRO_TAG_PREFIX = "itag";
static final int MAX_INTRODUCERS = 3;
+ private static final String[] PROP_INTRO_HOST;
+ private static final String[] PROP_INTRO_PORT;
+ private static final String[] PROP_INTRO_IKEY;
+ private static final String[] PROP_INTRO_TAG;
+ static {
+ // object churn
+ PROP_INTRO_HOST = new String[MAX_INTRODUCERS];
+ PROP_INTRO_PORT = new String[MAX_INTRODUCERS];
+ PROP_INTRO_IKEY = new String[MAX_INTRODUCERS];
+ PROP_INTRO_TAG = new String[MAX_INTRODUCERS];
+ for (int i = 0; i < MAX_INTRODUCERS; i++) {
+ PROP_INTRO_HOST[i] = PROP_INTRO_HOST_PREFIX + i;
+ PROP_INTRO_PORT[i] = PROP_INTRO_PORT_PREFIX + i;
+ PROP_INTRO_IKEY[i] = PROP_INTRO_KEY_PREFIX + i;
+ PROP_INTRO_TAG[i] = PROP_INTRO_TAG_PREFIX + i;
+ }
+ }
public UDPAddress(RouterAddress addr) {
// TODO make everything final
@@ -49,33 +67,38 @@ public class UDPAddress {
_port = addr.getPort();
try {
String mtu = addr.getOption(PROP_MTU);
- if (mtu != null)
- _mtu = MTU.rectify(Integer.parseInt(mtu));
+ if (mtu != null) {
+ boolean isIPv6 = _host != null && _host.contains(":");
+ _mtu = MTU.rectify(isIPv6, Integer.parseInt(mtu));
+ }
} catch (NumberFormatException nfe) {}
String key = addr.getOption(PROP_INTRO_KEY);
- if (key != null)
- _introKey = Base64.decode(key.trim());
+ if (key != null) {
+ byte[] ik = Base64.decode(key.trim());
+ if (ik != null && ik.length == SessionKey.KEYSIZE_BYTES)
+ _introKey = ik;
+ }
- for (int i = MAX_INTRODUCERS; i >= 0; i--) {
- String host = addr.getOption(PROP_INTRO_HOST_PREFIX + i);
+ for (int i = MAX_INTRODUCERS - 1; i >= 0; i--) {
+ String host = addr.getOption(PROP_INTRO_HOST[i]);
if (host == null) continue;
- String port = addr.getOption(PROP_INTRO_PORT_PREFIX + i);
+ String port = addr.getOption(PROP_INTRO_PORT[i]);
if (port == null) continue;
- String k = addr.getOption(PROP_INTRO_KEY_PREFIX + i);
+ String k = addr.getOption(PROP_INTRO_IKEY[i]);
if (k == null) continue;
byte ikey[] = Base64.decode(k);
if ( (ikey == null) || (ikey.length != SessionKey.KEYSIZE_BYTES) )
continue;
- String t = addr.getOption(PROP_INTRO_TAG_PREFIX + i);
+ String t = addr.getOption(PROP_INTRO_TAG[i]);
if (t == null) continue;
- int p = -1;
+ int p;
try {
p = Integer.parseInt(port);
- if (p <= 0 || p > 65535) continue;
+ if (p < UDPTransport.MIN_PEER_PORT || p > 65535) continue;
} catch (NumberFormatException nfe) {
continue;
}
- long tag = -1;
+ long tag;
try {
tag = Long.parseLong(t);
if (tag <= 0) continue;
@@ -131,14 +154,10 @@ public class UDPAddress {
}
public String getHost() { return _host; }
+
InetAddress getHostAddress() {
- if (_hostAddress == null) {
- try {
- _hostAddress = InetAddress.getByName(_host);
- } catch (UnknownHostException uhe) {
- _hostAddress = null;
- }
- }
+ if (_hostAddress == null)
+ _hostAddress = getByName(_host);
return _hostAddress;
}
@@ -150,18 +169,17 @@ public class UDPAddress {
byte[] getIntroKey() { return _introKey; }
int getIntroducerCount() { return (_introAddresses == null ? 0 : _introAddresses.length); }
+
InetAddress getIntroducerHost(int i) {
- if (_introAddresses[i] == null) {
- try {
- _introAddresses[i] = InetAddress.getByName(_introHosts[i]);
- } catch (UnknownHostException uhe) {
- _introAddresses[i] = null;
- }
- }
+ if (_introAddresses[i] == null)
+ _introAddresses[i] = getByName(_introHosts[i]);
return _introAddresses[i];
}
+
int getIntroducerPort(int i) { return _introPorts[i]; }
+
byte[] getIntroducerKey(int i) { return _introKeys[i]; }
+
long getIntroducerTag(int i) { return _introTags[i]; }
/**
@@ -192,4 +210,71 @@ public class UDPAddress {
}
return rv.toString();
}
+
+ ////////////////
+ // cache copied from Addresses.java but caching InetAddress instead of byte[]
+
+
+ /**
+ * Textual IP to InetAddress, because InetAddress.getByName() is slow.
+ *
+ * @since IPv6
+ */
+ private static final Map _inetAddressCache;
+
+ static {
+ long maxMemory = Runtime.getRuntime().maxMemory();
+ if (maxMemory == Long.MAX_VALUE)
+ maxMemory = 96*1024*1024l;
+ long min = 128;
+ long max = 2048;
+ // 512 nominal for 128 MB
+ int size = (int) Math.max(min, Math.min(max, 1 + (maxMemory / (256*1024))));
+ _inetAddressCache = new LHMCache(size);
+ }
+
+ /**
+ * Caching version of InetAddress.getByName(host), which is slow.
+ * Caches numeric host names only.
+ * Will resolve but not cache DNS host names.
+ *
+ * Unlike InetAddress.getByName(), we do NOT allow numeric IPs
+ * of the form d.d.d, d.d, or d, as these are almost certainly mistakes.
+ *
+ * @param host DNS or IPv4 or IPv6 host name; if null returns null
+ * @return InetAddress or null
+ * @since IPv6
+ */
+ private static InetAddress getByName(String host) {
+ if (host == null)
+ return null;
+ InetAddress rv;
+ synchronized (_inetAddressCache) {
+ rv = _inetAddressCache.get(host);
+ }
+ if (rv == null) {
+ try {
+ boolean isIPv4 = host.replaceAll("[0-9\\.]", "").length() == 0;
+ if (isIPv4 && host.replaceAll("[0-9]", "").length() != 3)
+ return null;
+ rv = InetAddress.getByName(host);
+ if (isIPv4 ||
+ host.replaceAll("[0-9a-fA-F:]", "").length() == 0) {
+ synchronized (_inetAddressCache) {
+ _inetAddressCache.put(host, rv);
+ }
+ }
+ } catch (UnknownHostException uhe) {}
+ }
+ return rv;
+ }
+
+ /**
+ * @since IPv6
+ */
+ static void clearCache() {
+ synchronized(_inetAddressCache) {
+ _inetAddressCache.clear();
+ }
+ }
}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
index 179649de5..b750df660 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java
@@ -1,15 +1,19 @@
package net.i2p.router.transport.udp;
+import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.SocketException;
+import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
- * Coordinate the low level datagram socket, managing the UDPSender and
- * UDPReceiver
+ * Coordinate the low-level datagram socket, creating and managing the UDPSender and
+ * UDPReceiver.
*/
class UDPEndpoint {
private final RouterContext _context;
@@ -20,8 +24,11 @@ class UDPEndpoint {
private UDPReceiver _receiver;
private DatagramSocket _socket;
private final InetAddress _bindAddress;
+ private final boolean _isIPv4, _isIPv6;
+ private static final AtomicInteger _counter = new AtomicInteger();
/**
+ * @param transport may be null for unit testing ONLY
* @param listenPort -1 or the requested port, may not be honored
* @param bindAddress null ok
*/
@@ -31,22 +38,27 @@ class UDPEndpoint {
_transport = transport;
_bindAddress = bindAddress;
_listenPort = listenPort;
+ _isIPv4 = bindAddress == null || bindAddress instanceof Inet4Address;
+ _isIPv6 = bindAddress == null || bindAddress instanceof Inet6Address;
}
/** caller should call getListenPort() after this to get the actual bound port and determine success */
- public synchronized void startup() {
+ public synchronized void startup() throws SocketException {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Starting up the UDP endpoint");
shutdown();
_socket = getSocket();
if (_socket == null) {
_log.log(Log.CRIT, "UDP Unable to open a port");
- return;
+ throw new SocketException("SSU Unable to bind to a port on " + _bindAddress);
}
- _sender = new UDPSender(_context, _socket, "UDPSender");
- _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver");
+ int count = _counter.incrementAndGet();
+ _sender = new UDPSender(_context, _socket, "UDPSender " + count);
_sender.startup();
- _receiver.startup();
+ if (_transport != null) {
+ _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver " + count);
+ _receiver.startup();
+ }
}
public synchronized void shutdown() {
@@ -103,9 +115,7 @@ class UDPEndpoint {
if (port <= 0) {
// try random ports rather than just do new DatagramSocket()
// so we stay out of the way of other I2P stuff
- int minPort = _context.getProperty(PROP_MIN_PORT, MIN_RANDOM_PORT);
- int maxPort = _context.getProperty(PROP_MAX_PORT, MAX_RANDOM_PORT);
- port = minPort + _context.random().nextInt(maxPort - minPort);
+ port = selectRandomPort(_context);
}
try {
if (_bindAddress == null)
@@ -131,6 +141,17 @@ class UDPEndpoint {
return socket;
}
+ /**
+ * Pick a random port between the configured boundaries
+ * @since IPv6
+ */
+ public static int selectRandomPort(RouterContext ctx) {
+ int minPort = Math.min(65535, Math.max(1, ctx.getProperty(PROP_MIN_PORT, MIN_RANDOM_PORT)));
+ int maxPort = Math.min(65535, Math.max(minPort, ctx.getProperty(PROP_MAX_PORT, MAX_RANDOM_PORT)));
+ return minPort + ctx.random().nextInt(1 + maxPort - minPort);
+ }
+
+
/** call after startup() to get actual port or -1 on startup failure */
public int getListenPort() { return _listenPort; }
public UDPSender getSender() { return _sender; }
@@ -143,15 +164,24 @@ class UDPEndpoint {
public void send(UDPPacket packet) {
_sender.add(packet);
}
-
+
/**
* Blocking call to receive the next inbound UDP packet from any peer.
- * @return null if we have shut down
+ *
+ * UNIT TESTING ONLY. Direct from the socket.
+ * In normal operation, UDPReceiver thread injects to PacketHandler queue.
+ *
+ * @return null if we have shut down, or on failure
*/
public UDPPacket receive() {
- if (_receiver == null)
+ UDPPacket packet = UDPPacket.acquire(_context, true);
+ try {
+ _socket.receive(packet.getPacket());
+ return packet;
+ } catch (IOException ioe) {
+ packet.release();
return null;
- return _receiver.receiveNext();
+ }
}
/**
@@ -162,4 +192,20 @@ class UDPEndpoint {
if (_sender != null)
_sender.clear();
}
+
+ /**
+ * @return true for wildcard too
+ * @since IPv6
+ */
+ public boolean isIPv4() {
+ return _isIPv4;
+ }
+
+ /**
+ * @return true for wildcard too
+ * @since IPv6
+ */
+ public boolean isIPv6() {
+ return _isIPv6;
+ }
}
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
index 0b888a098..8c13a57b7 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
@@ -10,6 +10,7 @@ import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.router.util.CDQEntry;
+import net.i2p.util.Addresses;
import net.i2p.util.Log;
/**
@@ -300,8 +301,7 @@ class UDPPacket implements CDQEntry {
StringBuilder buf = new StringBuilder(256);
buf.append(_packet.getLength());
buf.append(" byte pkt with ");
- buf.append(_packet.getAddress().getHostAddress()).append(":");
- buf.append(_packet.getPort());
+ buf.append(Addresses.toString(_packet.getAddress().getAddress(), _packet.getPort()));
//buf.append(" id=").append(System.identityHashCode(this));
if (_messageType >= 0)
buf.append(" msgType=").append(_messageType);
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
index e8c138a1c..6a82fcebd 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
@@ -730,6 +730,7 @@ class UDPPacketReader {
return (int)DataHelper.fromLong(_message, offset, 2);
}
+ /** @deprecated unused */
public int readAliceIPSize() {
int offset = readBodyOffset();
offset += DataHelper.fromLong(_message, offset, 1);
@@ -737,6 +738,7 @@ class UDPPacketReader {
offset += 2;
return (int)DataHelper.fromLong(_message, offset, 1);
}
+ /** @deprecated unused */
public void readAliceIP(byte target[], int targetOffset) {
int offset = readBodyOffset();
offset += DataHelper.fromLong(_message, offset, 1);
@@ -746,6 +748,7 @@ class UDPPacketReader {
offset++;
System.arraycopy(_message, offset, target, targetOffset, sz);
}
+ /** @deprecated unused */
public int readAlicePort() {
int offset = readBodyOffset();
offset += DataHelper.fromLong(_message, offset, 1);
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
index b7b06bb8d..d8976d483 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
@@ -3,11 +3,9 @@ package net.i2p.router.transport.udp;
import java.io.IOException;
import java.net.DatagramSocket;
import java.util.Arrays;
-import java.util.concurrent.BlockingQueue;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.FIFOBandwidthLimiter;
-import net.i2p.router.util.CoDelBlockingQueue;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
@@ -20,37 +18,31 @@ import net.i2p.util.SystemVersion;
* waiting around too long, they are dropped. Packets should be pulled off
* from the queue ASAP by a {@link PacketHandler}
*
+ * There is a UDPReceiver for each UDPEndpoint.
+ * It contains a thread but no queue. Received packets are queued
+ * in the common PacketHandler queue.
*/
class UDPReceiver {
private final RouterContext _context;
private final Log _log;
private final DatagramSocket _socket;
private String _name;
- private final BlockingQueue _inboundQueue;
private volatile boolean _keepRunning;
private final Runner _runner;
private final UDPTransport _transport;
- private static int __id;
- private final int _id;
+ private final PacketHandler _handler;
private static final boolean _isAndroid = SystemVersion.isAndroid();
- private static final int TYPE_POISON = -99999;
- private static final int MIN_QUEUE_SIZE = 16;
- private static final int MAX_QUEUE_SIZE = 192;
-
public UDPReceiver(RouterContext ctx, UDPTransport transport, DatagramSocket socket, String name) {
_context = ctx;
_log = ctx.logManager().getLog(UDPReceiver.class);
- _id = ++__id;
_name = name;
- long maxMemory = Runtime.getRuntime().maxMemory();
- if (maxMemory == Long.MAX_VALUE)
- maxMemory = 96*1024*1024l;
- int qsize = (int) Math.max(MIN_QUEUE_SIZE, Math.min(MAX_QUEUE_SIZE, maxMemory / (2*1024*1024)));
- _inboundQueue = new CoDelBlockingQueue(ctx, "UDP-Receiver", qsize);
_socket = socket;
_transport = transport;
+ _handler = transport.getPacketHandler();
+ if (_handler == null)
+ throw new IllegalStateException();
_runner = new Runner();
//_context.statManager().createRateStat("udp.receivePacketSize", "How large packets received are", "udp", UDPTransport.RATES);
//_context.statManager().createRateStat("udp.receiveRemaining", "How many packets are left sitting on the receiver's queue", "udp", UDPTransport.RATES);
@@ -62,24 +54,12 @@ class UDPReceiver {
public synchronized void startup() {
//adjustDropProbability();
_keepRunning = true;
- I2PThread t = new I2PThread(_runner, _name + '.' + _id, true);
+ I2PThread t = new I2PThread(_runner, _name, true);
t.start();
}
public synchronized void shutdown() {
_keepRunning = false;
- _inboundQueue.clear();
- for (int i = 0; i < _transport.getPacketHandlerCount(); i++) {
- UDPPacket poison = UDPPacket.acquire(_context, false);
- poison.setMessageType(TYPE_POISON);
- _inboundQueue.offer(poison);
- }
- for (int i = 1; i <= 5 && !_inboundQueue.isEmpty(); i++) {
- try {
- Thread.sleep(i * 50);
- } catch (InterruptedException ie) {}
- }
- _inboundQueue.clear();
}
/*********
@@ -171,7 +151,7 @@ class UDPReceiver {
}
// drop anything apparently from our IP (any port)
- if (Arrays.equals(from.getIP(), _transport.getExternalIP())) {
+ if (Arrays.equals(from.getIP(), _transport.getExternalIP()) && !_transport.allowLocal()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping (spoofed?) packet from ourselves");
packet.release();
@@ -194,7 +174,7 @@ class UDPReceiver {
if (!rejected) {
****/
try {
- _inboundQueue.put(packet);
+ _handler.queueReceived(packet);
} catch (InterruptedException ie) {
packet.release();
_keepRunning = false;
@@ -229,24 +209,6 @@ class UDPReceiver {
}
****/
- /**
- * Blocking call to retrieve the next inbound packet, or null if we have
- * shut down.
- *
- */
- public UDPPacket receiveNext() {
- UDPPacket rv = null;
- //int remaining = 0;
- while (_keepRunning && rv == null) {
- try {
- rv = _inboundQueue.take();
- } catch (InterruptedException ie) {}
- if (rv != null && rv.getMessageType() == TYPE_POISON)
- return null;
- }
- //_context.statManager().addRateData("udp.receiveRemaining", remaining, 0);
- return rv;
- }
private class Runner implements Runnable {
//private volatile boolean _socketChanged;
@@ -288,7 +250,10 @@ class UDPReceiver {
// DatagramSocket javadocs: If the message is longer than the packet's length, the message is truncated.
throw new IOException("packet too large! truncated and dropped from: " + packet.getRemoteHost());
}
- if (size > 0) {
+ if (_context.commSystem().isDummy()) {
+ // testing
+ packet.release();
+ } else if (size > 0) {
//FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(size, "UDP receiver");
//_context.bandwidthLimiter().requestInbound(req, size, "UDP receiver");
FIFOBandwidthLimiter.Request req =
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPSender.java b/router/java/src/net/i2p/router/transport/udp/UDPSender.java
index 885cc7299..aa1affbb5 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPSender.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPSender.java
@@ -14,6 +14,9 @@ import net.i2p.util.Log;
/**
* Lowest level packet sender, pushes anything on its queue ASAP.
*
+ * There is a UDPSender for each UDPEndpoint.
+ * It contains a thread and a queue. Packet to be sent are queued
+ * by the PacketPusher.
*/
class UDPSender {
private final RouterContext _context;
@@ -23,13 +26,16 @@ class UDPSender {
private final BlockingQueue _outboundQueue;
private volatile boolean _keepRunning;
private final Runner _runner;
+ private final boolean _dummy;
+
private static final int TYPE_POISON = 99999;
-
+
private static final int MIN_QUEUE_SIZE = 64;
private static final int MAX_QUEUE_SIZE = 384;
public UDPSender(RouterContext ctx, DatagramSocket socket, String name) {
_context = ctx;
+ _dummy = false; // ctx.commSystem().isDummy();
_log = ctx.logManager().getLog(UDPSender.class);
long maxMemory = Runtime.getRuntime().maxMemory();
if (maxMemory == Long.MAX_VALUE)
@@ -176,6 +182,12 @@ class UDPSender {
_log.error("Dropping large UDP packet " + psz + " bytes: " + packet);
return;
}
+ if (_dummy) {
+ // testing
+ // back to the cache
+ packet.release();
+ return;
+ }
try {
_outboundQueue.put(packet);
} catch (InterruptedException ie) {
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index bae48409b..988930315 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -3,6 +3,7 @@ package net.i2p.router.transport.udp;
import java.io.IOException;
import java.io.Writer;
import java.net.InetAddress;
+import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -18,6 +19,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataHelper;
@@ -32,26 +34,29 @@ import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
-import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.Transport;
+import static net.i2p.router.transport.Transport.AddressSource.*;
import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl;
+import net.i2p.router.transport.TransportUtil;
+import static net.i2p.router.transport.TransportUtil.IPv6Config.*;
+import static net.i2p.router.transport.udp.PeerTestState.Role.*;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.util.RandomIterator;
import net.i2p.util.Addresses;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
+import net.i2p.util.OrderedProperties;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
import net.i2p.util.SimpleTimer2;
-import net.i2p.util.Translate;
/**
* The SSU transport
*/
public class UDPTransport extends TransportImpl implements TimedWeightedPriorityMessageQueue.FailedListener {
private final Log _log;
- private UDPEndpoint _endpoint;
+ private final List _endpoints;
private final Object _addDropLock = new Object();
/** Peer (Hash) to PeerState */
private final Map _peersByIdent;
@@ -63,7 +68,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private final OutboundMessageFragments _fragments;
private final OutboundMessageFragments.ActiveThrottle _activeThrottle;
private OutboundRefiller _refiller;
- private PacketPusher _pusher;
+ private volatile PacketPusher _pusher;
private final InboundMessageFragments _inboundFragments;
//private UDPFlooder _flooder;
private PeerTestManager _testManager;
@@ -77,19 +82,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private long _lastInboundReceivedOn;
private final DHSessionKeyBuilder.Factory _dhFactory;
private int _mtu;
+ private int _mtu_ipv6;
+
+ /**
+ * Do we have a public IPv6 address?
+ * TODO periodically update via CSFI.NetMonitor?
+ */
+ private boolean _haveIPv6Address;
/** do we need to rebuild our external router address asap? */
private boolean _needsRebuild;
- /** summary info to distribute */
- private RouterAddress _externalAddress;
- /**
- * Port number on which we can be reached, or -1 for error, or 0 for unset
- * Do NOT use this for current internal port - use _endpoint.getListenPort()
- */
- private int _externalListenPort;
- /** IP address of externally reachable host, or null */
- private InetAddress _externalListenHost;
/** introduction key */
private SessionKey _introKey;
@@ -152,7 +155,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/** allowed sources of address updates */
public static final String PROP_SOURCES = "i2np.udp.addressSources";
- public static final String DEFAULT_SOURCES = "local,upnp,ssu";
+ public static final String DEFAULT_SOURCES = SOURCE_INTERFACE.toConfigString() + ',' +
+ SOURCE_UPNP.toConfigString() + ',' +
+ SOURCE_SSU.toConfigString();
/** remember IP changes */
public static final String PROP_IP= "i2np.lastIP";
public static final String PROP_IP_CHANGE = "i2np.lastIPChange";
@@ -209,6 +214,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_peersByIdent = new ConcurrentHashMap(128);
_peersByRemoteHost = new ConcurrentHashMap(128);
_dropList = new ConcurrentHashSet(2);
+ _endpoints = new CopyOnWriteArrayList();
// See comments in DummyThrottle.java
if (USE_PRIORITY) {
@@ -239,6 +245,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_introducersSelectedOn = -1;
_lastInboundReceivedOn = -1;
_mtu = PeerState.LARGE_MTU;
+ _mtu_ipv6 = PeerState.MIN_IPV6_MTU;
+ setupPort();
_needsRebuild = true;
_context.statManager().createRateStat("udp.alreadyConnected", "What is the lifetime of a reestablished session", "udp", RATES);
@@ -260,14 +268,36 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.simpleScheduler().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4);
}
- public synchronized void startup() {
+ /**
+ * Pick a port if not previously configured, so that TransportManager may
+ * call getRequestedPort() before we've started to get a best-guess of what our
+ * port is going to be, and pass that to NTCP
+ *
+ * @since IPv6
+ */
+ private void setupPort() {
+ int port = getRequestedPort();
+ if (port < 0) {
+ port = UDPEndpoint.selectRandomPort(_context);
+ Map changes = new HashMap();
+ changes.put(PROP_INTERNAL_PORT, Integer.toString(port));
+ changes.put(PROP_EXTERNAL_PORT, Integer.toString(port));
+ _context.router().saveConfig(changes, null);
+ _log.logAlways(Log.INFO, "UDP selected random port " + port);
+ }
+ }
+
+ private synchronized void startup() {
_fragments.shutdown();
if (_pusher != null)
_pusher.shutdown();
if (_handler != null)
_handler.shutdown();
- if (_endpoint != null)
- _endpoint.shutdown();
+ for (UDPEndpoint endpoint : _endpoints) {
+ endpoint.shutdown();
+ // should we remove?
+ _endpoints.remove(endpoint);
+ }
if (_establisher != null)
_establisher.shutdown();
if (_refiller != null)
@@ -278,11 +308,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_introManager.reset();
UDPPacket.clearCache();
+ if (_log.shouldLog(Log.WARN)) _log.warn("Starting SSU transport listening");
_introKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
System.arraycopy(_context.routerHash().getData(), 0, _introKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
- rebuildExternalAddress();
-
// bind host
String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
@@ -305,9 +334,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
try {
bindToAddr = InetAddress.getByName(bindTo);
} catch (UnknownHostException uhe) {
- _log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe);
- setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
- return;
+ _log.error("Invalid SSU bind interface specified [" + bindTo + "]", uhe);
+ //setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
+ //return;
+ // fall thru...
}
}
@@ -317,7 +347,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// we will check below after starting up the endpoint.
int port;
int oldIPort = _context.getProperty(PROP_INTERNAL_PORT, -1);
- int oldBindPort = _endpoint != null ? _endpoint.getListenPort() : -1;
+ int oldBindPort = getListenPort(false);
int oldEPort = _context.getProperty(PROP_EXTERNAL_PORT, -1);
if (oldIPort > 0)
port = oldIPort;
@@ -329,14 +359,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_log.warn("Binding only to " + bindToAddr);
if (_log.shouldLog(Log.INFO))
_log.info("Binding to the port: " + port);
- if (_endpoint == null) {
- _endpoint = new UDPEndpoint(_context, this, port, bindToAddr);
+ if (_endpoints.isEmpty()) {
+ // will always be empty since we are removing them above
+ UDPEndpoint endpoint = new UDPEndpoint(_context, this, port, bindToAddr);
+ _endpoints.add(endpoint);
+ // TODO add additional endpoints for additional addresses/ports
} else {
- // todo, set bind address too
- _endpoint.setListenPort(port);
+ // unused for now
+ for (UDPEndpoint endpoint : _endpoints) {
+ if (endpoint.isIPv4()) {
+ // hack, first IPv4 endpoint, FIXME
+ // todo, set bind address too
+ endpoint.setListenPort(port);
+ break;
+ }
+ }
}
setMTU(bindToAddr);
-
+
if (_establisher == null)
_establisher = new EstablishmentManager(_context, this);
@@ -344,7 +384,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_testManager = new PeerTestManager(_context, this);
if (_handler == null)
- _handler = new PacketHandler(_context, this, _endpoint, _establisher, _inboundFragments, _testManager, _introManager);
+ _handler = new PacketHandler(_context, this, _establisher, _inboundFragments, _testManager, _introManager);
// See comments in DummyThrottle.java
if (USE_PRIORITY && _refiller == null)
@@ -355,15 +395,25 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// Startup the endpoint with the requested port, check the actual port, and
// take action if it failed or was different than requested or it needs to be saved
- _endpoint.startup();
- int newPort = _endpoint.getListenPort();
- _externalListenPort = newPort;
- if (newPort <= 0) {
+ int newPort = -1;
+ for (UDPEndpoint endpoint : _endpoints) {
+ try {
+ endpoint.startup();
+ // hack, first IPv4 endpoint, FIXME
+ if (newPort < 0 && endpoint.isIPv4()) {
+ newPort = endpoint.getListenPort();
+ }
+ } catch (SocketException se) {
+ _endpoints.remove(endpoint);
+ }
+ }
+ if (_endpoints.isEmpty()) {
_log.log(Log.CRIT, "Unable to open UDP port");
setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
return;
}
- if (newPort != port || newPort != oldIPort || newPort != oldEPort) {
+ if (newPort > 0 &&
+ (newPort != port || newPort != oldIPort || newPort != oldEPort)) {
// attempt to use it as our external port - this will be overridden by
// externalAddressReceived(...)
Map changes = new HashMap();
@@ -372,12 +422,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.router().saveConfig(changes, null);
}
- _establisher.startup();
_handler.startup();
_fragments.startup();
_inboundFragments.startup();
- _pusher = new PacketPusher(_context, _fragments, _endpoint.getSender());
+ _pusher = new PacketPusher(_context, _fragments, _endpoints);
_pusher.startup();
+ // must be after pusher
+ _establisher.startup();
if (USE_PRIORITY)
_refiller.startup();
//if (SHOULD_FLOOD_PEERS)
@@ -385,12 +436,25 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_expireEvent.setIsAlive(true);
_testEvent.setIsAlive(true); // this queues it for 3-6 minutes in the future...
_testEvent.reschedule(10*1000); // lets requeue it for Real Soon
+
+ // set up external addresses
+ // REA param is false;
+ // TransportManager.startListening() calls router.rebuildRouterInfo()
+ if (newPort > 0 && bindToAddr == null) {
+ for (InetAddress ia : getSavedLocalAddresses()) {
+ rebuildExternalAddress(ia.getHostAddress(), newPort, false);
+ }
+ }
+ rebuildExternalAddress(false);
}
public synchronized void shutdown() {
destroyAll();
- if (_endpoint != null)
- _endpoint.shutdown();
+ for (UDPEndpoint endpoint : _endpoints) {
+ endpoint.shutdown();
+ // should we remove?
+ _endpoints.remove(endpoint);
+ }
//if (_flooder != null)
// _flooder.shutdown();
if (_refiller != null)
@@ -410,6 +474,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_dropList.clear();
_introManager.reset();
UDPPacket.clearCache();
+ UDPAddress.clearCache();
+ }
+
+ /** @since IPv6 */
+ private boolean isAlive() {
+ return _inboundFragments.isAlive();
}
/**
@@ -418,23 +488,76 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
*/
SessionKey getIntroKey() { return _introKey; }
- /** @deprecated unused */
- public int getLocalPort() {
- return _endpoint != null ? _endpoint.getListenPort() : -1;
+ int getExternalPort(boolean ipv6) {
+ RouterAddress addr = getCurrentAddress(ipv6);
+ if (addr != null) {
+ int rv = addr.getPort();
+ if (rv > 0)
+ return rv;
+ }
+ return getRequestedPort(ipv6);
}
- public InetAddress getLocalAddress() { return _externalListenHost; }
- public int getExternalPort() { return _externalListenPort; }
-
/**
+ * IPv4 only
* @return IP or null
* @since 0.9.2
*/
byte[] getExternalIP() {
- InetAddress ia = _externalListenHost;
- if (ia == null)
- return null;
- return ia.getAddress();
+ RouterAddress addr = getCurrentAddress(false);
+ if (addr != null)
+ return addr.getIP();
+ return null;
+ }
+
+ /**
+ * Is this IP too close to ours to trust it for
+ * things like relaying?
+ * @param ip IPv4 or IPv6
+ * @since IPv6
+ */
+ boolean isTooClose(byte[] ip) {
+ if (allowLocal())
+ return false;
+ for (RouterAddress addr : getCurrentAddresses()) {
+ byte[] myip = addr.getIP();
+ if (myip == null || ip.length != myip.length)
+ continue;
+ if (ip.length == 4) {
+ if (DataHelper.eq(ip, 0, myip, 0, 2))
+ return true;
+ } else if (ip.length == 16) {
+ if (DataHelper.eq(ip, 0, myip, 0, 8))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The current port of the first matching endpoint.
+ * To be enhanced to handle multiple endpoints of the same type.
+ * @return port or -1
+ * @since IPv6
+ */
+ private int getListenPort(boolean ipv6) {
+ for (UDPEndpoint endpoint : _endpoints) {
+ if (((!ipv6) && endpoint.isIPv4()) ||
+ (ipv6 && endpoint.isIPv6()))
+ return endpoint.getListenPort();
+ }
+ return -1;
+ }
+
+ /**
+ * The current or configured internal IPv4 port.
+ * UDPEndpoint should always be instantiated (and a random port picked if not configured)
+ * before this is called, so the returned value should be > 0
+ * unless the endpoint failed to bind.
+ */
+ @Override
+ public int getRequestedPort() {
+ return getRequestedPort(false);
}
/**
@@ -443,15 +566,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* before this is called, so the returned value should be > 0
* unless the endpoint failed to bind.
*/
- @Override
- public int getRequestedPort() {
- if (_endpoint != null) {
- int rv = _endpoint.getListenPort();
- if (rv > 0)
- return rv;
- }
+ private int getRequestedPort(boolean ipv6) {
+ int rv = getListenPort(ipv6);
+ if (rv > 0)
+ return rv;
// fallbacks
- int rv = _context.getProperty(PROP_INTERNAL_PORT, -1);
+ rv = _context.getProperty(PROP_INTERNAL_PORT, -1);
if (rv > 0)
return rv;
return _context.getProperty(PROP_EXTERNAL_PORT, -1);
@@ -460,20 +580,30 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/**
* Set the MTU for the socket interface at addr.
* @param addr null ok
+ * @return the mtu
* @since 0.9.2
*/
- private void setMTU(InetAddress addr) {
+ private int setMTU(InetAddress addr) {
String p = _context.getProperty(PROP_DEFAULT_MTU);
if (p != null) {
try {
- _mtu = MTU.rectify(Integer.parseInt(p));
- return;
+ int pmtu = Integer.parseInt(p);
+ _mtu = MTU.rectify(false, pmtu);
+ _mtu_ipv6 = MTU.rectify(true, pmtu);
+ return _mtu;
} catch (NumberFormatException nfe) {}
}
int mtu = MTU.getMTU(addr);
- if (mtu <= 0)
- mtu = PeerState.LARGE_MTU;
- _mtu = mtu;
+ if (addr != null && addr.getAddress().length == 16) {
+ if (mtu <= 0)
+ mtu = PeerState.MIN_IPV6_MTU;
+ _mtu_ipv6 = mtu;
+ } else {
+ if (mtu <= 0)
+ mtu = PeerState.LARGE_MTU;
+ _mtu = mtu;
+ }
+ return mtu;
}
/**
@@ -482,8 +612,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @return limited to range PeerState.MIN_MTU to PeerState.LARGE_MTU.
* @since 0.9.2
*/
- int getMTU() {
- return _mtu;
+ int getMTU(boolean ipv6) {
+ // TODO multiple interfaces of each type
+ return ipv6 ? _mtu_ipv6 : _mtu;
}
/**
@@ -497,26 +628,65 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_lastInboundReceivedOn = System.currentTimeMillis();
}
+ // temp prevent multiples
+ private boolean gotIPv4Addr = false;
+ private boolean gotIPv6Addr = false;
+
/**
* From config, UPnP, local i/f, ...
+ * Not for info received from peers - see externalAddressReceived(Hash, ip, port)
*
- * @param source used for logging only
- * @param ip publicly routable IPv4 only
+ * @param source as defined in Transport.SOURCE_xxx
+ * @param ip publicly routable IPv4 or IPv6, null ok
* @param port 0 if unknown
*/
@Override
- public void externalAddressReceived(String source, byte[] ip, int port) {
+ public void externalAddressReceived(Transport.AddressSource source, byte[] ip, int port) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received address: " + Addresses.toString(ip, port) + " from: " + source);
+ if (ip == null)
+ return;
+ if (source == SOURCE_INTERFACE && ip.length == 16) {
+ // must be set before isValid() call
+ _haveIPv6Address = true;
+ }
if (explicitAddressSpecified())
return;
String sources = _context.getProperty(PROP_SOURCES, DEFAULT_SOURCES);
- if (!sources.contains(source))
+ if (!sources.contains(source.toConfigString()))
return;
+ if (!isValid(ip)) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Invalid address: " + Addresses.toString(ip, port) + " from: " + source);
+ return;
+ }
+ if (!isAlive()) {
+ if (source == SOURCE_INTERFACE || source == SOURCE_UPNP) {
+ try {
+ InetAddress ia = InetAddress.getByAddress(ip);
+ saveLocalAddress(ia);
+ } catch (UnknownHostException uhe) {}
+ }
+ return;
+ }
+ if (source == SOURCE_INTERFACE) {
+ // temp prevent multiples
+ if (ip.length == 4) {
+ if (gotIPv4Addr)
+ return;
+ else
+ gotIPv4Addr = true;
+ } else if (ip.length == 16) {
+ if (gotIPv6Addr)
+ return;
+ else
+ gotIPv6Addr = true;
+ }
+ }
boolean changed = changeAddress(ip, port);
// Assume if we have an interface with a public IP that we aren't firewalled.
// If this is wrong, the peer test will figure it out and change the status.
- if (changed && source.equals(Transport.SOURCE_INTERFACE))
+ if (changed && ip.length == 4 && source == SOURCE_INTERFACE)
setReachabilityStatus(CommSystemFacade.STATUS_OK);
}
@@ -527,14 +697,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* Don't do anything if UPnP claims failure.
*/
@Override
- public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {
+ public void forwardPortStatus(byte[] ip, int port, int externalPort, boolean success, String reason) {
if (_log.shouldLog(Log.WARN)) {
if (success)
- _log.warn("UPnP has opened the SSU port: " + port + " via external port: " + externalPort);
+ _log.warn("UPnP has opened the SSU port: " + port + " via " + Addresses.toString(ip, externalPort));
else
_log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
}
- if (success && _externalListenHost != null)
+ if (success && ip != null && getExternalIP() != null)
setReachabilityStatus(CommSystemFacade.STATUS_OK);
}
@@ -555,9 +725,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @param ourPort >= 1024
*/
void externalAddressReceived(Hash from, byte ourIP[], int ourPort) {
+ if (ourIP.length != 4)
+ return;
boolean isValid = isValid(ourIP) &&
- ((ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT) ||
- ourPort == _externalListenPort || _externalListenPort <= 0);
+ (ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT);
boolean explicitSpecified = explicitAddressSpecified();
boolean inboundRecent = _lastInboundReceivedOn + ALLOW_IP_CHANGE_INTERVAL > System.currentTimeMillis();
if (_log.shouldLog(Log.INFO))
@@ -579,33 +750,49 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
markUnreachable(from);
//_context.banlist().banlistRouter(from, "They said we had an invalid IP", STYLE);
return;
- } else if (inboundRecent && _externalListenPort > 0 && _externalListenHost != null) {
+ }
+ RouterAddress addr = getCurrentAddress(false);
+ if (inboundRecent && addr != null && addr.getPort() > 0 && addr.getHost() != null) {
// use OS clock since its an ordering thing, not a time thing
// Note that this fails us if we switch from one IP to a second, then back to the first,
// as some routers still have the first IP and will successfully connect,
// leaving us thinking the second IP is still good.
if (_log.shouldLog(Log.INFO))
_log.info("Ignoring IP address suggestion, since we have received an inbound con recently");
- } else if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
- _lastFrom = from;
- _lastOurIP = ourIP;
- _lastOurPort = ourPort;
- if (_log.shouldLog(Log.INFO))
- _log.info("The router " + from + " told us we have a new IP - "
- + Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
} else {
- if (_log.shouldLog(Log.INFO))
- _log.info(from + " and " + _lastFrom + " agree we have a new IP - "
- + Addresses.toString(ourIP, ourPort) + ". Changing address.");
- _lastFrom = from;
- _lastOurIP = ourIP;
- _lastOurPort = ourPort;
- changeAddress(ourIP, ourPort);
+ // New IP
+ boolean changeIt = false;
+ synchronized(this) {
+ if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
+ _lastFrom = from;
+ _lastOurIP = ourIP;
+ _lastOurPort = ourPort;
+ if (_log.shouldLog(Log.INFO))
+ _log.info("The router " + from + " told us we have a new IP - "
+ + Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
+ } else {
+ _lastFrom = from;
+ _lastOurIP = ourIP;
+ _lastOurPort = ourPort;
+ changeIt = true;
+ }
+ }
+ if (changeIt) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info(from + " and " + _lastFrom + " agree we have a new IP - "
+ + Addresses.toString(ourIP, ourPort) + ". Changing address.");
+ changeAddress(ourIP, ourPort);
+ }
}
-
}
/**
+ * Possibly change our external address to the IP/port.
+ * IP/port are already validated, but not yet compared to current IP/port.
+ * We compare here.
+ *
+ * @param ourIP MUST have been previously validated with isValid()
+ * IPv4 or IPv6 OK
* @param ourPort >= 1024 or 0 for no change
*/
private boolean changeAddress(byte ourIP[], int ourPort) {
@@ -614,39 +801,33 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
boolean updated = false;
boolean fireTest = false;
+ boolean isIPv6 = ourIP.length == 16;
+ RouterAddress current = getCurrentAddress(isIPv6);
+ byte[] externalListenHost = current != null ? current.getIP() : null;
+ int externalListenPort = current != null ? current.getPort() : getRequestedPort(isIPv6);
+
if (_log.shouldLog(Log.INFO))
_log.info("Change address? status = " + _reachabilityStatus +
" diff = " + (_context.clock().now() - _reachabilityStatusLastUpdated) +
- " old = " + _externalListenHost + ':' + _externalListenPort +
+ " old = " + Addresses.toString(externalListenHost, externalListenPort) +
" new = " + Addresses.toString(ourIP, ourPort));
+ if ((fixedPort && externalListenPort > 0) || ourPort <= 0)
+ ourPort = externalListenPort;
+
synchronized (this) {
- if ( (_externalListenHost == null) ||
- (!eq(_externalListenHost.getAddress(), _externalListenPort, ourIP, ourPort)) ) {
+ if (ourPort > 0 &&
+ !eq(externalListenHost, externalListenPort, ourIP, ourPort)) {
// This prevents us from changing our IP when we are not firewalled
//if ( (_reachabilityStatus != CommSystemFacade.STATUS_OK) ||
// (_externalListenHost == null) || (_externalListenPort <= 0) ||
// (_context.clock().now() - _reachabilityStatusLastUpdated > 2*TEST_FREQUENCY) ) {
// they told us something different and our tests are either old or failing
- try {
- _externalListenHost = InetAddress.getByAddress(ourIP);
- // fixed port defaults to true so we never do this
- if (ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT && !fixedPort)
- _externalListenPort = ourPort;
if (_log.shouldLog(Log.WARN))
_log.warn("Trying to change our external address to " +
- Addresses.toString(ourIP, _externalListenPort));
- if (_externalListenPort > 0) {
- rebuildExternalAddress();
- replaceAddress(_externalAddress);
- updated = true;
- }
- } catch (UnknownHostException uhe) {
- _externalListenHost = null;
- if (_log.shouldLog(Log.WARN))
- _log.warn("Error trying to change our external address to " +
- Addresses.toString(ourIP, ourPort), uhe);
- }
+ Addresses.toString(ourIP, ourPort));
+ RouterAddress newAddr = rebuildExternalAddress(ourIP, ourPort, true);
+ updated = newAddr != null;
//} else {
// // they told us something different, but our tests are recent and positive,
// // so lets test again
@@ -666,13 +847,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} else if (updated) {
_context.statManager().addRateData("udp.addressUpdated", 1);
Map changes = new HashMap();
- if (!fixedPort)
+ if (ourIP.length == 4 && !fixedPort)
changes.put(PROP_EXTERNAL_PORT, Integer.toString(ourPort));
// queue a country code lookup of the new IP
- _context.commSystem().queueLookup(ourIP);
+ if (ourIP.length == 4)
+ _context.commSystem().queueLookup(ourIP);
// store these for laptop-mode (change ident on restart... or every time... when IP changes)
+ // IPV4 ONLY
String oldIP = _context.getProperty(PROP_IP);
- if (!_externalListenHost.getHostAddress().equals(oldIP)) {
+ String newIP = Addresses.toString(ourIP);
+ if (ourIP.length == 4 && !newIP.equals(oldIP)) {
long lastChanged = 0;
long now = _context.clock().now();
String lcs = _context.getProperty(PROP_IP_CHANGE);
@@ -682,7 +866,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} catch (NumberFormatException nfe) {}
}
- changes.put(PROP_IP, _externalListenHost.getHostAddress());
+ changes.put(PROP_IP, newIP);
changes.put(PROP_IP_CHANGE, Long.toString(now));
_context.router().saveConfig(changes, null);
@@ -702,7 +886,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.router().shutdown(Router.EXIT_HARD_RESTART);
// doesn't return
}
- } else if (!fixedPort) {
+ } else if (ourIP.length == 4 && !fixedPort) {
// save PROP_EXTERNAL_PORT
_context.router().saveConfig(changes, null);
}
@@ -713,15 +897,33 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return updated;
}
+ /**
+ * @param laddr and raddr may be null
+ */
private static final boolean eq(byte laddr[], int lport, byte raddr[], int rport) {
return (rport == lport) && DataHelper.eq(laddr, raddr);
}
- /** @param addr may be null */
+ /**
+ * An IPv6 address is only valid if we are configured to support IPv6
+ * AND we have a public IPv6 address.
+ *
+ * @param addr may be null, returns false
+ */
public final boolean isValid(byte addr[]) {
if (addr == null) return false;
- if (isPubliclyRoutable(addr))
+ if (isPubliclyRoutable(addr) &&
+ (addr.length != 16 || _haveIPv6Address))
return true;
+ return allowLocal();
+ }
+
+ /**
+ * Are we allowed to connect to local addresses?
+ *
+ * @since IPv6
+ */
+ boolean allowLocal() {
return _context.getBooleanProperty("i2np.udp.allowLocal");
}
@@ -751,12 +953,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/**
* Get the states for all peers at the given remote host, ignoring port.
* Used for a last-chance search for a peer that changed port, by PacketHandler.
+ * Always returns empty list for IPv6 hostInfo.
* @since 0.9.3
*/
List getPeerStatesByIP(RemoteHostId hostInfo) {
List rv = new ArrayList(4);
byte[] ip = hostInfo.getIP();
- if (ip != null) {
+ if (ip != null && ip.length == 4) {
for (PeerState ps : _peersByIdent.values()) {
if (DataHelper.eq(ip, ps.getRemoteIP()))
rv.add(ps);
@@ -777,7 +980,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* Remove and add to peersByRemoteHost map
* @since 0.9.3
*/
- public void changePeerPort(PeerState peer, int newPort) {
+ void changePeerPort(PeerState peer, int newPort) {
int oldPort;
synchronized (_addDropLock) {
oldPort = peer.getRemotePort();
@@ -799,6 +1002,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
EstablishmentManager getEstablisher() {
return _establisher;
}
+
/**
* Intercept RouterInfo entries received directly from a peer to inject them into
* the PeersByCapacity listing.
@@ -1128,11 +1332,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if ( (altByHost != null) && (peer != altByHost) ) locked_dropPeer(altByHost, shouldBanlist, "recurse");
}
+ /**
+ * Does the IPv4 external address need to be rebuilt?
+ */
private boolean needsRebuild() {
if (_needsRebuild) return true; // simple enough
if (_context.router().isHidden()) return false;
+ RouterAddress addr = getCurrentAddress(false);
if (introducersRequired()) {
- RouterAddress addr = _externalAddress;
UDPAddress ua = new UDPAddress(addr);
int valid = 0;
for (int i = 0; i < ua.getIntroducerCount(); i++) {
@@ -1158,26 +1365,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return true;
}
} else {
- boolean rv = (_externalListenHost == null) || (_externalListenPort <= 0);
+ byte[] externalListenHost = addr != null ? addr.getIP() : null;
+ int externalListenPort = addr != null ? addr.getPort() : -1;
+ boolean rv = (externalListenHost == null) || (externalListenPort <= 0);
if (!rv) {
- RouterAddress addr = _externalAddress;
- UDPAddress ua = new UDPAddress(addr);
- if (ua.getIntroducerCount() > 0)
+ // shortcut to determine if introducers are present
+ if (addr.getOption("ihost0") != null)
rv = true; // status == ok and we don't actually need introducers, so rebuild
}
- if (_log.shouldLog(Log.INFO)) {
- if (rv) {
- _log.info("Need to initialize our direct SSU info (" + _externalListenHost + ":" + _externalListenPort + ")");
- } else {
- RouterAddress addr = _externalAddress;
- UDPAddress ua = new UDPAddress(addr);
- if ( (ua.getPort() <= 0) || (ua.getHost() == null) ) {
- _log.info("Our direct SSU info is initialized, but not used in our address yet");
- rv = true;
- } else {
- //_log.info("Our direct SSU info is initialized");
- }
- }
+ if (rv) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Need to initialize our direct SSU info (" + Addresses.toString(externalListenHost, externalListenPort) + ')');
+ } else if (addr.getPort() <= 0 || addr.getHost() == null) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Our direct SSU info is initialized, but not used in our address yet");
+ rv = true;
+ } else {
+ //_log.info("Our direct SSU info is initialized");
}
return rv;
}
@@ -1209,14 +1413,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
/**
- * This sends it directly out, bypassing OutboundMessageFragments
- * and the PacketPusher. The only queueing is for the bandwidth limiter.
+ * This sends it directly out, bypassing OutboundMessageFragments.
+ * The only queueing is for the bandwidth limiter.
* BLOCKING if OB queue is full.
*/
void send(UDPPacket packet) {
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("Sending packet " + packet);
- _endpoint.send(packet);
+ if (_pusher != null) {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Sending packet " + packet);
+ _pusher.send(packet);
+ } else {
+ _log.error("No pusher", new Exception());
+ }
}
/**
@@ -1242,7 +1450,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.8.9
*/
private void destroyAll() {
- _endpoint.clearOutbound();
+ for (UDPEndpoint endpoint : _endpoints) {
+ endpoint.clearOutbound();
+ }
int howMany = _peersByIdent.size();
// use no more than 1/4 of configured bandwidth
final int burst = 8;
@@ -1341,7 +1551,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.9.6
*/
RouterAddress getTargetAddress(RouterInfo target) {
- List addrs = target.getTargetAddresses(STYLE);
+ List addrs = getTargetAddresses(target);
for (int i = 0; i < addrs.size(); i++) {
RouterAddress addr = addrs.get(i);
if (addr.getOption("ihost0") == null) {
@@ -1349,9 +1559,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
int port = addr.getPort();
if (ip == null || port < MIN_PEER_PORT ||
(!isValid(ip)) ||
- Arrays.equals(ip, getExternalIP())) {
+ (Arrays.equals(ip, getExternalIP()) && !allowLocal())) {
continue;
}
+ } else {
+ // introducers
+ if (getIPv6Config() == IPV6_ONLY)
+ continue;
}
return addr;
}
@@ -1457,15 +1671,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// we don't need the following, since we have our own queueing
protected void outboundMessageReady() { throw new UnsupportedOperationException("Not used for UDP"); }
- public RouterAddress startListening() {
+ public void startListening() {
startup();
- return _externalAddress;
}
public void stopListening() {
shutdown();
- // will this work?
- _externalAddress = null;
replaceAddress(null);
}
@@ -1481,45 +1692,99 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @since 0.7.12
*/
@Override
- public RouterAddress updateAddress() {
+ public List updateAddress() {
rebuildExternalAddress(false);
- return getCurrentAddress();
+ return getCurrentAddresses();
}
- private void rebuildExternalAddress() { rebuildExternalAddress(true); }
+ /**
+ * Update our IPv4 addresses AND tell the router to rebuild and republish the router info.
+ *
+ * @return the new address if changed, else null
+ */
+ private RouterAddress rebuildExternalAddress() {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("REA1");
+ return rebuildExternalAddress(true);
+ }
- private void rebuildExternalAddress(boolean allowRebuildRouterInfo) {
+ /**
+ * Update our IPv4 address and optionally tell the router to rebuild and republish the router info.
+ *
+ * @param allowRebuildRouterInfo whether to tell the router
+ * @return the new address if changed, else null
+ */
+ private RouterAddress rebuildExternalAddress(boolean allowRebuildRouterInfo) {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("REA2 " + allowRebuildRouterInfo);
// if the external port is specified, we want to use that to bind to even
// if we don't know the external host.
- _externalListenPort = _context.getProperty(PROP_EXTERNAL_PORT, -1);
+ int port = _context.getProperty(PROP_EXTERNAL_PORT, -1);
+ byte[] ip = null;
+ String host = null;
if (explicitAddressSpecified()) {
- try {
- String host = _context.getProperty(PROP_EXTERNAL_HOST);
- _externalListenHost = InetAddress.getByName(host);
- } catch (UnknownHostException uhe) {
- _externalListenHost = null;
- }
+ host = _context.getProperty(PROP_EXTERNAL_HOST);
+ } else {
+ RouterAddress cur = getCurrentAddress(false);
+ if (cur != null)
+ host = cur.getHost();
}
+ return rebuildExternalAddress(host, port, allowRebuildRouterInfo);
+ }
+ /**
+ * Update our IPv4 or IPv6 address and optionally tell the router to rebuild and republish the router info.
+ *
+ * @param ip new ip valid IPv4 or IPv6 or null
+ * @param port new valid port or -1
+ * @param allowRebuildRouterInfo whether to tell the router
+ * @return the new address if changed, else null
+ * @since IPv6
+ */
+ private RouterAddress rebuildExternalAddress(byte[] ip, int port, boolean allowRebuildRouterInfo) {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("REA3 " + Addresses.toString(ip, port));
+ if (ip == null)
+ return rebuildExternalAddress((String) null, port, allowRebuildRouterInfo);
+ if (isValid(ip))
+ return rebuildExternalAddress(Addresses.toString(ip), port, allowRebuildRouterInfo);
+ return null;
+ }
+
+ /**
+ * Update our IPv4 or IPv6 address and optionally tell the router to rebuild and republish the router info.
+ *
+ * @param host new validated IPv4 or IPv6 or DNS hostname or null
+ * @param port new validated port or 0/-1
+ * @param allowRebuildRouterInfo whether to tell the router
+ * @return the new address if changed, else null
+ * @since IPv6
+ */
+ private RouterAddress rebuildExternalAddress(String host, int port, boolean allowRebuildRouterInfo) {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("REA4 " + host + ':' + port);
if (_context.router().isHidden())
- return;
+ return null;
- Properties options = new Properties();
+ OrderedProperties options = new OrderedProperties();
boolean directIncluded = false;
- if ( allowDirectUDP() && (_externalListenPort > 0) && (_externalListenHost != null) && (isValid(_externalListenHost.getAddress())) ) {
- options.setProperty(UDPAddress.PROP_PORT, String.valueOf(_externalListenPort));
- options.setProperty(UDPAddress.PROP_HOST, _externalListenHost.getHostAddress());
+ // DNS name assumed IPv4
+ boolean isIPv6 = host != null && host.contains(":");
+ if (allowDirectUDP() && port > 0 && host != null) {
+ options.setProperty(UDPAddress.PROP_PORT, String.valueOf(port));
+ options.setProperty(UDPAddress.PROP_HOST, host);
directIncluded = true;
}
- boolean introducersRequired = introducersRequired();
+ boolean introducersRequired = (!isIPv6) && introducersRequired();
boolean introducersIncluded = false;
if (introducersRequired || !directIncluded) {
int found = _introManager.pickInbound(options, PUBLIC_RELAY_COUNT);
if (found > 0) {
if (_log.shouldLog(Log.INFO))
- _log.info("Picked peers: " + found);
+ _log.info("Direct? " + directIncluded + " reqd? " + introducersRequired +
+ " picked introducers: " + found);
_introducersSelectedOn = _context.clock().now();
introducersIncluded = true;
} else {
@@ -1527,7 +1792,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// maybe we should fail to publish an address at all in this case?
// YES that would be better
if (_log.shouldLog(Log.WARN))
- _log.warn("Need introducers but we don't know any");
+ _log.warn("Direct? " + directIncluded + " reqd? " + introducersRequired +
+ " no introducers");
}
}
@@ -1538,48 +1804,71 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
options.setProperty(UDPAddress.PROP_CAPACITY, ""+UDPAddress.CAPACITY_TESTING + UDPAddress.CAPACITY_INTRODUCER);
// MTU since 0.9.2
- if (_mtu < PeerState.LARGE_MTU)
- options.setProperty(UDPAddress.PROP_MTU, Integer.toString(_mtu));
+ int mtu;
+ if (host == null) {
+ mtu = _mtu;
+ } else {
+ try {
+ InetAddress ia = InetAddress.getByName(host);
+ mtu = setMTU(ia);
+ } catch (UnknownHostException uhe) {
+ mtu = _mtu;
+ }
+ }
+ if (mtu < PeerState.LARGE_MTU)
+ options.setProperty(UDPAddress.PROP_MTU, Integer.toString(mtu));
if (directIncluded || introducersIncluded) {
// This is called via TransportManager.configTransports() before startup(), prevent NPE
+ // Note that peers won't connect to us without this - see EstablishmentManager
if (_introKey != null)
options.setProperty(UDPAddress.PROP_INTRO_KEY, _introKey.toBase64());
- RouterAddress addr = new RouterAddress();
// SSU seems to regulate at about 85%, so make it a little higher.
// If this is too low, both NTCP and SSU always have incremented cost and
// the whole mechanism is not helpful.
+ int cost = DEFAULT_COST;
if (ADJUST_COST && !haveCapacity(91))
- addr.setCost(DEFAULT_COST + 1);
- else
- addr.setCost(DEFAULT_COST);
- //addr.setExpiration(null);
- addr.setTransportStyle(STYLE);
- addr.setOptions(options);
+ cost += CONGESTION_COST_ADJUSTMENT;
+ if (introducersIncluded)
+ cost += 2;
+ if (isIPv6) {
+ TransportUtil.IPv6Config config = getIPv6Config();
+ if (config == IPV6_PREFERRED)
+ cost--;
+ else if (config == IPV6_NOT_PREFERRED)
+ cost++;
+ }
+ RouterAddress addr = new RouterAddress(STYLE, options, cost);
- boolean wantsRebuild = false;
- if ( (_externalAddress == null) || !(_externalAddress.equals(addr)) )
- wantsRebuild = true;
+ RouterAddress current = getCurrentAddress(isIPv6);
+ boolean wantsRebuild = !addr.deepEquals(current);
- RouterAddress oldAddress = _externalAddress;
- _externalAddress = addr;
- if (_log.shouldLog(Log.INFO))
- _log.info("Address rebuilt: " + addr);
- replaceAddress(addr, oldAddress);
- if (allowRebuildRouterInfo && wantsRebuild)
- _context.router().rebuildRouterInfo();
- _needsRebuild = false;
+ if (wantsRebuild) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Address rebuilt: " + addr);
+ replaceAddress(addr);
+ if (allowRebuildRouterInfo)
+ _context.router().rebuildRouterInfo();
+ } else {
+ addr = null;
+ }
+ if (!isIPv6)
+ _needsRebuild = false;
+ return addr;
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Wanted to rebuild my SSU address, but couldn't specify either the direct or indirect info (needs introducers? "
+ introducersRequired + ")", new Exception("source"));
_needsRebuild = true;
+ return null;
}
}
/**
- * Replace then tell NTCP that we changed.
+ * Replace then tell NTCP that we changed.
+ *
+ * @param address the new address or null to remove all
*/
@Override
protected void replaceAddress(RouterAddress address) {
@@ -1587,6 +1876,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.commSystem().notifyReplaceAddress(address);
}
+ /**
+ * Calls replaceAddress(address), then shuts down the router if
+ * dynamic keys is enabled, which it never is, so all this is unused.
+ *
+ * @param address the new address or null to remove all
+ */
+/****
protected void replaceAddress(RouterAddress address, RouterAddress oldAddress) {
replaceAddress(address);
if (oldAddress != null) {
@@ -1612,7 +1908,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
}
}
+****/
+ /**
+ * Do we require introducers?
+ */
public boolean introducersRequired() {
/******************
* Don't do this anymore, as we are removing the checkbox from the UI,
@@ -1656,7 +1956,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
(!_context.router().isHidden()) &&
(!introducersRequired()) &&
haveCapacity() &&
- (!((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled()) &&
+ (!_context.netDb().floodfillEnabled()) &&
_introManager.introducedCount() < IntroductionManager.MAX_OUTBOUND &&
_introManager.introducedCount() < getMaxConnections() / 4;
}
@@ -1674,13 +1974,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return "";
}
- /** @since 0.8.8 */
- int getPacketHandlerCount() {
- PacketHandler handler = _handler;
- if (handler != null)
- return handler.getHandlerCount();
- else
- return 0;
+ /** @since IPv6 */
+ PacketHandler getPacketHandler() {
+ return _handler;
}
public void failed(OutboundMessageState msg) { failed(msg, true); }
@@ -1714,6 +2010,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// dropPeer(msg.getPeer(), false);
//else if (consecutive > 2 * MAX_CONSECUTIVE_FAILED) // they're sending us data, but we cant reply?
// dropPeer(msg.getPeer(), false);
+ } else {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Failed sending " + msg + " to " + msg.getPeer());
}
noteSend(msg, false);
if (m != null)
@@ -1761,7 +2060,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
public void failed(OutNetMessage msg, String reason) {
if (msg == null) return;
if (_log.shouldLog(Log.INFO))
- _log.info("Sending message failed: " + msg, new Exception("failed from"));
+ _log.info("Send failed: " + reason + " msg: " + msg, new Exception("failed from"));
if (_context.messageHistory().getDoLog())
_context.messageHistory().sendMessage(msg.getMessageType(), msg.getMessageId(), msg.getExpiration(),
@@ -2202,6 +2501,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
appendSortLinks(buf, urlBase, sortFlags, _("Sort by peer hash"), FLAG_ALPHA);
buf.append("
");
buf.append(formatKBps(bpsIn)).append(THINSP).append(formatKBps(bpsOut));
- long x = numPeers > 0 ? uptimeMsTotal/numPeers : 0;
+ long x = uptimeMsTotal/numPeers;
buf.append("
" +
"
").append(DataHelper.formatDuration2(x));
- x = numPeers > 0 ? offsetTotal/numPeers : 0;
+ x = offsetTotal/numPeers;
buf.append("