package edu.washington.cs.oneswarm.f2f.servicesharing;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class ServerPublicInfo implements Comparable<ServerPublicInfo> {
private String nickname;
private long id;
private int advertizedBandwith;
private PolicyTree exitPolicy;
private Date onlineSince;
private String version;
public ServerPublicInfo(String nickname, long id, int advertBandwidth, String[] exitPolicy,
Date lastOutage, String version) {
this.nickname = nickname;
this.id = id;
this.advertizedBandwith = advertBandwidth;
this.exitPolicy = new PolicyTree(exitPolicy);
this.onlineSince = lastOutage;
this.version = version;
}
/**
* Sets the exit policy of the server using Tor's notation.
*
* The format is: (reject|accept) (domain|ip)[:port] with one policy per
* line of the string.
*
* EX: reject 66.146.193.31:* accept *:80
*
* @param policy
* Tor style exit policy array
*/
public void setExitPolicy(String[] policy) {
exitPolicy = new PolicyTree(policy);
}
public boolean allowsConnectionTo(String url, int port) {
return exitPolicy.getPolicy(url, port);
}
public int compareTo(ServerPublicInfo other) {
return this.advertizedBandwith - other.advertizedBandwith;
}
public String getNickname() {
return nickname;
}
public long getId() {
return id;
}
public int getAdvertizedBandwith() {
return advertizedBandwith;
}
public Date getOnlineSinceDate() {
return onlineSince;
}
public String getVersion() {
return version;
}
private enum PolicyValue {
ACCEPT, REJECT
}
private enum NodeType {
DOMAIN, PORT
}
private class PolicyTree {
private PolicyNode root;
public PolicyTree(String[] policy) {
root = new PolicyNode("");
addPolicies(policy);
}
public void addPolicies(String[] policyStrings) {
for (int i = 0; i < policyStrings.length; i++)
addPolicy(policyStrings[i]);
}
public void addPolicy(String policyString) {
policyString = policyString.toLowerCase();
PolicyValue policy;
int port;
String[] policyParts = policyString.split("[ :]");
switch (policyParts.length) {
case 2:
port = -1;
break;
case 3:
port = policyParts[2].equals("*") ? -1 : Integer.parseInt(policyParts[2]);
if (port < -1 || port > 65535)
throw new IllegalArgumentException("Improper Format - Port out of range.");
break;
default:
throw new IllegalArgumentException(
"Improper Format - Should be (reject|accept) (domain|ip)[:port]");
}
if (policyParts[0].equalsIgnoreCase("accept"))
policy = PolicyValue.ACCEPT;
else if (policyParts[0].equalsIgnoreCase("reject"))
policy = PolicyValue.REJECT;
else
throw new IllegalArgumentException(
"Improper Format - First word is not (accept|reject)");
String[] urlParts = policyParts[1].split("\\.");
root = addPolicy(urlParts, urlParts.length - 1, port, policy, root);
}
private PolicyNode addPolicy(String[] url, int index, int port, PolicyValue policy,
PolicyNode root) {
if (index < 0) {
root.children.add(new PolicyNode(port, policy));
} else {
PolicyNode child = root.lastInstanceOfUrlPart(url[index]);
if (child == null)
child = root.add(new PolicyNode(url[index]));
child = addPolicy(url, index - 1, port, policy, child);
}
return root;
}
// Must be a specific url or ip, and a specific port
public boolean getPolicy(String url, int port) {
url = url.toLowerCase();
String[] urlParts = url.split("\\.");
PolicyValue policy = getPolicy(urlParts, urlParts.length - 1, port, root);
if (PolicyValue.ACCEPT == policy)
return true;
return false;
}
private PolicyValue getPolicy(String[] domain, int index, int port, PolicyNode root) {
for (PolicyNode child : root.children) {
if (index >= 0 && (child.domain.equals("*") || domain[index].equals(child.domain))) {
PolicyValue temp = getPolicy(domain, index - 1, port, child);
if (temp != null)
return temp;
} else if (port == child.port || child.port == -1)
return child.policy;
}
return null;
}
}
class PolicyNode {
/*
* Either domainPart or port will be filled for each node. "*" is wild
* card for domainPart, "" is unused field -1 means wild card for port,
* -2 is unused field
*/
String domain;
int port;
List<PolicyNode> children;
PolicyValue policy;
NodeType type;
// Constructs a domainPart node
public PolicyNode(String domainPart) {
this(domainPart, -2, null, NodeType.DOMAIN);
}
// Constructs a port node
public PolicyNode(int port, PolicyValue policy) {
this("", port, policy, NodeType.PORT);
}
private PolicyNode(String domainPart, int port, PolicyValue policy, NodeType type) {
this.domain = domainPart;
this.port = port;
this.policy = policy;
this.type = type;
this.children = new LinkedList<PolicyNode>();
}
public PolicyNode lastInstanceOfUrlPart(String urlPart) {
if (!children.isEmpty() && children.get(children.size() - 1).domain.equals(urlPart))
return children.get(children.size() - 1);
return null;
}
public PolicyNode lastInstanceOfPort(int port) {
if (!children.isEmpty() && children.get(children.size() - 1).port == port)
return children.get(children.size() - 1);
return null;
}
public PolicyNode add(PolicyNode node) {
children.add(node);
return node;
}
public String toString() {
String temp = "";
temp += domain.isEmpty() ? "--" : domain;
temp += ":";
temp += port == -1 ? "*" : port == -2 ? "--" : port;
return temp;
}
}
}