From: David ‘Bombe’ Roden Date: Wed, 9 Apr 2008 11:25:13 +0000 (+0000) Subject: add ListPeer and ListPeers commands and replies X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=9dbb34874ce7cd4fa4703f6bde00302bef2fb295;p=jSite2.git add ListPeer and ListPeers commands and replies git-svn-id: http://trooper/svn/projects/jSite/trunk@660 c3eda9e8-030b-0410-8277-bc7414b0a119 --- diff --git a/src/net/pterodactylus/util/fcp/FcpAdapter.java b/src/net/pterodactylus/util/fcp/FcpAdapter.java index 65ab77b..d867b91 100644 --- a/src/net/pterodactylus/util/fcp/FcpAdapter.java +++ b/src/net/pterodactylus/util/fcp/FcpAdapter.java @@ -4,7 +4,9 @@ package net.pterodactylus.util.fcp; import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName; +import net.pterodactylus.util.fcp.message.EndListPeers; import net.pterodactylus.util.fcp.message.NodeHello; +import net.pterodactylus.util.fcp.message.Peer; import net.pterodactylus.util.fcp.message.SSKKeypair; /** @@ -40,6 +42,22 @@ public class FcpAdapter implements FcpListener { } /** + * @see net.pterodactylus.util.fcp.FcpListener#receivedPeer(net.pterodactylus.util.fcp.FcpConnection, + * net.pterodactylus.util.fcp.message.Peer) + */ + public void receivedPeer(FcpConnection fcpConnection, Peer peer) { + /* empty. */ + } + + /** + * @see net.pterodactylus.util.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.util.fcp.FcpConnection, + * net.pterodactylus.util.fcp.message.EndListPeers) + */ + public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) { + /* empty. */ + } + + /** * @see net.pterodactylus.util.fcp.FcpListener#receivedMessage(net.pterodactylus.util.fcp.FcpConnection, * net.pterodactylus.util.fcp.FcpMessage) */ diff --git a/src/net/pterodactylus/util/fcp/FcpConnection.java b/src/net/pterodactylus/util/fcp/FcpConnection.java index 846e135..6d4ca97 100644 --- a/src/net/pterodactylus/util/fcp/FcpConnection.java +++ b/src/net/pterodactylus/util/fcp/FcpConnection.java @@ -29,7 +29,9 @@ import java.util.ArrayList; import java.util.List; import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName; +import net.pterodactylus.util.fcp.message.EndListPeers; import net.pterodactylus.util.fcp.message.NodeHello; +import net.pterodactylus.util.fcp.message.Peer; import net.pterodactylus.util.fcp.message.SSKKeypair; import net.pterodactylus.util.io.Closer; @@ -184,6 +186,30 @@ public class FcpConnection { } /** + * Notifies listeners that a “Peer” message was received. + * + * @param peer + * The “Peer” message + */ + private void fireReceivedPeer(Peer peer) { + for (FcpListener fcpListener: fcpListeners) { + fcpListener.receivedPeer(this, peer); + } + } + + /** + * Notifies all listeners that an “EndListPeers” message was received. + * + * @param endListPeers + * The “EndListPeers” message + */ + private void fireReceivedEndListPeers(EndListPeers endListPeers) { + for (FcpListener fcpListener: fcpListeners) { + fcpListener.receivedEndListPeers(this, endListPeers); + } + } + + /** * Notifies all registered listeners that a message has been received. * * @see FcpListener#receivedMessage(FcpConnection, FcpMessage) @@ -257,7 +283,11 @@ public class FcpConnection { */ void handleMessage(FcpMessage fcpMessage) { String messageName = fcpMessage.getName(); - if ("SSKKeypair".equals(messageName)) { + if ("Peer".equals(messageName)) { + fireReceivedPeer(new Peer(fcpMessage)); + } else if ("EndListPeers".equals(messageName)) { + fireReceivedEndListPeers(new EndListPeers(fcpMessage)); + } else if ("SSKKeypair".equals(messageName)) { fireReceivedSSKKeypair(new SSKKeypair(fcpMessage)); } else if ("NodeHello".equals(messageName)) { fireReceivedNodeHello(new NodeHello(fcpMessage)); diff --git a/src/net/pterodactylus/util/fcp/FcpListener.java b/src/net/pterodactylus/util/fcp/FcpListener.java index a983f88..2c72280 100644 --- a/src/net/pterodactylus/util/fcp/FcpListener.java +++ b/src/net/pterodactylus/util/fcp/FcpListener.java @@ -22,7 +22,9 @@ package net.pterodactylus.util.fcp; import java.util.EventListener; import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName; +import net.pterodactylus.util.fcp.message.EndListPeers; import net.pterodactylus.util.fcp.message.NodeHello; +import net.pterodactylus.util.fcp.message.Peer; import net.pterodactylus.util.fcp.message.SSKKeypair; /** @@ -65,6 +67,26 @@ public interface FcpListener extends EventListener { public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair); /** + * Notifies all listeners that a “Peer” message was received. + * + * @param fcpConnection + * The connection that received the message + * @param peer + * The “Peer” message + */ + public void receivedPeer(FcpConnection fcpConnection, Peer peer); + + /** + * Notifies a listener that an “EndListPeers” message was received. + * + * @param fcpConnection + * The connection that recevied the message + * @param endListPeers + * The “EndListPeers” message + */ + public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers); + + /** * Notifies listeners that a message has been received. This method is only * called if {@link FcpConnection#handleMessage(FcpMessage)} does not * recognize the message. Should that ever happen, please file a bug report! diff --git a/src/net/pterodactylus/util/fcp/FcpUtils.java b/src/net/pterodactylus/util/fcp/FcpUtils.java index 9892610..a492f2e 100644 --- a/src/net/pterodactylus/util/fcp/FcpUtils.java +++ b/src/net/pterodactylus/util/fcp/FcpUtils.java @@ -3,6 +3,7 @@ */ package net.pterodactylus.util.fcp; +import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicLong; /** @@ -25,4 +26,24 @@ public class FcpUtils { return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString(); } + /** + * Parses an integer field, separated by ‘;’ and returns the parsed values. + * + * @param field + * The field to parse + * @return An array with the parsed values + * @throws NumberFormatException + * if a value can not be converted to a number + */ + public static int[] parseMultiIntegerField(String field) throws NumberFormatException { + StringTokenizer fieldTokens = new StringTokenizer(field, ";"); + int[] result = new int[fieldTokens.countTokens()]; + int counter = 0; + while (fieldTokens.hasMoreTokens()) { + String fieldToken = fieldTokens.nextToken(); + result[counter++] = Integer.valueOf(fieldToken); + } + return result; + } + } diff --git a/src/net/pterodactylus/util/fcp/message/EndListPeers.java b/src/net/pterodactylus/util/fcp/message/EndListPeers.java new file mode 100644 index 0000000..2fac3fb --- /dev/null +++ b/src/net/pterodactylus/util/fcp/message/EndListPeers.java @@ -0,0 +1,26 @@ +/** + * © 2008 INA Service GmbH + */ +package net.pterodactylus.util.fcp.message; + +import net.pterodactylus.util.fcp.FcpMessage; + +/** + * This message marks the end of a list of “Peer” replies. + * + * @author David Roden + * @version $Id$ + */ +public class EndListPeers extends BaseMessage { + + /** + * Creates a new “EndListPeers” message that wraps the received message. + * + * @param receivedMessage + * The message that was received + */ + public EndListPeers(FcpMessage receivedMessage) { + super(receivedMessage); + } + +} diff --git a/src/net/pterodactylus/util/fcp/message/ListPeer.java b/src/net/pterodactylus/util/fcp/message/ListPeer.java new file mode 100644 index 0000000..a09a868 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/message/ListPeer.java @@ -0,0 +1,30 @@ +/** + * © 2008 INA Service GmbH + */ +package net.pterodactylus.util.fcp.message; + +import net.pterodactylus.util.fcp.FcpMessage; + +/** + * The “ListPeer” request asks the node about the details of a given peer. + * + * @author David Roden + * @version $Id$ + */ +public class ListPeer extends FcpMessage { + + /** + * Creates a new “ListPeer” request that returns information about the node + * specified by nodeIdentifier. nodeIdentifier + * can be of several formats: The node’s name, its identity, or its IP + * address and port (connection with a ‘:’). + * + * @param nodeIdentifier + * The identifier of the node to get details about + */ + public ListPeer(String nodeIdentifier) { + super("ListPeer"); + setField("NodeIdentifier", nodeIdentifier); + } + +} diff --git a/src/net/pterodactylus/util/fcp/message/ListPeers.java b/src/net/pterodactylus/util/fcp/message/ListPeers.java new file mode 100644 index 0000000..c585868 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/message/ListPeers.java @@ -0,0 +1,40 @@ +/** + * © 2008 INA Service GmbH + */ +package net.pterodactylus.util.fcp.message; + +import net.pterodactylus.util.fcp.FcpMessage; + +/** + * The “ListPeer” requests asks the node for a list of all peers it has. + * + * @author David Roden + * @version $Id$ + */ +public class ListPeers extends FcpMessage { + + /** + * Creates a new “ListPeers” request that only includes basic data of the + * peers. + */ + public ListPeers() { + this(false, false); + } + + /** + * Creates a new “ListPeers” request that includes wanted data of the peers. + * + * @param withMetadata + * If true metadata of the peers is included in + * the reply + * @param withVolatile + * if true volatile data of the peers is included + * in the reply + */ + public ListPeers(boolean withMetadata, boolean withVolatile) { + super("ListPeers"); + setField("WithMetadata", String.valueOf(withMetadata)); + setField("WithVolatile", String.valueOf(withVolatile)); + } + +} diff --git a/src/net/pterodactylus/util/fcp/message/Peer.java b/src/net/pterodactylus/util/fcp/message/Peer.java new file mode 100644 index 0000000..9a20378 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/message/Peer.java @@ -0,0 +1,372 @@ +/** + * © 2008 INA Service GmbH + */ +package net.pterodactylus.util.fcp.message; + +import java.security.interfaces.DSAParams; +import java.util.StringTokenizer; + +import net.pterodactylus.util.fcp.FcpMessage; +import net.pterodactylus.util.fcp.FcpUtils; + +/** + * The “Peer” reply by the node contains information about a peer. + * + * @author David Roden + * @version $Id$ + */ +public class Peer extends BaseMessage { + + /** + * Creates a new “Peer” reply from the received message. + * + * @param receivedMessage + * The received message + */ + public Peer(FcpMessage receivedMessage) { + super(receivedMessage); + } + + /** + * Returns the “physical.udp” line from the message. It contains all IP + * addresses and port numbers of the peer. + * + * @return The IP addresses and port numbers of the peer + */ + public String getPhysicalUDP() { + return getField("physical.udp"); + } + + /** + * Returns whether the listed peer is an opennet peer. + * + * @return true if the peer is an opennet peer, + * false if the peer is a darknet peer + */ + public boolean isOpennet() { + return Boolean.valueOf(getField("opennet")); + } + + /** + * Returns the “y” part of the peer’s public DSA key. + * + * @return The public DSA key + */ + public String getDSAPublicKey() { + return getField("dsaPubKey.y"); + } + + /** + * Returns the DSA group of the peer. + * + * @return The DSA group of the peer + */ + public DSAGroup getDSAGroup() { + return new DSAGroup(getField("dsaGroup.g"), getField("dsaGroup.p"), getField("dsaGroup.q")); + } + + /** + * Returns the last good version of the peer, i.e. the oldest version the + * peer will connect to. + * + * @return The last good version of the peer + */ + public Version getLastGoodVersion() { + return new Version(getField("lastGoodVersion")); + } + + /** + * Returns the ARK of the peer. + * + * @return The ARK of the peer + */ + public ARK getARK() { + return new ARK(getField("ark.pubURI"), getField("ark.number")); + } + + /** + * Returns the identity of the peer. + * + * @return The identity of the peer + */ + public String getIdentity() { + return getField("identity"); + } + + /** + * Returns the name of the peer. If the peer is not a darknet peer it will + * have no name. + * + * @return The name of the peer, or null if the peer is an + * opennet peer + */ + public String getMyName() { + return getField("myName"); + } + + /** + * Returns the location of the peer. + * + * @return The location of the peer + * @throws NumberFormatException + * if the field can not be parsed + */ + public double getLocation() throws NumberFormatException { + return Double.valueOf(getField("location")); + } + + /** + * Returns whether the peer is a testnet node. + * + * @return true if the peer is a testnet node, + * false otherwise + */ + public boolean isTestnet() { + return Boolean.valueOf("testnet"); + } + + /** + * Returns the version of the peer. + * + * @return The version of the peer + */ + public Version getVersion() { + return new Version(getField("version")); + } + + /** + * Returns the negotiation types the peer supports. + * + * @return The supported negotiation types + */ + public int[] getNegotiationTypes() { + return FcpUtils.parseMultiIntegerField(getField("auth.negTypes")); + } + + /** + * Container for the “lastGoodVersion” field. + * + * @author David Roden + * @version $Id$ + */ + public static class Version { + + /** The name of the node implementation. */ + private final String nodeName; + + /** The tree version of the node. */ + private final String treeVersion; + + /** The protocol version of the node. */ + private final String protocolVersion; + + /** The build number of the node. */ + private final int buildNumber; + + /** + * Creates a new Version from the given string. The string consists of + * the four required fields node name, tree version, protocol version, + * and build number, separated by a comma. + * + * @param version + * The version string + * @throws NullPointerException + * if version is null + * @throws IllegalArgumentException + * if version is not in the right format + */ + public Version(String version) { + if (version == null) { + throw new NullPointerException("version must not be null"); + } + StringTokenizer versionTokens = new StringTokenizer(version, ","); + if (versionTokens.countTokens() != 4) { + throw new IllegalArgumentException("version must consist of four fields"); + } + this.nodeName = versionTokens.nextToken(); + this.treeVersion = versionTokens.nextToken(); + this.protocolVersion = versionTokens.nextToken(); + try { + this.buildNumber = Integer.valueOf(versionTokens.nextToken()); + } catch (NumberFormatException nfe1) { + throw new IllegalArgumentException("last part of version must be numeric", nfe1); + } + } + + /** + * Creates a new Version from the given parts. + * + * @param nodeName + * The name of the node implementation + * @param treeVersion + * The tree version + * @param protocolVersion + * The protocol version + * @param buildNumber + * The build number of the node + */ + public Version(String nodeName, String treeVersion, String protocolVersion, int buildNumber) { + this.nodeName = nodeName; + this.treeVersion = treeVersion; + this.protocolVersion = protocolVersion; + this.buildNumber = buildNumber; + } + + /** + * Returns the name of the node implementation. + * + * @return The node name + */ + public String getNodeName() { + return nodeName; + } + + /** + * The tree version of the node. + * + * @return The tree version of the node + */ + public String getTreeVersion() { + return treeVersion; + } + + /** + * The protocol version of the node + * + * @return The protocol version of the node + */ + public String getProtocolVersion() { + return protocolVersion; + } + + /** + * The build number of the node. + * + * @return The build number of the node + */ + public int getBuildNumber() { + return buildNumber; + } + + } + + /** + * Container for ARKs (address resolution keys). + * + * @author David Roden + * @version $Id$ + */ + public static class ARK { + + /** The public URI of the ARK. */ + private final String publicURI; + + /** The number of the ARK. */ + private final int number; + + /** + * Creates a new ARK with the given URI and number. + * + * @param publicURI + * The public URI of the ARK + * @param number + * The number of the ARK + */ + public ARK(String publicURI, String number) { + if ((publicURI == null) || (number == null)) { + throw new NullPointerException(((publicURI == null) ? "publicURI" : "number") + " must not be null"); + } + this.publicURI = publicURI; + try { + this.number = Integer.valueOf(number); + } catch (NumberFormatException nfe1) { + throw new IllegalArgumentException("number must be numeric", nfe1); + } + } + + /** + * Returns the public URI of the ARK. + * + * @return The public URI of the ARK + */ + public String getPublicURI() { + return publicURI; + } + + /** + * Returns the number of the ARK. + * + * @return The number of the ARK + */ + public int getNumber() { + return number; + } + + } + + /** + * Container for the DSA group of a peer. A DSA group consists of a base + * (called “g”), a prime (called “p”) and a subprime (called “q”). + * + * @see DSAParams + * @author David Roden + * @version $Id$ + */ + public static class DSAGroup { + + /** The base of the DSA group. */ + private final String base; + + /** The prime of the DSA group. */ + private final String prime; + + /** The subprime of the DSA group. */ + private final String subprime; + + /** + * Creates a new DSA group with the given base (“g”), prime (“p”), and + * subprime (“q”). + * + * @param base + * The base of the DSA group + * @param prime + * The prime of the DSA group + * @param subprime + * The subprime of the DSA group + */ + public DSAGroup(String base, String prime, String subprime) { + this.base = base; + this.prime = prime; + this.subprime = subprime; + } + + /** + * Returns the base (“g”) of the DSA group. + * + * @return The base of the DSA group + */ + public String getBase() { + return base; + } + + /** + * Returns the prime (“p”) of the DSA group. + * + * @return The prime of the DSA group + */ + public String getPrime() { + return prime; + } + + /** + * Returns the subprime (“q”) of the DSA group. + * + * @return The subprime of the DSA group + */ + public String getSubprime() { + return subprime; + } + + } + +}