/**************************************************************************
* Copyright (c) 2001 by Punch Telematix. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``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 PUNCH TELEMATIX OR OTHER CONTRIBUTORS 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 SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.net;
import java.security.Permission;
public final class SocketPermission extends Permission implements java.io.Serializable {
private static final long serialVersionUID = -7204263841984476862L;
private static final String DNS_CHARS = "abcdefghijklmnopqrstuvwxyz-0123456789";
private static final int ACCEPT = 1;
private static final int CONNECT = 2;
private static final int LISTEN = 4;
private static final int RESOLVE = 8;
private String action;
private String host_suffix;
private boolean has_wild_prefix;
private boolean numeric;
private int lo_port;
private int hi_port;
private int bitmap;
public SocketPermission(String host, String action){
super("".equals(host) ? "localhost":host);
if ("".equals(host)) {
host="localhost";
}
int colon = host.indexOf(':');
if (colon < 0) {
parseHostName(host);
lo_port = 0;
hi_port = 65535;
}
else {
parseHostName(host.substring(0,colon));
String ranges = host.substring(colon+1);
int hyphen = ranges.indexOf('-');
if (hyphen < 0) {
lo_port = Integer.parseInt(ranges);
hi_port = lo_port;
}
else {
String s = ranges.substring(0,hyphen);
lo_port = ("".equals(s) ? 0 : Integer.parseInt(s));
s = ranges.substring(hyphen+1);
hi_port = ("".equals(s) ? 65535 : Integer.parseInt(s));
}
}
if (hi_port < lo_port || lo_port < 0 || hi_port > 65535) {
throw new IllegalArgumentException("bad port Numbers specified");
}
parseActions(action);
}
/**
** Analyse a hostname. The name may be either a numeric IP address,
** a DNS-compliant hostname, or ``*.'' followed by a valid suffix
** of a hostname.
*/
private void parseHostName(String name) throws IllegalArgumentException {
int dotcount;
int octet = 0;
if (name.equals("")) {
throw new IllegalArgumentException("bad hostname specified");
}
if (name.charAt(0) == '*') {
if (name.length() > 3 && (name.charAt(1) != '.' || Character.isDigit(name.charAt(2)))) {
throw new IllegalArgumentException("Attempt to use wildcard in numeric IP address");
}
int start = (name.length() > 1 ? 2 : 1);
host_suffix = name.substring(start).toLowerCase();
has_wild_prefix = true;
numeric = false;
}
else {
host_suffix = name.toLowerCase();
has_wild_prefix = false;
numeric = Character.isDigit(name.charAt(0));
}
// Sanity check ... numeric version only covers IPv4!
dotcount = 0;
for (int i = 0; i < host_suffix.length(); ++i) {
char c = host_suffix.charAt(i);
if (c == '.') {
if (numeric) {
if (++dotcount > 3) {
throw new IllegalArgumentException("Too many dots in hostname");
}
octet = 0;
}
}
else if (numeric) {
if (!Character.isDigit(c)) {
throw new IllegalArgumentException("Non-digit '"+c+"'in numeric IP address");
}
octet = octet * 10 + Character.digit(c,10);
if (octet > 255 || octet < 0) {
throw new IllegalArgumentException("Illegal octet '"+octet+"'in numeric IP address");
}
}
else if (DNS_CHARS.indexOf(c) < 0) {
throw new IllegalArgumentException("Bad character '"+c+"'in hostname");
}
}
}
/**
** Analyse the action. Result is a bitmap.
*/
private void parseActions(String s) {
String sx = s.toLowerCase();
while (sx != "") {
int i = sx.indexOf(',');
String s0;
if (i<0) {
s0 = sx.trim();
sx = "";
}
else {
s0 = sx.substring(0,i).trim();
sx = sx.substring(i+1);
}
if (s0.equals("accept")) {
bitmap |= ACCEPT | RESOLVE;
}
else if (s0.equals("connect")) {
bitmap |= CONNECT | RESOLVE;
}
else if (s0.equals("listen")) {
bitmap |= LISTEN | RESOLVE;
}
else if (s0.equals("resolve")) {
bitmap |= RESOLVE;
}
else {
throw new IllegalArgumentException("bad action specified -->"+s0);
}
}
if (bitmap == 0) {
throw new IllegalArgumentException("no actions specified");
}
StringBuffer buf = new StringBuffer();
if ((bitmap & CONNECT) > 0) {
buf.append("connect,");
}
if ((bitmap & LISTEN) > 0) {
buf.append("listen,");
}
if ((bitmap & ACCEPT) > 0) {
buf.append("accept,");
}
if ((bitmap & RESOLVE) > 0) {
buf.append("resolve");
}
action = buf.toString();
}
public boolean equals (Object obj) {
if (!( obj instanceof SocketPermission)){
return false;
}
SocketPermission p = (SocketPermission) obj;
if(!this.has_wild_prefix){
try {
return !p.has_wild_prefix
&& this.lo_port == p.lo_port
&& this.hi_port == p.hi_port
&& this.action.equals(p.action)
&& InetAddress.getByNameImpl(host_suffix).equals(InetAddress.getByNameImpl(p.host_suffix));
}
catch(UnknownHostException ukhe){
}
}
return this.getName().equals(p.getName())
&& this.action.equals(p.action);
}
public int hashCode() {
if(!has_wild_prefix){
try {
return InetAddress.getByNameImpl(host_suffix).hashCode() ^ lo_port ^ (hi_port<<16);
}
catch(UnknownHostException ukhe){
}
}
return this.getName().hashCode();// ^ action.hashCode();
}
public String getActions() {
return action;
}
public boolean implies (Permission p) {
if (!(p instanceof SocketPermission)){
return false;
}
SocketPermission that = (SocketPermission)p;
return
(that.bitmap & (~this.bitmap)) == 0
&&
that.lo_port >= this.lo_port
&&
that.hi_port <= this.hi_port
&& nameImplies(this,that)
;
}
/**
** This is where we check the implications of a hostname.
** This stuff is tricky and security-sensitive, so read very carefully ...
** one of this four cases should be true ...
** 1. this permission is initialized with a numeric IP adress and one of p's adresses is equal to it
** 2. this object is *.wathever and p is something.whatever
** 3. if this objects hostname IP adresses equals one of p's IP adresses
** 4. if this canonical name equals p's canonical name
**
*/
private boolean nameImplies(SocketPermission a, SocketPermission b) {
String b_as_numeric = b.host_suffix;
String b_as_hostname = b.host_suffix;
// Quick checks to eliminate the simplest cases:
// -1- If both are non-wild and equal, fine. (checks for case 4) ...
if (!a.has_wild_prefix && !b.has_wild_prefix && a.host_suffix.equals(b.host_suffix)) {
return true;
}
// -2- If both are wild, a must be a suffix of b.
if (a.has_wild_prefix && b.has_wild_prefix) {
return b.host_suffix.endsWith(a.host_suffix);
}
// -3- A non-wild name can never imply a wild one.
if (!a.has_wild_prefix && b.has_wild_prefix) {
return false;
}
// O.K., now for the serious stuff.
// First we canonicalize b. If reverse DNS lookup fails, the game is up.
try {
if (b.numeric) {
b_as_hostname = InetAddress.getByNameImpl(b.host_suffix).getHostName();
}
else if (!b.has_wild_prefix) {
b_as_numeric = InetAddress.getByNameImpl(b.host_suffix).getHostAddress();
b_as_hostname = InetAddress.getByNameImpl(b.host_suffix).getHostName();
if (b_as_hostname != b.host_suffix) {
return false;
}
}
}
catch (UnknownHostException e) {
return false;
}
// What if both are non-wild, but the names are not identical?
// Then a must be a hostname, and either:
// -4a- b is numeric and is a possible translation of a, or
// -4b- b is a hostname and its IP address is a possible translation of a.
if (!a.has_wild_prefix && !b.has_wild_prefix) {
if (a.numeric) {
return false;
}
InetAddress[] translations;
try {
translations = InetAddress.getAllByName(a.host_suffix);
}
catch (UnknownHostException e) {
return false;
}
for (int i = 0; i < translations.length; ++i) {
if (translations[i].getHostAddress().equals(b_as_numeric)) {
return true;
}
}
return false;
}
// So a is wild and b is not.
// -5- The canonical hostname of b must match in the obvious way.
return b_as_hostname.endsWith(a.host_suffix);
}
public java.security.PermissionCollection newPermissionCollection() {
return new wonka.security.SocketPermissionCollection();
}
}