* susidns:

- Enforce basic rules on host name additions
- Split up form
- IDN toASCII improvements
- Log message improvements
This commit is contained in:
zzz
2011-03-29 22:46:59 +00:00
parent 172541a368
commit 9e37a5a4af
5 changed files with 148 additions and 56 deletions

View File

@@ -72,13 +72,23 @@ public class AddressBean
/**
* The Unicode name, translated from Punycode
* @return the original string on error
* @since 0.8.6
*/
public String getDisplayName()
{
return toUnicode(name);
}
/**
* The Unicode name, translated from Punycode
* @return the original string on error
* @since 0.8.6
*/
public static String toUnicode(String host) {
if (haveIDN)
return IDN.toUnicode(name);
return name;
return IDN.toUnicode(host);
return host;
}
/**
@@ -90,6 +100,65 @@ public class AddressBean
return haveIDN && !IDN.toUnicode(name).equals(name);
}
private static final char DOT = '.';
private static final char DOT2 = 0x3002;
private static final char DOT3 = 0xFF0E;
private static final char DOT4 = 0xFF61;
/**
* Ref: java.net.IDN and RFC 3940
* @param name will be converted to lower case
* @return name converted to lower case and punycoded if necessary
* @throws IAE on various errors or if IDN is needed but not available
* @since 0.8.6
*/
static String toASCII(String host) throws IllegalArgumentException {
host = host.toLowerCase();
boolean needsIDN = false;
// Here we do easy checks and throw translated exceptions.
// We do checks on the whole host name, not on each "label", so
// we allow '.', and some untranslated errors will be thrown by IDN.toASCII()
for (int i = 0; i < host.length(); i++) {
char c = host.charAt(i);
if (c <= 0x2c ||
c == 0x2f ||
c >= 0x3a && c <= 0x40 ||
c >= 0x5b && c <= 0x60 ||
c >= 0x7b && c <= 0x7f) {
String bad = "\"" + c + "\" (0x" + Integer.toHexString(c) + ')';
throw new IllegalArgumentException(_("Host name \"{0}\" contains illegal character {1}",
host, bad));
}
if (c == DOT2)
host = host.replace(DOT2, DOT);
else if (c == DOT3)
host = host.replace(DOT3, DOT);
else if (c == DOT4)
host = host.replace(DOT4, DOT);
else if (c > 0x7f)
needsIDN = true;
}
if (host.startsWith("-"))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "-"));
if (host.startsWith("."))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "."));
if (host.endsWith("-"))
throw new IllegalArgumentException(_("Host name cannot end with \"{0}\"", "-"));
if (host.endsWith("."))
throw new IllegalArgumentException(_("Host name cannot end with \"{0}\"", "."));
if (needsIDN) {
if (host.startsWith("xn--"))
throw new IllegalArgumentException(_("Host name cannot start with \"{0}\"", "xn--"));
if (host.contains(".xn--"))
throw new IllegalArgumentException(_("Host name cannot contain \"{0}\"", ".xn--"));
if (haveIDN)
return IDN.toASCII(host, IDN.ALLOW_UNASSIGNED);
throw new IllegalArgumentException(_("Host name \"{0}\" requires conversion to ASCII but the conversion library is unavailable in this JVM", host));
}
return host;
}
/** @since 0.8.6 */
public String getB32()
{
@@ -183,4 +252,9 @@ public class AddressBean
private static String _(String s, Object o) {
return Messages.getString(s, o);
}
/** translate */
private static String _(String s, Object o, Object o2) {
return Messages.getString(s, o, o2);
}
}

View File

