diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 160b43afe9..c73589e601 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -1215,12 +1215,39 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn } } else if("i2p".equals(host)) { clientDest = null; - } else if (destination.length() >= 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { + } else if (destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { + int len = destination.length(); + if (len < 60 || (len >= 61 && len <= 63)) { + // 8-59 or 61-63 chars, this won't work + String header = getErrorPage("b32", ERR_DESTINATION_UNKNOWN); + try { + writeErrorMessage(header, _t("Corrupt b32 address"), out, targetRequest, false, destination); + } catch (IOException ioe) {} + return; + } + if (len >= 64) { + // catch b33 errors before session lookup + try { + BlindData bd = Blinding.decode(_context, destination); + if (_log.shouldWarn()) + _log.warn("Resolved b33 " + bd); + // TESTING + //sess.sendBlindingInfo(bd, 24*60*60*1000); + } catch (IllegalArgumentException iae) { + if (_log.shouldWarn()) + _log.warn("Unable to resolve b33 " + destination, iae); + // b33 error page + String header = getErrorPage("b32", ERR_DESTINATION_UNKNOWN); + try { + writeErrorMessage(header, iae.getMessage(), out, targetRequest, false, destination); + } catch (IOException ioe) {} + return; + } + } // use existing session to look up for efficiency verifySocketManager(); I2PSession sess = sockMgr.getSession(); if (!sess.isClosed()) { - int len = destination.length(); if (len == 60) { byte[] hData = Base32.decode(destination.substring(0, 52)); if (hData != null) { @@ -1234,33 +1261,43 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn } else if (len >= 64) { if (_log.shouldInfo()) _log.info("lookup b33 in-session " + destination); - try { - BlindData bd = Blinding.decode(_context, destination); - if (_log.shouldWarn()) - _log.warn("Resolved b33 " + bd); - // TESTING - //sess.sendBlindingInfo(bd, 24*60*60*1000); - } catch (IllegalArgumentException iae) { - if (_log.shouldWarn()) - _log.warn("Unable to resolve b33 " + destination, iae); - // TODO new error page - } LookupResult lresult = sess.lookupDest2(destination, 20*1000); clientDest = lresult.getDestination(); int code = lresult.getResultCode(); - if (code != 0) { + if (code != LookupResult.RESULT_SUCCESS) { if (_log.shouldWarn()) _log.warn("Unable to resolve b33 " + destination + " error code " + code); - // TODO new form + // TODO new form to supply missing data + if (code != LookupResult.RESULT_FAILURE) { + String header = getErrorPage("b32", ERR_DESTINATION_UNKNOWN); + String msg; + if (code == LookupResult.RESULT_SECRET_REQUIRED) + msg = "b32 address requires lookup password"; + else if (code == LookupResult.RESULT_KEY_REQUIRED) + msg = "b32 address requires encryption key"; + else if (code == LookupResult.RESULT_SECRET_AND_KEY_REQUIRED) + msg = "b32 address requires encryption key and lookup password"; + else if (code == LookupResult.RESULT_DECRYPTION_FAILURE) + msg = "b32 address decryption failure, check encryption key"; + else + msg = "lookup failure code " + code; + try { + writeErrorMessage(header, msg, out, targetRequest, false, destination); + } catch (IOException ioe) {} + return; + + } } - } else { - // 61-63 chars, this won't work - clientDest = _context.namingService().lookup(destination); } } else { + if (_log.shouldInfo()) + _log.info("lookup b32 out of session " + destination); + // TODO can't get result code from here clientDest = _context.namingService().lookup(destination); } } else { + if (_log.shouldInfo()) + _log.info("lookup hostname " + destination); clientDest = _context.namingService().lookup(destination); } @@ -1518,7 +1555,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn if(host == null) { return null; } - if(host.length() == 60 && host.toLowerCase(Locale.US).endsWith(".b32.i2p")) { + if (host.toLowerCase(Locale.US).endsWith(".b32.i2p")) { return host; } Destination dest = _context.namingService().lookup(host); diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java index 816bbbfc73..d4664a3133 100644 --- a/core/java/src/net/i2p/crypto/Blinding.java +++ b/core/java/src/net/i2p/crypto/Blinding.java @@ -209,7 +209,7 @@ public final class Blinding { throw new IllegalArgumentException("Not a .b32.i2p address"); byte[] b = Base32.decode(address.substring(0, address.length() - 8)); if (b == null) - throw new IllegalArgumentException("Bad base32 encoding"); + throw new IllegalArgumentException("Corrupt b32 address"); if (b.length < 35) throw new IllegalArgumentException("Not a new-format address"); return decode(ctx, b); @@ -234,22 +234,22 @@ public final class Blinding { b[2] ^= (byte) (check >> 16); int flag = b[0] & 0xff; if ((flag & 0xf8) != 0) - throw new IllegalArgumentException("Corrupt b32 or unsupported options"); + throw new IllegalArgumentException("Corrupt b32 address (or unsupported options)"); if ((flag & FLAG_TWOBYTE) != 0) - throw new IllegalArgumentException("Two byte sig types unsupported"); + throw new IllegalArgumentException("Two byte signature types unsupported"); // TODO two-byte sigtypes int st1 = b[1] & 0xff; int st2 = b[2] & 0xff; SigType sigt1 = SigType.getByCode(st1); SigType sigt2 = SigType.getByCode(st2); if (sigt1 == null) - throw new IllegalArgumentException("Unknown sig type " + st1); + throw new IllegalArgumentException("Unsupported signature type " + st1); if (!sigt1.isAvailable()) - throw new IllegalArgumentException("Unavailable sig type " + sigt1); + throw new IllegalArgumentException("Unavailable signature type " + sigt1); if (sigt2 == null) - throw new IllegalArgumentException("Unknown blinded sig type " + st2); + throw new IllegalArgumentException("Unsupported blinded signature type " + st2); if (!sigt2.isAvailable()) - throw new IllegalArgumentException("Unavailable blinded sig type " + sigt2); + throw new IllegalArgumentException("Unavailable blinded signature type " + sigt2); // todo secret/privkey int spkLen = sigt1.getPubkeyLen(); if (3 + spkLen > b.length) diff --git a/core/java/src/net/i2p/data/Base32.java b/core/java/src/net/i2p/data/Base32.java index a573df03f2..40faa60d9b 100644 --- a/core/java/src/net/i2p/data/Base32.java +++ b/core/java/src/net/i2p/data/Base32.java @@ -235,6 +235,8 @@ public class Base32 { fivebits = DECODABET[source[i] - '2']; if (fivebits >= 0) { + if (outBuffPosn >= len58) + return null; if (usedbits == 0) { outBuff[outBuffPosn] = (byte) ((fivebits << 3) & 0xf8); usedbits = 5; diff --git a/installer/resources/proxy/b32-header.ht b/installer/resources/proxy/b32-header.ht new file mode 100644 index 0000000000..19f1b87752 --- /dev/null +++ b/installer/resources/proxy/b32-header.ht @@ -0,0 +1,25 @@ +HTTP/1.1 400 Destination Not Found +Content-Type: text/html; charset=UTF-8 +Cache-Control: no-cache +Connection: close +Proxy-Connection: close + + +
++_("The b32 address is invalid.") +
_("Could not find the following destination:") +
diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java index ec3c2386f0..8468539bf0 100644 --- a/router/java/src/net/i2p/router/client/LookupDestJob.java +++ b/router/java/src/net/i2p/router/client/LookupDestJob.java @@ -129,7 +129,7 @@ class LookupDestJob extends JobImpl { Destination d = _blindData.getDestination(); if (d != null) { if (_log.shouldDebug()) - _log.debug("Found cached b33 lookup " + _name + " to " + d); + _log.debug("Found cached b33 lookup " + _blindData.getUnblindedPubKey() + " to " + d); returnDest(d); return; } @@ -144,7 +144,7 @@ class LookupDestJob extends JobImpl { else code = HostReplyMessage.RESULT_SECRET_REQUIRED; if (_log.shouldDebug()) - _log.debug("Failed b33 lookup " + _name + " with code " + code); + _log.debug("Failed b33 lookup " + _blindData.getUnblindedPubKey() + " with code " + code); returnFail(code); } }