@@ -58,7 +58,7 @@ public class AddressbookBean
return search;
}
public void setSearch(String search) {
this.search = search;
this.search = DataHelper.stripHTML(search).trim(); // XSS;
}
public boolean isHasFilter()
{
@@ -331,6 +331,8 @@ public class AddressbookBean
} else {
message = _("No entries selected to delete.");
}
if (action.equals(_("Delete Entry")))
search = null;
}
if( changed ) {
try {
@@ -390,7 +392,7 @@ public class AddressbookBean
filter = null;
search = null;
}
this.filter = filter;
this.filter = DataHelper.stripHTML(filter); // XSS
}
public String getDestination() {
return destination;
@@ -405,7 +407,7 @@ public class AddressbookBean
deletionMarks.clear();
}
public void setMarkedForDeletion( String name ) {
deletionMarks.addLast( name );
deletionMarks.addLast( DataHelper.stripHTML(name) ); // XSS
}
public void setHostname(String hostname) {
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS

View File

@@ -221,42 +221,47 @@ public class NamingServiceBean extends AddressbookBean
boolean changed = false;
if (action.equals(_("Add")) || action.equals(_("Replace"))) {
if(hostname != null && destination != null) {
Properties outProperties= new Properties();
Destination oldDest = getNamingService().lookup(hostname, nsOptions, outProperties);
if (oldDest != null && destination.equals(oldDest.toBase64())) {
message = _("Host name {0} is already in addressbook, unchanged.", hostname);
} else if (oldDest != null && !action.equals(_("Replace"))) {
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname);
} else {
try {
String host = hostname;
if (AddressBean.haveIDN) {
try {
host = IDN.toASCII(hostname, IDN.ALLOW_UNASSIGNED);
} catch (IllegalArgumentException iae) {}
try {
// throws IAE with translated message
String host = AddressBean.toASCII(hostname);
String displayHost = host.equals(hostname) ? hostname :
hostname + " (" + host + ')';
Properties outProperties= new Properties();
Destination oldDest = getNamingService().lookup(host, nsOptions, outProperties);
if (oldDest != null && destination.equals(oldDest.toBase64())) {
message = _("Host name {0} is already in addressbook, unchanged.", displayHost);
} else if (oldDest != null && !action.equals(_("Replace"))) {
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", displayHost);
} else {
try {
Destination dest = new Destination(destination);
if (oldDest != null) {
nsOptions.putAll(outProperties);
nsOptions.setProperty("m", Long.toString(I2PAppContext.getGlobalContext().clock().now()));
}
nsOptions.setProperty("s", _("Manually added via SusiDNS"));
boolean success = getNamingService().put(host, dest, nsOptions);
if (success) {
changed = true;
if (oldDest == null)
message = _("Destination added for {0}.", displayHost);
else
message = _("Destination changed for {0}.", displayHost);
// clear form
hostname = null;
destination = null;
} else {
message = _("Failed to add Destination for {0} to naming service {1}", displayHost, getNamingService()) + "<br>";
}
} catch (DataFormatException dfe) {
message = _("Invalid Base 64 destination.");
}
Destination dest = new Destination(destination);
if (oldDest != null) {
nsOptions.putAll(outProperties);
nsOptions.setProperty("m", Long.toString(I2PAppContext.getGlobalContext().clock().now()));
}
nsOptions.setProperty("s", _("Manually added via SusiDNS"));
boolean success = getNamingService().put(host, dest, nsOptions);
if (success) {
changed = true;
if (oldDest == null)
message = _("Destination added for {0}.", hostname);
else
message = _("Destination changed for {0}.", hostname);
// clear form
hostname = null;
destination = null;
} else {
message = _("Failed to add Destination for {0} to naming service {1}", hostname, getNamingService()) + "<br>";
}
} catch (DataFormatException dfe) {
message = _("Invalid Base 64 destination.");
}
} catch (IllegalArgumentException iae) {
message = iae.getMessage();
if (message == null)
message = _("Invalid host name \"{0}\".", hostname);
}
} else {
message = _("Please enter a host name and destination");
@@ -268,15 +273,18 @@ public class NamingServiceBean extends AddressbookBean
int deleted = 0;
for (String n : deletionMarks) {
boolean success = getNamingService().remove(n, nsOptions);
String uni = AddressBean.toUnicode(n);
String displayHost = uni.equals(n) ? n : uni + " (" + n + ')';
if (!success) {
message += _("Failed to delete Destination for {0} from naming service {1}", name, getNamingService()) + "<br>";
message += _("Failed to delete Destination for {0} from naming service {1}", displayHost, getNamingService()) + "<br>";
} else if (deleted++ == 0) {
changed = true;
name = n;
name = displayHost;
}
}
if( changed ) {
if (deleted == 1)
// parameter is a host name
message += _("Destination {0} deleted.", name);
else
// parameter will always be >= 2
@@ -284,6 +292,9 @@ public class NamingServiceBean extends AddressbookBean
} else {
message = _("No entries selected to delete.");
}
// clear search when deleting
if (action.equals(_("Delete Entry")))
search = null;
}
if( changed ) {
message += "<br>" + _("Addressbook saved.");

View File

@@ -110,29 +110,29 @@ ${book.loadBookMessages}
</c:if>
</div>
<div id="search">
<form method="POST" action="addressbook.jsp">
<input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99">
<div id="search">
<table><tr>
<td class="search"><%=intl._("Search")%>: <input type="text" name="search" value="${book.search}" size="20" ></td>
<td class="search"><input type="submit" name="submitsearch" value="<%=intl._("Search")%>" ></td>
</tr>
</table>
</div>
</form>
</form></div>
</c:if>
<%
// have to only do this once per page
String susiNonce = book.getSerial();
%>
<c:if test="${book.notEmpty}">
<form method="POST" action="addressbook.jsp">
<input type="hidden" name="serial" value="${book.serial}">
<input type="hidden" name="serial" value="<%=susiNonce%>">
<input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99">
<c:if test="${book.notEmpty}">
<div id="book">
<jsp:setProperty name="book" property="trClass" value="0" />
<div id="book">
<table class="book" cellspacing="0" cellpadding="5">
<tr class="head">
@@ -150,11 +150,11 @@ ${book.loadBookMessages}
<c:if test="${book.master || book.router || book.published || book.private}">
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" title="<%=intl._("Mark for deletion")%>"></td>
</c:if>
<td class="names"><a href="http://${addr.name}/">${addr.displayName}</a>
<td class="names"><a href="${addr.uri}">${addr.displayName}</a>
</td><td class="names">
<span class="addrhlpr"><a href="http://${addr.b32}/" title="<%=intl._("Base 32 address")%>">b32</a></span>
</td><td class="names">
<span class="addrhlpr"><a href="details.jsp?h=${addr.name}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span>
<span class="addrhlpr"><a href="details.jsp?h=${addr.query}" title="<%=intl._("More information on this entry")%>"><%=intl._("details")%></a></span>
</td>
<td class="destinations"><textarea rows="1" style="height: 3em;" cols="40" wrap="off" readonly="readonly" name="dest_${addr.name}" >${addr.destination}</textarea></td>
</tr>
@@ -168,7 +168,7 @@ ${book.loadBookMessages}
<input type="reset" value="<%=intl._("Cancel")%>" >
<input type="submit" name="action" value="<%=intl._("Delete Selected")%>" >
</p>
</div>
</div></form>
</c:if>
</c:if>
@@ -179,6 +179,10 @@ ${book.loadBookMessages}
</div>
</c:if>
<form method="POST" action="addressbook.jsp">
<input type="hidden" name="serial" value="<%=susiNonce%>">
<input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99">
<div id="add">
<h3><%=intl._("Add new destination")%>:</h3>
<table><tr><td>
@@ -186,14 +190,13 @@ ${book.loadBookMessages}
</td></tr><tr><td>
<b><%=intl._("Destination")%></b></td><td><textarea name="destination" rows="1" style="height: 3em;" cols="70" wrap="off" spellcheck="false">${book.destination}</textarea>
</td></tr></table>
<p>
<p class="buttons">
<input type="reset" value="<%=intl._("Cancel")%>" >
<input type="submit" name="action" value="<%=intl._("Replace")%>" >
<input type="submit" name="action" value="<%=intl._("Add")%>" >
</p>
</div>
</div></form>
</form>
<hr>
<div id="footer">
<p class="footer">susidns v${version.version} &copy; <a href="${version.url}">susi</a> 2005</p>

View File

@@ -126,11 +126,13 @@
</div>
<div id="buttons">
<form method="POST" action="addressbook.jsp">
<p class="buttons">
<input type="hidden" name="serial" value="${book.serial}">
<input type="hidden" name="begin" value="0">
<input type="hidden" name="end" value="99">
<input type="hidden" name="checked" value="<%=detail%>">
<input type="submit" name="action" value="<%=intl._("Delete Entry")%>" >
</p>
</form>
</div>
<%