ip6Addresses) {
+ ServiceInfoImpl si = new ServiceInfoImpl(type, name, "", port, 0, 0, text);
+ for (Inet4Address a : ip4Addresses) si.addAddress(a);
+ for (Inet6Address a : ip6Addresses) si.addAddress(a);
+ return si;
+ }
+
+ /**
+ * Returns true if the service info is filled with data.
+ *
+ * @return true
if the service info has data, false
otherwise.
+ */
+ public abstract boolean hasData();
+
+ /**
+ * Fully qualified service type name, such as _http._tcp.local.
+ *
+ * @return service type name
+ */
+ public abstract String getType();
+
+ /**
+ * Fully qualified service type name with the subtype if appropriate, such as
+ * _printer._sub._http._tcp.local.
+ *
+ * @return service type name
+ */
+ public abstract String getTypeWithSubtype();
+
+ /**
+ * Unqualified service instance name, such as foobar
.
+ *
+ * @return service name
+ */
+ public abstract String getName();
+
+ /**
+ * The key is used to retrieve service info in hash tables.
+ * The key is the lower case qualified name.
+ *
+ * @return the key
+ */
+ public abstract String getKey();
+
+ /**
+ * Fully qualified service name, such as foobar._http._tcp.local.
.
+ *
+ * @return qualified service name
+ */
+ public abstract String getQualifiedName();
+
+ /**
+ * Get the name of the server.
+ *
+ * @return server name
+ */
+ public abstract String getServer();
+
+ /**
+ * Returns a list of all IPv4 InetAddresses that can be used for this service.
+ *
+ * In a multi-homed environment service info can be associated with more than one address.
+ *
+ * @return list of InetAddress objects
+ */
+ public abstract Inet4Address[] getInet4Addresses();
+
+ /**
+ * Returns a list of all IPv6 InetAddresses that can be used for this service.
+ *
+ *
In a multi-homed environment service info can be associated with more than one address.
+ *
+ * @return list of InetAddress objects
+ */
+ public abstract Inet6Address[] getInet6Addresses();
+
+ /**
+ * Get the port for the service.
+ *
+ * @return service port
+ */
+ public abstract int getPort();
+
+ /**
+ * Get the priority of the service.
+ *
+ * @return service priority
+ */
+ public abstract int getPriority();
+
+ /**
+ * Get the weight of the service.
+ *
+ * @return service weight
+ */
+ public abstract int getWeight();
+
+ /**
+ * Get the text for the service as raw bytes.
+ *
+ * @return raw service text
+ */
+ public abstract byte[] getTextBytes();
+
+ /**
+ * Returns the domain of the service info suitable for printing.
+ *
+ * @return service domain
+ */
+ public abstract String getDomain();
+
+ /**
+ * Returns the protocol of the service info suitable for printing.
+ *
+ * @return service protocol
+ */
+ public abstract String getProtocol();
+
+ /**
+ * Returns the application of the service info suitable for printing.
+ *
+ * @return service application
+ */
+ public abstract String getApplication();
+
+ /**
+ * Returns the sub type of the service info suitable for printing.
+ *
+ * @return service sub type
+ */
+ public abstract String getSubtype();
+
+ /**
+ * Returns a dictionary of the fully qualified name component of this service.
+ *
+ * @return dictionary of the fully qualified name components
+ */
+ public abstract Map getQualifiedNameMap();
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public ServiceInfo clone() throws CloneNotSupportedException {
+ return (ServiceInfo) super.clone();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSEntry.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSEntry.java
index 1f90e988c..a49b9934f 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSEntry.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSEntry.java
@@ -4,214 +4,226 @@
package io.libp2p.discovery.mdns.impl;
+import io.libp2p.discovery.mdns.ServiceInfo.Fields;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
-
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
-import io.libp2p.discovery.mdns.ServiceInfo.Fields;
-
/**
* DNS entry with a name, type, and class. This is the base class for questions and records.
*
* @author Arthur van Hoff, Pierre Frisch, Rick Blair
*/
public abstract class DNSEntry {
- // private static Logger logger = LoggerFactory.getLogger(DNSEntry.class.getName());
- private final String _key;
-
- private final String _name;
-
- private final String _type;
-
- private final DNSRecordType _recordType;
-
- private final DNSRecordClass _dnsClass;
-
- private final boolean _unique;
-
- final Map _qualifiedNameMap;
-
- /**
- * Create an entry.
- */
- DNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
- _name = name;
- // _key = (name != null ? name.trim().toLowerCase() : null);
- _recordType = type;
- _dnsClass = recordClass;
- _unique = unique;
- _qualifiedNameMap = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getName());
- String domain = _qualifiedNameMap.get(Fields.Domain);
- String protocol = _qualifiedNameMap.get(Fields.Protocol);
- String application = _qualifiedNameMap.get(Fields.Application);
- String instance = _qualifiedNameMap.get(Fields.Instance).toLowerCase();
- _type = (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
- _key = ((instance.length() > 0 ? instance + "." : "") + _type).toLowerCase();
- }
-
- /*
- * (non-Javadoc)
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(Object obj) {
- boolean result = false;
- if (obj instanceof DNSEntry) {
- DNSEntry other = (DNSEntry) obj;
- result = this.getKey().equals(other.getKey()) && this.getRecordType().equals(other.getRecordType()) && this.getRecordClass() == other.getRecordClass();
- }
- return result;
- }
-
- /**
- * Check if two entries have exactly the same name, type, and class.
- *
- * @param entry
- * @return true
if the two entries have are for the same record, false
otherwise
- */
- public boolean isSameEntry(DNSEntry entry) {
- return this.getKey().equals(entry.getKey()) && this.matchRecordType(entry.getRecordType()) && this.matchRecordClass(entry.getRecordClass());
- }
-
- /**
- * Check if the requested record class match the current record class
- *
- * @param recordClass
- * @return true
if the two entries have compatible class, false
otherwise
- */
- public boolean matchRecordClass(DNSRecordClass recordClass) {
- return (DNSRecordClass.CLASS_ANY == recordClass) || (DNSRecordClass.CLASS_ANY == this.getRecordClass()) || this.getRecordClass().equals(recordClass);
- }
-
- /**
- * Check if the requested record tyep match the current record type
- *
- * @param recordType
- * @return true
if the two entries have compatible type, false
otherwise
- */
- public boolean matchRecordType(DNSRecordType recordType) {
- return this.getRecordType().equals(recordType);
- }
-
- /**
- * Returns the subtype of this entry
- *
- * @return subtype of this entry
- */
- public String getSubtype() {
- String subtype = this.getQualifiedNameMap().get(Fields.Subtype);
- return (subtype != null ? subtype : "");
- }
-
- /**
- * Returns the name of this entry
- *
- * @return name of this entry
- */
- public String getName() {
- return (_name != null ? _name : "");
- }
-
- /**
- * @return the type
- */
- public String getType() {
- return (_type != null ? _type : "");
- }
-
- /**
- * Returns the key for this entry. The key is the lower case name.
- *
- * @return key for this entry
- */
- public String getKey() {
- return (_key != null ? _key : "");
- }
-
- /**
- * @return record type
- */
- public DNSRecordType getRecordType() {
- return (_recordType != null ? _recordType : DNSRecordType.TYPE_IGNORE);
- }
-
- /**
- * @return record class
- */
- public DNSRecordClass getRecordClass() {
- return (_dnsClass != null ? _dnsClass : DNSRecordClass.CLASS_UNKNOWN);
- }
-
- /**
- * @return true if unique
- */
- public boolean isUnique() {
- return _unique;
- }
-
- public Map getQualifiedNameMap() {
- return Collections.unmodifiableMap(_qualifiedNameMap);
- }
-
- /**
- * Check if the record is expired.
- *
- * @param now
- * update date
- * @return true
is the record is expired, false
otherwise.
- */
- public abstract boolean isExpired(long now);
-
- /**
- * @param dout
- * @exception IOException
- */
- protected void toByteArray(DataOutputStream dout) throws IOException {
- dout.write(this.getName().getBytes("UTF8"));
- dout.writeShort(this.getRecordType().indexValue());
- dout.writeShort(this.getRecordClass().indexValue());
- }
-
- /**
- * Creates a byte array representation of this record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
- *
- * @return byte array representation
- */
- protected byte[] toByteArray() {
- try {
- ByteArrayOutputStream bout = new ByteArrayOutputStream();
- DataOutputStream dout = new DataOutputStream(bout);
- this.toByteArray(dout);
- dout.close();
- return bout.toByteArray();
- } catch (IOException e) {
- throw new InternalError();
- }
- }
-
- /**
- * Overriden, to return a value which is consistent with the value returned by equals(Object).
- */
- @Override
- public int hashCode() {
- return this.getKey().hashCode() + this.getRecordType().indexValue() + this.getRecordClass().indexValue();
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(200);
- sb.append('[').append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
- sb.append(" type: ").append(this.getRecordType());
- sb.append(", class: ").append(this.getRecordClass());
- sb.append((_unique ? "-unique," : ","));
- sb.append(" name: ").append( _name);
- sb.append(']');
-
- return sb.toString();
- }
+ // private static Logger logger = LoggerFactory.getLogger(DNSEntry.class.getName());
+ private final String _key;
+
+ private final String _name;
+
+ private final String _type;
+
+ private final DNSRecordType _recordType;
+
+ private final DNSRecordClass _dnsClass;
+
+ private final boolean _unique;
+
+ final Map _qualifiedNameMap;
+
+ /** Create an entry. */
+ DNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
+ _name = name;
+ // _key = (name != null ? name.trim().toLowerCase() : null);
+ _recordType = type;
+ _dnsClass = recordClass;
+ _unique = unique;
+ _qualifiedNameMap = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getName());
+ String domain = _qualifiedNameMap.get(Fields.Domain);
+ String protocol = _qualifiedNameMap.get(Fields.Protocol);
+ String application = _qualifiedNameMap.get(Fields.Application);
+ String instance = _qualifiedNameMap.get(Fields.Instance).toLowerCase();
+ _type =
+ (application.length() > 0 ? "_" + application + "." : "")
+ + (protocol.length() > 0 ? "_" + protocol + "." : "")
+ + domain
+ + ".";
+ _key = ((instance.length() > 0 ? instance + "." : "") + _type).toLowerCase();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ boolean result = false;
+ if (obj instanceof DNSEntry) {
+ DNSEntry other = (DNSEntry) obj;
+ result =
+ this.getKey().equals(other.getKey())
+ && this.getRecordType().equals(other.getRecordType())
+ && this.getRecordClass() == other.getRecordClass();
+ }
+ return result;
+ }
+
+ /**
+ * Check if two entries have exactly the same name, type, and class.
+ *
+ * @param entry
+ * @return true
if the two entries have are for the same record, false
+ * otherwise
+ */
+ public boolean isSameEntry(DNSEntry entry) {
+ return this.getKey().equals(entry.getKey())
+ && this.matchRecordType(entry.getRecordType())
+ && this.matchRecordClass(entry.getRecordClass());
+ }
+
+ /**
+ * Check if the requested record class match the current record class
+ *
+ * @param recordClass
+ * @return true
if the two entries have compatible class, false
+ * otherwise
+ */
+ public boolean matchRecordClass(DNSRecordClass recordClass) {
+ return (DNSRecordClass.CLASS_ANY == recordClass)
+ || (DNSRecordClass.CLASS_ANY == this.getRecordClass())
+ || this.getRecordClass().equals(recordClass);
+ }
+
+ /**
+ * Check if the requested record tyep match the current record type
+ *
+ * @param recordType
+ * @return true
if the two entries have compatible type, false
otherwise
+ */
+ public boolean matchRecordType(DNSRecordType recordType) {
+ return this.getRecordType().equals(recordType);
+ }
+
+ /**
+ * Returns the subtype of this entry
+ *
+ * @return subtype of this entry
+ */
+ public String getSubtype() {
+ String subtype = this.getQualifiedNameMap().get(Fields.Subtype);
+ return (subtype != null ? subtype : "");
+ }
+
+ /**
+ * Returns the name of this entry
+ *
+ * @return name of this entry
+ */
+ public String getName() {
+ return (_name != null ? _name : "");
+ }
+
+ /**
+ * @return the type
+ */
+ public String getType() {
+ return (_type != null ? _type : "");
+ }
+
+ /**
+ * Returns the key for this entry. The key is the lower case name.
+ *
+ * @return key for this entry
+ */
+ public String getKey() {
+ return (_key != null ? _key : "");
+ }
+
+ /**
+ * @return record type
+ */
+ public DNSRecordType getRecordType() {
+ return (_recordType != null ? _recordType : DNSRecordType.TYPE_IGNORE);
+ }
+
+ /**
+ * @return record class
+ */
+ public DNSRecordClass getRecordClass() {
+ return (_dnsClass != null ? _dnsClass : DNSRecordClass.CLASS_UNKNOWN);
+ }
+
+ /**
+ * @return true if unique
+ */
+ public boolean isUnique() {
+ return _unique;
+ }
+
+ public Map getQualifiedNameMap() {
+ return Collections.unmodifiableMap(_qualifiedNameMap);
+ }
+
+ /**
+ * Check if the record is expired.
+ *
+ * @param now update date
+ * @return true
is the record is expired, false
otherwise.
+ */
+ public abstract boolean isExpired(long now);
+
+ /**
+ * @param dout
+ * @exception IOException
+ */
+ protected void toByteArray(DataOutputStream dout) throws IOException {
+ dout.write(this.getName().getBytes("UTF8"));
+ dout.writeShort(this.getRecordType().indexValue());
+ dout.writeShort(this.getRecordClass().indexValue());
+ }
+
+ /**
+ * Creates a byte array representation of this record. This is needed for tie-break tests
+ * according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
+ *
+ * @return byte array representation
+ */
+ protected byte[] toByteArray() {
+ try {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+ this.toByteArray(dout);
+ dout.close();
+ return bout.toByteArray();
+ } catch (IOException e) {
+ throw new InternalError();
+ }
+ }
+
+ /** Overriden, to return a value which is consistent with the value returned by equals(Object). */
+ @Override
+ public int hashCode() {
+ return this.getKey().hashCode()
+ + this.getRecordType().indexValue()
+ + this.getRecordClass().indexValue();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(200);
+ sb.append('[')
+ .append(this.getClass().getSimpleName())
+ .append('@')
+ .append(System.identityHashCode(this));
+ sb.append(" type: ").append(this.getRecordType());
+ sb.append(", class: ").append(this.getRecordClass());
+ sb.append((_unique ? "-unique," : ","));
+ sb.append(" name: ").append(_name);
+ sb.append(']');
+
+ return sb.toString();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSIncoming.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSIncoming.java
index f71bf1e29..078832553 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSIncoming.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSIncoming.java
@@ -4,594 +4,666 @@
package io.libp2p.discovery.mdns.impl;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
+import io.libp2p.discovery.mdns.impl.constants.DNSLabel;
+import io.libp2p.discovery.mdns.impl.constants.DNSOptionCode;
+import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
+import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
+import io.libp2p.discovery.mdns.impl.constants.DNSResultCode;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
-
-import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
-import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
-import io.libp2p.discovery.mdns.impl.constants.DNSResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-import io.libp2p.discovery.mdns.impl.constants.DNSLabel;
-import io.libp2p.discovery.mdns.impl.constants.DNSOptionCode;
-
/**
* Parse an incoming DNS message into its components.
*
* @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert
*/
public final class DNSIncoming extends DNSMessage {
- private static Logger logger = LoggerFactory.getLogger(DNSIncoming.class.getName());
-
- // This is a hack to handle a bug in the BonjourConformanceTest
- // It is sending out target strings that don't follow the "domain name" format.
- public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
-
- public static class MessageInputStream extends ByteArrayInputStream {
- private static Logger logger1 = LoggerFactory.getLogger(MessageInputStream.class.getName());
+ private static Logger logger = LoggerFactory.getLogger(DNSIncoming.class.getName());
- final Map _names;
+ // This is a hack to handle a bug in the BonjourConformanceTest
+ // It is sending out target strings that don't follow the "domain name" format.
+ public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
- public MessageInputStream(byte[] buffer, int length) {
- this(buffer, 0, length);
- }
-
- /**
- * @param buffer
- * @param offset
- * @param length
- */
- public MessageInputStream(byte[] buffer, int offset, int length) {
- super(buffer, offset, length);
- _names = new HashMap();
- }
+ public static class MessageInputStream extends ByteArrayInputStream {
+ private static Logger logger1 = LoggerFactory.getLogger(MessageInputStream.class.getName());
- public int readByte() {
- return this.read();
- }
+ final Map _names;
- public int readUnsignedByte() {
- return (this.read() & 0xFF);
- }
+ public MessageInputStream(byte[] buffer, int length) {
+ this(buffer, 0, length);
+ }
- public int readUnsignedShort() {
- return (this.readUnsignedByte() << 8) | this.readUnsignedByte();
- }
+ /**
+ * @param buffer
+ * @param offset
+ * @param length
+ */
+ public MessageInputStream(byte[] buffer, int offset, int length) {
+ super(buffer, offset, length);
+ _names = new HashMap();
+ }
- public int readInt() {
- return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
- }
+ public int readByte() {
+ return this.read();
+ }
- public byte[] readBytes(int len) {
- byte bytes[] = new byte[len];
- this.read(bytes, 0, len);
- return bytes;
- }
+ public int readUnsignedByte() {
+ return (this.read() & 0xFF);
+ }
- public String readUTF(int len) {
- final StringBuilder sb = new StringBuilder(len);
- for (int index = 0; index < len; index++) {
- int ch = this.readUnsignedByte();
- switch (ch >> 4) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- // 0xxxxxxx
- break;
- case 12:
- case 13:
- // 110x xxxx 10xx xxxx
- ch = ((ch & 0x1F) << 6) | (this.readUnsignedByte() & 0x3F);
- index++;
- break;
- case 14:
- // 1110 xxxx 10xx xxxx 10xx xxxx
- ch = ((ch & 0x0f) << 12) | ((this.readUnsignedByte() & 0x3F) << 6) | (this.readUnsignedByte() & 0x3F);
- index++;
- index++;
- break;
- default:
- // 10xx xxxx, 1111 xxxx
- ch = ((ch & 0x3F) << 4) | (this.readUnsignedByte() & 0x0f);
- index++;
- break;
- }
- sb.append((char) ch);
- }
- return sb.toString();
- }
+ public int readUnsignedShort() {
+ return (this.readUnsignedByte() << 8) | this.readUnsignedByte();
+ }
- protected synchronized int peek() {
- return (pos < count) ? (buf[pos] & 0xff) : -1;
- }
+ public int readInt() {
+ return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
+ }
- public String readName() {
- Map names = new HashMap();
- final StringBuilder sb = new StringBuilder();
- boolean finished = false;
- while (!finished) {
- int len = this.readUnsignedByte();
- if (len == 0) {
- finished = true;
- break;
- }
- switch (DNSLabel.labelForByte(len)) {
- case Standard:
- int offset = pos - 1;
- String label = this.readUTF(len) + ".";
- sb.append(label);
- for (StringBuilder previousLabel : names.values()) {
- previousLabel.append(label);
- }
- names.put(Integer.valueOf(offset), new StringBuilder(label));
- break;
- case Compressed:
- int index = (DNSLabel.labelValue(len) << 8) | this.readUnsignedByte();
- String compressedLabel = _names.get(Integer.valueOf(index));
- if (compressedLabel == null) {
- logger1.warn("Bad domain name: possible circular name detected. Bad offset: 0x{} at 0x{}",
- Integer.toHexString(index),
- Integer.toHexString(pos - 2)
- );
- compressedLabel = "";
- }
- sb.append(compressedLabel);
- for (StringBuilder previousLabel : names.values()) {
- previousLabel.append(compressedLabel);
- }
- finished = true;
- break;
- case Extended:
- // int extendedLabelClass = DNSLabel.labelValue(len);
- logger1.debug("Extended label are not currently supported.");
- break;
- case Unknown:
- default:
- logger1.warn("Unsupported DNS label type: '{}'", Integer.toHexString(len & 0xC0) );
- }
- }
- for (final Map.Entry entry : names.entrySet()) {
- final Integer index = entry.getKey();
- _names.put(index, entry.getValue().toString());
- }
- return sb.toString();
- }
+ public byte[] readBytes(int len) {
+ byte bytes[] = new byte[len];
+ this.read(bytes, 0, len);
+ return bytes;
+ }
- public String readNonNameString() {
- int len = this.readUnsignedByte();
- return this.readUTF(len);
+ public String readUTF(int len) {
+ final StringBuilder sb = new StringBuilder(len);
+ for (int index = 0; index < len; index++) {
+ int ch = this.readUnsignedByte();
+ switch (ch >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // 0xxxxxxx
+ break;
+ case 12:
+ case 13:
+ // 110x xxxx 10xx xxxx
+ ch = ((ch & 0x1F) << 6) | (this.readUnsignedByte() & 0x3F);
+ index++;
+ break;
+ case 14:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ ch =
+ ((ch & 0x0f) << 12)
+ | ((this.readUnsignedByte() & 0x3F) << 6)
+ | (this.readUnsignedByte() & 0x3F);
+ index++;
+ index++;
+ break;
+ default:
+ // 10xx xxxx, 1111 xxxx
+ ch = ((ch & 0x3F) << 4) | (this.readUnsignedByte() & 0x0f);
+ index++;
+ break;
}
-
+ sb.append((char) ch);
+ }
+ return sb.toString();
}
- private final DatagramPacket _packet;
-
- private final long _receivedTime;
-
- private final MessageInputStream _messageInputStream;
-
- private int _senderUDPPayload;
-
- /**
- * Parse a message from a datagram packet.
- *
- * @param packet
- * @exception IOException
- */
- public DNSIncoming(DatagramPacket packet) throws IOException {
- super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
- this._packet = packet;
- InetAddress source = packet.getAddress();
- this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
- this._receivedTime = System.currentTimeMillis();
- this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
-
- try {
- this.setId(_messageInputStream.readUnsignedShort());
- this.setFlags(_messageInputStream.readUnsignedShort());
- if (this.getOperationCode() > 0) {
- throw new IOException("Received a message with a non standard operation code. Currently unsupported in the specification.");
- }
- int numQuestions = _messageInputStream.readUnsignedShort();
- int numAnswers = _messageInputStream.readUnsignedShort();
- int numAuthorities = _messageInputStream.readUnsignedShort();
- int numAdditionals = _messageInputStream.readUnsignedShort();
-
- logger.debug("DNSIncoming() questions:{} answers:{} authorities:{} additionals:{}",
- numQuestions,
- numAnswers,
- numAuthorities,
- numAdditionals
- );
-
- // We need some sanity checks
- // A question is at least 5 bytes and answer 11 so check what we have
-
- if ((numQuestions * 5 + (numAnswers + numAuthorities + numAdditionals) * 11) > packet.getLength()) {
- throw new IOException("questions:" + numQuestions + " answers:" + numAnswers + " authorities:" + numAuthorities + " additionals:" + numAdditionals);
- }
-
- // parse questions
- for (int i = 0; i < numQuestions; i++) {
- DNSQuestion question = this.readQuestion();
- if (question != null)
- _questions.add(question);
- }
-
- // parse answers
- for (int i = 0; i < numAnswers; i++) {
- DNSRecord rec = this.readAnswer();
- if (rec != null) {
- // Add a record, if we were able to create one.
- _answers.add(rec);
- }
- }
-
- for (int i = 0; i < numAuthorities; i++) {
- DNSRecord rec = this.readAnswer();
- if (rec != null) {
- // Add a record, if we were able to create one.
- _authoritativeAnswers.add(rec);
- }
- }
+ protected synchronized int peek() {
+ return (pos < count) ? (buf[pos] & 0xff) : -1;
+ }
- for (int i = 0; i < numAdditionals; i++) {
- DNSRecord rec = this.readAnswer();
- if (rec != null) {
- // Add a record, if we were able to create one.
- _additionals.add(rec);
- }
+ public String readName() {
+ Map names = new HashMap();
+ final StringBuilder sb = new StringBuilder();
+ boolean finished = false;
+ while (!finished) {
+ int len = this.readUnsignedByte();
+ if (len == 0) {
+ finished = true;
+ break;
+ }
+ switch (DNSLabel.labelForByte(len)) {
+ case Standard:
+ int offset = pos - 1;
+ String label = this.readUTF(len) + ".";
+ sb.append(label);
+ for (StringBuilder previousLabel : names.values()) {
+ previousLabel.append(label);
}
-
- // We should have drained the entire stream by now
- if (_messageInputStream.available() > 0) {
- throw new IOException("Received a message with the wrong length.");
+ names.put(Integer.valueOf(offset), new StringBuilder(label));
+ break;
+ case Compressed:
+ int index = (DNSLabel.labelValue(len) << 8) | this.readUnsignedByte();
+ String compressedLabel = _names.get(Integer.valueOf(index));
+ if (compressedLabel == null) {
+ logger1.warn(
+ "Bad domain name: possible circular name detected. Bad offset: 0x{} at 0x{}",
+ Integer.toHexString(index),
+ Integer.toHexString(pos - 2));
+ compressedLabel = "";
}
- } catch (Exception e) {
- logger.warn("DNSIncoming() dump " + print(true) + "\n exception ", e);
- // This ugly but some JVM don't implement the cause on IOException
- IOException ioe = new IOException("DNSIncoming corrupted message");
- ioe.initCause(e);
- throw ioe;
- } finally {
- try {
- _messageInputStream.close();
- } catch (Exception e) {
- logger.warn("MessageInputStream close error");
+ sb.append(compressedLabel);
+ for (StringBuilder previousLabel : names.values()) {
+ previousLabel.append(compressedLabel);
}
+ finished = true;
+ break;
+ case Extended:
+ // int extendedLabelClass = DNSLabel.labelValue(len);
+ logger1.debug("Extended label are not currently supported.");
+ break;
+ case Unknown:
+ default:
+ logger1.warn("Unsupported DNS label type: '{}'", Integer.toHexString(len & 0xC0));
}
+ }
+ for (final Map.Entry entry : names.entrySet()) {
+ final Integer index = entry.getKey();
+ _names.put(index, entry.getValue().toString());
+ }
+ return sb.toString();
}
- private DNSIncoming(int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
- super(flags, id, multicast);
- this._packet = packet;
- this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
- this._receivedTime = receivedTime;
+ public String readNonNameString() {
+ int len = this.readUnsignedByte();
+ return this.readUTF(len);
}
+ }
+
+ private final DatagramPacket _packet;
+
+ private final long _receivedTime;
+
+ private final MessageInputStream _messageInputStream;
+
+ private int _senderUDPPayload;
+
+ /**
+ * Parse a message from a datagram packet.
+ *
+ * @param packet
+ * @exception IOException
+ */
+ public DNSIncoming(DatagramPacket packet) throws IOException {
+ super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
+ this._packet = packet;
+ InetAddress source = packet.getAddress();
+ this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
+ this._receivedTime = System.currentTimeMillis();
+ this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
+
+ try {
+ this.setId(_messageInputStream.readUnsignedShort());
+ this.setFlags(_messageInputStream.readUnsignedShort());
+ if (this.getOperationCode() > 0) {
+ throw new IOException(
+ "Received a message with a non standard operation code. Currently unsupported in the specification.");
+ }
+ int numQuestions = _messageInputStream.readUnsignedShort();
+ int numAnswers = _messageInputStream.readUnsignedShort();
+ int numAuthorities = _messageInputStream.readUnsignedShort();
+ int numAdditionals = _messageInputStream.readUnsignedShort();
+
+ logger.debug(
+ "DNSIncoming() questions:{} answers:{} authorities:{} additionals:{}",
+ numQuestions,
+ numAnswers,
+ numAuthorities,
+ numAdditionals);
+
+ // We need some sanity checks
+ // A question is at least 5 bytes and answer 11 so check what we have
+
+ if ((numQuestions * 5 + (numAnswers + numAuthorities + numAdditionals) * 11)
+ > packet.getLength()) {
+ throw new IOException(
+ "questions:"
+ + numQuestions
+ + " answers:"
+ + numAnswers
+ + " authorities:"
+ + numAuthorities
+ + " additionals:"
+ + numAdditionals);
+ }
+
+ // parse questions
+ for (int i = 0; i < numQuestions; i++) {
+ DNSQuestion question = this.readQuestion();
+ if (question != null) _questions.add(question);
+ }
+
+ // parse answers
+ for (int i = 0; i < numAnswers; i++) {
+ DNSRecord rec = this.readAnswer();
+ if (rec != null) {
+ // Add a record, if we were able to create one.
+ _answers.add(rec);
+ }
+ }
- /*
- * (non-Javadoc)
- * @see java.lang.Object#clone()
- */
- @Override
- public DNSIncoming clone() {
- DNSIncoming in = new DNSIncoming(this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
- in._senderUDPPayload = this._senderUDPPayload;
- in._questions.addAll(this._questions);
- in._answers.addAll(this._answers);
- in._authoritativeAnswers.addAll(this._authoritativeAnswers);
- in._additionals.addAll(this._additionals);
- return in;
- }
+ for (int i = 0; i < numAuthorities; i++) {
+ DNSRecord rec = this.readAnswer();
+ if (rec != null) {
+ // Add a record, if we were able to create one.
+ _authoritativeAnswers.add(rec);
+ }
+ }
- private DNSQuestion readQuestion() {
- String domain = _messageInputStream.readName();
- DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
- if (type == DNSRecordType.TYPE_IGNORE) {
- logger.warn("Could not find record type: {}", this.print(true));
+ for (int i = 0; i < numAdditionals; i++) {
+ DNSRecord rec = this.readAnswer();
+ if (rec != null) {
+ // Add a record, if we were able to create one.
+ _additionals.add(rec);
}
- int recordClassIndex = _messageInputStream.readUnsignedShort();
- DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
- boolean unique = recordClass.isUnique(recordClassIndex);
- return DNSQuestion.newQuestion(domain, type, recordClass, unique);
+ }
+
+ // We should have drained the entire stream by now
+ if (_messageInputStream.available() > 0) {
+ throw new IOException("Received a message with the wrong length.");
+ }
+ } catch (Exception e) {
+ logger.warn("DNSIncoming() dump " + print(true) + "\n exception ", e);
+ // This ugly but some JVM don't implement the cause on IOException
+ IOException ioe = new IOException("DNSIncoming corrupted message");
+ ioe.initCause(e);
+ throw ioe;
+ } finally {
+ try {
+ _messageInputStream.close();
+ } catch (Exception e) {
+ logger.warn("MessageInputStream close error");
+ }
}
-
- private DNSRecord readAnswer() {
- String domain = _messageInputStream.readName();
- DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
- if (type == DNSRecordType.TYPE_IGNORE) {
- logger.warn("Could not find record type. domain: {}\n{}", domain, this.print(true));
+ }
+
+ private DNSIncoming(
+ int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
+ super(flags, id, multicast);
+ this._packet = packet;
+ this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
+ this._receivedTime = receivedTime;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public DNSIncoming clone() {
+ DNSIncoming in =
+ new DNSIncoming(
+ this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
+ in._senderUDPPayload = this._senderUDPPayload;
+ in._questions.addAll(this._questions);
+ in._answers.addAll(this._answers);
+ in._authoritativeAnswers.addAll(this._authoritativeAnswers);
+ in._additionals.addAll(this._additionals);
+ return in;
+ }
+
+ private DNSQuestion readQuestion() {
+ String domain = _messageInputStream.readName();
+ DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
+ if (type == DNSRecordType.TYPE_IGNORE) {
+ logger.warn("Could not find record type: {}", this.print(true));
+ }
+ int recordClassIndex = _messageInputStream.readUnsignedShort();
+ DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
+ boolean unique = recordClass.isUnique(recordClassIndex);
+ return DNSQuestion.newQuestion(domain, type, recordClass, unique);
+ }
+
+ private DNSRecord readAnswer() {
+ String domain = _messageInputStream.readName();
+ DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
+ if (type == DNSRecordType.TYPE_IGNORE) {
+ logger.warn("Could not find record type. domain: {}\n{}", domain, this.print(true));
+ }
+ int recordClassIndex = _messageInputStream.readUnsignedShort();
+ DNSRecordClass recordClass =
+ (type == DNSRecordType.TYPE_OPT
+ ? DNSRecordClass.CLASS_UNKNOWN
+ : DNSRecordClass.classForIndex(recordClassIndex));
+ if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
+ logger.warn(
+ "Could not find record class. domain: {} type: {}\n{}", domain, type, this.print(true));
+ }
+ boolean unique = recordClass.isUnique(recordClassIndex);
+ int ttl = _messageInputStream.readInt();
+ int len = _messageInputStream.readUnsignedShort();
+ DNSRecord rec = null;
+
+ switch (type) {
+ case TYPE_A: // IPv4
+ rec =
+ new DNSRecord.IPv4Address(
+ domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
+ break;
+ case TYPE_AAAA: // IPv6
+ rec =
+ new DNSRecord.IPv6Address(
+ domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
+ break;
+ case TYPE_CNAME:
+ case TYPE_PTR:
+ String service = "";
+ service = _messageInputStream.readName();
+ if (service.length() > 0) {
+ rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
+ } else {
+ logger.warn(
+ "PTR record of class: {}, there was a problem reading the service name of the answer for domain: {}",
+ recordClass,
+ domain);
}
- int recordClassIndex = _messageInputStream.readUnsignedShort();
- DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
- if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
- logger.warn("Could not find record class. domain: {} type: {}\n{}", domain, type, this.print(true));
+ break;
+ case TYPE_TXT:
+ rec =
+ new DNSRecord.Text(
+ domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
+ break;
+ case TYPE_SRV:
+ int priority = _messageInputStream.readUnsignedShort();
+ int weight = _messageInputStream.readUnsignedShort();
+ int port = _messageInputStream.readUnsignedShort();
+ String target = "";
+ // This is a hack to handle a bug in the BonjourConformanceTest
+ // It is sending out target strings that don't follow the "domain name" format.
+ if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
+ target = _messageInputStream.readName();
+ } else {
+ // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed
+ // to start by a length.
+ target = _messageInputStream.readNonNameString();
}
- boolean unique = recordClass.isUnique(recordClassIndex);
- int ttl = _messageInputStream.readInt();
- int len = _messageInputStream.readUnsignedShort();
- DNSRecord rec = null;
-
- switch (type) {
- case TYPE_A: // IPv4
- rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
- break;
- case TYPE_AAAA: // IPv6
- rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
- break;
- case TYPE_CNAME:
- case TYPE_PTR:
- String service = "";
- service = _messageInputStream.readName();
- if (service.length() > 0) {
- rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
- } else {
- logger.warn("PTR record of class: {}, there was a problem reading the service name of the answer for domain: {}", recordClass, domain);
+ rec =
+ new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
+ break;
+ case TYPE_HINFO:
+ final StringBuilder sb = new StringBuilder();
+ sb.append(_messageInputStream.readUTF(len));
+ int index = sb.indexOf(" ");
+ String cpu = (index > 0 ? sb.substring(0, index) : sb.toString()).trim();
+ String os = (index > 0 ? sb.substring(index + 1) : "").trim();
+ rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
+ break;
+ case TYPE_OPT:
+ DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
+ int version = (ttl & 0x00ff0000) >> 16;
+ if (version == 0) {
+ _senderUDPPayload = recordClassIndex;
+ while (_messageInputStream.available() > 0) {
+ // Read RDData
+ int optionCodeInt = 0;
+ DNSOptionCode optionCode = null;
+ if (_messageInputStream.available() >= 2) {
+ optionCodeInt = _messageInputStream.readUnsignedShort();
+ optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
+ } else {
+ logger.warn("There was a problem reading the OPT record. Ignoring.");
+ break;
+ }
+ int optionLength = 0;
+ if (_messageInputStream.available() >= 2) {
+ optionLength = _messageInputStream.readUnsignedShort();
+ } else {
+ logger.warn("There was a problem reading the OPT record. Ignoring.");
+ break;
+ }
+ byte[] optiondata = new byte[0];
+ if (_messageInputStream.available() >= optionLength) {
+ optiondata = _messageInputStream.readBytes(optionLength);
+ }
+ //
+ // We should really do something with those options.
+ switch (optionCode) {
+ case Owner:
+ // Valid length values are 8, 14, 18 and 20
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |Opt|Len|V|S|Primary MAC|Wakeup MAC | Password |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ int ownerVersion = 0;
+ int ownerSequence = 0;
+ byte[] ownerPrimaryMacAddress = null;
+ byte[] ownerWakeupMacAddress = null;
+ byte[] ownerPassword = null;
+ try {
+ ownerVersion = optiondata[0];
+ ownerSequence = optiondata[1];
+ ownerPrimaryMacAddress =
+ new byte[] {
+ optiondata[2],
+ optiondata[3],
+ optiondata[4],
+ optiondata[5],
+ optiondata[6],
+ optiondata[7]
+ };
+ ownerWakeupMacAddress = ownerPrimaryMacAddress;
+ if (optiondata.length > 8) {
+ // We have a wakeupMacAddress.
+ ownerWakeupMacAddress =
+ new byte[] {
+ optiondata[8],
+ optiondata[9],
+ optiondata[10],
+ optiondata[11],
+ optiondata[12],
+ optiondata[13]
+ };
+ }
+ if (optiondata.length == 18) {
+ // We have a short password.
+ ownerPassword =
+ new byte[] {optiondata[14], optiondata[15], optiondata[16], optiondata[17]};
+ }
+ if (optiondata.length == 22) {
+ // We have a long password.
+ ownerPassword =
+ new byte[] {
+ optiondata[14],
+ optiondata[15],
+ optiondata[16],
+ optiondata[17],
+ optiondata[18],
+ optiondata[19],
+ optiondata[20],
+ optiondata[21]
+ };
+ }
+ } catch (Exception exception) {
+ logger.warn(
+ "Malformed OPT answer. Option code: Owner data: {}",
+ this._hexString(optiondata));
}
- break;
- case TYPE_TXT:
- rec = new DNSRecord.Text(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
- break;
- case TYPE_SRV:
- int priority = _messageInputStream.readUnsignedShort();
- int weight = _messageInputStream.readUnsignedShort();
- int port = _messageInputStream.readUnsignedShort();
- String target = "";
- // This is a hack to handle a bug in the BonjourConformanceTest
- // It is sending out target strings that don't follow the "domain name" format.
- if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
- target = _messageInputStream.readName();
- } else {
- // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length.
- target = _messageInputStream.readNonNameString();
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "Unhandled Owner OPT version: {} sequence: {} MAC address: {} {}{} {}{}",
+ ownerVersion,
+ ownerSequence,
+ this._hexString(ownerPrimaryMacAddress),
+ (ownerWakeupMacAddress != ownerPrimaryMacAddress
+ ? " wakeup MAC address: "
+ : ""),
+ (ownerWakeupMacAddress != ownerPrimaryMacAddress
+ ? this._hexString(ownerWakeupMacAddress)
+ : ""),
+ (ownerPassword != null ? " password: " : ""),
+ (ownerPassword != null ? this._hexString(ownerPassword) : ""));
}
- rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
break;
- case TYPE_HINFO:
- final StringBuilder sb = new StringBuilder();
- sb.append(_messageInputStream.readUTF(len));
- int index = sb.indexOf(" ");
- String cpu = (index > 0 ? sb.substring(0, index) : sb.toString()).trim();
- String os = (index > 0 ? sb.substring(index + 1) : "").trim();
- rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
+ case LLQ:
+ case NSID:
+ case UL:
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "There was an OPT answer. Option code: {} data: {}",
+ optionCode,
+ this._hexString(optiondata));
+ }
break;
- case TYPE_OPT:
- DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
- int version = (ttl & 0x00ff0000) >> 16;
- if (version == 0) {
- _senderUDPPayload = recordClassIndex;
- while (_messageInputStream.available() > 0) {
- // Read RDData
- int optionCodeInt = 0;
- DNSOptionCode optionCode = null;
- if (_messageInputStream.available() >= 2) {
- optionCodeInt = _messageInputStream.readUnsignedShort();
- optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
- } else {
- logger.warn("There was a problem reading the OPT record. Ignoring.");
- break;
- }
- int optionLength = 0;
- if (_messageInputStream.available() >= 2) {
- optionLength = _messageInputStream.readUnsignedShort();
- } else {
- logger.warn("There was a problem reading the OPT record. Ignoring.");
- break;
- }
- byte[] optiondata = new byte[0];
- if (_messageInputStream.available() >= optionLength) {
- optiondata = _messageInputStream.readBytes(optionLength);
- }
- //
- // We should really do something with those options.
- switch (optionCode) {
- case Owner:
- // Valid length values are 8, 14, 18 and 20
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- // |Opt|Len|V|S|Primary MAC|Wakeup MAC | Password |
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- //
- int ownerVersion = 0;
- int ownerSequence = 0;
- byte[] ownerPrimaryMacAddress = null;
- byte[] ownerWakeupMacAddress = null;
- byte[] ownerPassword = null;
- try {
- ownerVersion = optiondata[0];
- ownerSequence = optiondata[1];
- ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
- ownerWakeupMacAddress = ownerPrimaryMacAddress;
- if (optiondata.length > 8) {
- // We have a wakeupMacAddress.
- ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
- }
- if (optiondata.length == 18) {
- // We have a short password.
- ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
- }
- if (optiondata.length == 22) {
- // We have a long password.
- ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17], optiondata[18], optiondata[19], optiondata[20], optiondata[21] };
- }
- } catch (Exception exception) {
- logger.warn("Malformed OPT answer. Option code: Owner data: {}", this._hexString(optiondata));
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Unhandled Owner OPT version: {} sequence: {} MAC address: {} {}{} {}{}",
- ownerVersion,
- ownerSequence,
- this._hexString(ownerPrimaryMacAddress),
- (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " : ""),
- (ownerWakeupMacAddress != ownerPrimaryMacAddress ? this._hexString(ownerWakeupMacAddress) : ""),
- (ownerPassword != null ? " password: ": ""),
- (ownerPassword != null ? this._hexString(ownerPassword) : "")
- );
- }
- break;
- case LLQ:
- case NSID:
- case UL:
- if (logger.isDebugEnabled()) {
- logger.debug("There was an OPT answer. Option code: {} data: {}", optionCode, this._hexString(optiondata));
- }
- break;
- case Unknown:
- if (optionCodeInt >= 65001 && optionCodeInt <= 65534) {
- // RFC 6891 defines this range as used for experimental/local purposes.
- logger.debug("There was an OPT answer using an experimental/local option code: {} data: {}", optionCodeInt, this._hexString(optiondata));
- } else {
- logger.warn("There was an OPT answer. Not currently handled. Option code: {} data: {}", optionCodeInt, this._hexString(optiondata));
- }
- break;
- default:
- // This is to keep the compiler happy.
- break;
- }
- }
+ case Unknown:
+ if (optionCodeInt >= 65001 && optionCodeInt <= 65534) {
+ // RFC 6891 defines this range as used for experimental/local purposes.
+ logger.debug(
+ "There was an OPT answer using an experimental/local option code: {} data: {}",
+ optionCodeInt,
+ this._hexString(optiondata));
} else {
- logger.warn("There was an OPT answer. Wrong version number: {} result code: {}", version, extendedResultCode);
+ logger.warn(
+ "There was an OPT answer. Not currently handled. Option code: {} data: {}",
+ optionCodeInt,
+ this._hexString(optiondata));
}
break;
- default:
- logger.debug("DNSIncoming() unknown type: {}", type);
- _messageInputStream.skip(len);
+ default:
+ // This is to keep the compiler happy.
break;
+ }
+ }
+ } else {
+ logger.warn(
+ "There was an OPT answer. Wrong version number: {} result code: {}",
+ version,
+ extendedResultCode);
}
- return rec;
+ break;
+ default:
+ logger.debug("DNSIncoming() unknown type: {}", type);
+ _messageInputStream.skip(len);
+ break;
}
-
- /**
- * Debugging.
- */
- String print(boolean dump) {
- final StringBuilder sb = new StringBuilder();
- sb.append(this.print());
- if (dump) {
- byte[] data = new byte[_packet.getLength()];
- System.arraycopy(_packet.getData(), 0, data, 0, data.length);
- sb.append(this.print(data));
- }
- return sb.toString();
+ return rec;
+ }
+
+ /** Debugging. */
+ String print(boolean dump) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(this.print());
+ if (dump) {
+ byte[] data = new byte[_packet.getLength()];
+ System.arraycopy(_packet.getData(), 0, data, 0, data.length);
+ sb.append(this.print(data));
}
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- sb.append(isQuery() ? "dns[query," : "dns[response,");
- if (_packet.getAddress() != null) {
- sb.append(_packet.getAddress().getHostAddress());
- }
- sb.append(':');
- sb.append(_packet.getPort());
- sb.append(", length=");
- sb.append(_packet.getLength());
- sb.append(", id=0x");
- sb.append(Integer.toHexString(this.getId()));
- if (this.getFlags() != 0) {
- sb.append(", flags=0x");
- sb.append(Integer.toHexString(this.getFlags()));
- if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
- sb.append(":r");
- }
- if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
- sb.append(":aa");
- }
- if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
- sb.append(":tc");
- }
- }
- if (this.getNumberOfQuestions() > 0) {
- sb.append(", questions=");
- sb.append(this.getNumberOfQuestions());
- }
- if (this.getNumberOfAnswers() > 0) {
- sb.append(", answers=");
- sb.append(this.getNumberOfAnswers());
- }
- if (this.getNumberOfAuthorities() > 0) {
- sb.append(", authorities=");
- sb.append(this.getNumberOfAuthorities());
- }
- if (this.getNumberOfAdditionals() > 0) {
- sb.append(", additionals=");
- sb.append(this.getNumberOfAdditionals());
- }
- if (this.getNumberOfQuestions() > 0) {
- sb.append("\nquestions:");
- for (DNSQuestion question : _questions) {
- sb.append("\n\t");
- sb.append(question);
- }
- }
- if (this.getNumberOfAnswers() > 0) {
- sb.append("\nanswers:");
- for (DNSRecord record : _answers) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- if (this.getNumberOfAuthorities() > 0) {
- sb.append("\nauthorities:");
- for (DNSRecord record : _authoritativeAnswers) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- if (this.getNumberOfAdditionals() > 0) {
- sb.append("\nadditionals:");
- for (DNSRecord record : _additionals) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- sb.append(']');
-
- return sb.toString();
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(isQuery() ? "dns[query," : "dns[response,");
+ if (_packet.getAddress() != null) {
+ sb.append(_packet.getAddress().getHostAddress());
}
-
- public int elapseSinceArrival() {
- return (int) (System.currentTimeMillis() - _receivedTime);
+ sb.append(':');
+ sb.append(_packet.getPort());
+ sb.append(", length=");
+ sb.append(_packet.getLength());
+ sb.append(", id=0x");
+ sb.append(Integer.toHexString(this.getId()));
+ if (this.getFlags() != 0) {
+ sb.append(", flags=0x");
+ sb.append(Integer.toHexString(this.getFlags()));
+ if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
+ sb.append(":r");
+ }
+ if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
+ sb.append(":aa");
+ }
+ if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
+ sb.append(":tc");
+ }
}
-
- /**
- * This will return the default UDP payload except if an OPT record was found with a different size.
- *
- * @return the senderUDPPayload
- */
- public int getSenderUDPPayload() {
- return this._senderUDPPayload;
+ if (this.getNumberOfQuestions() > 0) {
+ sb.append(", questions=");
+ sb.append(this.getNumberOfQuestions());
}
-
- private static final char[] _nibbleToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
- /**
- * Returns a hex-string for printing
- *
- * @param bytes
- * @return Returns a hex-string which can be used within a SQL expression
- */
- private String _hexString(byte[] bytes) {
-
- final StringBuilder result = new StringBuilder(2 * bytes.length);
-
- for (int i = 0; i < bytes.length; i++) {
- int b = bytes[i] & 0xFF;
- result.append(_nibbleToHex[b / 16]);
- result.append(_nibbleToHex[b % 16]);
- }
-
- return result.toString();
+ if (this.getNumberOfAnswers() > 0) {
+ sb.append(", answers=");
+ sb.append(this.getNumberOfAnswers());
+ }
+ if (this.getNumberOfAuthorities() > 0) {
+ sb.append(", authorities=");
+ sb.append(this.getNumberOfAuthorities());
+ }
+ if (this.getNumberOfAdditionals() > 0) {
+ sb.append(", additionals=");
+ sb.append(this.getNumberOfAdditionals());
+ }
+ if (this.getNumberOfQuestions() > 0) {
+ sb.append("\nquestions:");
+ for (DNSQuestion question : _questions) {
+ sb.append("\n\t");
+ sb.append(question);
+ }
+ }
+ if (this.getNumberOfAnswers() > 0) {
+ sb.append("\nanswers:");
+ for (DNSRecord record : _answers) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ if (this.getNumberOfAuthorities() > 0) {
+ sb.append("\nauthorities:");
+ for (DNSRecord record : _authoritativeAnswers) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ if (this.getNumberOfAdditionals() > 0) {
+ sb.append("\nadditionals:");
+ for (DNSRecord record : _additionals) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ sb.append(']');
+
+ return sb.toString();
+ }
+
+ public int elapseSinceArrival() {
+ return (int) (System.currentTimeMillis() - _receivedTime);
+ }
+
+ /**
+ * This will return the default UDP payload except if an OPT record was found with a different
+ * size.
+ *
+ * @return the senderUDPPayload
+ */
+ public int getSenderUDPPayload() {
+ return this._senderUDPPayload;
+ }
+
+ private static final char[] _nibbleToHex = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ /**
+ * Returns a hex-string for printing
+ *
+ * @param bytes
+ * @return Returns a hex-string which can be used within a SQL expression
+ */
+ private String _hexString(byte[] bytes) {
+
+ final StringBuilder result = new StringBuilder(2 * bytes.length);
+
+ for (int i = 0; i < bytes.length; i++) {
+ int b = bytes[i] & 0xFF;
+ result.append(_nibbleToHex[b / 16]);
+ result.append(_nibbleToHex[b % 16]);
}
+ return result.toString();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSMessage.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSMessage.java
index a22a63a42..24a6cc812 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSMessage.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSMessage.java
@@ -1,16 +1,13 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
/**
* DNSMessage define a DNS message either incoming or outgoing.
*
@@ -18,318 +15,315 @@
*/
public abstract class DNSMessage {
- /**
- *
- */
- public static final boolean MULTICAST = true;
-
- /**
- *
- */
- public static final boolean UNICAST = false;
-
- // protected DatagramPacket _packet;
- // protected int _off;
- // protected int _len;
- // protected byte[] _data;
-
- private int _id;
-
- boolean _multicast;
-
- private int _flags;
-
- protected final List _questions;
-
- protected final List _answers;
-
- protected final List _authoritativeAnswers;
-
- protected final List _additionals;
-
- /**
- * @param flags
- * @param id
- * @param multicast
- */
- protected DNSMessage(int flags, int id, boolean multicast) {
- super();
- _flags = flags;
- _id = id;
- _multicast = multicast;
- _questions = Collections.synchronizedList(new LinkedList());
- _answers = Collections.synchronizedList(new LinkedList());
- _authoritativeAnswers = Collections.synchronizedList(new LinkedList());
- _additionals = Collections.synchronizedList(new LinkedList());
+ /** */
+ public static final boolean MULTICAST = true;
+
+ /** */
+ public static final boolean UNICAST = false;
+
+ // protected DatagramPacket _packet;
+ // protected int _off;
+ // protected int _len;
+ // protected byte[] _data;
+
+ private int _id;
+
+ boolean _multicast;
+
+ private int _flags;
+
+ protected final List _questions;
+
+ protected final List _answers;
+
+ protected final List _authoritativeAnswers;
+
+ protected final List _additionals;
+
+ /**
+ * @param flags
+ * @param id
+ * @param multicast
+ */
+ protected DNSMessage(int flags, int id, boolean multicast) {
+ super();
+ _flags = flags;
+ _id = id;
+ _multicast = multicast;
+ _questions = Collections.synchronizedList(new LinkedList());
+ _answers = Collections.synchronizedList(new LinkedList());
+ _authoritativeAnswers = Collections.synchronizedList(new LinkedList());
+ _additionals = Collections.synchronizedList(new LinkedList());
+ }
+
+ // public DatagramPacket getPacket() {
+ // return _packet;
+ // }
+ //
+ // public int getOffset() {
+ // return _off;
+ // }
+ //
+ // public int getLength() {
+ // return _len;
+ // }
+ //
+ // public byte[] getData() {
+ // if ( _data == null ) _data = new byte[DNSConstants.MAX_MSG_TYPICAL];
+ // return _data;
+ // }
+
+ /**
+ * @return message id
+ */
+ public int getId() {
+ return (_multicast ? 0 : _id);
+ }
+
+ /**
+ * @param id the id to set
+ */
+ public void setId(int id) {
+ this._id = id;
+ }
+
+ /**
+ * @return message flags
+ */
+ public int getFlags() {
+ return _flags;
+ }
+
+ /**
+ * @param flags the flags to set
+ */
+ public void setFlags(int flags) {
+ this._flags = flags;
+ }
+
+ /**
+ * @return true if multicast
+ */
+ public boolean isMulticast() {
+ return _multicast;
+ }
+
+ /**
+ * @return list of questions
+ */
+ public Collection extends DNSQuestion> getQuestions() {
+ return _questions;
+ }
+
+ /**
+ * @return number of questions in the message
+ */
+ public int getNumberOfQuestions() {
+ return this.getQuestions().size();
+ }
+
+ public List getAllAnswers() {
+ List aList =
+ new ArrayList(
+ _answers.size() + _authoritativeAnswers.size() + _additionals.size());
+ aList.addAll(_answers);
+ aList.addAll(_authoritativeAnswers);
+ aList.addAll(_additionals);
+ return aList;
+ }
+
+ /**
+ * @return list of answers
+ */
+ public Collection extends DNSRecord> getAnswers() {
+ return _answers;
+ }
+
+ /**
+ * @return number of answers in the message
+ */
+ public int getNumberOfAnswers() {
+ return this.getAnswers().size();
+ }
+
+ /**
+ * @return list of authorities
+ */
+ public Collection extends DNSRecord> getAuthorities() {
+ return _authoritativeAnswers;
+ }
+
+ /**
+ * @return number of authorities in the message
+ */
+ public int getNumberOfAuthorities() {
+ return this.getAuthorities().size();
+ }
+
+ /**
+ * @return list of additional answers
+ */
+ public Collection extends DNSRecord> getAdditionals() {
+ return _additionals;
+ }
+
+ /**
+ * @return number of additional in the message
+ */
+ public int getNumberOfAdditionals() {
+ return this.getAdditionals().size();
+ }
+
+ /**
+ * Check is the response code is valid
+ * The only valid value is zero all other values signify an error and the message must be ignored.
+ *
+ * @return true if the message has a valid response code.
+ */
+ public boolean isValidResponseCode() {
+ return (_flags & DNSConstants.FLAGS_RCODE) == 0;
+ }
+
+ /**
+ * Returns the operation code value. Currently only standard query 0 is valid.
+ *
+ * @return The operation code value.
+ */
+ public int getOperationCode() {
+ return (_flags & DNSConstants.FLAGS_OPCODE) >> 11;
+ }
+
+ /**
+ * Check if the message is truncated.
+ *
+ * @return true if the message was truncated
+ */
+ public boolean isTruncated() {
+ return (_flags & DNSConstants.FLAGS_TC) != 0;
+ }
+
+ /**
+ * Check if the message is an authoritative answer.
+ *
+ * @return true if the message is an authoritative answer
+ */
+ public boolean isAuthoritativeAnswer() {
+ return (_flags & DNSConstants.FLAGS_AA) != 0;
+ }
+
+ /**
+ * Check if the message is a query.
+ *
+ * @return true is the message is a query
+ */
+ public boolean isQuery() {
+ return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
+ }
+
+ /**
+ * Check if the message is a response.
+ *
+ * @return true is the message is a response
+ */
+ public boolean isResponse() {
+ return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_RESPONSE;
+ }
+
+ /**
+ * Check if the message is empty
+ *
+ * @return true is the message is empty
+ */
+ public boolean isEmpty() {
+ return (this.getNumberOfQuestions()
+ + this.getNumberOfAnswers()
+ + this.getNumberOfAuthorities()
+ + this.getNumberOfAdditionals())
+ == 0;
+ }
+
+ /** Debugging. */
+ String print() {
+ final StringBuilder sb = new StringBuilder(200);
+ sb.append(this.toString());
+ sb.append("\n");
+ for (final DNSQuestion question : _questions) {
+ sb.append("\tquestion: ");
+ sb.append(question);
+ sb.append("\n");
}
-
- // public DatagramPacket getPacket() {
- // return _packet;
- // }
- //
- // public int getOffset() {
- // return _off;
- // }
- //
- // public int getLength() {
- // return _len;
- // }
- //
- // public byte[] getData() {
- // if ( _data == null ) _data = new byte[DNSConstants.MAX_MSG_TYPICAL];
- // return _data;
- // }
-
- /**
- * @return message id
- */
- public int getId() {
- return (_multicast ? 0 : _id);
- }
-
- /**
- * @param id
- * the id to set
- */
- public void setId(int id) {
- this._id = id;
- }
-
- /**
- * @return message flags
- */
- public int getFlags() {
- return _flags;
- }
-
- /**
- * @param flags
- * the flags to set
- */
- public void setFlags(int flags) {
- this._flags = flags;
+ for (final DNSRecord answer : _answers) {
+ sb.append("\tanswer: ");
+ sb.append(answer);
+ sb.append("\n");
}
-
- /**
- * @return true if multicast
- */
- public boolean isMulticast() {
- return _multicast;
+ for (final DNSRecord answer : _authoritativeAnswers) {
+ sb.append("\tauthoritative: ");
+ sb.append(answer);
+ sb.append("\n");
}
-
- /**
- * @return list of questions
- */
- public Collection extends DNSQuestion> getQuestions() {
- return _questions;
- }
-
- /**
- * @return number of questions in the message
- */
- public int getNumberOfQuestions() {
- return this.getQuestions().size();
- }
-
- public List getAllAnswers() {
- List aList = new ArrayList(_answers.size() + _authoritativeAnswers.size() + _additionals.size());
- aList.addAll(_answers);
- aList.addAll(_authoritativeAnswers);
- aList.addAll(_additionals);
- return aList;
- }
-
- /**
- * @return list of answers
- */
- public Collection extends DNSRecord> getAnswers() {
- return _answers;
+ for (DNSRecord answer : _additionals) {
+ sb.append("\tadditional: ");
+ sb.append(answer);
+ sb.append("\n");
}
-
- /**
- * @return number of answers in the message
- */
- public int getNumberOfAnswers() {
- return this.getAnswers().size();
- }
-
- /**
- * @return list of authorities
- */
- public Collection extends DNSRecord> getAuthorities() {
- return _authoritativeAnswers;
- }
-
- /**
- * @return number of authorities in the message
- */
- public int getNumberOfAuthorities() {
- return this.getAuthorities().size();
- }
-
- /**
- * @return list of additional answers
- */
- public Collection extends DNSRecord> getAdditionals() {
- return _additionals;
- }
-
- /**
- * @return number of additional in the message
- */
- public int getNumberOfAdditionals() {
- return this.getAdditionals().size();
- }
-
- /**
- * Check is the response code is valid
- * The only valid value is zero all other values signify an error and the message must be ignored.
- *
- * @return true if the message has a valid response code.
- */
- public boolean isValidResponseCode() {
- return (_flags & DNSConstants.FLAGS_RCODE) == 0;
- }
-
- /**
- * Returns the operation code value. Currently only standard query 0 is valid.
- *
- * @return The operation code value.
- */
- public int getOperationCode() {
- return (_flags & DNSConstants.FLAGS_OPCODE) >> 11;
- }
-
- /**
- * Check if the message is truncated.
- *
- * @return true if the message was truncated
- */
- public boolean isTruncated() {
- return (_flags & DNSConstants.FLAGS_TC) != 0;
- }
-
- /**
- * Check if the message is an authoritative answer.
- *
- * @return true if the message is an authoritative answer
- */
- public boolean isAuthoritativeAnswer() {
- return (_flags & DNSConstants.FLAGS_AA) != 0;
- }
-
- /**
- * Check if the message is a query.
- *
- * @return true is the message is a query
- */
- public boolean isQuery() {
- return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
- }
-
- /**
- * Check if the message is a response.
- *
- * @return true is the message is a response
- */
- public boolean isResponse() {
- return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_RESPONSE;
- }
-
- /**
- * Check if the message is empty
- *
- * @return true is the message is empty
- */
- public boolean isEmpty() {
- return (this.getNumberOfQuestions() + this.getNumberOfAnswers() + this.getNumberOfAuthorities() + this.getNumberOfAdditionals()) == 0;
- }
-
- /**
- * Debugging.
- */
- String print() {
- final StringBuilder sb = new StringBuilder(200);
- sb.append(this.toString());
- sb.append("\n");
- for (final DNSQuestion question : _questions) {
- sb.append("\tquestion: ");
- sb.append(question);
- sb.append("\n");
+ return sb.toString();
+ }
+
+ /**
+ * Debugging.
+ *
+ * @param data
+ * @return data dump
+ */
+ protected String print(byte[] data) {
+ final StringBuilder sb = new StringBuilder(4000);
+ for (int off = 0, len = data.length; off < len; off += 32) {
+ int n = Math.min(32, len - off);
+ if (off < 0x10) {
+ sb.append(' ');
+ }
+ if (off < 0x100) {
+ sb.append(' ');
+ }
+ if (off < 0x1000) {
+ sb.append(' ');
+ }
+ sb.append(Integer.toHexString(off));
+ sb.append(':');
+ int index = 0;
+ for (index = 0; index < n; index++) {
+ if ((index % 8) == 0) {
+ sb.append(' ');
}
- for (final DNSRecord answer : _answers) {
- sb.append("\tanswer: ");
- sb.append(answer);
- sb.append("\n");
+ sb.append(Integer.toHexString((data[off + index] & 0xF0) >> 4));
+ sb.append(Integer.toHexString((data[off + index] & 0x0F) >> 0));
+ }
+ // for incomplete lines
+ if (index < 32) {
+ for (int i = index; i < 32; i++) {
+ if ((i % 8) == 0) {
+ sb.append(' ');
+ }
+ sb.append(" ");
}
- for (final DNSRecord answer : _authoritativeAnswers) {
- sb.append("\tauthoritative: ");
- sb.append(answer);
- sb.append("\n");
+ }
+ sb.append(" ");
+ for (index = 0; index < n; index++) {
+ if ((index % 8) == 0) {
+ sb.append(' ');
}
- for (DNSRecord answer : _additionals) {
- sb.append("\tadditional: ");
- sb.append(answer);
- sb.append("\n");
- }
- return sb.toString();
+ int ch = data[off + index] & 0xFF;
+ sb.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.');
+ }
+ sb.append("\n");
+
+ // limit message size
+ if (off + 32 >= 2048) {
+ sb.append("....\n");
+ break;
+ }
}
-
- /**
- * Debugging.
- *
- * @param data
- * @return data dump
- */
- protected String print(byte[] data) {
- final StringBuilder sb = new StringBuilder(4000);
- for (int off = 0, len = data.length; off < len; off += 32) {
- int n = Math.min(32, len - off);
- if (off < 0x10) {
- sb.append(' ');
- }
- if (off < 0x100) {
- sb.append(' ');
- }
- if (off < 0x1000) {
- sb.append(' ');
- }
- sb.append(Integer.toHexString(off));
- sb.append(':');
- int index = 0;
- for (index = 0; index < n; index++) {
- if ((index % 8) == 0) {
- sb.append(' ');
- }
- sb.append(Integer.toHexString((data[off + index] & 0xF0) >> 4));
- sb.append(Integer.toHexString((data[off + index] & 0x0F) >> 0));
- }
- // for incomplete lines
- if (index < 32) {
- for (int i = index; i < 32; i++) {
- if ((i % 8) == 0) {
- sb.append(' ');
- }
- sb.append(" ");
- }
- }
- sb.append(" ");
- for (index = 0; index < n; index++) {
- if ((index % 8) == 0) {
- sb.append(' ');
- }
- int ch = data[off + index] & 0xFF;
- sb.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.');
- }
- sb.append("\n");
-
- // limit message size
- if (off + 32 >= 2048) {
- sb.append("....\n");
- break;
- }
- }
- return sb.toString();
- }
-
+ return sb.toString();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSOutgoing.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSOutgoing.java
index fe2b33242..3b42a1738 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSOutgoing.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSOutgoing.java
@@ -4,425 +4,426 @@
package io.libp2p.discovery.mdns.impl;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
/**
* An outgoing DNS message.
*
* @author Arthur van Hoff, Rick Blair, Werner Randelshofer
*/
public final class DNSOutgoing extends DNSMessage {
- public static class MessageOutputStream extends ByteArrayOutputStream {
- private final DNSOutgoing _out;
-
- private final int _offset;
-
- /**
- * Creates a new message stream, with a buffer capacity of the specified size, in bytes.
- *
- * @param size
- * the initial size.
- * @exception IllegalArgumentException
- * if size is negative.
- */
- MessageOutputStream(int size, DNSOutgoing out) {
- this(size, out, 0);
- }
+ public static class MessageOutputStream extends ByteArrayOutputStream {
+ private final DNSOutgoing _out;
- MessageOutputStream(int size, DNSOutgoing out, int offset) {
- super(size);
- _out = out;
- _offset = offset;
- }
+ private final int _offset;
- void writeByte(int value) {
- this.write(value & 0xFF);
- }
+ /**
+ * Creates a new message stream, with a buffer capacity of the specified size, in bytes.
+ *
+ * @param size the initial size.
+ * @exception IllegalArgumentException if size is negative.
+ */
+ MessageOutputStream(int size, DNSOutgoing out) {
+ this(size, out, 0);
+ }
- void writeBytes(String str, int off, int len) {
- for (int i = 0; i < len; i++) {
- writeByte(str.charAt(off + i));
- }
- }
+ MessageOutputStream(int size, DNSOutgoing out, int offset) {
+ super(size);
+ _out = out;
+ _offset = offset;
+ }
- public void writeBytes(byte data[]) {
- if (data != null) {
- writeBytes(data, 0, data.length);
- }
- }
+ void writeByte(int value) {
+ this.write(value & 0xFF);
+ }
- void writeBytes(byte data[], int off, int len) {
- for (int i = 0; i < len; i++) {
- writeByte(data[off + i]);
- }
- }
+ void writeBytes(String str, int off, int len) {
+ for (int i = 0; i < len; i++) {
+ writeByte(str.charAt(off + i));
+ }
+ }
- void writeShort(int value) {
- writeByte(value >> 8);
- writeByte(value);
- }
+ public void writeBytes(byte data[]) {
+ if (data != null) {
+ writeBytes(data, 0, data.length);
+ }
+ }
- void writeInt(int value) {
- writeShort(value >> 16);
- writeShort(value);
- }
+ void writeBytes(byte data[], int off, int len) {
+ for (int i = 0; i < len; i++) {
+ writeByte(data[off + i]);
+ }
+ }
- void writeUTF(String str, int off, int len) {
- // compute utf length
- int utflen = 0;
- for (int i = 0; i < len; i++) {
- int ch = str.charAt(off + i);
- if ((ch >= 0x0001) && (ch <= 0x007F)) {
- utflen += 1;
- } else {
- if (ch > 0x07FF) {
- utflen += 3;
- } else {
- utflen += 2;
- }
- }
- }
- // write utf length
- writeByte(utflen);
- // write utf data
- for (int i = 0; i < len; i++) {
- int ch = str.charAt(off + i);
- if ((ch >= 0x0001) && (ch <= 0x007F)) {
- writeByte(ch);
- } else {
- if (ch > 0x07FF) {
- writeByte(0xE0 | ((ch >> 12) & 0x0F));
- writeByte(0x80 | ((ch >> 6) & 0x3F));
- writeByte(0x80 | ((ch >> 0) & 0x3F));
- } else {
- writeByte(0xC0 | ((ch >> 6) & 0x1F));
- writeByte(0x80 | ((ch >> 0) & 0x3F));
- }
- }
- }
- }
+ void writeShort(int value) {
+ writeByte(value >> 8);
+ writeByte(value);
+ }
- void writeName(String name) {
- writeName(name, true);
- }
+ void writeInt(int value) {
+ writeShort(value >> 16);
+ writeShort(value);
+ }
- void writeName(String name, boolean useCompression) {
- String aName = name;
- while (true) {
- int n = indexOfSeparator(aName);
- if (n < 0) {
- n = aName.length();
- }
- if (n <= 0) {
- writeByte(0);
- return;
- }
- String label = aName.substring(0, n).replace("\\.", ".");
- if (useCompression && USE_DOMAIN_NAME_COMPRESSION) {
- Integer offset = _out._names.get(aName);
- if (offset != null) {
- int val = offset.intValue();
- writeByte((val >> 8) | 0xC0);
- writeByte(val & 0xFF);
- return;
- }
- _out._names.put(aName, Integer.valueOf(this.size() + _offset));
- writeUTF(label, 0, label.length());
- } else {
- writeUTF(label, 0, label.length());
- }
- aName = aName.substring(n);
- if (aName.startsWith(".")) {
- aName = aName.substring(1);
- }
- }
+ void writeUTF(String str, int off, int len) {
+ // compute utf length
+ int utflen = 0;
+ for (int i = 0; i < len; i++) {
+ int ch = str.charAt(off + i);
+ if ((ch >= 0x0001) && (ch <= 0x007F)) {
+ utflen += 1;
+ } else {
+ if (ch > 0x07FF) {
+ utflen += 3;
+ } else {
+ utflen += 2;
+ }
}
+ }
+ // write utf length
+ writeByte(utflen);
+ // write utf data
+ for (int i = 0; i < len; i++) {
+ int ch = str.charAt(off + i);
+ if ((ch >= 0x0001) && (ch <= 0x007F)) {
+ writeByte(ch);
+ } else {
+ if (ch > 0x07FF) {
+ writeByte(0xE0 | ((ch >> 12) & 0x0F));
+ writeByte(0x80 | ((ch >> 6) & 0x3F));
+ writeByte(0x80 | ((ch >> 0) & 0x3F));
+ } else {
+ writeByte(0xC0 | ((ch >> 6) & 0x1F));
+ writeByte(0x80 | ((ch >> 0) & 0x3F));
+ }
+ }
+ }
+ }
- private static int indexOfSeparator(String aName) {
- int offset = 0;
- int n = 0;
-
- while (true) {
- n = aName.indexOf('.', offset);
- if (n < 0)
- return -1;
-
- if (n == 0 || aName.charAt(n - 1) != '\\')
- return n;
+ void writeName(String name) {
+ writeName(name, true);
+ }
- offset = n + 1;
- }
+ void writeName(String name, boolean useCompression) {
+ String aName = name;
+ while (true) {
+ int n = indexOfSeparator(aName);
+ if (n < 0) {
+ n = aName.length();
}
-
- void writeQuestion(DNSQuestion question) {
- writeName(question.getName());
- writeShort(question.getRecordType().indexValue());
- writeShort(question.getRecordClass().indexValue());
+ if (n <= 0) {
+ writeByte(0);
+ return;
}
-
- void writeRecord(DNSRecord rec, long now) {
- writeName(rec.getName());
- writeShort(rec.getRecordType().indexValue());
- writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
- writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
-
- // We need to take into account the 2 size bytes
- MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2);
- rec.write(record);
- byte[] byteArray = record.toByteArray();
-
- writeShort(byteArray.length);
- write(byteArray, 0, byteArray.length);
+ String label = aName.substring(0, n).replace("\\.", ".");
+ if (useCompression && USE_DOMAIN_NAME_COMPRESSION) {
+ Integer offset = _out._names.get(aName);
+ if (offset != null) {
+ int val = offset.intValue();
+ writeByte((val >> 8) | 0xC0);
+ writeByte(val & 0xFF);
+ return;
+ }
+ _out._names.put(aName, Integer.valueOf(this.size() + _offset));
+ writeUTF(label, 0, label.length());
+ } else {
+ writeUTF(label, 0, label.length());
}
-
+ aName = aName.substring(n);
+ if (aName.startsWith(".")) {
+ aName = aName.substring(1);
+ }
+ }
}
- /**
- * This can be used to turn off domain name compression. This was helpful for tracking problems interacting with other mdns implementations.
- */
- public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
-
- Map _names;
+ private static int indexOfSeparator(String aName) {
+ int offset = 0;
+ int n = 0;
- private int _maxUDPPayload;
+ while (true) {
+ n = aName.indexOf('.', offset);
+ if (n < 0) return -1;
- private final MessageOutputStream _questionsBytes;
+ if (n == 0 || aName.charAt(n - 1) != '\\') return n;
- private final MessageOutputStream _answersBytes;
-
- private final MessageOutputStream _authoritativeAnswersBytes;
-
- private final MessageOutputStream _additionalsAnswersBytes;
-
- private final static int HEADER_SIZE = 12;
-
- private InetSocketAddress _destination;
-
- /**
- * Create an outgoing multicast query or response.
- *
- * @param flags
- */
- public DNSOutgoing(int flags) {
- this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
+ offset = n + 1;
+ }
}
- /**
- * Create an outgoing query or response.
- *
- * @param flags
- * @param multicast
- * @param senderUDPPayload
- * The sender's UDP payload size is the number of bytes of the largest UDP payload that can be reassembled and delivered in the sender's network stack.
- */
- public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) {
- super(flags, 0, multicast);
- _names = new HashMap();
- _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
- _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
- _answersBytes = new MessageOutputStream(senderUDPPayload, this);
- _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
- _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
+ void writeQuestion(DNSQuestion question) {
+ writeName(question.getName());
+ writeShort(question.getRecordType().indexValue());
+ writeShort(question.getRecordClass().indexValue());
}
- /**
- * Get the forced destination address if a specific one was set.
- *
- * @return a forced destination address or null if no address is forced.
- */
- public InetSocketAddress getDestination() {
- return _destination;
+ void writeRecord(DNSRecord rec, long now) {
+ writeName(rec.getName());
+ writeShort(rec.getRecordType().indexValue());
+ writeShort(
+ rec.getRecordClass().indexValue()
+ | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
+ writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
+
+ // We need to take into account the 2 size bytes
+ MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2);
+ rec.write(record);
+ byte[] byteArray = record.toByteArray();
+
+ writeShort(byteArray.length);
+ write(byteArray, 0, byteArray.length);
}
-
- /**
- * Force a specific destination address if packet is sent.
- *
- * @param destination
- * Set a destination address a packet should be sent to (instead the default one). You could use null to unset the forced destination.
- */
- public void setDestination(InetSocketAddress destination) {
- _destination = destination;
- }
-
- /**
- * Return the number of byte available in the message.
- *
- * @return available space
- */
- public int availableSpace() {
- return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size();
+ }
+
+ /**
+ * This can be used to turn off domain name compression. This was helpful for tracking problems
+ * interacting with other mdns implementations.
+ */
+ public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
+
+ Map _names;
+
+ private int _maxUDPPayload;
+
+ private final MessageOutputStream _questionsBytes;
+
+ private final MessageOutputStream _answersBytes;
+
+ private final MessageOutputStream _authoritativeAnswersBytes;
+
+ private final MessageOutputStream _additionalsAnswersBytes;
+
+ private static final int HEADER_SIZE = 12;
+
+ private InetSocketAddress _destination;
+
+ /**
+ * Create an outgoing multicast query or response.
+ *
+ * @param flags
+ */
+ public DNSOutgoing(int flags) {
+ this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
+ }
+
+ /**
+ * Create an outgoing query or response.
+ *
+ * @param flags
+ * @param multicast
+ * @param senderUDPPayload The sender's UDP payload size is the number of bytes of the largest UDP
+ * payload that can be reassembled and delivered in the sender's network stack.
+ */
+ public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) {
+ super(flags, 0, multicast);
+ _names = new HashMap();
+ _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
+ _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
+ _answersBytes = new MessageOutputStream(senderUDPPayload, this);
+ _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
+ _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
+ }
+
+ /**
+ * Get the forced destination address if a specific one was set.
+ *
+ * @return a forced destination address or null if no address is forced.
+ */
+ public InetSocketAddress getDestination() {
+ return _destination;
+ }
+
+ /**
+ * Force a specific destination address if packet is sent.
+ *
+ * @param destination Set a destination address a packet should be sent to (instead the default
+ * one). You could use null to unset the forced destination.
+ */
+ public void setDestination(InetSocketAddress destination) {
+ _destination = destination;
+ }
+
+ /**
+ * Return the number of byte available in the message.
+ *
+ * @return available space
+ */
+ public int availableSpace() {
+ return _maxUDPPayload
+ - HEADER_SIZE
+ - _questionsBytes.size()
+ - _answersBytes.size()
+ - _authoritativeAnswersBytes.size()
+ - _additionalsAnswersBytes.size();
+ }
+
+ /**
+ * Add a question to the message.
+ *
+ * @param rec
+ * @exception IOException
+ */
+ public void addQuestion(DNSQuestion rec) throws IOException {
+ MessageOutputStream record = new MessageOutputStream(512, this);
+ record.writeQuestion(rec);
+ byte[] byteArray = record.toByteArray();
+ record.close();
+ if (byteArray.length < this.availableSpace()) {
+ _questions.add(rec);
+ _questionsBytes.write(byteArray, 0, byteArray.length);
+ } else {
+ throw new IOException("message full");
}
-
- /**
- * Add a question to the message.
- *
- * @param rec
- * @exception IOException
- */
- public void addQuestion(DNSQuestion rec) throws IOException {
+ }
+
+ /**
+ * Add an answer if it is not suppressed.
+ *
+ * @param rec
+ * @exception IOException
+ */
+ public void addAnswer(DNSRecord rec) throws IOException {
+ this.addAnswer(rec, 0);
+ }
+
+ /**
+ * Add an answer to the message.
+ *
+ * @param rec
+ * @param now
+ * @exception IOException
+ */
+ public void addAnswer(DNSRecord rec, long now) throws IOException {
+ if (rec != null) {
+ if ((now == 0) || !rec.isExpired(now)) {
MessageOutputStream record = new MessageOutputStream(512, this);
- record.writeQuestion(rec);
+ record.writeRecord(rec, now);
byte[] byteArray = record.toByteArray();
record.close();
if (byteArray.length < this.availableSpace()) {
- _questions.add(rec);
- _questionsBytes.write(byteArray, 0, byteArray.length);
+ _answers.add(rec);
+ _answersBytes.write(byteArray, 0, byteArray.length);
} else {
- throw new IOException("message full");
+ throw new IOException("message full");
}
+ }
}
-
- /**
- * Add an answer if it is not suppressed.
- *
- * @param rec
- * @exception IOException
- */
- public void addAnswer(DNSRecord rec) throws IOException {
- this.addAnswer(rec, 0);
+ }
+
+ /**
+ * Builds the final message buffer to be send and returns it.
+ *
+ * @return bytes to send.
+ */
+ public byte[] data() {
+ long now = System.currentTimeMillis(); // System.currentTimeMillis()
+ _names.clear();
+
+ MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
+ message.writeShort(_multicast ? 0 : this.getId());
+ message.writeShort(this.getFlags());
+ message.writeShort(this.getNumberOfQuestions());
+ message.writeShort(this.getNumberOfAnswers());
+ message.writeShort(this.getNumberOfAuthorities());
+ message.writeShort(this.getNumberOfAdditionals());
+ for (DNSQuestion question : _questions) {
+ message.writeQuestion(question);
}
-
- /**
- * Add an answer to the message.
- *
- * @param rec
- * @param now
- * @exception IOException
- */
- public void addAnswer(DNSRecord rec, long now) throws IOException {
- if (rec != null) {
- if ((now == 0) || !rec.isExpired(now)) {
- MessageOutputStream record = new MessageOutputStream(512, this);
- record.writeRecord(rec, now);
- byte[] byteArray = record.toByteArray();
- record.close();
- if (byteArray.length < this.availableSpace()) {
- _answers.add(rec);
- _answersBytes.write(byteArray, 0, byteArray.length);
- } else {
- throw new IOException("message full");
- }
- }
- }
+ for (DNSRecord record : _answers) {
+ message.writeRecord(record, now);
}
-
- /**
- * Builds the final message buffer to be send and returns it.
- *
- * @return bytes to send.
- */
- public byte[] data() {
- long now = System.currentTimeMillis(); // System.currentTimeMillis()
- _names.clear();
-
- MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
- message.writeShort(_multicast ? 0 : this.getId());
- message.writeShort(this.getFlags());
- message.writeShort(this.getNumberOfQuestions());
- message.writeShort(this.getNumberOfAnswers());
- message.writeShort(this.getNumberOfAuthorities());
- message.writeShort(this.getNumberOfAdditionals());
- for (DNSQuestion question : _questions) {
- message.writeQuestion(question);
- }
- for (DNSRecord record : _answers) {
- message.writeRecord(record, now);
- }
- for (DNSRecord record : _authoritativeAnswers) {
- message.writeRecord(record, now);
- }
- for (DNSRecord record : _additionals) {
- message.writeRecord(record, now);
- }
- byte[] result = message.toByteArray();
- try {
- message.close();
- } catch (IOException exception) {}
- return result;
+ for (DNSRecord record : _authoritativeAnswers) {
+ message.writeRecord(record, now);
}
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- sb.append(isQuery() ? "dns[query:" : "dns[response:");
- sb.append(" id=0x");
- sb.append(Integer.toHexString(this.getId()));
- if (this.getFlags() != 0) {
- sb.append(", flags=0x");
- sb.append(Integer.toHexString(this.getFlags()));
- if (this.isResponse()) {
- sb.append(":r");
- }
- if (this.isAuthoritativeAnswer()) {
- sb.append(":aa");
- }
- if (this.isTruncated()) {
- sb.append(":tc");
- }
- }
- if (this.getNumberOfQuestions() > 0) {
- sb.append(", questions=");
- sb.append(this.getNumberOfQuestions());
- }
- if (this.getNumberOfAnswers() > 0) {
- sb.append(", answers=");
- sb.append(this.getNumberOfAnswers());
- }
- if (this.getNumberOfAuthorities() > 0) {
- sb.append(", authorities=");
- sb.append(this.getNumberOfAuthorities());
- }
- if (this.getNumberOfAdditionals() > 0) {
- sb.append(", additionals=");
- sb.append(this.getNumberOfAdditionals());
- }
- if (this.getNumberOfQuestions() > 0) {
- sb.append("\nquestions:");
- for (DNSQuestion question : _questions) {
- sb.append("\n\t");
- sb.append(question);
- }
- }
- if (this.getNumberOfAnswers() > 0) {
- sb.append("\nanswers:");
- for (DNSRecord record : _answers) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- if (this.getNumberOfAuthorities() > 0) {
- sb.append("\nauthorities:");
- for (DNSRecord record : _authoritativeAnswers) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- if (this.getNumberOfAdditionals() > 0) {
- sb.append("\nadditionals:");
- for (DNSRecord record : _additionals) {
- sb.append("\n\t");
- sb.append(record);
- }
- }
- sb.append("\nnames=");
- sb.append(_names);
- sb.append("]");
- return sb.toString();
+ for (DNSRecord record : _additionals) {
+ message.writeRecord(record, now);
}
-
- /**
- * @return the maxUDPPayload
- */
- public int getMaxUDPPayload() {
- return this._maxUDPPayload;
+ byte[] result = message.toByteArray();
+ try {
+ message.close();
+ } catch (IOException exception) {
}
-
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(isQuery() ? "dns[query:" : "dns[response:");
+ sb.append(" id=0x");
+ sb.append(Integer.toHexString(this.getId()));
+ if (this.getFlags() != 0) {
+ sb.append(", flags=0x");
+ sb.append(Integer.toHexString(this.getFlags()));
+ if (this.isResponse()) {
+ sb.append(":r");
+ }
+ if (this.isAuthoritativeAnswer()) {
+ sb.append(":aa");
+ }
+ if (this.isTruncated()) {
+ sb.append(":tc");
+ }
+ }
+ if (this.getNumberOfQuestions() > 0) {
+ sb.append(", questions=");
+ sb.append(this.getNumberOfQuestions());
+ }
+ if (this.getNumberOfAnswers() > 0) {
+ sb.append(", answers=");
+ sb.append(this.getNumberOfAnswers());
+ }
+ if (this.getNumberOfAuthorities() > 0) {
+ sb.append(", authorities=");
+ sb.append(this.getNumberOfAuthorities());
+ }
+ if (this.getNumberOfAdditionals() > 0) {
+ sb.append(", additionals=");
+ sb.append(this.getNumberOfAdditionals());
+ }
+ if (this.getNumberOfQuestions() > 0) {
+ sb.append("\nquestions:");
+ for (DNSQuestion question : _questions) {
+ sb.append("\n\t");
+ sb.append(question);
+ }
+ }
+ if (this.getNumberOfAnswers() > 0) {
+ sb.append("\nanswers:");
+ for (DNSRecord record : _answers) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ if (this.getNumberOfAuthorities() > 0) {
+ sb.append("\nauthorities:");
+ for (DNSRecord record : _authoritativeAnswers) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ if (this.getNumberOfAdditionals() > 0) {
+ sb.append("\nadditionals:");
+ for (DNSRecord record : _additionals) {
+ sb.append("\n\t");
+ sb.append(record);
+ }
+ }
+ sb.append("\nnames=");
+ sb.append(_names);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * @return the maxUDPPayload
+ */
+ public int getMaxUDPPayload() {
+ return this._maxUDPPayload;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSQuestion.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSQuestion.java
index 789aaf418..ed87bb56f 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSQuestion.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSQuestion.java
@@ -4,87 +4,89 @@
package io.libp2p.discovery.mdns.impl;
-import java.util.Set;
-
+import io.libp2p.discovery.mdns.ServiceInfo;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
+import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import io.libp2p.discovery.mdns.ServiceInfo;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
/**
* A DNS question.
*
* @author Arthur van Hoff, Pierre Frisch
*/
public class DNSQuestion extends DNSEntry {
- private static Logger logger = LoggerFactory.getLogger(DNSQuestion.class.getName());
-
- /**
- * Pointer question.
- */
- private static class Pointer extends DNSQuestion {
- Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
- super(name, type, recordClass, unique);
- }
+ private static Logger logger = LoggerFactory.getLogger(DNSQuestion.class.getName());
- @Override
- public void addAnswers(JmDNSImpl jmDNSImpl, Set answers) {
- // find matching services
- for (ServiceInfo serviceInfo : jmDNSImpl.getServices().values()) {
- this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) serviceInfo);
- }
- }
+ /** Pointer question. */
+ private static class Pointer extends DNSQuestion {
+ Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
+ super(name, type, recordClass, unique);
}
- DNSQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
- super(name, type, recordClass, unique);
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set answers) {
+ // find matching services
+ for (ServiceInfo serviceInfo : jmDNSImpl.getServices().values()) {
+ this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) serviceInfo);
+ }
}
+ }
- /**
- * Create a question.
- *
- * @param name
- * DNS name to be resolved
- * @param type
- * Record type to resolve
- * @param recordClass
- * Record class to resolve
- * @param unique
- * Request unicast response (Currently not supported in this implementation)
- * @return new question
- */
- public static DNSQuestion newQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
- return (type == DNSRecordType.TYPE_PTR)
- ? new Pointer(name, type, recordClass, unique)
- : null;
- }
+ DNSQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
+ super(name, type, recordClass, unique);
+ }
- /**
- * Adds answers to the list for our question.
- *
- * @param jmDNSImpl
- * DNS holding the records
- * @param answers
- * List of previous answer to append.
- */
- public void addAnswers(JmDNSImpl jmDNSImpl, Set answers) {
- // By default we do nothing
- }
+ /**
+ * Create a question.
+ *
+ * @param name DNS name to be resolved
+ * @param type Record type to resolve
+ * @param recordClass Record class to resolve
+ * @param unique Request unicast response (Currently not supported in this implementation)
+ * @return new question
+ */
+ public static DNSQuestion newQuestion(
+ String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
+ return (type == DNSRecordType.TYPE_PTR) ? new Pointer(name, type, recordClass, unique) : null;
+ }
- protected void addAnswersForServiceInfo(JmDNSImpl jmDNSImpl, Set answers, ServiceInfoImpl info) {
- if (info != null) {
- if (this.getName().equalsIgnoreCase(info.getQualifiedName()) || this.getName().equalsIgnoreCase(info.getType()) || this.getName().equalsIgnoreCase(info.getTypeWithSubtype())) {
- answers.addAll(info.answers(this.getRecordClass(), DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, jmDNSImpl.getLocalHost()));
- }
- logger.debug("{} DNSQuestion({}).addAnswersForServiceInfo(): info: {}\n{}", jmDNSImpl.getName(), this.getName(), info, answers);
- }
- }
+ /**
+ * Adds answers to the list for our question.
+ *
+ * @param jmDNSImpl DNS holding the records
+ * @param answers List of previous answer to append.
+ */
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set answers) {
+ // By default we do nothing
+ }
- @Override
- public boolean isExpired(long now) {
- return false;
+ protected void addAnswersForServiceInfo(
+ JmDNSImpl jmDNSImpl, Set answers, ServiceInfoImpl info) {
+ if (info != null) {
+ if (this.getName().equalsIgnoreCase(info.getQualifiedName())
+ || this.getName().equalsIgnoreCase(info.getType())
+ || this.getName().equalsIgnoreCase(info.getTypeWithSubtype())) {
+ answers.addAll(
+ info.answers(
+ this.getRecordClass(),
+ DNSRecordClass.UNIQUE,
+ DNSConstants.DNS_TTL,
+ jmDNSImpl.getLocalHost()));
+ }
+ logger.debug(
+ "{} DNSQuestion({}).addAnswersForServiceInfo(): info: {}\n{}",
+ jmDNSImpl.getName(),
+ this.getName(),
+ info,
+ answers);
}
-}
\ No newline at end of file
+ }
+
+ @Override
+ public boolean isExpired(long now) {
+ return false;
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSRecord.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSRecord.java
index 6e75a6c47..6201af584 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSRecord.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/DNSRecord.java
@@ -8,9 +8,6 @@
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
import io.libp2p.discovery.mdns.impl.util.ByteWrangler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -18,7 +15,8 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* DNS record
@@ -26,358 +24,378 @@
* @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch
*/
public abstract class DNSRecord extends DNSEntry {
- private static Logger logger = LoggerFactory.getLogger(DNSRecord.class.getName());
-
- private int _ttl;
- private long _created;
-
- /**
- * Create a DNSRecord with a name, type, class, and ttl.
- */
- DNSRecord(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl) {
- super(name, type, recordClass, unique);
- this._ttl = ttl;
- this._created = System.currentTimeMillis();
+ private static Logger logger = LoggerFactory.getLogger(DNSRecord.class.getName());
+
+ private int _ttl;
+ private long _created;
+
+ /** Create a DNSRecord with a name, type, class, and ttl. */
+ DNSRecord(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl) {
+ super(name, type, recordClass, unique);
+ this._ttl = ttl;
+ this._created = System.currentTimeMillis();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof DNSRecord) && super.equals(other) && sameValue((DNSRecord) other);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), _ttl, _created);
+ }
+
+ abstract boolean sameValue(DNSRecord other);
+
+ /** Get the expiration time of this record. */
+ long getExpirationTime(int percent) {
+ // ttl is in seconds the constant 10 is 1000 ms / 100 %
+ return _created + (percent * ((long) _ttl) * 10L);
+ }
+
+ /** Get the remaining TTL for this record. */
+ int getRemainingTTL(long now) {
+ return (int) Math.max(0, (getExpirationTime(100) - now) / 1000);
+ }
+
+ @Override
+ public boolean isExpired(long now) {
+ return getExpirationTime(100) <= now;
+ }
+
+ abstract void write(MessageOutputStream out);
+
+ public static class IPv4Address extends Address {
+ IPv4Address(
+ String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
+ super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, addr);
}
- @Override
- public boolean equals(Object other) {
- return (other instanceof DNSRecord) && super.equals(other) && sameValue((DNSRecord) other);
+ IPv4Address(
+ String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
+ super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, rawAddress);
}
@Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), _ttl, _created);
+ void write(MessageOutputStream out) {
+ if (_addr != null) {
+ byte[] buffer = _addr.getAddress();
+ // If we have a type A records we should answer with a IPv4 address
+ if (_addr instanceof Inet4Address) {
+ // All is good
+ } else {
+ // Get the last four bytes
+ byte[] tempbuffer = buffer;
+ buffer = new byte[4];
+ System.arraycopy(tempbuffer, 12, buffer, 0, 4);
+ }
+ int length = buffer.length;
+ out.writeBytes(buffer, 0, length);
+ }
}
+ }
- abstract boolean sameValue(DNSRecord other);
-
- /**
- * Get the expiration time of this record.
- */
- long getExpirationTime(int percent) {
- // ttl is in seconds the constant 10 is 1000 ms / 100 %
- return _created + (percent * ((long)_ttl) * 10L);
+ public static class IPv6Address extends Address {
+ IPv6Address(
+ String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
+ super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, addr);
}
- /**
- * Get the remaining TTL for this record.
- */
- int getRemainingTTL(long now) {
- return (int) Math.max(0, (getExpirationTime(100) - now) / 1000);
+ IPv6Address(
+ String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
+ super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, rawAddress);
}
@Override
- public boolean isExpired(long now) {
- return getExpirationTime(100) <= now;
- }
-
- abstract void write(MessageOutputStream out);
-
- public static class IPv4Address extends Address {
- IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
- super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, addr);
- }
-
- IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
- super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, rawAddress);
- }
-
- @Override
- void write(MessageOutputStream out) {
- if (_addr != null) {
- byte[] buffer = _addr.getAddress();
- // If we have a type A records we should answer with a IPv4 address
- if (_addr instanceof Inet4Address) {
- // All is good
- } else {
- // Get the last four bytes
- byte[] tempbuffer = buffer;
- buffer = new byte[4];
- System.arraycopy(tempbuffer, 12, buffer, 0, 4);
- }
- int length = buffer.length;
- out.writeBytes(buffer, 0, length);
+ void write(MessageOutputStream out) {
+ if (_addr != null) {
+ byte[] buffer = _addr.getAddress();
+ // If we have a type AAAA records we should answer with a IPv6 address
+ if (_addr instanceof Inet4Address) {
+ byte[] tempbuffer = buffer;
+ buffer = new byte[16];
+ for (int i = 0; i < 16; i++) {
+ if (i < 11) {
+ buffer[i] = tempbuffer[i - 12];
+ } else {
+ buffer[i] = 0;
}
+ }
}
+ int length = buffer.length;
+ out.writeBytes(buffer, 0, length);
+ }
}
-
- public static class IPv6Address extends Address {
- IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
- super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, addr);
- }
-
- IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
- super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, rawAddress);
- }
-
- @Override
- void write(MessageOutputStream out) {
- if (_addr != null) {
- byte[] buffer = _addr.getAddress();
- // If we have a type AAAA records we should answer with a IPv6 address
- if (_addr instanceof Inet4Address) {
- byte[] tempbuffer = buffer;
- buffer = new byte[16];
- for (int i = 0; i < 16; i++) {
- if (i < 11) {
- buffer[i] = tempbuffer[i - 12];
- } else {
- buffer[i] = 0;
- }
- }
- }
- int length = buffer.length;
- out.writeBytes(buffer, 0, length);
- }
- }
+ }
+
+ /** Address record. */
+ public abstract static class Address extends DNSRecord {
+ InetAddress _addr;
+
+ protected Address(
+ String name,
+ DNSRecordType type,
+ DNSRecordClass recordClass,
+ boolean unique,
+ int ttl,
+ InetAddress addr) {
+ super(name, type, recordClass, unique, ttl);
+ this._addr = addr;
}
- /**
- * Address record.
- */
- public static abstract class Address extends DNSRecord {
- InetAddress _addr;
-
- protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
- super(name, type, recordClass, unique, ttl);
- this._addr = addr;
- }
+ protected Address(
+ String name,
+ DNSRecordType type,
+ DNSRecordClass recordClass,
+ boolean unique,
+ int ttl,
+ byte[] rawAddress) {
+ super(name, type, recordClass, unique, ttl);
+ try {
+ this._addr = InetAddress.getByAddress(rawAddress);
+ } catch (UnknownHostException exception) {
+ logger.warn("Address() exception ", exception);
+ }
+ }
- protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
- super(name, type, recordClass, unique, ttl);
- try {
- this._addr = InetAddress.getByAddress(rawAddress);
- } catch (UnknownHostException exception) {
- logger.warn("Address() exception ", exception);
- }
+ @Override
+ boolean sameValue(DNSRecord other) {
+ try {
+ if (!(other instanceof Address)) {
+ return false;
}
-
- @Override
- boolean sameValue(DNSRecord other) {
- try {
- if (!(other instanceof Address)) {
- return false;
- }
- Address address = (Address) other;
- if ((this.getAddress() == null) && (address.getAddress() != null)) {
- return false;
- }
- return this.getAddress().equals(address.getAddress());
- } catch (Exception e) {
- logger.info("Failed to compare addresses of DNSRecords", e);
- return false;
- }
- }
-
- public InetAddress getAddress() {
- return _addr;
+ Address address = (Address) other;
+ if ((this.getAddress() == null) && (address.getAddress() != null)) {
+ return false;
}
+ return this.getAddress().equals(address.getAddress());
+ } catch (Exception e) {
+ logger.info("Failed to compare addresses of DNSRecords", e);
+ return false;
+ }
+ }
- /**
- * Creates a byte array representation of this record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
- */
- @Override
- protected void toByteArray(DataOutputStream dout) throws IOException {
- super.toByteArray(dout);
- byte[] buffer = this.getAddress().getAddress();
- for (int i = 0; i < buffer.length; i++) {
- dout.writeByte(buffer[i]);
- }
- }
+ public InetAddress getAddress() {
+ return _addr;
}
/**
- * Pointer record.
+ * Creates a byte array representation of this record. This is needed for tie-break tests
+ * according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
*/
- public static class Pointer extends DNSRecord {
- private final String _alias;
-
- public Pointer(String name, DNSRecordClass recordClass, boolean unique, int ttl, String alias) {
- super(name, DNSRecordType.TYPE_PTR, recordClass, unique, ttl);
- this._alias = alias;
- }
-
- @Override
- public boolean isSameEntry(DNSEntry entry) {
- return super.isSameEntry(entry) && (entry instanceof Pointer) && this.sameValue((Pointer) entry);
- }
+ @Override
+ protected void toByteArray(DataOutputStream dout) throws IOException {
+ super.toByteArray(dout);
+ byte[] buffer = this.getAddress().getAddress();
+ for (int i = 0; i < buffer.length; i++) {
+ dout.writeByte(buffer[i]);
+ }
+ }
+ }
- @Override
- void write(MessageOutputStream out) {
- out.writeName(_alias);
- }
+ /** Pointer record. */
+ public static class Pointer extends DNSRecord {
+ private final String _alias;
- @Override
- boolean sameValue(DNSRecord other) {
- if (!(other instanceof Pointer)) {
- return false;
- }
- Pointer pointer = (Pointer) other;
- if ((_alias == null) && (pointer._alias != null)) {
- return false;
- }
- return _alias.equals(pointer._alias);
- }
+ public Pointer(String name, DNSRecordClass recordClass, boolean unique, int ttl, String alias) {
+ super(name, DNSRecordType.TYPE_PTR, recordClass, unique, ttl);
+ this._alias = alias;
}
- public static class Text extends DNSRecord {
- private final byte[] _text;
+ @Override
+ public boolean isSameEntry(DNSEntry entry) {
+ return super.isSameEntry(entry)
+ && (entry instanceof Pointer)
+ && this.sameValue((Pointer) entry);
+ }
- public Text(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte text[]) {
- super(name, DNSRecordType.TYPE_TXT, recordClass, unique, ttl);
- this._text = (text != null && text.length > 0 ? text : ByteWrangler.EMPTY_TXT);
- }
+ @Override
+ void write(MessageOutputStream out) {
+ out.writeName(_alias);
+ }
- /**
- * @return the text
- */
- public byte[] getText() {
- return this._text;
- }
+ @Override
+ boolean sameValue(DNSRecord other) {
+ if (!(other instanceof Pointer)) {
+ return false;
+ }
+ Pointer pointer = (Pointer) other;
+ if ((_alias == null) && (pointer._alias != null)) {
+ return false;
+ }
+ return _alias.equals(pointer._alias);
+ }
+ }
- @Override
- void write(MessageOutputStream out) {
- out.writeBytes(_text, 0, _text.length);
- }
+ public static class Text extends DNSRecord {
+ private final byte[] _text;
- @Override
- boolean sameValue(DNSRecord other) {
- if (!(other instanceof Text)) {
- return false;
- }
- Text txt = (Text) other;
- if ((_text == null) && (txt._text != null)) {
- return false;
- }
- if (txt._text.length != _text.length) {
- return false;
- }
- for (int i = _text.length; i-- > 0;) {
- if (txt._text[i] != _text[i]) {
- return false;
- }
- }
- return true;
- }
+ public Text(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte text[]) {
+ super(name, DNSRecordType.TYPE_TXT, recordClass, unique, ttl);
+ this._text = (text != null && text.length > 0 ? text : ByteWrangler.EMPTY_TXT);
}
/**
- * Service record.
+ * @return the text
*/
- public static class Service extends DNSRecord {
- private final int _priority;
- private final int _weight;
- private final int _port;
- private final String _server;
-
- public Service(String name, DNSRecordClass recordClass, boolean unique, int ttl, int priority, int weight, int port, String server) {
- super(name, DNSRecordType.TYPE_SRV, recordClass, unique, ttl);
- this._priority = priority;
- this._weight = weight;
- this._port = port;
- this._server = server;
- }
+ public byte[] getText() {
+ return this._text;
+ }
- @Override
- void write(MessageOutputStream out) {
- out.writeShort(_priority);
- out.writeShort(_weight);
- out.writeShort(_port);
- if (DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
- out.writeName(_server);
- } else {
- // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length.
- out.writeUTF(_server, 0, _server.length());
+ @Override
+ void write(MessageOutputStream out) {
+ out.writeBytes(_text, 0, _text.length);
+ }
- // add a zero byte to the end just to be safe, this is the strange form
- // used by the BonjourConformanceTest
- out.writeByte(0);
- }
+ @Override
+ boolean sameValue(DNSRecord other) {
+ if (!(other instanceof Text)) {
+ return false;
+ }
+ Text txt = (Text) other;
+ if ((_text == null) && (txt._text != null)) {
+ return false;
+ }
+ if (txt._text.length != _text.length) {
+ return false;
+ }
+ for (int i = _text.length; i-- > 0; ) {
+ if (txt._text[i] != _text[i]) {
+ return false;
}
+ }
+ return true;
+ }
+ }
+
+ /** Service record. */
+ public static class Service extends DNSRecord {
+ private final int _priority;
+ private final int _weight;
+ private final int _port;
+ private final String _server;
+
+ public Service(
+ String name,
+ DNSRecordClass recordClass,
+ boolean unique,
+ int ttl,
+ int priority,
+ int weight,
+ int port,
+ String server) {
+ super(name, DNSRecordType.TYPE_SRV, recordClass, unique, ttl);
+ this._priority = priority;
+ this._weight = weight;
+ this._port = port;
+ this._server = server;
+ }
- @Override
- protected void toByteArray(DataOutputStream dout) throws IOException {
- super.toByteArray(dout);
- dout.writeShort(_priority);
- dout.writeShort(_weight);
- dout.writeShort(_port);
- try {
- dout.write(_server.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException exception) {
- /* UTF-8 is always present */
- }
- }
+ @Override
+ void write(MessageOutputStream out) {
+ out.writeShort(_priority);
+ out.writeShort(_weight);
+ out.writeShort(_port);
+ if (DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
+ out.writeName(_server);
+ } else {
+ // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed
+ // to start by a length.
+ out.writeUTF(_server, 0, _server.length());
+
+ // add a zero byte to the end just to be safe, this is the strange form
+ // used by the BonjourConformanceTest
+ out.writeByte(0);
+ }
+ }
- /**
- * @return the weight
- */
- public int getWeight() {
- return this._weight;
- }
+ @Override
+ protected void toByteArray(DataOutputStream dout) throws IOException {
+ super.toByteArray(dout);
+ dout.writeShort(_priority);
+ dout.writeShort(_weight);
+ dout.writeShort(_port);
+ try {
+ dout.write(_server.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException exception) {
+ /* UTF-8 is always present */
+ }
+ }
- /**
- * @return the port
- */
- public int getPort() {
- return this._port;
- }
+ /**
+ * @return the weight
+ */
+ public int getWeight() {
+ return this._weight;
+ }
- @Override
- boolean sameValue(DNSRecord other) {
- if (!(other instanceof Service)) {
- return false;
- }
- Service s = (Service) other;
- return (_priority == s._priority) && (_weight == s._weight) && (_port == s._port) && _server.equals(s._server);
- }
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return this._port;
}
- public static class HostInformation extends DNSRecord {
- String _os;
- String _cpu;
-
- /**
- * @param name
- * @param recordClass
- * @param unique
- * @param ttl
- * @param cpu
- * @param os
- */
- public HostInformation(String name, DNSRecordClass recordClass, boolean unique, int ttl, String cpu, String os) {
- super(name, DNSRecordType.TYPE_HINFO, recordClass, unique, ttl);
- _cpu = cpu;
- _os = os;
- }
+ @Override
+ boolean sameValue(DNSRecord other) {
+ if (!(other instanceof Service)) {
+ return false;
+ }
+ Service s = (Service) other;
+ return (_priority == s._priority)
+ && (_weight == s._weight)
+ && (_port == s._port)
+ && _server.equals(s._server);
+ }
+ }
- /*
- * (non-Javadoc)
- * @see javax.jmdns.impl.DNSRecord#sameValue(javax.jmdns.impl.DNSRecord)
- */
- @Override
- boolean sameValue(DNSRecord other) {
- if (!(other instanceof HostInformation)) {
- return false;
- }
- HostInformation hinfo = (HostInformation) other;
- if ((_cpu == null) && (hinfo._cpu != null)) {
- return false;
- }
- if ((_os == null) && (hinfo._os != null)) {
- return false;
- }
- return _cpu.equals(hinfo._cpu) && _os.equals(hinfo._os);
- }
+ public static class HostInformation extends DNSRecord {
+ String _os;
+ String _cpu;
- @Override
- void write(MessageOutputStream out) {
- String hostInfo = _cpu + " " + _os;
- out.writeUTF(hostInfo, 0, hostInfo.length());
- }
+ /**
+ * @param name
+ * @param recordClass
+ * @param unique
+ * @param ttl
+ * @param cpu
+ * @param os
+ */
+ public HostInformation(
+ String name, DNSRecordClass recordClass, boolean unique, int ttl, String cpu, String os) {
+ super(name, DNSRecordType.TYPE_HINFO, recordClass, unique, ttl);
+ _cpu = cpu;
+ _os = os;
}
- public int getTTL() {
- return _ttl;
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.DNSRecord#sameValue(javax.jmdns.impl.DNSRecord)
+ */
+ @Override
+ boolean sameValue(DNSRecord other) {
+ if (!(other instanceof HostInformation)) {
+ return false;
+ }
+ HostInformation hinfo = (HostInformation) other;
+ if ((_cpu == null) && (hinfo._cpu != null)) {
+ return false;
+ }
+ if ((_os == null) && (hinfo._os != null)) {
+ return false;
+ }
+ return _cpu.equals(hinfo._cpu) && _os.equals(hinfo._os);
+ }
+
+ @Override
+ void write(MessageOutputStream out) {
+ String hostInfo = _cpu + " " + _os;
+ out.writeUTF(hostInfo, 0, hostInfo.length());
}
+ }
+
+ public int getTTL() {
+ return _ttl;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/HostInfo.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/HostInfo.java
index c93d6d983..3db3ca6b6 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/HostInfo.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/HostInfo.java
@@ -7,7 +7,6 @@
import java.io.IOException;
import java.net.*;
import java.util.*;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,185 +16,192 @@
* @author Pierre Frisch, Werner Randelshofer
*/
public class HostInfo {
- private static Logger logger = LoggerFactory.getLogger(HostInfo.class.getName());
-
- protected String _name;
-
- protected InetAddress _address;
-
- protected NetworkInterface _interface;
-
- /**
- * @param address
- * IP address to bind
- * @param dns
- * JmDNS instance
- * @param jmdnsName
- * JmDNS name
- * @return new HostInfo
- */
- public static HostInfo newHostInfo(InetAddress address, JmDNSImpl dns, String jmdnsName) {
- HostInfo localhost = null;
- String aName = (jmdnsName != null ? jmdnsName : "");
- InetAddress addr = address;
- try {
- if (addr == null) {
- String ip = System.getProperty("net.mdns.interface");
- if (ip != null) {
- addr = InetAddress.getByName(ip);
- } else {
- addr = InetAddress.getLocalHost();
- if (addr.isLoopbackAddress()) {
- // Find local address that isn't a loopback address
- InetAddress[] addresses = getInetAddresses();
- if (addresses.length > 0) {
- addr = addresses[0];
- }
- }
- }
- if (addr.isLoopbackAddress()) {
- logger.warn("Could not find any address beside the loopback.");
- }
- }
- if (aName.length() == 0) {
- aName = addr.getHostName();
+ private static Logger logger = LoggerFactory.getLogger(HostInfo.class.getName());
+
+ protected String _name;
+
+ protected InetAddress _address;
+
+ protected NetworkInterface _interface;
+
+ /**
+ * @param address IP address to bind
+ * @param dns JmDNS instance
+ * @param jmdnsName JmDNS name
+ * @return new HostInfo
+ */
+ public static HostInfo newHostInfo(InetAddress address, JmDNSImpl dns, String jmdnsName) {
+ HostInfo localhost = null;
+ String aName = (jmdnsName != null ? jmdnsName : "");
+ InetAddress addr = address;
+ try {
+ if (addr == null) {
+ String ip = System.getProperty("net.mdns.interface");
+ if (ip != null) {
+ addr = InetAddress.getByName(ip);
+ } else {
+ addr = InetAddress.getLocalHost();
+ if (addr.isLoopbackAddress()) {
+ // Find local address that isn't a loopback address
+ InetAddress[] addresses = getInetAddresses();
+ if (addresses.length > 0) {
+ addr = addresses[0];
}
- if (aName.contains("in-addr.arpa") || (aName.equals(addr.getHostAddress()))) {
- aName = ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : addr.getHostAddress());
- }
- } catch (final IOException e) {
- logger.warn("Could not initialize the host network interface on " + address + "because of an error: " + e.getMessage(), e);
- // This is only used for running unit test on Debian / Ubuntu
- addr = loopbackAddress();
- aName = ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : "computer");
+ }
}
- // A host name with "." is illegal. so strip off everything and append .local.
- // We also need to be carefull that the .local may already be there
- int index = aName.indexOf(".local");
- if (index > 0) {
- aName = aName.substring(0, index);
+ if (addr.isLoopbackAddress()) {
+ logger.warn("Could not find any address beside the loopback.");
}
- aName = aName.replaceAll("[:%\\.]", "-");
- aName += ".local.";
- localhost = new HostInfo(addr, aName, dns);
- return localhost;
+ }
+ if (aName.length() == 0) {
+ aName = addr.getHostName();
+ }
+ if (aName.contains("in-addr.arpa") || (aName.equals(addr.getHostAddress()))) {
+ aName =
+ ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : addr.getHostAddress());
+ }
+ } catch (final IOException e) {
+ logger.warn(
+ "Could not initialize the host network interface on "
+ + address
+ + "because of an error: "
+ + e.getMessage(),
+ e);
+ // This is only used for running unit test on Debian / Ubuntu
+ addr = loopbackAddress();
+ aName = ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : "computer");
}
-
- private static InetAddress[] getInetAddresses() {
- Set result = new HashSet();
- try {
-
- for (Enumeration nifs = NetworkInterface.getNetworkInterfaces(); nifs.hasMoreElements();) {
- NetworkInterface nif = nifs.nextElement();
- if (useInterface(nif)) {
- for (Enumeration iaenum = nif.getInetAddresses(); iaenum.hasMoreElements();) {
- InetAddress interfaceAddress = iaenum.nextElement();
- logger.trace("Found NetworkInterface/InetAddress: {} -- {}", nif , interfaceAddress);
- result.add(interfaceAddress);
- }
- }
- }
- } catch (SocketException se) {
- logger.warn("Error while fetching network interfaces addresses: " + se);
- }
- return result.toArray(new InetAddress[result.size()]);
+ // A host name with "." is illegal. so strip off everything and append .local.
+ // We also need to be carefull that the .local may already be there
+ int index = aName.indexOf(".local");
+ if (index > 0) {
+ aName = aName.substring(0, index);
}
-
- private static boolean useInterface(NetworkInterface networkInterface) {
- try {
- if (!networkInterface.isUp()) {
- return false;
- }
-
- if (!networkInterface.supportsMulticast()) {
- return false;
- }
-
- if (networkInterface.isLoopback()) {
- return false;
- }
-
- return true;
- } catch (Exception exception) {
- return false;
+ aName = aName.replaceAll("[:%\\.]", "-");
+ aName += ".local.";
+ localhost = new HostInfo(addr, aName, dns);
+ return localhost;
+ }
+
+ private static InetAddress[] getInetAddresses() {
+ Set result = new HashSet();
+ try {
+
+ for (Enumeration nifs = NetworkInterface.getNetworkInterfaces();
+ nifs.hasMoreElements(); ) {
+ NetworkInterface nif = nifs.nextElement();
+ if (useInterface(nif)) {
+ for (Enumeration iaenum = nif.getInetAddresses();
+ iaenum.hasMoreElements(); ) {
+ InetAddress interfaceAddress = iaenum.nextElement();
+ logger.trace("Found NetworkInterface/InetAddress: {} -- {}", nif, interfaceAddress);
+ result.add(interfaceAddress);
+ }
}
+ }
+ } catch (SocketException se) {
+ logger.warn("Error while fetching network interfaces addresses: " + se);
}
-
- private static InetAddress loopbackAddress() {
- try {
- return InetAddress.getByName(null);
- } catch (UnknownHostException exception) {
- return null;
- }
+ return result.toArray(new InetAddress[result.size()]);
+ }
+
+ private static boolean useInterface(NetworkInterface networkInterface) {
+ try {
+ if (!networkInterface.isUp()) {
+ return false;
+ }
+
+ if (!networkInterface.supportsMulticast()) {
+ return false;
+ }
+
+ if (networkInterface.isLoopback()) {
+ return false;
+ }
+
+ return true;
+ } catch (Exception exception) {
+ return false;
}
+ }
- private HostInfo(final InetAddress address, final String name, final JmDNSImpl dns) {
- super();
- this._address = address;
- this._name = name;
- if (address != null) {
- try {
- _interface = NetworkInterface.getByInetAddress(address);
- } catch (Exception exception) {
- logger.warn("LocalHostInfo() exception ", exception);
- }
- }
+ private static InetAddress loopbackAddress() {
+ try {
+ return InetAddress.getByName(null);
+ } catch (UnknownHostException exception) {
+ return null;
}
-
- public String getName() {
- return _name;
+ }
+
+ private HostInfo(final InetAddress address, final String name, final JmDNSImpl dns) {
+ super();
+ this._address = address;
+ this._name = name;
+ if (address != null) {
+ try {
+ _interface = NetworkInterface.getByInetAddress(address);
+ } catch (Exception exception) {
+ logger.warn("LocalHostInfo() exception ", exception);
+ }
}
-
- public InetAddress getInetAddress() {
- return _address;
- }
-
- public NetworkInterface getInterface() {
- return _interface;
- }
-
- boolean shouldIgnorePacket(DatagramPacket packet) {
- boolean result = false;
- if (this.getInetAddress() != null) {
- InetAddress from = packet.getAddress();
- if (from != null) {
- if ((this.getInetAddress().isLinkLocalAddress() || this.getInetAddress().isMCLinkLocal()) && (!from.isLinkLocalAddress())) {
- // A host sending Multicast DNS queries to a link-local destination
- // address (including the 224.0.0.251 and FF02::FB link-local multicast
- // addresses) MUST only accept responses to that query that originate
- // from the local link, and silently discard any other response packets.
- // Without this check, it could be possible for remote rogue hosts to
- // send spoof answer packets (perhaps unicast to the victim host) which
- // the receiving machine could misinterpret as having originated on the
- // local link.
- result = true;
- }
- // if (from.isLinkLocalAddress() && (!this.getInetAddress().isLinkLocalAddress())) {
- // // Ignore linklocal packets on regular interfaces, unless this is
- // // also a linklocal interface. This is to avoid duplicates. This is
- // // a terrible hack caused by the lack of an API to get the address
- // // of the interface on which the packet was received.
- // result = true;
- // }
- if (from.isLoopbackAddress() && (!this.getInetAddress().isLoopbackAddress())) {
- // Ignore loopback packets on a regular interface unless this is also a loopback interface.
- result = true;
- }
- }
+ }
+
+ public String getName() {
+ return _name;
+ }
+
+ public InetAddress getInetAddress() {
+ return _address;
+ }
+
+ public NetworkInterface getInterface() {
+ return _interface;
+ }
+
+ boolean shouldIgnorePacket(DatagramPacket packet) {
+ boolean result = false;
+ if (this.getInetAddress() != null) {
+ InetAddress from = packet.getAddress();
+ if (from != null) {
+ if ((this.getInetAddress().isLinkLocalAddress() || this.getInetAddress().isMCLinkLocal())
+ && (!from.isLinkLocalAddress())) {
+ // A host sending Multicast DNS queries to a link-local destination
+ // address (including the 224.0.0.251 and FF02::FB link-local multicast
+ // addresses) MUST only accept responses to that query that originate
+ // from the local link, and silently discard any other response packets.
+ // Without this check, it could be possible for remote rogue hosts to
+ // send spoof answer packets (perhaps unicast to the victim host) which
+ // the receiving machine could misinterpret as having originated on the
+ // local link.
+ result = true;
}
- return result;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(1024);
- sb.append("local host info[");
- sb.append(getName() != null ? getName() : "no name");
- sb.append(", ");
- sb.append(getInterface() != null ? getInterface().getDisplayName() : "???");
- sb.append(":");
- sb.append(getInetAddress() != null ? getInetAddress().getHostAddress() : "no address");
- sb.append("]");
- return sb.toString();
+ // if (from.isLinkLocalAddress() && (!this.getInetAddress().isLinkLocalAddress())) {
+ // // Ignore linklocal packets on regular interfaces, unless this is
+ // // also a linklocal interface. This is to avoid duplicates. This is
+ // // a terrible hack caused by the lack of an API to get the address
+ // // of the interface on which the packet was received.
+ // result = true;
+ // }
+ if (from.isLoopbackAddress() && (!this.getInetAddress().isLoopbackAddress())) {
+ // Ignore loopback packets on a regular interface unless this is also a loopback
+ // interface.
+ result = true;
+ }
+ }
}
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(1024);
+ sb.append("local host info[");
+ sb.append(getName() != null ? getName() : "no name");
+ sb.append(", ");
+ sb.append(getInterface() != null ? getInterface().getDisplayName() : "???");
+ sb.append(":");
+ sb.append(getInetAddress() != null ? getInetAddress().getHostAddress() : "no address");
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/JmDNSImpl.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/JmDNSImpl.java
index d050053c4..82d9e4b13 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/JmDNSImpl.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/JmDNSImpl.java
@@ -12,9 +12,6 @@
import io.libp2p.discovery.mdns.impl.tasks.Responder;
import io.libp2p.discovery.mdns.impl.tasks.ServiceResolver;
import io.libp2p.discovery.mdns.impl.util.NamedThreadFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.Inet6Address;
@@ -39,431 +36,426 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Derived from mDNS implementation in Java.
*
- * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Scott Lewis, Kai Kreuzer, Victor Toni
+ * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Scott
+ * Lewis, Kai Kreuzer, Victor Toni
*/
public class JmDNSImpl extends JmDNS {
- private static Logger logger = LoggerFactory.getLogger(JmDNSImpl.class.getName());
-
- /**
- * This is the multicast group, we are listening to for multicast DNS messages.
- */
- private volatile InetAddress _group;
- /**
- * This is our multicast socket.
- */
- private volatile MulticastSocket _socket;
-
- private final ConcurrentMap> _answerListeners;
- private final ConcurrentMap _serviceResolvers;
-
- /**
- * This hashtable holds the services that have been registered. Keys are instances of String which hold an all lower-case version of the fully qualified service name. Values are instances of ServiceInfo.
- */
- private final ConcurrentMap _services;
-
- /**
- * Handle on the local host
- */
- private HostInfo _localHost;
-
- private SocketListener _incomingListener;
-
- private final ExecutorService _executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("JmDNS"));
-
- /**
- * The source for random values. This is used to introduce random delays in responses. This reduces the potential for collisions on the network.
- */
- private final static Random _random = new Random();
-
- /**
- * This lock is used to coordinate processing of incoming and outgoing messages. This is needed, because the Rendezvous Conformance Test does not forgive race conditions.
- */
- private final ReentrantLock _ioLock = new ReentrantLock();
-
- private final String _name;
-
- /**
- * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
- *
- * @param address IP address to bind to.
- * @param name name of the newly created JmDNS
- * @throws IOException
- */
- public JmDNSImpl(InetAddress address, String name) {
- super();
- logger.debug("JmDNS instance created");
-
- _answerListeners = new ConcurrentHashMap<>();
- _serviceResolvers = new ConcurrentHashMap<>();
-
- _services = new ConcurrentHashMap<>(20);
-
- _localHost = HostInfo.newHostInfo(address, this, name);
- _name = (name != null ? name : _localHost.getName());
- }
-
- public void start() throws IOException {
- // Bind to multicast socket
- this.openMulticastSocket(this.getLocalHost());
- this.start(this.getServices().values());
+ private static Logger logger = LoggerFactory.getLogger(JmDNSImpl.class.getName());
+
+ /** This is the multicast group, we are listening to for multicast DNS messages. */
+ private volatile InetAddress _group;
+
+ /** This is our multicast socket. */
+ private volatile MulticastSocket _socket;
+
+ private final ConcurrentMap> _answerListeners;
+ private final ConcurrentMap _serviceResolvers;
+
+ /**
+ * This hashtable holds the services that have been registered. Keys are instances of String which
+ * hold an all lower-case version of the fully qualified service name. Values are instances of
+ * ServiceInfo.
+ */
+ private final ConcurrentMap _services;
+
+ /** Handle on the local host */
+ private HostInfo _localHost;
+
+ private SocketListener _incomingListener;
+
+ private final ExecutorService _executor =
+ Executors.newSingleThreadExecutor(new NamedThreadFactory("JmDNS"));
+
+ /**
+ * The source for random values. This is used to introduce random delays in responses. This
+ * reduces the potential for collisions on the network.
+ */
+ private static final Random _random = new Random();
+
+ /**
+ * This lock is used to coordinate processing of incoming and outgoing messages. This is needed,
+ * because the Rendezvous Conformance Test does not forgive race conditions.
+ */
+ private final ReentrantLock _ioLock = new ReentrantLock();
+
+ private final String _name;
+
+ /**
+ * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
+ *
+ * @param address IP address to bind to.
+ * @param name name of the newly created JmDNS
+ * @throws IOException
+ */
+ public JmDNSImpl(InetAddress address, String name) {
+ super();
+ logger.debug("JmDNS instance created");
+
+ _answerListeners = new ConcurrentHashMap<>();
+ _serviceResolvers = new ConcurrentHashMap<>();
+
+ _services = new ConcurrentHashMap<>(20);
+
+ _localHost = HostInfo.newHostInfo(address, this, name);
+ _name = (name != null ? name : _localHost.getName());
+ }
+
+ public void start() throws IOException {
+ // Bind to multicast socket
+ this.openMulticastSocket(this.getLocalHost());
+ this.start(this.getServices().values());
+ }
+
+ private void start(Collection extends ServiceInfo> serviceInfos) {
+ if (_incomingListener == null) {
+ _incomingListener = new SocketListener(this);
+ _incomingListener.start();
}
-
- private void start(Collection extends ServiceInfo> serviceInfos) {
- if (_incomingListener == null) {
- _incomingListener = new SocketListener(this);
- _incomingListener.start();
- }
- for (ServiceInfo info : serviceInfos) {
- try {
- this.registerService(new ServiceInfoImpl(info));
- } catch (final Exception exception) {
- logger.warn("start() Registration exception ", exception);
- }
- }
- }
-
- private void openMulticastSocket(HostInfo hostInfo) throws IOException {
- if (_group == null) {
- if (hostInfo.getInetAddress() instanceof Inet6Address) {
- _group = InetAddress.getByName(DNSConstants.MDNS_GROUP_IPV6);
- } else {
- _group = InetAddress.getByName(DNSConstants.MDNS_GROUP);
- }
- }
- if (_socket != null) {
- this.closeMulticastSocket();
- }
- // SocketAddress address = new InetSocketAddress((hostInfo != null ? hostInfo.getInetAddress() : null), DNSConstants.MDNS_PORT);
- // System.out.println("Socket Address: " + address);
- // try {
- // _socket = new MulticastSocket(address);
- // } catch (Exception exception) {
- // logger.warn("openMulticastSocket() Open socket exception Address: " + address + ", ", exception);
- // // The most likely cause is a duplicate address lets open without specifying the address
- // _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
- // }
- _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
- if ((hostInfo != null) && (hostInfo.getInterface() != null)) {
- final SocketAddress multicastAddr = new InetSocketAddress(_group, DNSConstants.MDNS_PORT);
- _socket.setNetworkInterface(hostInfo.getInterface());
-
- logger.trace("Trying to joinGroup({}, {})", multicastAddr, hostInfo.getInterface());
-
- // this joinGroup() might be less surprisingly so this is the default
- _socket.joinGroup(multicastAddr, hostInfo.getInterface());
- } else {
- logger.trace("Trying to joinGroup({})", _group);
- _socket.joinGroup(_group);
- }
-
- _socket.setTimeToLive(255);
+ for (ServiceInfo info : serviceInfos) {
+ try {
+ this.registerService(new ServiceInfoImpl(info));
+ } catch (final Exception exception) {
+ logger.warn("start() Registration exception ", exception);
+ }
}
-
- private void closeMulticastSocket() {
- // jP: 20010-01-18. See below. We'll need this monitor...
- // assert (Thread.holdsLock(this));
- logger.debug("closeMulticastSocket()");
- if (_socket != null) {
- // close socket
- try {
- try {
- _socket.leaveGroup(_group);
- } catch (SocketException exception) {
- //
- }
- _socket.close();
- } catch (final Exception exception) {
- logger.warn("closeMulticastSocket() Close socket exception ", exception);
- }
- _socket = null;
- }
+ }
+
+ private void openMulticastSocket(HostInfo hostInfo) throws IOException {
+ if (_group == null) {
+ if (hostInfo.getInetAddress() instanceof Inet6Address) {
+ _group = InetAddress.getByName(DNSConstants.MDNS_GROUP_IPV6);
+ } else {
+ _group = InetAddress.getByName(DNSConstants.MDNS_GROUP);
+ }
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getName() {
- return _name;
+ if (_socket != null) {
+ this.closeMulticastSocket();
}
-
- /**
- * Returns the local host info
- *
- * @return local host info
- */
- public HostInfo getLocalHost() {
- return _localHost;
+ // SocketAddress address = new InetSocketAddress((hostInfo != null ? hostInfo.getInetAddress() :
+ // null), DNSConstants.MDNS_PORT);
+ // System.out.println("Socket Address: " + address);
+ // try {
+ // _socket = new MulticastSocket(address);
+ // } catch (Exception exception) {
+ // logger.warn("openMulticastSocket() Open socket exception Address: " + address + ", ",
+ // exception);
+ // // The most likely cause is a duplicate address lets open without specifying the address
+ // _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
+ // }
+ _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
+ if ((hostInfo != null) && (hostInfo.getInterface() != null)) {
+ final SocketAddress multicastAddr = new InetSocketAddress(_group, DNSConstants.MDNS_PORT);
+ _socket.setNetworkInterface(hostInfo.getInterface());
+
+ logger.trace("Trying to joinGroup({}, {})", multicastAddr, hostInfo.getInterface());
+
+ // this joinGroup() might be less surprisingly so this is the default
+ _socket.joinGroup(multicastAddr, hostInfo.getInterface());
+ } else {
+ logger.trace("Trying to joinGroup({})", _group);
+ _socket.joinGroup(_group);
}
- void handleServiceAnswers(List answers) {
- DNSRecord ptr = answers.get(0);
- if (!DNSRecordType.TYPE_PTR.equals(ptr.getRecordType()))
- return;
- List list = _answerListeners.get(ptr.getKey());
-
- if ((list != null) && (!list.isEmpty())) {
- final List listCopy;
- synchronized (list) {
- listCopy = new ArrayList<>(list);
- }
- for (final AnswerListener listener : listCopy) {
- _executor.submit(new Runnable() {
- @Override
- public void run() {
- listener.answersReceived(answers);
- }
- });
- }
- }
- }
+ _socket.setTimeToLive(255);
+ }
- @Override
- public void addAnswerListener(String type, int queryInterval, AnswerListener listener) {
- final String loType = type.toLowerCase();
- List list = _answerListeners.get(loType);
- if (list == null) {
- _answerListeners.putIfAbsent(loType, new LinkedList<>());
- list = _answerListeners.get(loType);
- }
- if (list != null) {
- synchronized (list) {
- if (!list.contains(listener)) {
- list.add(listener);
- }
- }
+ private void closeMulticastSocket() {
+ // jP: 20010-01-18. See below. We'll need this monitor...
+ // assert (Thread.holdsLock(this));
+ logger.debug("closeMulticastSocket()");
+ if (_socket != null) {
+ // close socket
+ try {
+ try {
+ _socket.leaveGroup(_group);
+ } catch (SocketException exception) {
+ //
}
-
- startServiceResolver(loType, queryInterval);
+ _socket.close();
+ } catch (final Exception exception) {
+ logger.warn("closeMulticastSocket() Close socket exception ", exception);
+ }
+ _socket = null;
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void registerService(ServiceInfo infoAbstract) throws IOException {
- final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
-
- info.setServer(_localHost.getName());
-
- _services.putIfAbsent(info.getKey(), info);
-
- logger.debug("registerService() JmDNS registered service as {}", info);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * Returns the local host info
+ *
+ * @return local host info
+ */
+ public HostInfo getLocalHost() {
+ return _localHost;
+ }
+
+ void handleServiceAnswers(List answers) {
+ DNSRecord ptr = answers.get(0);
+ if (!DNSRecordType.TYPE_PTR.equals(ptr.getRecordType())) return;
+ List list = _answerListeners.get(ptr.getKey());
+
+ if ((list != null) && (!list.isEmpty())) {
+ final List listCopy;
+ synchronized (list) {
+ listCopy = new ArrayList<>(list);
+ }
+ for (final AnswerListener listener : listCopy) {
+ _executor.submit(
+ new Runnable() {
+ @Override
+ public void run() {
+ listener.answersReceived(answers);
+ }
+ });
+ }
}
-
- /**
- * Handle an incoming response. Cache answers, and pass them on to the appropriate questions.
- *
- * @throws IOException
- */
- void handleResponse(DNSIncoming msg) throws IOException {
- List allAnswers = msg.getAllAnswers();
- allAnswers = aRecordsLast(allAnswers);
-
- handleServiceAnswers(allAnswers);
+ }
+
+ @Override
+ public void addAnswerListener(String type, int queryInterval, AnswerListener listener) {
+ final String loType = type.toLowerCase();
+ List list = _answerListeners.get(loType);
+ if (list == null) {
+ _answerListeners.putIfAbsent(loType, new LinkedList<>());
+ list = _answerListeners.get(loType);
}
-
- /**
- * In case the a record is received before the srv record the ip address would not be set.
- *
- * Multicast Domain Name System (response)
- * Transaction ID: 0x0000
- * Flags: 0x8400 Standard query response, No error
- * Questions: 0
- * Answer RRs: 2
- * Authority RRs: 0
- * Additional RRs: 8
- * Answers
- * _ibisip_http._tcp.local: type PTR, class IN, DeviceManagementService._ibisip_http._tcp.local
- * _ibisip_http._tcp.local: type PTR, class IN, PassengerCountingService._ibisip_http._tcp.local
- * Additional records
- * DeviceManagementService._ibisip_http._tcp.local: type TXT, class IN, cache flush
- * PassengerCountingService._ibisip_http._tcp.local: type TXT, class IN, cache flush
- * DIST500_7-F07_OC030_05_03941.local: type A, class IN, cache flush, addr 192.168.88.236
- * DeviceManagementService._ibisip_http._tcp.local: type SRV, class IN, cache flush, priority 0, weight 0, port 5000, target DIST500_7-F07_OC030_05_03941.local
- * PassengerCountingService._ibisip_http._tcp.local: type SRV, class IN, cache flush, priority 0, weight 0, port 5001, target DIST500_7-F07_OC030_05_03941.local
- * DeviceManagementService._ibisip_http._tcp.local: type NSEC, class IN, cache flush, next domain name DeviceManagementService._ibisip_http._tcp.local
- * PassengerCountingService._ibisip_http._tcp.local: type NSEC, class IN, cache flush, next domain name PassengerCountingService._ibisip_http._tcp.local
- * DIST500_7-F07_OC030_05_03941.local: type NSEC, class IN, cache flush, next domain name DIST500_7-F07_OC030_05_03941.local
- */
- private List aRecordsLast(List allAnswers) {
- ArrayList ret = new ArrayList(allAnswers.size());
- ArrayList arecords = new ArrayList();
-
- for (DNSRecord answer : allAnswers) {
- DNSRecordType type = answer.getRecordType();
- if (type.equals(DNSRecordType.TYPE_A) || type.equals(DNSRecordType.TYPE_AAAA)) {
- arecords.add(answer);
- } else if (type.equals(DNSRecordType.TYPE_PTR)) {
- ret.add(0, answer);
- } else {
- ret.add(answer);
- }
+ if (list != null) {
+ synchronized (list) {
+ if (!list.contains(listener)) {
+ list.add(listener);
}
- ret.addAll(arecords);
- return ret;
+ }
}
-
- /**
- * Handle an incoming query. See if we can answer any part of it given our service infos.
- *
- * @param in
- * @param addr
- * @param port
- * @throws IOException
- */
- void handleQuery(DNSIncoming in, InetAddress addr, int port) throws IOException {
- logger.debug("{} handle query: {}", this.getName(), in);
- this.ioLock();
- try {
- DNSIncoming plannedAnswer = in.clone();
- this.startResponder(plannedAnswer, addr, port);
- } finally {
- this.ioUnlock();
- }
+ startServiceResolver(loType, queryInterval);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void registerService(ServiceInfo infoAbstract) throws IOException {
+ final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
+
+ info.setServer(_localHost.getName());
+
+ _services.putIfAbsent(info.getKey(), info);
+
+ logger.debug("registerService() JmDNS registered service as {}", info);
+ }
+
+ /**
+ * Handle an incoming response. Cache answers, and pass them on to the appropriate questions.
+ *
+ * @throws IOException
+ */
+ void handleResponse(DNSIncoming msg) throws IOException {
+ List allAnswers = msg.getAllAnswers();
+ allAnswers = aRecordsLast(allAnswers);
+
+ handleServiceAnswers(allAnswers);
+ }
+
+ /**
+ * In case the a record is received before the srv record the ip address would not be set.
+ *
+ * Multicast Domain Name System (response) Transaction ID: 0x0000 Flags: 0x8400 Standard query
+ * response, No error Questions: 0 Answer RRs: 2 Authority RRs: 0 Additional RRs: 8 Answers
+ * _ibisip_http._tcp.local: type PTR, class IN, DeviceManagementService._ibisip_http._tcp.local
+ * _ibisip_http._tcp.local: type PTR, class IN, PassengerCountingService._ibisip_http._tcp.local
+ * Additional records DeviceManagementService._ibisip_http._tcp.local: type TXT, class IN, cache
+ * flush PassengerCountingService._ibisip_http._tcp.local: type TXT, class IN, cache flush
+ * DIST500_7-F07_OC030_05_03941.local: type A, class IN, cache flush, addr 192.168.88.236
+ * DeviceManagementService._ibisip_http._tcp.local: type SRV, class IN, cache flush, priority 0,
+ * weight 0, port 5000, target DIST500_7-F07_OC030_05_03941.local
+ * PassengerCountingService._ibisip_http._tcp.local: type SRV, class IN, cache flush, priority 0,
+ * weight 0, port 5001, target DIST500_7-F07_OC030_05_03941.local
+ * DeviceManagementService._ibisip_http._tcp.local: type NSEC, class IN, cache flush, next domain
+ * name DeviceManagementService._ibisip_http._tcp.local
+ * PassengerCountingService._ibisip_http._tcp.local: type NSEC, class IN, cache flush, next domain
+ * name PassengerCountingService._ibisip_http._tcp.local DIST500_7-F07_OC030_05_03941.local: type
+ * NSEC, class IN, cache flush, next domain name DIST500_7-F07_OC030_05_03941.local
+ */
+ private List aRecordsLast(List allAnswers) {
+ ArrayList ret = new ArrayList(allAnswers.size());
+ ArrayList arecords = new ArrayList();
+
+ for (DNSRecord answer : allAnswers) {
+ DNSRecordType type = answer.getRecordType();
+ if (type.equals(DNSRecordType.TYPE_A) || type.equals(DNSRecordType.TYPE_AAAA)) {
+ arecords.add(answer);
+ } else if (type.equals(DNSRecordType.TYPE_PTR)) {
+ ret.add(0, answer);
+ } else {
+ ret.add(answer);
+ }
}
-
- /**
- * Send an outgoing multicast DNS message.
- *
- * @param out
- * @throws IOException
- */
- public void send(DNSOutgoing out) throws IOException {
- if (!out.isEmpty()) {
- final InetAddress addr;
- final int port;
-
- if (out.getDestination() != null) {
- addr = out.getDestination().getAddress();
- port = out.getDestination().getPort();
- } else {
- addr = _group;
- port = DNSConstants.MDNS_PORT;
- }
-
- byte[] message = out.data();
- final DatagramPacket packet = new DatagramPacket(message, message.length, addr, port);
-
- if (logger.isTraceEnabled()) {
- try {
- final DNSIncoming msg = new DNSIncoming(packet);
- if (logger.isTraceEnabled()) {
- logger.trace("send({}) JmDNS out:{}", this.getName(), msg.print(true));
- }
- } catch (final IOException e) {
- logger.debug(getClass().toString(), ".send(" + this.getName() + ") - JmDNS can not parse what it sends!!!", e);
- }
- }
- final MulticastSocket ms = _socket;
- if (ms != null && !ms.isClosed()) {
- ms.send(packet);
- }
- }
+ ret.addAll(arecords);
+ return ret;
+ }
+
+ /**
+ * Handle an incoming query. See if we can answer any part of it given our service infos.
+ *
+ * @param in
+ * @param addr
+ * @param port
+ * @throws IOException
+ */
+ void handleQuery(DNSIncoming in, InetAddress addr, int port) throws IOException {
+ logger.debug("{} handle query: {}", this.getName(), in);
+ this.ioLock();
+ try {
+ DNSIncoming plannedAnswer = in.clone();
+ this.startResponder(plannedAnswer, addr, port);
+ } finally {
+ this.ioUnlock();
}
-
- private void startServiceResolver(String type, int queryInterval) {
- if (_serviceResolvers.containsKey(type))
- return;
-
- ServiceResolver resolver = new ServiceResolver(this, type, queryInterval);
- if (_serviceResolvers.putIfAbsent(type, resolver) == null)
- resolver.start();
+ }
+
+ /**
+ * Send an outgoing multicast DNS message.
+ *
+ * @param out
+ * @throws IOException
+ */
+ public void send(DNSOutgoing out) throws IOException {
+ if (!out.isEmpty()) {
+ final InetAddress addr;
+ final int port;
+
+ if (out.getDestination() != null) {
+ addr = out.getDestination().getAddress();
+ port = out.getDestination().getPort();
+ } else {
+ addr = _group;
+ port = DNSConstants.MDNS_PORT;
+ }
+
+ byte[] message = out.data();
+ final DatagramPacket packet = new DatagramPacket(message, message.length, addr, port);
+
+ if (logger.isTraceEnabled()) {
+ try {
+ final DNSIncoming msg = new DNSIncoming(packet);
+ if (logger.isTraceEnabled()) {
+ logger.trace("send({}) JmDNS out:{}", this.getName(), msg.print(true));
+ }
+ } catch (final IOException e) {
+ logger.debug(
+ getClass().toString(),
+ ".send(" + this.getName() + ") - JmDNS can not parse what it sends!!!",
+ e);
+ }
+ }
+ final MulticastSocket ms = _socket;
+ if (ms != null && !ms.isClosed()) {
+ ms.send(packet);
+ }
}
+ }
- private void startResponder(DNSIncoming in, InetAddress addr, int port) {
- new Responder(this, in, addr, port).start();
- }
+ private void startServiceResolver(String type, int queryInterval) {
+ if (_serviceResolvers.containsKey(type)) return;
- public void stop() {
- logger.debug("Stopping JmDNS: {}", this);
+ ServiceResolver resolver = new ServiceResolver(this, type, queryInterval);
+ if (_serviceResolvers.putIfAbsent(type, resolver) == null) resolver.start();
+ }
- List> shutdowns = new ArrayList<>();
+ private void startResponder(DNSIncoming in, InetAddress addr, int port) {
+ new Responder(this, in, addr, port).start();
+ }
- shutdowns.add(_incomingListener.stop());
- _incomingListener = null;
+ public void stop() {
+ logger.debug("Stopping JmDNS: {}", this);
- for (ServiceResolver resolver : _serviceResolvers.values())
- shutdowns.add(resolver.stop());
+ List> shutdowns = new ArrayList<>();
- // close socket
- this.closeMulticastSocket();
+ shutdowns.add(_incomingListener.stop());
+ _incomingListener = null;
- logger.debug("JmDNS waiting for service stop...");
+ for (ServiceResolver resolver : _serviceResolvers.values()) shutdowns.add(resolver.stop());
- for (Future shutdown : shutdowns) {
- try {
- shutdown.get(10, TimeUnit.SECONDS);
- } catch (CancellationException e) {
- logger.trace("Task was already cancelled", e);
- } catch (InterruptedException e) {
- logger.trace("Stopping was interrupted", e);
- Thread.currentThread().interrupt();
- } catch (ExecutionException | TimeoutException e) {
- logger.debug("Exception when stopping JmDNS: ", e);
- throw new RuntimeException(e);
- }
- }
+ // close socket
+ this.closeMulticastSocket();
- _executor.shutdown();
+ logger.debug("JmDNS waiting for service stop...");
- logger.debug("JmDNS stopped.");
+ for (Future shutdown : shutdowns) {
+ try {
+ shutdown.get(10, TimeUnit.SECONDS);
+ } catch (CancellationException e) {
+ logger.trace("Task was already cancelled", e);
+ } catch (InterruptedException e) {
+ logger.trace("Stopping was interrupted", e);
+ Thread.currentThread().interrupt();
+ } catch (ExecutionException | TimeoutException e) {
+ logger.debug("Exception when stopping JmDNS: ", e);
+ throw new RuntimeException(e);
+ }
}
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(2048);
- sb.append("\n");
- sb.append("\t---- Local Host -----");
- sb.append("\n\t");
- sb.append(_localHost);
- sb.append("\n\t---- Services -----");
- for (final Map.Entry entry : _services.entrySet()) {
- sb.append("\n\t\tService: ");
- sb.append(entry.getKey());
- sb.append(": ");
- sb.append(entry.getValue());
- }
- sb.append("\n");
- sb.append("\t---- Answer Listeners ----");
- for (final Map.Entry> entry : _answerListeners.entrySet()) {
- sb.append("\n\t\tAnswer Listener: ");
- sb.append(entry.getKey());
- sb.append(": ");
- sb.append(entry.getValue());
- }
- return sb.toString();
+ _executor.shutdown();
+
+ logger.debug("JmDNS stopped.");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(2048);
+ sb.append("\n");
+ sb.append("\t---- Local Host -----");
+ sb.append("\n\t");
+ sb.append(_localHost);
+ sb.append("\n\t---- Services -----");
+ for (final Map.Entry entry : _services.entrySet()) {
+ sb.append("\n\t\tService: ");
+ sb.append(entry.getKey());
+ sb.append(": ");
+ sb.append(entry.getValue());
}
-
- public Map getServices() {
- return _services;
+ sb.append("\n");
+ sb.append("\t---- Answer Listeners ----");
+ for (final Map.Entry> entry : _answerListeners.entrySet()) {
+ sb.append("\n\t\tAnswer Listener: ");
+ sb.append(entry.getKey());
+ sb.append(": ");
+ sb.append(entry.getValue());
}
+ return sb.toString();
+ }
- public static Random getRandom() {
- return _random;
- }
+ public Map getServices() {
+ return _services;
+ }
- private void ioLock() {
- _ioLock.lock();
- }
+ public static Random getRandom() {
+ return _random;
+ }
- private void ioUnlock() {
- _ioLock.unlock();
- }
+ private void ioLock() {
+ _ioLock.lock();
+ }
- public MulticastSocket getSocket() {
- return _socket;
- }
+ private void ioUnlock() {
+ _ioLock.unlock();
+ }
- public InetAddress getGroup() {
- return _group;
- }
+ public MulticastSocket getSocket() {
+ return _socket;
+ }
+
+ public InetAddress getGroup() {
+ return _group;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/ServiceInfoImpl.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/ServiceInfoImpl.java
index cc0c94dbf..54efc0ef7 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/ServiceInfoImpl.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/ServiceInfoImpl.java
@@ -7,9 +7,6 @@
import io.libp2p.discovery.mdns.ServiceInfo;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
import io.libp2p.discovery.mdns.impl.util.ByteWrangler;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -23,6 +20,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* JmDNS service information.
@@ -30,435 +29,488 @@
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Victor Toni
*/
public class ServiceInfoImpl extends ServiceInfo {
- private static Logger logger = LoggerFactory.getLogger(ServiceInfoImpl.class.getName());
-
- private String _domain;
- private String _protocol;
- private String _application;
- private String _name;
- private String _subtype;
- private String _server;
- private int _port;
- private int _weight;
- private int _priority;
- private byte[] _text;
- private final Set _ipv4Addresses;
- private final Set _ipv6Addresses;
-
- private transient String _key;
-
- /**
- * @param type
- * @param name
- * @param subtype
- * @param port
- * @param weight
- * @param priority
- * @param text
- */
- public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, String text) {
- this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, (byte[]) null);
-
- try {
- this._text = ByteWrangler.encodeText(text);
- } catch (final IOException e) {
- throw new RuntimeException("Unexpected exception: " + e);
+ private static Logger logger = LoggerFactory.getLogger(ServiceInfoImpl.class.getName());
+
+ private String _domain;
+ private String _protocol;
+ private String _application;
+ private String _name;
+ private String _subtype;
+ private String _server;
+ private int _port;
+ private int _weight;
+ private int _priority;
+ private byte[] _text;
+ private final Set _ipv4Addresses;
+ private final Set _ipv6Addresses;
+
+ private transient String _key;
+
+ /**
+ * @param type
+ * @param name
+ * @param subtype
+ * @param port
+ * @param weight
+ * @param priority
+ * @param text
+ */
+ public ServiceInfoImpl(
+ String type, String name, String subtype, int port, int weight, int priority, String text) {
+ this(
+ ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype),
+ port,
+ weight,
+ priority,
+ (byte[]) null);
+
+ try {
+ this._text = ByteWrangler.encodeText(text);
+ } catch (final IOException e) {
+ throw new RuntimeException("Unexpected exception: " + e);
+ }
+
+ _server = text;
+ }
+
+ ServiceInfoImpl(
+ Map qualifiedNameMap, int port, int weight, int priority, byte text[]) {
+ Map map = ServiceInfoImpl.checkQualifiedNameMap(qualifiedNameMap);
+
+ this._domain = map.get(Fields.Domain);
+ this._protocol = map.get(Fields.Protocol);
+ this._application = map.get(Fields.Application);
+ this._name = map.get(Fields.Instance);
+ this._subtype = map.get(Fields.Subtype);
+
+ this._port = port;
+ this._weight = weight;
+ this._priority = priority;
+ this._text = text;
+ this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet());
+ this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet());
+ }
+
+ ServiceInfoImpl(ServiceInfo info) {
+ this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet());
+ this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet());
+ if (info != null) {
+ this._domain = info.getDomain();
+ this._protocol = info.getProtocol();
+ this._application = info.getApplication();
+ this._name = info.getName();
+ this._subtype = info.getSubtype();
+ this._port = info.getPort();
+ this._weight = info.getWeight();
+ this._priority = info.getPriority();
+ this._text = info.getTextBytes();
+ Inet6Address[] ipv6Addresses = info.getInet6Addresses();
+ for (Inet6Address address : ipv6Addresses) {
+ this._ipv6Addresses.add(address);
+ }
+ Inet4Address[] ipv4Addresses = info.getInet4Addresses();
+ for (Inet4Address address : ipv4Addresses) {
+ this._ipv4Addresses.add(address);
+ }
+ }
+ }
+
+ public static Map decodeQualifiedNameMap(
+ String type, String name, String subtype) {
+ Map qualifiedNameMap = decodeQualifiedNameMapForType(type);
+
+ qualifiedNameMap.put(Fields.Instance, name);
+ qualifiedNameMap.put(Fields.Subtype, subtype);
+
+ return checkQualifiedNameMap(qualifiedNameMap);
+ }
+
+ public static Map decodeQualifiedNameMapForType(String type) {
+ int index;
+
+ String casePreservedType = type;
+
+ String aType = type.toLowerCase();
+ String application = aType;
+ String protocol = "";
+ String subtype = "";
+ String name = "";
+ String domain = "";
+
+ if (aType.contains("in-addr.arpa") || aType.contains("ip6.arpa")) {
+ index =
+ (aType.contains("in-addr.arpa")
+ ? aType.indexOf("in-addr.arpa")
+ : aType.indexOf("ip6.arpa"));
+ name = removeSeparators(casePreservedType.substring(0, index));
+ domain = casePreservedType.substring(index);
+ application = "";
+ } else if ((!aType.contains("_")) && aType.contains(".")) {
+ index = aType.indexOf('.');
+ name = removeSeparators(casePreservedType.substring(0, index));
+ domain = removeSeparators(casePreservedType.substring(index));
+ application = "";
+ } else {
+ // First remove the name if it there.
+ if (!aType.startsWith("_") || aType.startsWith("_services")) {
+ index = aType.indexOf("._");
+ if (index > 0) {
+ // We need to preserve the case for the user readable name.
+ name = casePreservedType.substring(0, index);
+ if (index + 1 < aType.length()) {
+ aType = aType.substring(index + 1);
+ casePreservedType = casePreservedType.substring(index + 1);
+ }
}
-
- _server = text;
- }
-
- ServiceInfoImpl(Map qualifiedNameMap, int port, int weight, int priority, byte text[]) {
- Map map = ServiceInfoImpl.checkQualifiedNameMap(qualifiedNameMap);
-
- this._domain = map.get(Fields.Domain);
- this._protocol = map.get(Fields.Protocol);
- this._application = map.get(Fields.Application);
- this._name = map.get(Fields.Instance);
- this._subtype = map.get(Fields.Subtype);
-
- this._port = port;
- this._weight = weight;
- this._priority = priority;
- this._text = text;
- this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet());
- this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet());
- }
-
- ServiceInfoImpl(ServiceInfo info) {
- this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet());
- this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet());
- if (info != null) {
- this._domain = info.getDomain();
- this._protocol = info.getProtocol();
- this._application = info.getApplication();
- this._name = info.getName();
- this._subtype = info.getSubtype();
- this._port = info.getPort();
- this._weight = info.getWeight();
- this._priority = info.getPriority();
- this._text = info.getTextBytes();
- Inet6Address[] ipv6Addresses = info.getInet6Addresses();
- for (Inet6Address address : ipv6Addresses) {
- this._ipv6Addresses.add(address);
- }
- Inet4Address[] ipv4Addresses = info.getInet4Addresses();
- for (Inet4Address address : ipv4Addresses) {
- this._ipv4Addresses.add(address);
- }
+ }
+
+ index = aType.lastIndexOf("._");
+ if (index > 0) {
+ int start = index + 2;
+ int end = aType.indexOf('.', start);
+ protocol = casePreservedType.substring(start, end);
+ }
+ if (protocol.length() > 0) {
+ index = aType.indexOf("_" + protocol.toLowerCase() + ".");
+ int start = index + protocol.length() + 2;
+ int end = aType.length() - (aType.endsWith(".") ? 1 : 0);
+ if (end > start) {
+ domain = casePreservedType.substring(start, end);
}
- }
-
- public static Map decodeQualifiedNameMap(String type, String name, String subtype) {
- Map qualifiedNameMap = decodeQualifiedNameMapForType(type);
-
- qualifiedNameMap.put(Fields.Instance, name);
- qualifiedNameMap.put(Fields.Subtype, subtype);
-
- return checkQualifiedNameMap(qualifiedNameMap);
- }
-
- public static Map decodeQualifiedNameMapForType(String type) {
- int index;
-
- String casePreservedType = type;
-
- String aType = type.toLowerCase();
- String application = aType;
- String protocol = "";
- String subtype = "";
- String name = "";
- String domain = "";
-
- if (aType.contains("in-addr.arpa") || aType.contains("ip6.arpa")) {
- index = (aType.contains("in-addr.arpa") ? aType.indexOf("in-addr.arpa") : aType.indexOf("ip6.arpa"));
- name = removeSeparators(casePreservedType.substring(0, index));
- domain = casePreservedType.substring(index);
- application = "";
- } else if ((!aType.contains("_")) && aType.contains(".")) {
- index = aType.indexOf('.');
- name = removeSeparators(casePreservedType.substring(0, index));
- domain = removeSeparators(casePreservedType.substring(index));
- application = "";
+ if (index > 0) {
+ application = casePreservedType.substring(0, index - 1);
} else {
- // First remove the name if it there.
- if (!aType.startsWith("_") || aType.startsWith("_services")) {
- index = aType.indexOf("._");
- if (index > 0) {
- // We need to preserve the case for the user readable name.
- name = casePreservedType.substring(0, index);
- if (index + 1 < aType.length()) {
- aType = aType.substring(index + 1);
- casePreservedType = casePreservedType.substring(index + 1);
- }
- }
- }
-
- index = aType.lastIndexOf("._");
- if (index > 0) {
- int start = index + 2;
- int end = aType.indexOf('.', start);
- protocol = casePreservedType.substring(start, end);
- }
- if (protocol.length() > 0) {
- index = aType.indexOf("_" + protocol.toLowerCase() + ".");
- int start = index + protocol.length() + 2;
- int end = aType.length() - (aType.endsWith(".") ? 1 : 0);
- if (end > start) {
- domain = casePreservedType.substring(start, end);
- }
- if (index > 0) {
- application = casePreservedType.substring(0, index - 1);
- } else {
- application = "";
- }
- }
- index = application.toLowerCase().indexOf("._sub");
- if (index > 0) {
- int start = index + 5;
- subtype = removeSeparators(application.substring(0, index));
- application = application.substring(start);
- }
- }
-
- final Map qualifiedNameMap = new HashMap(5);
- qualifiedNameMap.put(Fields.Domain, removeSeparators(domain));
- qualifiedNameMap.put(Fields.Protocol, protocol);
- qualifiedNameMap.put(Fields.Application, removeSeparators(application));
- qualifiedNameMap.put(Fields.Instance, name);
- qualifiedNameMap.put(Fields.Subtype, subtype);
-
- return qualifiedNameMap;
- }
-
- protected static Map checkQualifiedNameMap(Map qualifiedNameMap) {
- Map checkedQualifiedNameMap = new HashMap(5);
-
- // Optional domain
- String domain = (qualifiedNameMap.containsKey(Fields.Domain) ? qualifiedNameMap.get(Fields.Domain) : "local");
- if ((domain == null) || (domain.length() == 0)) {
- domain = "local";
- }
- domain = removeSeparators(domain);
- checkedQualifiedNameMap.put(Fields.Domain, domain);
- // Optional protocol
- String protocol = (qualifiedNameMap.containsKey(Fields.Protocol) ? qualifiedNameMap.get(Fields.Protocol) : "tcp");
- if ((protocol == null) || (protocol.length() == 0)) {
- protocol = "tcp";
- }
- protocol = removeSeparators(protocol);
- checkedQualifiedNameMap.put(Fields.Protocol, protocol);
- // Application
- String application = (qualifiedNameMap.containsKey(Fields.Application) ? qualifiedNameMap.get(Fields.Application) : "");
- if ((application == null) || (application.length() == 0)) {
- application = "";
+ application = "";
}
- application = removeSeparators(application);
- checkedQualifiedNameMap.put(Fields.Application, application);
- // Instance
- String instance = (qualifiedNameMap.containsKey(Fields.Instance) ? qualifiedNameMap.get(Fields.Instance) : "");
- if ((instance == null) || (instance.length() == 0)) {
- instance = "";
- // throw new IllegalArgumentException("The instance name component of a fully qualified service cannot be empty.");
- }
- instance = removeSeparators(instance);
- checkedQualifiedNameMap.put(Fields.Instance, instance);
- // Optional Subtype
- String subtype = (qualifiedNameMap.containsKey(Fields.Subtype) ? qualifiedNameMap.get(Fields.Subtype) : "");
- if ((subtype == null) || (subtype.length() == 0)) {
- subtype = "";
- }
- subtype = removeSeparators(subtype);
- checkedQualifiedNameMap.put(Fields.Subtype, subtype);
-
- return checkedQualifiedNameMap;
- }
-
- private static String removeSeparators(String name) {
- if (name == null) {
- return "";
- }
- String newName = name.trim();
- if (newName.startsWith(".")) {
- newName = newName.substring(1);
- }
- if (newName.startsWith("_")) {
- newName = newName.substring(1);
- }
- if (newName.endsWith(".")) {
- newName = newName.substring(0, newName.length() - 1);
- }
- return newName;
- }
-
- @Override
- public String getType() {
- String domain = this.getDomain();
- String protocol = this.getProtocol();
- String application = this.getApplication();
- return (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
- }
-
- @Override
- public String getTypeWithSubtype() {
- String subtype = this.getSubtype();
- return (subtype.length() > 0 ? "_" + subtype + "._sub." : "") + this.getType();
- }
-
- @Override
- public String getName() {
- return (_name != null ? _name : "");
- }
-
- @Override
- public String getKey() {
- if (this._key == null) {
- this._key = this.getQualifiedName().toLowerCase();
- }
- return this._key;
- }
-
- @Override
- public String getQualifiedName() {
- String domain = this.getDomain();
- String protocol = this.getProtocol();
- String application = this.getApplication();
- String instance = this.getName();
- return (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
- }
-
- @Override
- public String getServer() {
- return (_server != null ? _server : "");
- }
- void setServer(String server) {
- this._server = server;
- }
-
- public void addAddress(Inet4Address addr) {
- _ipv4Addresses.add(addr);
- }
- public void addAddress(Inet6Address addr) {
- _ipv6Addresses.add(addr);
- }
-
- @Override
- public Inet4Address[] getInet4Addresses() {
- return _ipv4Addresses.toArray(new Inet4Address[_ipv4Addresses.size()]);
- }
-
- @Override
- public Inet6Address[] getInet6Addresses() {
- return _ipv6Addresses.toArray(new Inet6Address[_ipv6Addresses.size()]);
- }
-
- @Override
- public int getPort() {
- return _port;
- }
-
- @Override
- public int getPriority() {
- return _priority;
- }
-
- @Override
- public int getWeight() {
- return _weight;
- }
-
- @Override
- public byte[] getTextBytes() {
- return (this._text != null && this._text.length > 0 ? this._text : ByteWrangler.EMPTY_TXT);
- }
-
- @Override
- public String getApplication() {
- return (_application != null ? _application : "");
- }
-
- @Override
- public String getDomain() {
- return (_domain != null ? _domain : "local");
- }
-
- @Override
- public String getProtocol() {
- return (_protocol != null ? _protocol : "tcp");
- }
-
- @Override
- public String getSubtype() {
- return (_subtype != null ? _subtype : "");
- }
-
- @Override
- public Map getQualifiedNameMap() {
- Map map = new HashMap(5);
-
- map.put(Fields.Domain, this.getDomain());
- map.put(Fields.Protocol, this.getProtocol());
- map.put(Fields.Application, this.getApplication());
- map.put(Fields.Instance, this.getName());
- map.put(Fields.Subtype, this.getSubtype());
- return map;
- }
-
- @Override
- public synchronized boolean hasData() {
- return this.getServer() != null && this.hasInetAddress() && this.getTextBytes() != null && this.getTextBytes().length > 0;
- }
-
- private boolean hasInetAddress() {
- return _ipv4Addresses.size() > 0 || _ipv6Addresses.size() > 0;
- }
-
- @Override
- public int hashCode() {
- return getQualifiedName().hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof ServiceInfoImpl) && getQualifiedName().equals(((ServiceInfoImpl) obj).getQualifiedName());
- }
-
- @Override
- public ServiceInfoImpl clone() {
- ServiceInfoImpl serviceInfo = new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, _text);
- serviceInfo._ipv6Addresses.addAll(Arrays.asList(getInet6Addresses()));
- serviceInfo._ipv4Addresses.addAll(Arrays.asList(getInet4Addresses()));
- serviceInfo._server = _server;
- return serviceInfo;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- sb.append('[').append(this.getClass().getSimpleName()).append('@').append(System.identityHashCode(this));
- sb.append(" name: '");
- if (0 < this.getName().length()) {
- sb.append(this.getName()).append('.');
- }
- sb.append(this.getTypeWithSubtype());
- sb.append("' address: '");
- Inet4Address[] addresses4 = this.getInet4Addresses();
- for (InetAddress address : addresses4) {
- sb.append(address).append(':').append(this.getPort()).append(' ');
- }
- Inet6Address[] addresses6 = this.getInet6Addresses();
- for (InetAddress address : addresses6) {
- sb.append(address).append(':').append(this.getPort()).append(' ');
- }
-
- sb.append(this.hasData() ? " has data" : " has NO data");
- sb.append(']');
-
- return sb.toString();
- }
-
- /**
- * Create a series of answer that correspond with the give service info.
- *
- * @param recordClass
- * record class of the query
- * @param unique
- * @param ttl
- * @param localHost
- * @return collection of answers
- */
- public Collection answers(DNSRecordClass recordClass, boolean unique, int ttl, HostInfo localHost) {
- List list = new ArrayList();
-
- if ((recordClass == DNSRecordClass.CLASS_ANY) || (recordClass == DNSRecordClass.CLASS_IN)) {
- if (this.getSubtype().length() > 0) {
- list.add(new DNSRecord.Pointer(this.getTypeWithSubtype(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
- }
- list.add(new DNSRecord.Pointer(this.getType(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
- list.add(new DNSRecord.Service(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, _priority, _weight, _port, localHost.getName()));
- list.add(new DNSRecord.Text(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getTextBytes()));
- for (InetAddress address : _ipv4Addresses)
- list.add(
- new DNSRecord.IPv4Address(
- this.getQualifiedName(),
- DNSRecordClass.CLASS_IN,
- unique,
- ttl,
- address
- )
- );
- for (InetAddress address : _ipv6Addresses)
- list.add(
- new DNSRecord.IPv6Address(
- this.getQualifiedName(),
- DNSRecordClass.CLASS_IN,
- unique,
- ttl,
- address
- )
- );
- }
-
- return list;
- }
+ }
+ index = application.toLowerCase().indexOf("._sub");
+ if (index > 0) {
+ int start = index + 5;
+ subtype = removeSeparators(application.substring(0, index));
+ application = application.substring(start);
+ }
+ }
+
+ final Map qualifiedNameMap = new HashMap(5);
+ qualifiedNameMap.put(Fields.Domain, removeSeparators(domain));
+ qualifiedNameMap.put(Fields.Protocol, protocol);
+ qualifiedNameMap.put(Fields.Application, removeSeparators(application));
+ qualifiedNameMap.put(Fields.Instance, name);
+ qualifiedNameMap.put(Fields.Subtype, subtype);
+
+ return qualifiedNameMap;
+ }
+
+ protected static Map checkQualifiedNameMap(Map qualifiedNameMap) {
+ Map checkedQualifiedNameMap = new HashMap(5);
+
+ // Optional domain
+ String domain =
+ (qualifiedNameMap.containsKey(Fields.Domain)
+ ? qualifiedNameMap.get(Fields.Domain)
+ : "local");
+ if ((domain == null) || (domain.length() == 0)) {
+ domain = "local";
+ }
+ domain = removeSeparators(domain);
+ checkedQualifiedNameMap.put(Fields.Domain, domain);
+ // Optional protocol
+ String protocol =
+ (qualifiedNameMap.containsKey(Fields.Protocol)
+ ? qualifiedNameMap.get(Fields.Protocol)
+ : "tcp");
+ if ((protocol == null) || (protocol.length() == 0)) {
+ protocol = "tcp";
+ }
+ protocol = removeSeparators(protocol);
+ checkedQualifiedNameMap.put(Fields.Protocol, protocol);
+ // Application
+ String application =
+ (qualifiedNameMap.containsKey(Fields.Application)
+ ? qualifiedNameMap.get(Fields.Application)
+ : "");
+ if ((application == null) || (application.length() == 0)) {
+ application = "";
+ }
+ application = removeSeparators(application);
+ checkedQualifiedNameMap.put(Fields.Application, application);
+ // Instance
+ String instance =
+ (qualifiedNameMap.containsKey(Fields.Instance)
+ ? qualifiedNameMap.get(Fields.Instance)
+ : "");
+ if ((instance == null) || (instance.length() == 0)) {
+ instance = "";
+ // throw new IllegalArgumentException("The instance name component of a fully qualified
+ // service cannot be empty.");
+ }
+ instance = removeSeparators(instance);
+ checkedQualifiedNameMap.put(Fields.Instance, instance);
+ // Optional Subtype
+ String subtype =
+ (qualifiedNameMap.containsKey(Fields.Subtype) ? qualifiedNameMap.get(Fields.Subtype) : "");
+ if ((subtype == null) || (subtype.length() == 0)) {
+ subtype = "";
+ }
+ subtype = removeSeparators(subtype);
+ checkedQualifiedNameMap.put(Fields.Subtype, subtype);
+
+ return checkedQualifiedNameMap;
+ }
+
+ private static String removeSeparators(String name) {
+ if (name == null) {
+ return "";
+ }
+ String newName = name.trim();
+ if (newName.startsWith(".")) {
+ newName = newName.substring(1);
+ }
+ if (newName.startsWith("_")) {
+ newName = newName.substring(1);
+ }
+ if (newName.endsWith(".")) {
+ newName = newName.substring(0, newName.length() - 1);
+ }
+ return newName;
+ }
+
+ @Override
+ public String getType() {
+ String domain = this.getDomain();
+ String protocol = this.getProtocol();
+ String application = this.getApplication();
+ return (application.length() > 0 ? "_" + application + "." : "")
+ + (protocol.length() > 0 ? "_" + protocol + "." : "")
+ + domain
+ + ".";
+ }
+
+ @Override
+ public String getTypeWithSubtype() {
+ String subtype = this.getSubtype();
+ return (subtype.length() > 0 ? "_" + subtype + "._sub." : "") + this.getType();
+ }
+
+ @Override
+ public String getName() {
+ return (_name != null ? _name : "");
+ }
+
+ @Override
+ public String getKey() {
+ if (this._key == null) {
+ this._key = this.getQualifiedName().toLowerCase();
+ }
+ return this._key;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ String domain = this.getDomain();
+ String protocol = this.getProtocol();
+ String application = this.getApplication();
+ String instance = this.getName();
+ return (instance.length() > 0 ? instance + "." : "")
+ + (application.length() > 0 ? "_" + application + "." : "")
+ + (protocol.length() > 0 ? "_" + protocol + "." : "")
+ + domain
+ + ".";
+ }
+
+ @Override
+ public String getServer() {
+ return (_server != null ? _server : "");
+ }
+
+ void setServer(String server) {
+ this._server = server;
+ }
+
+ public void addAddress(Inet4Address addr) {
+ _ipv4Addresses.add(addr);
+ }
+
+ public void addAddress(Inet6Address addr) {
+ _ipv6Addresses.add(addr);
+ }
+
+ @Override
+ public Inet4Address[] getInet4Addresses() {
+ return _ipv4Addresses.toArray(new Inet4Address[_ipv4Addresses.size()]);
+ }
+
+ @Override
+ public Inet6Address[] getInet6Addresses() {
+ return _ipv6Addresses.toArray(new Inet6Address[_ipv6Addresses.size()]);
+ }
+
+ @Override
+ public int getPort() {
+ return _port;
+ }
+
+ @Override
+ public int getPriority() {
+ return _priority;
+ }
+
+ @Override
+ public int getWeight() {
+ return _weight;
+ }
+
+ @Override
+ public byte[] getTextBytes() {
+ return (this._text != null && this._text.length > 0 ? this._text : ByteWrangler.EMPTY_TXT);
+ }
+
+ @Override
+ public String getApplication() {
+ return (_application != null ? _application : "");
+ }
+
+ @Override
+ public String getDomain() {
+ return (_domain != null ? _domain : "local");
+ }
+
+ @Override
+ public String getProtocol() {
+ return (_protocol != null ? _protocol : "tcp");
+ }
+
+ @Override
+ public String getSubtype() {
+ return (_subtype != null ? _subtype : "");
+ }
+
+ @Override
+ public Map getQualifiedNameMap() {
+ Map map = new HashMap(5);
+
+ map.put(Fields.Domain, this.getDomain());
+ map.put(Fields.Protocol, this.getProtocol());
+ map.put(Fields.Application, this.getApplication());
+ map.put(Fields.Instance, this.getName());
+ map.put(Fields.Subtype, this.getSubtype());
+ return map;
+ }
+
+ @Override
+ public synchronized boolean hasData() {
+ return this.getServer() != null
+ && this.hasInetAddress()
+ && this.getTextBytes() != null
+ && this.getTextBytes().length > 0;
+ }
+
+ private boolean hasInetAddress() {
+ return _ipv4Addresses.size() > 0 || _ipv6Addresses.size() > 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return getQualifiedName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ServiceInfoImpl)
+ && getQualifiedName().equals(((ServiceInfoImpl) obj).getQualifiedName());
+ }
+
+ @Override
+ public ServiceInfoImpl clone() {
+ ServiceInfoImpl serviceInfo =
+ new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, _text);
+ serviceInfo._ipv6Addresses.addAll(Arrays.asList(getInet6Addresses()));
+ serviceInfo._ipv4Addresses.addAll(Arrays.asList(getInet4Addresses()));
+ serviceInfo._server = _server;
+ return serviceInfo;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append('[')
+ .append(this.getClass().getSimpleName())
+ .append('@')
+ .append(System.identityHashCode(this));
+ sb.append(" name: '");
+ if (0 < this.getName().length()) {
+ sb.append(this.getName()).append('.');
+ }
+ sb.append(this.getTypeWithSubtype());
+ sb.append("' address: '");
+ Inet4Address[] addresses4 = this.getInet4Addresses();
+ for (InetAddress address : addresses4) {
+ sb.append(address).append(':').append(this.getPort()).append(' ');
+ }
+ Inet6Address[] addresses6 = this.getInet6Addresses();
+ for (InetAddress address : addresses6) {
+ sb.append(address).append(':').append(this.getPort()).append(' ');
+ }
+
+ sb.append(this.hasData() ? " has data" : " has NO data");
+ sb.append(']');
+
+ return sb.toString();
+ }
+
+ /**
+ * Create a series of answer that correspond with the give service info.
+ *
+ * @param recordClass record class of the query
+ * @param unique
+ * @param ttl
+ * @param localHost
+ * @return collection of answers
+ */
+ public Collection answers(
+ DNSRecordClass recordClass, boolean unique, int ttl, HostInfo localHost) {
+ List list = new ArrayList();
+
+ if ((recordClass == DNSRecordClass.CLASS_ANY) || (recordClass == DNSRecordClass.CLASS_IN)) {
+ if (this.getSubtype().length() > 0) {
+ list.add(
+ new DNSRecord.Pointer(
+ this.getTypeWithSubtype(),
+ DNSRecordClass.CLASS_IN,
+ DNSRecordClass.NOT_UNIQUE,
+ ttl,
+ this.getQualifiedName()));
+ }
+ list.add(
+ new DNSRecord.Pointer(
+ this.getType(),
+ DNSRecordClass.CLASS_IN,
+ DNSRecordClass.NOT_UNIQUE,
+ ttl,
+ this.getQualifiedName()));
+ list.add(
+ new DNSRecord.Service(
+ this.getQualifiedName(),
+ DNSRecordClass.CLASS_IN,
+ unique,
+ ttl,
+ _priority,
+ _weight,
+ _port,
+ localHost.getName()));
+ list.add(
+ new DNSRecord.Text(
+ this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getTextBytes()));
+ for (InetAddress address : _ipv4Addresses)
+ list.add(
+ new DNSRecord.IPv4Address(
+ this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, address));
+ for (InetAddress address : _ipv6Addresses)
+ list.add(
+ new DNSRecord.IPv6Address(
+ this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, address));
+ }
+
+ return list;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/SocketListener.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/SocketListener.java
index 3edbf9acc..17a3a97d6 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/SocketListener.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/SocketListener.java
@@ -4,85 +4,81 @@
package io.libp2p.discovery.mdns.impl;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
+import io.libp2p.discovery.mdns.impl.util.NamedThreadFactory;
import java.io.IOException;
import java.net.DatagramPacket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-
-import io.libp2p.discovery.mdns.impl.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
-/**
- * Listen for multicast packets.
- */
+/** Listen for multicast packets. */
class SocketListener implements Runnable {
- static Logger logger = LoggerFactory.getLogger(SocketListener.class.getName());
+ static Logger logger = LoggerFactory.getLogger(SocketListener.class.getName());
- private final JmDNSImpl _jmDNSImpl;
- private final String _name;
- private volatile boolean _closed;
- private final ExecutorService _executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("JmDNS"));
- private Future _isShutdown;
+ private final JmDNSImpl _jmDNSImpl;
+ private final String _name;
+ private volatile boolean _closed;
+ private final ExecutorService _executor =
+ Executors.newSingleThreadExecutor(new NamedThreadFactory("JmDNS"));
+ private Future _isShutdown;
- SocketListener(JmDNSImpl jmDNSImpl) {
- _name = "SocketListener(" + (jmDNSImpl != null ? jmDNSImpl.getName() : "") + ")";
- this._jmDNSImpl = jmDNSImpl;
- }
+ SocketListener(JmDNSImpl jmDNSImpl) {
+ _name = "SocketListener(" + (jmDNSImpl != null ? jmDNSImpl.getName() : "") + ")";
+ this._jmDNSImpl = jmDNSImpl;
+ }
- public void start() {
- _isShutdown = _executor.submit(this, null);
- }
- public Future stop() {
- _closed = true;
- _executor.shutdown();
- return _isShutdown;
- }
+ public void start() {
+ _isShutdown = _executor.submit(this, null);
+ }
- @Override
- public void run() {
+ public Future stop() {
+ _closed = true;
+ _executor.shutdown();
+ return _isShutdown;
+ }
+
+ @Override
+ public void run() {
+ try {
+ byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE];
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ while (!_closed) {
+ packet.setLength(buf.length);
+ this._jmDNSImpl.getSocket().receive(packet);
+ if (_closed) break;
try {
- byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE];
- DatagramPacket packet = new DatagramPacket(buf, buf.length);
- while (!_closed) {
- packet.setLength(buf.length);
- this._jmDNSImpl.getSocket().receive(packet);
- if (_closed)
- break;
- try {
- if (this._jmDNSImpl.getLocalHost().shouldIgnorePacket(packet)) {
- continue;
- }
+ if (this._jmDNSImpl.getLocalHost().shouldIgnorePacket(packet)) {
+ continue;
+ }
- DNSIncoming msg = new DNSIncoming(packet);
- if (msg.isValidResponseCode()) {
- if (logger.isTraceEnabled()) {
- logger.trace("{}.run() JmDNS in:{}", _name, msg.print(true));
- }
- if (msg.isQuery()) {
- if (packet.getPort() != DNSConstants.MDNS_PORT) {
- this._jmDNSImpl.handleQuery(msg, packet.getAddress(), packet.getPort());
- }
- this._jmDNSImpl.handleQuery(msg, this._jmDNSImpl.getGroup(), DNSConstants.MDNS_PORT);
- } else {
- this._jmDNSImpl.handleResponse(msg);
- }
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("{}.run() JmDNS in message with error code: {}", _name, msg.print(true));
- }
- }
- } catch (IOException e) {
- logger.warn(_name + ".run() exception ", e);
- }
+ DNSIncoming msg = new DNSIncoming(packet);
+ if (msg.isValidResponseCode()) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("{}.run() JmDNS in:{}", _name, msg.print(true));
+ }
+ if (msg.isQuery()) {
+ if (packet.getPort() != DNSConstants.MDNS_PORT) {
+ this._jmDNSImpl.handleQuery(msg, packet.getAddress(), packet.getPort());
+ }
+ this._jmDNSImpl.handleQuery(msg, this._jmDNSImpl.getGroup(), DNSConstants.MDNS_PORT);
+ } else {
+ this._jmDNSImpl.handleResponse(msg);
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("{}.run() JmDNS in message with error code: {}", _name, msg.print(true));
}
+ }
} catch (IOException e) {
- if (!_closed)
- logger.warn(_name + ".run() exception ", e);
+ logger.warn(_name + ".run() exception ", e);
}
- logger.trace("{}.run() exiting.", _name);
+ }
+ } catch (IOException e) {
+ if (!_closed) logger.warn(_name + ".run() exception ", e);
}
+ logger.trace("{}.run() exiting.", _name);
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSConstants.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSConstants.java
index 7def48772..4bcfcdfed 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSConstants.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSConstants.java
@@ -10,57 +10,63 @@
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public final class DNSConstants {
- // http://www.iana.org/assignments/dns-parameters
+ // http://www.iana.org/assignments/dns-parameters
- // changed to final class - jeffs
- public static final String MDNS_GROUP = "224.0.0.251";
- public static final String MDNS_GROUP_IPV6 = "FF02::FB";
- public static final int MDNS_PORT = Integer.getInteger("net.mdns.port", 5353);
- public static final int DNS_PORT = 53;
- public static final int DNS_TTL = Integer.getInteger("net.dns.ttl", 60 * 60); // default one hour TTL
- // public static final int DNS_TTL = 120 * 60; // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13)
+ // changed to final class - jeffs
+ public static final String MDNS_GROUP = "224.0.0.251";
+ public static final String MDNS_GROUP_IPV6 = "FF02::FB";
+ public static final int MDNS_PORT = Integer.getInteger("net.mdns.port", 5353);
+ public static final int DNS_PORT = 53;
+ public static final int DNS_TTL =
+ Integer.getInteger("net.dns.ttl", 60 * 60); // default one hour TTL
+ // public static final int DNS_TTL = 120 * 60; // two hour TTL
+ // (draft-cheshire-dnsext-multicastdns.txt ch 13)
- public static final int MAX_MSG_TYPICAL = 1460;
- public static final int MAX_MSG_ABSOLUTE = 8972;
+ public static final int MAX_MSG_TYPICAL = 1460;
+ public static final int MAX_MSG_ABSOLUTE = 8972;
- public static final int FLAGS_QR_MASK = 0x8000; // Query response mask
- public static final int FLAGS_QR_QUERY = 0x0000; // Query
- public static final int FLAGS_QR_RESPONSE = 0x8000; // Response
+ public static final int FLAGS_QR_MASK = 0x8000; // Query response mask
+ public static final int FLAGS_QR_QUERY = 0x0000; // Query
+ public static final int FLAGS_QR_RESPONSE = 0x8000; // Response
- public static final int FLAGS_OPCODE = 0x7800; // Operation code
- public static final int FLAGS_AA = 0x0400; // Authorative answer
- public static final int FLAGS_TC = 0x0200; // Truncated
- public static final int FLAGS_RD = 0x0100; // Recursion desired
- public static final int FLAGS_RA = 0x8000; // Recursion available
+ public static final int FLAGS_OPCODE = 0x7800; // Operation code
+ public static final int FLAGS_AA = 0x0400; // Authorative answer
+ public static final int FLAGS_TC = 0x0200; // Truncated
+ public static final int FLAGS_RD = 0x0100; // Recursion desired
+ public static final int FLAGS_RA = 0x8000; // Recursion available
- public static final int FLAGS_Z = 0x0040; // Zero
- public static final int FLAGS_AD = 0x0020; // Authentic data
- public static final int FLAGS_CD = 0x0010; // Checking disabled
- public static final int FLAGS_RCODE = 0x000F; // Response code
+ public static final int FLAGS_Z = 0x0040; // Zero
+ public static final int FLAGS_AD = 0x0020; // Authentic data
+ public static final int FLAGS_CD = 0x0010; // Checking disabled
+ public static final int FLAGS_RCODE = 0x000F; // Response code
- // Time Intervals for various functions
+ // Time Intervals for various functions
- public static final int SHARED_QUERY_TIME = 20; // milliseconds before send shared query
- public static final int QUERY_WAIT_INTERVAL = 225; // milliseconds between query loops.
- public static final int PROBE_WAIT_INTERVAL = 250; // milliseconds between probe loops.
- public static final int RESPONSE_MIN_WAIT_INTERVAL = 20; // minimal wait interval for response.
- public static final int RESPONSE_MAX_WAIT_INTERVAL = 115; // maximal wait interval for response
- public static final int PROBE_CONFLICT_INTERVAL = 1000; // milliseconds to wait after conflict.
- public static final int PROBE_THROTTLE_COUNT = 10; // After x tries go 1 time a sec. on probes.
- public static final int PROBE_THROTTLE_COUNT_INTERVAL = 5000; // We only increment the throttle count, if the previous increment is inside this interval.
- public static final int ANNOUNCE_WAIT_INTERVAL = 1000; // milliseconds between Announce loops.
- public static final int RECORD_REAPER_INTERVAL = 10000; // milliseconds between cache cleanups.
- public static final int RECORD_EXPIRY_DELAY = 1; // This is 1s delay used in ttl and therefore in seconds
- public static final int KNOWN_ANSWER_TTL = 120;
- public static final int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; // 50% of the TTL in milliseconds
- public static final int FLUSH_RECORD_OLDER_THAN_1_SECOND = 1; // rfc6762, section 10.2 Flush outdated cache (older than 1 second)
+ public static final int SHARED_QUERY_TIME = 20; // milliseconds before send shared query
+ public static final int QUERY_WAIT_INTERVAL = 225; // milliseconds between query loops.
+ public static final int PROBE_WAIT_INTERVAL = 250; // milliseconds between probe loops.
+ public static final int RESPONSE_MIN_WAIT_INTERVAL = 20; // minimal wait interval for response.
+ public static final int RESPONSE_MAX_WAIT_INTERVAL = 115; // maximal wait interval for response
+ public static final int PROBE_CONFLICT_INTERVAL = 1000; // milliseconds to wait after conflict.
+ public static final int PROBE_THROTTLE_COUNT = 10; // After x tries go 1 time a sec. on probes.
+ public static final int PROBE_THROTTLE_COUNT_INTERVAL =
+ 5000; // We only increment the throttle count, if the previous increment is inside this
+ // interval.
+ public static final int ANNOUNCE_WAIT_INTERVAL = 1000; // milliseconds between Announce loops.
+ public static final int RECORD_REAPER_INTERVAL = 10000; // milliseconds between cache cleanups.
+ public static final int RECORD_EXPIRY_DELAY =
+ 1; // This is 1s delay used in ttl and therefore in seconds
+ public static final int KNOWN_ANSWER_TTL = 120;
+ public static final int ANNOUNCED_RENEWAL_TTL_INTERVAL =
+ DNS_TTL * 500; // 50% of the TTL in milliseconds
+ public static final int FLUSH_RECORD_OLDER_THAN_1_SECOND =
+ 1; // rfc6762, section 10.2 Flush outdated cache (older than 1 second)
- public static final int STALE_REFRESH_INCREMENT = 5;
- public static final int STALE_REFRESH_STARTING_PERCENTAGE = 80;
+ public static final int STALE_REFRESH_INCREMENT = 5;
+ public static final int STALE_REFRESH_STARTING_PERCENTAGE = 80;
- public static final long CLOSE_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 5L;
- public static final long SERVICE_INFO_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 6L;
-
- public static final int NETWORK_CHECK_INTERVAL = 10 * 1000; // 10 secondes
+ public static final long CLOSE_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 5L;
+ public static final long SERVICE_INFO_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 6L;
+ public static final int NETWORK_CHECK_INTERVAL = 10 * 1000; // 10 secondes
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSLabel.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSLabel.java
index 86bf62a73..986c31efa 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSLabel.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSLabel.java
@@ -1,87 +1,75 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
/**
* DNS label.
- *
+ *
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public enum DNSLabel {
- /**
- * This is unallocated.
- */
- Unknown("", 0x80),
- /**
- * Standard label [RFC 1035]
- */
- Standard("standard label", 0x00),
- /**
- * Compressed label [RFC 1035]
- */
- Compressed("compressed label", 0xC0),
- /**
- * Extended label [RFC 2671]
- */
- Extended("extended label", 0x40);
+ /** This is unallocated. */
+ Unknown("", 0x80),
+ /** Standard label [RFC 1035] */
+ Standard("standard label", 0x00),
+ /** Compressed label [RFC 1035] */
+ Compressed("compressed label", 0xC0),
+ /** Extended label [RFC 2671] */
+ Extended("extended label", 0x40);
- /**
- * DNS label types are encoded on the first 2 bits
- */
- static final int LABEL_MASK = 0xC0;
- static final int LABEL_NOT_MASK = 0x3F;
+ /** DNS label types are encoded on the first 2 bits */
+ static final int LABEL_MASK = 0xC0;
- private final String _externalName;
+ static final int LABEL_NOT_MASK = 0x3F;
- private final int _index;
+ private final String _externalName;
- DNSLabel(String name, int index) {
- _externalName = name;
- _index = index;
- }
+ private final int _index;
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
+ DNSLabel(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
- }
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
- /**
- * @param index
- * @return label
- */
- public static DNSLabel labelForByte(int index) {
- int maskedIndex = index & LABEL_MASK;
- for (DNSLabel aLabel : DNSLabel.values()) {
- if (aLabel._index == maskedIndex) return aLabel;
- }
- return Unknown;
- }
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
- /**
- * @param index
- * @return masked value
- */
- public static int labelValue(int index) {
- return index & LABEL_NOT_MASK;
+ /**
+ * @param index
+ * @return label
+ */
+ public static DNSLabel labelForByte(int index) {
+ int maskedIndex = index & LABEL_MASK;
+ for (DNSLabel aLabel : DNSLabel.values()) {
+ if (aLabel._index == maskedIndex) return aLabel;
}
+ return Unknown;
+ }
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
- }
+ /**
+ * @param index
+ * @return masked value
+ */
+ public static int labelValue(int index) {
+ return index & LABEL_NOT_MASK;
+ }
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOperationCode.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOperationCode.java
index 5045e0ea7..9c495fddb 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOperationCode.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOperationCode.java
@@ -1,86 +1,69 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
/**
* DNS operation code.
- *
+ *
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public enum DNSOperationCode {
- /**
- * Query [RFC1035]
- */
- Query("Query", 0),
- /**
- * IQuery (Inverse Query, Obsolete) [RFC3425]
- */
- IQuery("Inverse Query", 1),
- /**
- * Status [RFC1035]
- */
- Status("Status", 2),
- /**
- * Unassigned
- */
- Unassigned("Unassigned", 3),
- /**
- * Notify [RFC1996]
- */
- Notify("Notify", 4),
- /**
- * Update [RFC2136]
- */
- Update("Update", 5);
+ /** Query [RFC1035] */
+ Query("Query", 0),
+ /** IQuery (Inverse Query, Obsolete) [RFC3425] */
+ IQuery("Inverse Query", 1),
+ /** Status [RFC1035] */
+ Status("Status", 2),
+ /** Unassigned */
+ Unassigned("Unassigned", 3),
+ /** Notify [RFC1996] */
+ Notify("Notify", 4),
+ /** Update [RFC2136] */
+ Update("Update", 5);
- /**
- * DNS RCode types are encoded on the last 4 bits
- */
- static final int OpCode_MASK = 0x7800;
+ /** DNS RCode types are encoded on the last 4 bits */
+ static final int OpCode_MASK = 0x7800;
- private final String _externalName;
+ private final String _externalName;
- private final int _index;
+ private final int _index;
- DNSOperationCode(String name, int index) {
- _externalName = name;
- _index = index;
- }
+ DNSOperationCode(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
-
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
- }
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
- /**
- * @param flags
- * @return label
- */
- public static DNSOperationCode operationCodeForFlags(int flags) {
- int maskedIndex = (flags & OpCode_MASK) >> 11;
- for (DNSOperationCode aCode : DNSOperationCode.values()) {
- if (aCode._index == maskedIndex) return aCode;
- }
- return Unassigned;
- }
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
+ /**
+ * @param flags
+ * @return label
+ */
+ public static DNSOperationCode operationCodeForFlags(int flags) {
+ int maskedIndex = (flags & OpCode_MASK) >> 11;
+ for (DNSOperationCode aCode : DNSOperationCode.values()) {
+ if (aCode._index == maskedIndex) return aCode;
}
+ return Unassigned;
+ }
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOptionCode.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOptionCode.java
index d254c2424..a30f437bf 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOptionCode.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSOptionCode.java
@@ -1,78 +1,65 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
/**
* DNS option code.
- *
+ *
* @author Arthur van Hoff, Pierre Frisch, Rick Blair
*/
public enum DNSOptionCode {
- /**
- * Token
- */
- Unknown("Unknown", 65535),
- /**
- * Long-Lived Queries Option [http://files.dns-sd.org/draft-sekar-dns-llq.txt]
- */
- LLQ("LLQ", 1),
- /**
- * Update Leases Option [http://files.dns-sd.org/draft-sekar-dns-ul.txt]
- */
- UL("UL", 2),
- /**
- * Name Server Identifier Option [RFC5001]
- */
- NSID("NSID", 3),
- /**
- * Owner Option [draft-cheshire-edns0-owner-option]
- */
- Owner("Owner", 4);
+ /** Token */
+ Unknown("Unknown", 65535),
+ /** Long-Lived Queries Option [http://files.dns-sd.org/draft-sekar-dns-llq.txt] */
+ LLQ("LLQ", 1),
+ /** Update Leases Option [http://files.dns-sd.org/draft-sekar-dns-ul.txt] */
+ UL("UL", 2),
+ /** Name Server Identifier Option [RFC5001] */
+ NSID("NSID", 3),
+ /** Owner Option [draft-cheshire-edns0-owner-option] */
+ Owner("Owner", 4);
- private final String _externalName;
+ private final String _externalName;
- private final int _index;
+ private final int _index;
- DNSOptionCode(String name, int index) {
- _externalName = name;
- _index = index;
- }
+ DNSOptionCode(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
-
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
- }
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
- /**
- * @param optioncode
- * @return label
- */
- public static DNSOptionCode resultCodeForFlags(int optioncode) {
- int maskedIndex = optioncode;
- for (DNSOptionCode aCode : DNSOptionCode.values()) {
- if (aCode._index == maskedIndex) return aCode;
- }
- return Unknown;
- }
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
+ /**
+ * @param optioncode
+ * @return label
+ */
+ public static DNSOptionCode resultCodeForFlags(int optioncode) {
+ int maskedIndex = optioncode;
+ for (DNSOptionCode aCode : DNSOptionCode.values()) {
+ if (aCode._index == maskedIndex) return aCode;
}
+ return Unknown;
+ }
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordClass.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordClass.java
index 359279f88..40bb610f4 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordClass.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordClass.java
@@ -1,6 +1,4 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
import org.slf4j.Logger;
@@ -8,131 +6,112 @@
/**
* DNS Record Class
- *
+ *
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public enum DNSRecordClass {
- /**
- *
- */
- CLASS_UNKNOWN("?", 0),
- /**
- * static final Internet
- */
- CLASS_IN("in", 1),
- /**
- * CSNET
- */
- CLASS_CS("cs", 2),
- /**
- * CHAOS
- */
- CLASS_CH("ch", 3),
- /**
- * Hesiod
- */
- CLASS_HS("hs", 4),
- /**
- * Used in DNS UPDATE [RFC 2136]
- */
- CLASS_NONE("none", 254),
- /**
- * Not a DNS class, but a DNS query class, meaning "all classes"
- */
- CLASS_ANY("any", 255);
-
- private static Logger logger = LoggerFactory.getLogger(DNSRecordClass.class.getName());
-
- /**
- * Multicast DNS uses the bottom 15 bits to identify the record class...
- * Except for pseudo records like OPT.
- */
- public static final int CLASS_MASK = 0x7FFF;
-
- /**
- * For answers the top bit indicates that all other cached records are now invalid.
- * For questions it indicates that we should send a unicast response.
- */
- public static final int CLASS_UNIQUE = 0x8000;
-
- /**
- *
- */
- public static final boolean UNIQUE = true;
-
- /**
- *
- */
- public static final boolean NOT_UNIQUE = false;
-
- private final String _externalName;
-
- private final int _index;
-
- DNSRecordClass(String name, int index) {
- _externalName = name;
- _index = index;
- }
-
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
-
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
+ /** */
+ CLASS_UNKNOWN("?", 0),
+ /** static final Internet */
+ CLASS_IN("in", 1),
+ /** CSNET */
+ CLASS_CS("cs", 2),
+ /** CHAOS */
+ CLASS_CH("ch", 3),
+ /** Hesiod */
+ CLASS_HS("hs", 4),
+ /** Used in DNS UPDATE [RFC 2136] */
+ CLASS_NONE("none", 254),
+ /** Not a DNS class, but a DNS query class, meaning "all classes" */
+ CLASS_ANY("any", 255);
+
+ private static Logger logger = LoggerFactory.getLogger(DNSRecordClass.class.getName());
+
+ /**
+ * Multicast DNS uses the bottom 15 bits to identify the record class...
+ * Except for pseudo records like OPT.
+ */
+ public static final int CLASS_MASK = 0x7FFF;
+
+ /**
+ * For answers the top bit indicates that all other cached records are now invalid.
+ * For questions it indicates that we should send a unicast response.
+ */
+ public static final int CLASS_UNIQUE = 0x8000;
+
+ /** */
+ public static final boolean UNIQUE = true;
+
+ /** */
+ public static final boolean NOT_UNIQUE = false;
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSRecordClass(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
+
+ /**
+ * Checks if the class is unique
+ *
+ * @param index
+ * @return true
is the class is unique, false
otherwise.
+ */
+ public boolean isUnique(int index) {
+ return (this != CLASS_UNKNOWN) && ((index & CLASS_UNIQUE) != 0);
+ }
+
+ /**
+ * @param name
+ * @return class for name
+ */
+ public static DNSRecordClass classForName(String name) {
+ if (name != null) {
+ String aName = name.toLowerCase();
+ for (DNSRecordClass aClass : DNSRecordClass.values()) {
+ if (aClass._externalName.equals(aName)) return aClass;
+ }
}
-
- /**
- * Checks if the class is unique
- *
- * @param index
- * @return true
is the class is unique, false
otherwise.
- */
- public boolean isUnique(int index) {
- return (this != CLASS_UNKNOWN) && ((index & CLASS_UNIQUE) != 0);
- }
-
- /**
- * @param name
- * @return class for name
- */
- public static DNSRecordClass classForName(String name) {
- if (name != null) {
- String aName = name.toLowerCase();
- for (DNSRecordClass aClass : DNSRecordClass.values()) {
- if (aClass._externalName.equals(aName)) return aClass;
- }
- }
- logger.warn("Could not find record class for name: {}", name);
- return CLASS_UNKNOWN;
- }
-
- /**
- * @param index
- * @return class for name
- */
- public static DNSRecordClass classForIndex(int index) {
- int maskedIndex = index & CLASS_MASK;
- for (DNSRecordClass aClass : DNSRecordClass.values()) {
- if (aClass._index == maskedIndex) return aClass;
- }
- logger.warn("Could not find record class for index: {}", index);
- return CLASS_UNKNOWN;
- }
-
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
+ logger.warn("Could not find record class for name: {}", name);
+ return CLASS_UNKNOWN;
+ }
+
+ /**
+ * @param index
+ * @return class for name
+ */
+ public static DNSRecordClass classForIndex(int index) {
+ int maskedIndex = index & CLASS_MASK;
+ for (DNSRecordClass aClass : DNSRecordClass.values()) {
+ if (aClass._index == maskedIndex) return aClass;
}
-
+ logger.warn("Could not find record class for index: {}", index);
+ return CLASS_UNKNOWN;
+ }
+
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordType.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordType.java
index 0ad1e8e9f..9ff8754ac 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordType.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSRecordType.java
@@ -1,6 +1,4 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
import org.slf4j.Logger;
@@ -8,306 +6,187 @@
/**
* DNS Record Type
- *
+ *
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public enum DNSRecordType {
- /**
- * Address
- */
- TYPE_IGNORE("ignore", 0),
- /**
- * Address
- */
- TYPE_A("a", 1),
- /**
- * Name Server
- */
- TYPE_NS("ns", 2),
- /**
- * Mail Destination
- */
- TYPE_MD("md", 3),
- /**
- * Mail Forwarder
- */
- TYPE_MF("mf", 4),
- /**
- * Canonical Name
- */
- TYPE_CNAME("cname", 5),
- /**
- * Start of Authority
- */
- TYPE_SOA("soa", 6),
- /**
- * Mailbox
- */
- TYPE_MB("mb", 7),
- /**
- * Mail Group
- */
- TYPE_MG("mg", 8),
- /**
- * Mail Rename
- */
- TYPE_MR("mr", 9),
- /**
- * NULL RR
- */
- TYPE_NULL("null", 10),
- /**
- * Well-known-service
- */
- TYPE_WKS("wks", 11),
- /**
- * Domain Name pointer
- */
- TYPE_PTR("ptr", 12),
- /**
- * Host information
- */
- TYPE_HINFO("hinfo", 13),
- /**
- * Mailbox information
- */
- TYPE_MINFO("minfo", 14),
- /**
- * Mail exchanger
- */
- TYPE_MX("mx", 15),
- /**
- * Arbitrary text string
- */
- TYPE_TXT("txt", 16),
- /**
- * for Responsible Person [RFC1183]
- */
- TYPE_RP("rp", 17),
- /**
- * for AFS Data Base location [RFC1183]
- */
- TYPE_AFSDB("afsdb", 18),
- /**
- * for X.25 PSDN address [RFC1183]
- */
- TYPE_X25("x25", 19),
- /**
- * for ISDN address [RFC1183]
- */
- TYPE_ISDN("isdn", 20),
- /**
- * for Route Through [RFC1183]
- */
- TYPE_RT("rt", 21),
- /**
- * for NSAP address, NSAP style A record [RFC1706]
- */
- TYPE_NSAP("nsap", 22),
- /**
- *
- */
- TYPE_NSAP_PTR("nsap-otr", 23),
- /**
- * for security signature [RFC2931]
- */
- TYPE_SIG("sig", 24),
- /**
- * for security key [RFC2535]
- */
- TYPE_KEY("key", 25),
- /**
- * X.400 mail mapping information [RFC2163]
- */
- TYPE_PX("px", 26),
- /**
- * Geographical Position [RFC1712]
- */
- TYPE_GPOS("gpos", 27),
- /**
- * IP6 Address [Thomson]
- */
- TYPE_AAAA("aaaa", 28),
- /**
- * Location Information [Vixie]
- */
- TYPE_LOC("loc", 29),
- /**
- * Next Domain - OBSOLETE [RFC2535, RFC3755]
- */
- TYPE_NXT("nxt", 30),
- /**
- * Endpoint Identifier [Patton]
- */
- TYPE_EID("eid", 31),
- /**
- * Nimrod Locator [Patton]
- */
- TYPE_NIMLOC("nimloc", 32),
- /**
- * Server Selection [RFC2782]
- */
- TYPE_SRV("srv", 33),
- /**
- * ATM Address [Dobrowski]
- */
- TYPE_ATMA("atma", 34),
- /**
- * Naming Authority Pointer [RFC2168, RFC2915]
- */
- TYPE_NAPTR("naptr", 35),
- /**
- * Key Exchanger [RFC2230]
- */
- TYPE_KX("kx", 36),
- /**
- * CERT [RFC2538]
- */
- TYPE_CERT("cert", 37),
- /**
- * A6 [RFC2874]
- */
- TYPE_A6("a6", 38),
- /**
- * DNAME [RFC2672]
- */
- TYPE_DNAME("dname", 39),
- /**
- * SINK [Eastlake]
- */
- TYPE_SINK("sink", 40),
- /**
- * OPT [RFC2671]
- */
- TYPE_OPT("opt", 41),
- /**
- * APL [RFC3123]
- */
- TYPE_APL("apl", 42),
- /**
- * Delegation Signer [RFC3658]
- */
- TYPE_DS("ds", 43),
- /**
- * SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt]
- */
- TYPE_SSHFP("sshfp", 44),
- /**
- * RRSIG [RFC3755]
- */
- TYPE_RRSIG("rrsig", 46),
- /**
- * NSEC [RFC3755]
- */
- TYPE_NSEC("nsec", 47),
- /**
- * DNSKEY [RFC3755]
- */
- TYPE_DNSKEY("dnskey", 48),
- /**
- * [IANA-Reserved]
- */
- TYPE_UINFO("uinfo", 100),
- /**
- * [IANA-Reserved]
- */
- TYPE_UID("uid", 101),
- /**
- * [IANA-Reserved]
- */
- TYPE_GID("gid", 102),
- /**
- * [IANA-Reserved]
- */
- TYPE_UNSPEC("unspec", 103),
- /**
- * Transaction Key [RFC2930]
- */
- TYPE_TKEY("tkey", 249),
- /**
- * Transaction Signature [RFC2845]
- */
- TYPE_TSIG("tsig", 250),
- /**
- * Incremental transfer [RFC1995]
- */
- TYPE_IXFR("ixfr", 251),
- /**
- * Transfer of an entire zone [RFC1035]
- */
- TYPE_AXFR("axfr", 252),
- /**
- * Mailbox-related records (MB, MG or MR) [RFC1035]
- */
- TYPE_MAILA("mails", 253),
- /**
- * Mail agent RRs (Obsolete - see MX) [RFC1035]
- */
- TYPE_MAILB("mailb", 254),
- /**
- * Request for all records [RFC1035]
- */
- TYPE_ANY("any", 255);
+ /** Address */
+ TYPE_IGNORE("ignore", 0),
+ /** Address */
+ TYPE_A("a", 1),
+ /** Name Server */
+ TYPE_NS("ns", 2),
+ /** Mail Destination */
+ TYPE_MD("md", 3),
+ /** Mail Forwarder */
+ TYPE_MF("mf", 4),
+ /** Canonical Name */
+ TYPE_CNAME("cname", 5),
+ /** Start of Authority */
+ TYPE_SOA("soa", 6),
+ /** Mailbox */
+ TYPE_MB("mb", 7),
+ /** Mail Group */
+ TYPE_MG("mg", 8),
+ /** Mail Rename */
+ TYPE_MR("mr", 9),
+ /** NULL RR */
+ TYPE_NULL("null", 10),
+ /** Well-known-service */
+ TYPE_WKS("wks", 11),
+ /** Domain Name pointer */
+ TYPE_PTR("ptr", 12),
+ /** Host information */
+ TYPE_HINFO("hinfo", 13),
+ /** Mailbox information */
+ TYPE_MINFO("minfo", 14),
+ /** Mail exchanger */
+ TYPE_MX("mx", 15),
+ /** Arbitrary text string */
+ TYPE_TXT("txt", 16),
+ /** for Responsible Person [RFC1183] */
+ TYPE_RP("rp", 17),
+ /** for AFS Data Base location [RFC1183] */
+ TYPE_AFSDB("afsdb", 18),
+ /** for X.25 PSDN address [RFC1183] */
+ TYPE_X25("x25", 19),
+ /** for ISDN address [RFC1183] */
+ TYPE_ISDN("isdn", 20),
+ /** for Route Through [RFC1183] */
+ TYPE_RT("rt", 21),
+ /** for NSAP address, NSAP style A record [RFC1706] */
+ TYPE_NSAP("nsap", 22),
+ /** */
+ TYPE_NSAP_PTR("nsap-otr", 23),
+ /** for security signature [RFC2931] */
+ TYPE_SIG("sig", 24),
+ /** for security key [RFC2535] */
+ TYPE_KEY("key", 25),
+ /** X.400 mail mapping information [RFC2163] */
+ TYPE_PX("px", 26),
+ /** Geographical Position [RFC1712] */
+ TYPE_GPOS("gpos", 27),
+ /** IP6 Address [Thomson] */
+ TYPE_AAAA("aaaa", 28),
+ /** Location Information [Vixie] */
+ TYPE_LOC("loc", 29),
+ /** Next Domain - OBSOLETE [RFC2535, RFC3755] */
+ TYPE_NXT("nxt", 30),
+ /** Endpoint Identifier [Patton] */
+ TYPE_EID("eid", 31),
+ /** Nimrod Locator [Patton] */
+ TYPE_NIMLOC("nimloc", 32),
+ /** Server Selection [RFC2782] */
+ TYPE_SRV("srv", 33),
+ /** ATM Address [Dobrowski] */
+ TYPE_ATMA("atma", 34),
+ /** Naming Authority Pointer [RFC2168, RFC2915] */
+ TYPE_NAPTR("naptr", 35),
+ /** Key Exchanger [RFC2230] */
+ TYPE_KX("kx", 36),
+ /** CERT [RFC2538] */
+ TYPE_CERT("cert", 37),
+ /** A6 [RFC2874] */
+ TYPE_A6("a6", 38),
+ /** DNAME [RFC2672] */
+ TYPE_DNAME("dname", 39),
+ /** SINK [Eastlake] */
+ TYPE_SINK("sink", 40),
+ /** OPT [RFC2671] */
+ TYPE_OPT("opt", 41),
+ /** APL [RFC3123] */
+ TYPE_APL("apl", 42),
+ /** Delegation Signer [RFC3658] */
+ TYPE_DS("ds", 43),
+ /** SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt] */
+ TYPE_SSHFP("sshfp", 44),
+ /** RRSIG [RFC3755] */
+ TYPE_RRSIG("rrsig", 46),
+ /** NSEC [RFC3755] */
+ TYPE_NSEC("nsec", 47),
+ /** DNSKEY [RFC3755] */
+ TYPE_DNSKEY("dnskey", 48),
+ /** [IANA-Reserved] */
+ TYPE_UINFO("uinfo", 100),
+ /** [IANA-Reserved] */
+ TYPE_UID("uid", 101),
+ /** [IANA-Reserved] */
+ TYPE_GID("gid", 102),
+ /** [IANA-Reserved] */
+ TYPE_UNSPEC("unspec", 103),
+ /** Transaction Key [RFC2930] */
+ TYPE_TKEY("tkey", 249),
+ /** Transaction Signature [RFC2845] */
+ TYPE_TSIG("tsig", 250),
+ /** Incremental transfer [RFC1995] */
+ TYPE_IXFR("ixfr", 251),
+ /** Transfer of an entire zone [RFC1035] */
+ TYPE_AXFR("axfr", 252),
+ /** Mailbox-related records (MB, MG or MR) [RFC1035] */
+ TYPE_MAILA("mails", 253),
+ /** Mail agent RRs (Obsolete - see MX) [RFC1035] */
+ TYPE_MAILB("mailb", 254),
+ /** Request for all records [RFC1035] */
+ TYPE_ANY("any", 255);
- private static Logger logger = LoggerFactory.getLogger(DNSRecordType.class.getName());
+ private static Logger logger = LoggerFactory.getLogger(DNSRecordType.class.getName());
- private final String _externalName;
+ private final String _externalName;
- private final int _index;
+ private final int _index;
- DNSRecordType(String name, int index) {
- _externalName = name;
- _index = index;
- }
+ DNSRecordType(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
-
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
- }
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
- /**
- * @param name
- * @return type for name
- */
- public static DNSRecordType typeForName(String name) {
- if (name != null) {
- String aName = name.toLowerCase();
- for (DNSRecordType aType : DNSRecordType.values()) {
- if (aType._externalName.equals(aName)) return aType;
- }
- }
- logger.warn("Could not find record type for name: {}", name);
- return TYPE_IGNORE;
- }
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
- /**
- * @param index
- * @return type for name
- */
- public static DNSRecordType typeForIndex(int index) {
- for (DNSRecordType aType : DNSRecordType.values()) {
- if (aType._index == index) return aType;
- }
- logger.warn("Could not find record type for index: {}", index);
- return TYPE_IGNORE;
+ /**
+ * @param name
+ * @return type for name
+ */
+ public static DNSRecordType typeForName(String name) {
+ if (name != null) {
+ String aName = name.toLowerCase();
+ for (DNSRecordType aType : DNSRecordType.values()) {
+ if (aType._externalName.equals(aName)) return aType;
+ }
}
+ logger.warn("Could not find record type for name: {}", name);
+ return TYPE_IGNORE;
+ }
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
+ /**
+ * @param index
+ * @return type for name
+ */
+ public static DNSRecordType typeForIndex(int index) {
+ for (DNSRecordType aType : DNSRecordType.values()) {
+ if (aType._index == index) return aType;
}
+ logger.warn("Could not find record type for index: {}", index);
+ return TYPE_IGNORE;
+ }
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSResultCode.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSResultCode.java
index 9b73ba628..7175e599c 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSResultCode.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSResultCode.java
@@ -1,149 +1,118 @@
-/**
- *
- */
+/** */
package io.libp2p.discovery.mdns.impl.constants;
/**
* DNS result code.
- *
+ *
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
public enum DNSResultCode {
- /**
- * Token
- */
- Unknown("Unknown", 65535),
- /**
- * No Error [RFC1035]
- */
- NoError("No Error", 0),
- /**
- * Format Error [RFC1035]
- */
- FormErr("Format Error", 1),
- /**
- * Server Failure [RFC1035]
- */
- ServFail("Server Failure", 2),
- /**
- * Non-Existent Domain [RFC1035]
- */
- NXDomain("Non-Existent Domain", 3),
- /**
- * Not Implemented [RFC1035]
- */
- NotImp("Not Implemented", 4),
- /**
- * Query Refused [RFC1035]
- */
- Refused("Query Refused", 5),
- /**
- * Name Exists when it should not [RFC2136]
- */
- YXDomain("Name Exists when it should not", 6),
- /**
- * RR Set Exists when it should not [RFC2136]
- */
- YXRRSet("RR Set Exists when it should not", 7),
- /**
- * RR Set that should exist does not [RFC2136]
- */
- NXRRSet("RR Set that should exist does not", 8),
- /**
- * Server Not Authoritative for zone [RFC2136]]
- */
- NotAuth("Server Not Authoritative for zone", 9),
- /**
- * Name not contained in zone [RFC2136]
- */
- NotZone("NotZone Name not contained in zone", 10),
+ /** Token */
+ Unknown("Unknown", 65535),
+ /** No Error [RFC1035] */
+ NoError("No Error", 0),
+ /** Format Error [RFC1035] */
+ FormErr("Format Error", 1),
+ /** Server Failure [RFC1035] */
+ ServFail("Server Failure", 2),
+ /** Non-Existent Domain [RFC1035] */
+ NXDomain("Non-Existent Domain", 3),
+ /** Not Implemented [RFC1035] */
+ NotImp("Not Implemented", 4),
+ /** Query Refused [RFC1035] */
+ Refused("Query Refused", 5),
+ /** Name Exists when it should not [RFC2136] */
+ YXDomain("Name Exists when it should not", 6),
+ /** RR Set Exists when it should not [RFC2136] */
+ YXRRSet("RR Set Exists when it should not", 7),
+ /** RR Set that should exist does not [RFC2136] */
+ NXRRSet("RR Set that should exist does not", 8),
+ /** Server Not Authoritative for zone [RFC2136]] */
+ NotAuth("Server Not Authoritative for zone", 9),
+ /** Name not contained in zone [RFC2136] */
+ NotZone("NotZone Name not contained in zone", 10),
+ ;
- ;
+ // 0 NoError No Error [RFC1035]
+ // 1 FormErr Format Error [RFC1035]
+ // 2 ServFail Server Failure [RFC1035]
+ // 3 NXDomain Non-Existent Domain [RFC1035]
+ // 4 NotImp Not Implemented [RFC1035]
+ // 5 Refused Query Refused [RFC1035]
+ // 6 YXDomain Name Exists when it should not [RFC2136]
+ // 7 YXRRSet RR Set Exists when it should not [RFC2136]
+ // 8 NXRRSet RR Set that should exist does not [RFC2136]
+ // 9 NotAuth Server Not Authoritative for zone [RFC2136]
+ // 10 NotZone Name not contained in zone [RFC2136]
+ // 11-15 Unassigned
+ // 16 BADVERS Bad OPT Version [RFC2671]
+ // 16 BADSIG TSIG Signature Failure [RFC2845]
+ // 17 BADKEY Key not recognized [RFC2845]
+ // 18 BADTIME Signature out of time window [RFC2845]
+ // 19 BADMODE Bad TKEY Mode [RFC2930]
+ // 20 BADNAME Duplicate key name [RFC2930]
+ // 21 BADALG Algorithm not supported [RFC2930]
+ // 22 BADTRUNC Bad Truncation [RFC4635]
+ // 23-3840 Unassigned
+ // 3841-4095 Reserved for Private Use [RFC5395]
+ // 4096-65534 Unassigned
+ // 65535 Reserved, can be allocated by Standards Action [RFC5395]
- // 0 NoError No Error [RFC1035]
- // 1 FormErr Format Error [RFC1035]
- // 2 ServFail Server Failure [RFC1035]
- // 3 NXDomain Non-Existent Domain [RFC1035]
- // 4 NotImp Not Implemented [RFC1035]
- // 5 Refused Query Refused [RFC1035]
- // 6 YXDomain Name Exists when it should not [RFC2136]
- // 7 YXRRSet RR Set Exists when it should not [RFC2136]
- // 8 NXRRSet RR Set that should exist does not [RFC2136]
- // 9 NotAuth Server Not Authoritative for zone [RFC2136]
- // 10 NotZone Name not contained in zone [RFC2136]
- // 11-15 Unassigned
- // 16 BADVERS Bad OPT Version [RFC2671]
- // 16 BADSIG TSIG Signature Failure [RFC2845]
- // 17 BADKEY Key not recognized [RFC2845]
- // 18 BADTIME Signature out of time window [RFC2845]
- // 19 BADMODE Bad TKEY Mode [RFC2930]
- // 20 BADNAME Duplicate key name [RFC2930]
- // 21 BADALG Algorithm not supported [RFC2930]
- // 22 BADTRUNC Bad Truncation [RFC4635]
- // 23-3840 Unassigned
- // 3841-4095 Reserved for Private Use [RFC5395]
- // 4096-65534 Unassigned
- // 65535 Reserved, can be allocated by Standards Action [RFC5395]
+ /** DNS Result Code types are encoded on the last 4 bits */
+ static final int RCode_MASK = 0x0F;
- /**
- * DNS Result Code types are encoded on the last 4 bits
- */
- final static int RCode_MASK = 0x0F;
- /**
- * DNS Extended Result Code types are encoded on the first 8 bits
- */
- final static int ExtendedRCode_MASK = 0xFF;
+ /** DNS Extended Result Code types are encoded on the first 8 bits */
+ static final int ExtendedRCode_MASK = 0xFF;
- private final String _externalName;
+ private final String _externalName;
- private final int _index;
+ private final int _index;
- DNSResultCode(String name, int index) {
- _externalName = name;
- _index = index;
- }
+ DNSResultCode(String name, int index) {
+ _externalName = name;
+ _index = index;
+ }
- /**
- * Return the string representation of this type
- *
- * @return String
- */
- public String externalName() {
- return _externalName;
- }
-
- /**
- * Return the numeric value of this type
- *
- * @return String
- */
- public int indexValue() {
- return _index;
- }
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName() {
+ return _externalName;
+ }
- /**
- * @param flags
- * @return label
- */
- public static DNSResultCode resultCodeForFlags(int flags) {
- int maskedIndex = flags & RCode_MASK;
- for (DNSResultCode aCode : DNSResultCode.values()) {
- if (aCode._index == maskedIndex) return aCode;
- }
- return Unknown;
- }
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue() {
+ return _index;
+ }
- public static DNSResultCode resultCodeForFlags(int flags, int extendedRCode) {
- int maskedIndex = ((extendedRCode >> 28) & ExtendedRCode_MASK) | (flags & RCode_MASK);
- for (DNSResultCode aCode : DNSResultCode.values()) {
- if (aCode._index == maskedIndex) return aCode;
- }
- return Unknown;
+ /**
+ * @param flags
+ * @return label
+ */
+ public static DNSResultCode resultCodeForFlags(int flags) {
+ int maskedIndex = flags & RCode_MASK;
+ for (DNSResultCode aCode : DNSResultCode.values()) {
+ if (aCode._index == maskedIndex) return aCode;
}
+ return Unknown;
+ }
- @Override
- public String toString() {
- return this.name() + " index " + this.indexValue();
+ public static DNSResultCode resultCodeForFlags(int flags, int extendedRCode) {
+ int maskedIndex = ((extendedRCode >> 28) & ExtendedRCode_MASK) | (flags & RCode_MASK);
+ for (DNSResultCode aCode : DNSResultCode.values()) {
+ if (aCode._index == maskedIndex) return aCode;
}
+ return Unknown;
+ }
+ @Override
+ public String toString() {
+ return this.name() + " index " + this.indexValue();
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSState.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSState.java
index 9b1b99995..9b7578ea9 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSState.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/DNSState.java
@@ -11,205 +11,189 @@
*/
public enum DNSState {
- /**
- *
- */
- PROBING_1("probing 1", StateClass.probing),
- /**
- *
- */
- PROBING_2("probing 2", StateClass.probing),
- /**
- *
- */
- PROBING_3("probing 3", StateClass.probing),
- /**
- *
- */
- ANNOUNCING_1("announcing 1", StateClass.announcing),
- /**
- *
- */
- ANNOUNCING_2("announcing 2", StateClass.announcing),
- /**
- *
- */
- ANNOUNCED("announced", StateClass.announced),
- /**
- *
- */
- CANCELING_1("canceling 1", StateClass.canceling),
- /**
- *
- */
- CANCELING_2("canceling 2", StateClass.canceling),
- /**
- *
- */
- CANCELING_3("canceling 3", StateClass.canceling),
- /**
- *
- */
- CANCELED("canceled", StateClass.canceled),
- /**
- *
- */
- CLOSING("closing", StateClass.closing),
- /**
- *
- */
- CLOSED("closed", StateClass.closed);
-
- private enum StateClass {
- probing, announcing, announced, canceling, canceled, closing, closed
+ /** */
+ PROBING_1("probing 1", StateClass.probing),
+ /** */
+ PROBING_2("probing 2", StateClass.probing),
+ /** */
+ PROBING_3("probing 3", StateClass.probing),
+ /** */
+ ANNOUNCING_1("announcing 1", StateClass.announcing),
+ /** */
+ ANNOUNCING_2("announcing 2", StateClass.announcing),
+ /** */
+ ANNOUNCED("announced", StateClass.announced),
+ /** */
+ CANCELING_1("canceling 1", StateClass.canceling),
+ /** */
+ CANCELING_2("canceling 2", StateClass.canceling),
+ /** */
+ CANCELING_3("canceling 3", StateClass.canceling),
+ /** */
+ CANCELED("canceled", StateClass.canceled),
+ /** */
+ CLOSING("closing", StateClass.closing),
+ /** */
+ CLOSED("closed", StateClass.closed);
+
+ private enum StateClass {
+ probing,
+ announcing,
+ announced,
+ canceling,
+ canceled,
+ closing,
+ closed
+ }
+
+ // private static Logger logger = LoggerFactory.getLogger(DNSState.class.getName());
+
+ private final String _name;
+
+ private final StateClass _state;
+
+ private DNSState(String name, StateClass state) {
+ _name = name;
+ _state = state;
+ }
+
+ @Override
+ public final String toString() {
+ return _name;
+ }
+
+ /**
+ * Returns the next advanced state.
+ * In general, this advances one step in the following sequence: PROBING_1, PROBING_2, PROBING_3,
+ * ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED.
+ * or CANCELING_1, CANCELING_2, CANCELING_3, CANCELED Does not advance for ANNOUNCED and CANCELED
+ * state.
+ *
+ * @return next state
+ */
+ public final DNSState advance() {
+ switch (this) {
+ case PROBING_1:
+ return PROBING_2;
+ case PROBING_2:
+ return PROBING_3;
+ case PROBING_3:
+ return ANNOUNCING_1;
+ case ANNOUNCING_1:
+ return ANNOUNCING_2;
+ case ANNOUNCING_2:
+ return ANNOUNCED;
+ case ANNOUNCED:
+ return ANNOUNCED;
+ case CANCELING_1:
+ return CANCELING_2;
+ case CANCELING_2:
+ return CANCELING_3;
+ case CANCELING_3:
+ return CANCELED;
+ case CANCELED:
+ return CANCELED;
+ case CLOSING:
+ return CLOSED;
+ case CLOSED:
+ return CLOSED;
+ default:
+ // This is just to keep the compiler happy as we have covered all cases before.
+ return this;
}
-
- // private static Logger logger = LoggerFactory.getLogger(DNSState.class.getName());
-
- private final String _name;
-
- private final StateClass _state;
-
- private DNSState(String name, StateClass state) {
- _name = name;
- _state = state;
+ }
+
+ /**
+ * Returns to the next reverted state. All states except CANCELED revert to PROBING_1. Status
+ * CANCELED does not revert.
+ *
+ * @return reverted state
+ */
+ public final DNSState revert() {
+ switch (this) {
+ case PROBING_1:
+ case PROBING_2:
+ case PROBING_3:
+ case ANNOUNCING_1:
+ case ANNOUNCING_2:
+ case ANNOUNCED:
+ return PROBING_1;
+ case CANCELING_1:
+ case CANCELING_2:
+ case CANCELING_3:
+ return CANCELING_1;
+ case CANCELED:
+ return CANCELED;
+ case CLOSING:
+ return CLOSING;
+ case CLOSED:
+ return CLOSED;
+ default:
+ // This is just to keep the compiler happy as we have covered all cases before.
+ return this;
}
-
- @Override
- public final String toString() {
- return _name;
- }
-
- /**
- * Returns the next advanced state.
- * In general, this advances one step in the following sequence: PROBING_1, PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED.
- * or CANCELING_1, CANCELING_2, CANCELING_3, CANCELED Does not advance for ANNOUNCED and CANCELED state.
- *
- * @return next state
- */
- public final DNSState advance() {
- switch (this) {
- case PROBING_1:
- return PROBING_2;
- case PROBING_2:
- return PROBING_3;
- case PROBING_3:
- return ANNOUNCING_1;
- case ANNOUNCING_1:
- return ANNOUNCING_2;
- case ANNOUNCING_2:
- return ANNOUNCED;
- case ANNOUNCED:
- return ANNOUNCED;
- case CANCELING_1:
- return CANCELING_2;
- case CANCELING_2:
- return CANCELING_3;
- case CANCELING_3:
- return CANCELED;
- case CANCELED:
- return CANCELED;
- case CLOSING:
- return CLOSED;
- case CLOSED:
- return CLOSED;
- default:
- // This is just to keep the compiler happy as we have covered all cases before.
- return this;
- }
- }
-
- /**
- * Returns to the next reverted state. All states except CANCELED revert to PROBING_1. Status CANCELED does not revert.
- *
- * @return reverted state
- */
- public final DNSState revert() {
- switch (this) {
- case PROBING_1:
- case PROBING_2:
- case PROBING_3:
- case ANNOUNCING_1:
- case ANNOUNCING_2:
- case ANNOUNCED:
- return PROBING_1;
- case CANCELING_1:
- case CANCELING_2:
- case CANCELING_3:
- return CANCELING_1;
- case CANCELED:
- return CANCELED;
- case CLOSING:
- return CLOSING;
- case CLOSED:
- return CLOSED;
- default:
- // This is just to keep the compiler happy as we have covered all cases before.
- return this;
- }
- }
-
- /**
- * Returns true, if this is a probing state.
- *
- * @return true
if probing state, false
otherwise
- */
- public final boolean isProbing() {
- return _state == StateClass.probing;
- }
-
- /**
- * Returns true, if this is an announcing state.
- *
- * @return true
if announcing state, false
otherwise
- */
- public final boolean isAnnouncing() {
- return _state == StateClass.announcing;
- }
-
- /**
- * Returns true, if this is an announced state.
- *
- * @return true
if announced state, false
otherwise
- */
- public final boolean isAnnounced() {
- return _state == StateClass.announced;
- }
-
- /**
- * Returns true, if this is a canceling state.
- *
- * @return true
if canceling state, false
otherwise
- */
- public final boolean isCanceling() {
- return _state == StateClass.canceling;
- }
-
- /**
- * Returns true, if this is a canceled state.
- *
- * @return true
if canceled state, false
otherwise
- */
- public final boolean isCanceled() {
- return _state == StateClass.canceled;
- }
-
- /**
- * Returns true, if this is a closing state.
- *
- * @return true
if closing state, false
otherwise
- */
- public final boolean isClosing() {
- return _state == StateClass.closing;
- }
-
- /**
- * Returns true, if this is a closing state.
- *
- * @return true
if closed state, false
otherwise
- */
- public final boolean isClosed() {
- return _state == StateClass.closed;
- }
-
+ }
+
+ /**
+ * Returns true, if this is a probing state.
+ *
+ * @return true
if probing state, false
otherwise
+ */
+ public final boolean isProbing() {
+ return _state == StateClass.probing;
+ }
+
+ /**
+ * Returns true, if this is an announcing state.
+ *
+ * @return true
if announcing state, false
otherwise
+ */
+ public final boolean isAnnouncing() {
+ return _state == StateClass.announcing;
+ }
+
+ /**
+ * Returns true, if this is an announced state.
+ *
+ * @return true
if announced state, false
otherwise
+ */
+ public final boolean isAnnounced() {
+ return _state == StateClass.announced;
+ }
+
+ /**
+ * Returns true, if this is a canceling state.
+ *
+ * @return true
if canceling state, false
otherwise
+ */
+ public final boolean isCanceling() {
+ return _state == StateClass.canceling;
+ }
+
+ /**
+ * Returns true, if this is a canceled state.
+ *
+ * @return true
if canceled state, false
otherwise
+ */
+ public final boolean isCanceled() {
+ return _state == StateClass.canceled;
+ }
+
+ /**
+ * Returns true, if this is a closing state.
+ *
+ * @return true
if closing state, false
otherwise
+ */
+ public final boolean isClosing() {
+ return _state == StateClass.closing;
+ }
+
+ /**
+ * Returns true, if this is a closing state.
+ *
+ * @return true
if closed state, false
otherwise
+ */
+ public final boolean isClosed() {
+ return _state == StateClass.closed;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/package-info.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/package-info.java
index 829cb4144..41316a160 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/package-info.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/constants/package-info.java
@@ -1,2 +1 @@
package io.libp2p.discovery.mdns.impl.constants;
-
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/package-info.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/package-info.java
index cc0bfbe66..effecf1ed 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/package-info.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/package-info.java
@@ -1,2 +1 @@
package io.libp2p.discovery.mdns.impl;
-
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/DNSTask.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/DNSTask.java
index 7b4595a83..880c87cc2 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/DNSTask.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/DNSTask.java
@@ -4,53 +4,50 @@
import io.libp2p.discovery.mdns.impl.DNSOutgoing;
import io.libp2p.discovery.mdns.impl.DNSQuestion;
import io.libp2p.discovery.mdns.impl.JmDNSImpl;
-
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
public abstract class DNSTask implements Runnable {
- private final JmDNSImpl _jmDNSImpl;
- protected final ScheduledExecutorService _scheduler =
- Executors.newScheduledThreadPool(1);
-
- protected DNSTask(JmDNSImpl jmDNSImpl) {
- super();
- this._jmDNSImpl = jmDNSImpl;
- }
-
- protected JmDNSImpl dns() {
- return _jmDNSImpl;
- }
-
- public abstract void start();
-
- protected abstract String getName();
-
- @Override
- public String toString() {
- return this.getName();
- }
-
- protected DNSOutgoing addQuestion(DNSOutgoing out, DNSQuestion rec) throws IOException {
- DNSOutgoing newOut = out;
- try {
- newOut.addQuestion(rec);
- } catch (final IOException e) {
- int flags = newOut.getFlags();
- boolean multicast = newOut.isMulticast();
- int maxUDPPayload = newOut.getMaxUDPPayload();
- int id = newOut.getId();
-
- newOut.setFlags(flags | DNSConstants.FLAGS_TC);
- newOut.setId(id);
- this._jmDNSImpl.send(newOut);
-
- newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
- newOut.addQuestion(rec);
- }
- return newOut;
+ private final JmDNSImpl _jmDNSImpl;
+ protected final ScheduledExecutorService _scheduler = Executors.newScheduledThreadPool(1);
+
+ protected DNSTask(JmDNSImpl jmDNSImpl) {
+ super();
+ this._jmDNSImpl = jmDNSImpl;
+ }
+
+ protected JmDNSImpl dns() {
+ return _jmDNSImpl;
+ }
+
+ public abstract void start();
+
+ protected abstract String getName();
+
+ @Override
+ public String toString() {
+ return this.getName();
+ }
+
+ protected DNSOutgoing addQuestion(DNSOutgoing out, DNSQuestion rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addQuestion(rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addQuestion(rec);
}
+ return newOut;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/Responder.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/Responder.java
index 77dab738e..cd62930b1 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/Responder.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/Responder.java
@@ -4,131 +4,131 @@
package io.libp2p.discovery.mdns.impl.tasks;
+import io.libp2p.discovery.mdns.impl.DNSIncoming;
+import io.libp2p.discovery.mdns.impl.DNSOutgoing;
+import io.libp2p.discovery.mdns.impl.DNSQuestion;
+import io.libp2p.discovery.mdns.impl.DNSRecord;
+import io.libp2p.discovery.mdns.impl.JmDNSImpl;
+import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import io.libp2p.discovery.mdns.impl.DNSIncoming;
-import io.libp2p.discovery.mdns.impl.DNSOutgoing;
-import io.libp2p.discovery.mdns.impl.DNSQuestion;
-import io.libp2p.discovery.mdns.impl.DNSRecord;
-import io.libp2p.discovery.mdns.impl.JmDNSImpl;
-import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
-
-/**
- * The Responder sends a single answer for the specified service infos and for the host name.
- */
+/** The Responder sends a single answer for the specified service infos and for the host name. */
public class Responder extends DNSTask {
- static Logger logger = LoggerFactory.getLogger(Responder.class.getName());
+ static Logger logger = LoggerFactory.getLogger(Responder.class.getName());
+
+ private final DNSIncoming _in;
+
+ private final InetAddress _addr;
+ private final int _port;
+
+ private final boolean _unicast;
+
+ public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, InetAddress addr, int port) {
+ super(jmDNSImpl);
+ this._in = in;
+ this._addr = addr;
+ this._port = port;
+ this._unicast = (port != DNSConstants.MDNS_PORT);
+ }
+
+ @Override
+ protected String getName() {
+ return "Responder(" + (this.dns() != null ? this.dns().getName() : "") + ")";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " incoming: " + _in;
+ }
+
+ @Override
+ public void start() {
+ int delay =
+ DNSConstants.RESPONSE_MIN_WAIT_INTERVAL
+ + JmDNSImpl.getRandom()
+ .nextInt(
+ DNSConstants.RESPONSE_MAX_WAIT_INTERVAL
+ - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL
+ + 1)
+ - _in.elapseSinceArrival();
+ if (delay < 0) {
+ delay = 0;
+ }
+ logger.trace("{}.start() Responder chosen delay={}", this.getName(), delay);
- private final DNSIncoming _in;
+ _scheduler.schedule(this, delay, TimeUnit.MILLISECONDS);
+ }
- private final InetAddress _addr;
- private final int _port;
+ @Override
+ public void run() {
+ // We use these sets to prevent duplicate records
+ Set questions = new HashSet();
+ Set answers = new HashSet();
- private final boolean _unicast;
+ try {
+ // Answer questions
+ for (DNSQuestion question : _in.getQuestions()) {
+ logger.debug("{}.run() JmDNS responding to: {}", this.getName(), question);
- public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, InetAddress addr, int port) {
- super(jmDNSImpl);
- this._in = in;
- this._addr = addr;
- this._port = port;
- this._unicast = (port != DNSConstants.MDNS_PORT);
- }
+ // for unicast responses the question must be included
+ if (_unicast) {
+ questions.add(question);
+ }
- @Override
- protected String getName() {
- return "Responder(" + (this.dns() != null ? this.dns().getName() : "") + ")";
- }
+ question.addAnswers(this.dns(), answers);
+ }
- @Override
- public String toString() {
- return super.toString() + " incoming: " + _in;
- }
+ // respond if we have answers
+ if (!answers.isEmpty()) {
+ logger.debug("{}.run() JmDNS responding", this.getName());
- @Override
- public void start() {
- int delay =
- DNSConstants.RESPONSE_MIN_WAIT_INTERVAL +
- JmDNSImpl.getRandom().nextInt(
- DNSConstants.RESPONSE_MAX_WAIT_INTERVAL -
- DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1
- ) -
- _in.elapseSinceArrival();
- if (delay < 0) {
- delay = 0;
+ DNSOutgoing out =
+ new DNSOutgoing(
+ DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA,
+ !_unicast,
+ _in.getSenderUDPPayload());
+ if (_unicast) {
+ out.setDestination(new InetSocketAddress(_addr, _port));
}
- logger.trace("{}.start() Responder chosen delay={}", this.getName(), delay);
-
- _scheduler.schedule(this, delay, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public void run() {
- // We use these sets to prevent duplicate records
- Set questions = new HashSet();
- Set answers = new HashSet();
-
- try {
- // Answer questions
- for (DNSQuestion question : _in.getQuestions()) {
- logger.debug("{}.run() JmDNS responding to: {}", this.getName(), question);
-
- // for unicast responses the question must be included
- if (_unicast) {
- questions.add(question);
- }
-
- question.addAnswers(this.dns(), answers);
- }
-
- // respond if we have answers
- if (!answers.isEmpty()) {
- logger.debug("{}.run() JmDNS responding", this.getName());
-
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload());
- if (_unicast) {
- out.setDestination(new InetSocketAddress(_addr, _port));
- }
- out.setId(_in.getId());
- for (DNSQuestion question : questions) {
- out = this.addQuestion(out, question);
- }
- for (DNSRecord answer : answers) {
- out = this.addAnswer(out, answer);
- }
- if (!out.isEmpty())
- this.dns().send(out);
- }
- } catch (Throwable e) {
- logger.warn(this.getName() + "run() exception ", e);
+ out.setId(_in.getId());
+ for (DNSQuestion question : questions) {
+ out = this.addQuestion(out, question);
}
- _scheduler.shutdown();
- }
-
- private DNSOutgoing addAnswer(DNSOutgoing out, DNSRecord rec) throws IOException {
- DNSOutgoing newOut = out;
- try {
- newOut.addAnswer(rec);
- } catch (final IOException e) {
- int flags = newOut.getFlags();
- boolean multicast = newOut.isMulticast();
- int maxUDPPayload = newOut.getMaxUDPPayload();
- int id = newOut.getId();
-
- newOut.setFlags(flags | DNSConstants.FLAGS_TC);
- newOut.setId(id);
- this.dns().send(newOut);
-
- newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
- newOut.addAnswer(rec);
+ for (DNSRecord answer : answers) {
+ out = this.addAnswer(out, answer);
}
- return newOut;
+ if (!out.isEmpty()) this.dns().send(out);
+ }
+ } catch (Throwable e) {
+ logger.warn(this.getName() + "run() exception ", e);
+ }
+ _scheduler.shutdown();
+ }
+
+ private DNSOutgoing addAnswer(DNSOutgoing out, DNSRecord rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addAnswer(rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this.dns().send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAnswer(rec);
}
-}
\ No newline at end of file
+ return newOut;
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/ServiceResolver.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/ServiceResolver.java
index 4b15a5544..71c34a244 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/ServiceResolver.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/ServiceResolver.java
@@ -10,72 +10,73 @@
import io.libp2p.discovery.mdns.impl.constants.DNSConstants;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordClass;
import io.libp2p.discovery.mdns.impl.constants.DNSRecordType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * The ServiceResolver queries three times consecutively for services of a given type, and then removes itself from the timer.
+ * The ServiceResolver queries three times consecutively for services of a given type, and then
+ * removes itself from the timer.
*/
public class ServiceResolver extends DNSTask {
- private static Logger logger = LoggerFactory.getLogger(ServiceResolver.class.getName());
+ private static Logger logger = LoggerFactory.getLogger(ServiceResolver.class.getName());
- private final String _type;
- private final int _queryInterval;
- private ScheduledFuture> _isShutdown;
+ private final String _type;
+ private final int _queryInterval;
+ private ScheduledFuture> _isShutdown;
- public ServiceResolver(JmDNSImpl jmDNSImpl, String type, int queryInterval) {
- super(jmDNSImpl);
- this._type = type;
- this._queryInterval = queryInterval;
- }
+ public ServiceResolver(JmDNSImpl jmDNSImpl, String type, int queryInterval) {
+ super(jmDNSImpl);
+ this._type = type;
+ this._queryInterval = queryInterval;
+ }
- @Override
- protected String getName() {
- return "ServiceResolver(" + (this.dns() != null ? this.dns().getName() : "") + ")";
- }
+ @Override
+ protected String getName() {
+ return "ServiceResolver(" + (this.dns() != null ? this.dns().getName() : "") + ")";
+ }
- @Override
- public void start() {
- _isShutdown = _scheduler.scheduleAtFixedRate(
- this,
- DNSConstants.QUERY_WAIT_INTERVAL,
- _queryInterval * 1000,
- TimeUnit.MILLISECONDS
- );
- }
+ @Override
+ public void start() {
+ _isShutdown =
+ _scheduler.scheduleAtFixedRate(
+ this, DNSConstants.QUERY_WAIT_INTERVAL, _queryInterval * 1000, TimeUnit.MILLISECONDS);
+ }
- @SuppressWarnings("unchecked")
- public Future stop() {
- _scheduler.shutdown();
- return (Future)_isShutdown;
- }
+ @SuppressWarnings("unchecked")
+ public Future stop() {
+ _scheduler.shutdown();
+ return (Future) _isShutdown;
+ }
- @Override
- public void run() {
- try {
- logger.debug("{}.run() JmDNS {}",this.getName(), this.description());
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- out = this.addQuestions(out);
- if (!out.isEmpty()) {
- this.dns().send(out);
- }
- } catch (Throwable e) {
- logger.warn(this.getName() + ".run() exception ", e);
- }
+ @Override
+ public void run() {
+ try {
+ logger.debug("{}.run() JmDNS {}", this.getName(), this.description());
+ DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
+ out = this.addQuestions(out);
+ if (!out.isEmpty()) {
+ this.dns().send(out);
+ }
+ } catch (Throwable e) {
+ logger.warn(this.getName() + ".run() exception ", e);
}
+ }
- private DNSOutgoing addQuestions(DNSOutgoing out) throws IOException {
- DNSOutgoing newOut = out;
- newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_type, DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
- return newOut;
- }
+ private DNSOutgoing addQuestions(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ newOut =
+ this.addQuestion(
+ newOut,
+ DNSQuestion.newQuestion(
+ _type, DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ return newOut;
+ }
- private String description() {
- return "querying service";
- }
-}
\ No newline at end of file
+ private String description() {
+ return "querying service";
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/package-info.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/package-info.java
index 0262d1c57..6e2ee0569 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/package-info.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/tasks/package-info.java
@@ -1,2 +1 @@
package io.libp2p.discovery.mdns.impl.tasks;
-
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/ByteWrangler.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/ByteWrangler.java
index a628f7217..baf198486 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/ByteWrangler.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/ByteWrangler.java
@@ -5,57 +5,48 @@
import java.nio.charset.Charset;
/**
- * This class contains all the byte shifting
- *
- * @author Victor Toni
+ * This class contains all the byte shifting
*
+ * @author Victor Toni
*/
public class ByteWrangler {
- /**
- * Maximum number of bytes a value can consist of.
- */
- public static final int MAX_VALUE_LENGTH = 255;
-
- /**
- * Maximum number of bytes record data can consist of.
- * It is {@link #MAX_VALUE_LENGTH} + 1 because the first byte contains the number of the following bytes.
- */
- public static final int MAX_DATA_LENGTH = MAX_VALUE_LENGTH + 1;
-
- /**
- * Representation of no value. A zero length array of bytes.
- */
- public static final byte[] NO_VALUE = new byte[0];
-
- /**
- * Representation of empty text.
- * The first byte denotes the length of the following character bytes (in this case zero.)
- *
- * FIXME: Should this be exported as a method since it could change externally???
- */
- public final static byte[] EMPTY_TXT = new byte[] { 0 };
-
- /**
- * Name for charset used to convert Strings to/from wire bytes: {@value #CHARSET_NAME}.
- */
- public final static String CHARSET_NAME = "UTF-8";
-
- /**
- * Charset used to convert Strings to/from wire bytes: {@value #CHARSET_NAME}.
- */
- private final static Charset CHARSET_UTF_8 = Charset.forName(CHARSET_NAME);
-
- public static byte[] encodeText(final String text) throws IOException {
- final byte data[] = text.getBytes(CHARSET_UTF_8);
- if (data.length > MAX_VALUE_LENGTH) {
- return EMPTY_TXT;
- }
-
- final ByteArrayOutputStream out = new ByteArrayOutputStream(MAX_DATA_LENGTH);
- out.write((byte) data.length);
- out.write(data, 0, data.length);
-
- final byte[] encodedText = out.toByteArray();
- return encodedText;
+ /** Maximum number of bytes a value can consist of. */
+ public static final int MAX_VALUE_LENGTH = 255;
+
+ /**
+ * Maximum number of bytes record data can consist of. It is {@link #MAX_VALUE_LENGTH} + 1 because
+ * the first byte contains the number of the following bytes.
+ */
+ public static final int MAX_DATA_LENGTH = MAX_VALUE_LENGTH + 1;
+
+ /** Representation of no value. A zero length array of bytes. */
+ public static final byte[] NO_VALUE = new byte[0];
+
+ /**
+ * Representation of empty text. The first byte denotes the length of the following character
+ * bytes (in this case zero.)
+ *
+ * FIXME: Should this be exported as a method since it could change externally???
+ */
+ public static final byte[] EMPTY_TXT = new byte[] {0};
+
+ /** Name for charset used to convert Strings to/from wire bytes: {@value #CHARSET_NAME}. */
+ public static final String CHARSET_NAME = "UTF-8";
+
+ /** Charset used to convert Strings to/from wire bytes: {@value #CHARSET_NAME}. */
+ private static final Charset CHARSET_UTF_8 = Charset.forName(CHARSET_NAME);
+
+ public static byte[] encodeText(final String text) throws IOException {
+ final byte data[] = text.getBytes(CHARSET_UTF_8);
+ if (data.length > MAX_VALUE_LENGTH) {
+ return EMPTY_TXT;
}
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream(MAX_DATA_LENGTH);
+ out.write((byte) data.length);
+ out.write(data, 0, data.length);
+
+ final byte[] encodedText = out.toByteArray();
+ return encodedText;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/NamedThreadFactory.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/NamedThreadFactory.java
index 7906a63ee..e5f0d1765 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/NamedThreadFactory.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/impl/util/NamedThreadFactory.java
@@ -8,28 +8,30 @@
import java.util.concurrent.ThreadFactory;
/**
- * Custom thread factory which sets the name to make it easier to identify where the pooled threads were created.
+ * Custom thread factory which sets the name to make it easier to identify where the pooled threads
+ * were created.
*
* @author Trejkaz, Pierre Frisch
*/
public class NamedThreadFactory implements ThreadFactory {
- private final ThreadFactory _delegate;
- private final String _namePrefix;
+ private final ThreadFactory _delegate;
+ private final String _namePrefix;
- /**
- * Constructs the thread factory.
- *
- * @param namePrefix a prefix to append to thread names (will be separated from the default thread name by a space.)
- */
- public NamedThreadFactory(String namePrefix) {
- this._namePrefix = namePrefix;
- _delegate = Executors.defaultThreadFactory();
- }
+ /**
+ * Constructs the thread factory.
+ *
+ * @param namePrefix a prefix to append to thread names (will be separated from the default thread
+ * name by a space.)
+ */
+ public NamedThreadFactory(String namePrefix) {
+ this._namePrefix = namePrefix;
+ _delegate = Executors.defaultThreadFactory();
+ }
- @Override
- public Thread newThread(Runnable runnable) {
- Thread thread = _delegate.newThread(runnable);
- thread.setName(_namePrefix + ' ' + thread.getName());
- return thread;
- }
+ @Override
+ public Thread newThread(Runnable runnable) {
+ Thread thread = _delegate.newThread(runnable);
+ thread.setName(_namePrefix + ' ' + thread.getName());
+ return thread;
+ }
}
diff --git a/libp2p/src/main/java/io/libp2p/discovery/mdns/package-info.java b/libp2p/src/main/java/io/libp2p/discovery/mdns/package-info.java
index a93d49ef7..ed16fce56 100644
--- a/libp2p/src/main/java/io/libp2p/discovery/mdns/package-info.java
+++ b/libp2p/src/main/java/io/libp2p/discovery/mdns/package-info.java
@@ -3,24 +3,19 @@
/**
* Java code in this package is derived from the JmDNS project.
*
- * JmDNS is a Java implementation of multi-cast DNS and can be used for
- * service registration and discovery in local area networks. JmDNS is
- * fully compatible with Apple's Bonjour. The project was originally
- * started in December 2002 by Arthur van Hoff at Strangeberry. In
- * November 2003 the project was moved to SourceForge, and the name
- * was changed from JRendezvous to JmDNS for legal reasons. Many
- * thanks to Stuart Cheshire for help and moral support. In 2014, it was
- * been moved from Sourceforge to Github by Kai Kreuzer with the kind
- * approval from Arthur and Rick.
- *
- * https://github.com/jmdns/jmdns/
- *
+ * JmDNS is a Java implementation of multi-cast DNS and can be used for service registration and
+ * discovery in local area networks. JmDNS is fully compatible with Apple's Bonjour. The project was
+ * originally started in December 2002 by Arthur van Hoff at Strangeberry. In November 2003 the
+ * project was moved to SourceForge, and the name was changed from JRendezvous to JmDNS for legal
+ * reasons. Many thanks to Stuart Cheshire for help and moral support. In 2014, it was been moved
+ * from Sourceforge to Github by Kai Kreuzer with the kind approval from Arthur and Rick.
*
- * JmDNS was originally licensed under the GNU Lesser General Public
- * License as jRendevous. It was re-released under the
- * Apache License, Version 2.0 in 2005. It is under those terms it is reused here.
- *
- * JmDNS License Notice
- * JmDNS Changelog
- *
- **/
+ * https://github.com/jmdns/jmdns/ JmDNS was
+ * originally licensed under the GNU Lesser General Public License as jRendevous. It was re-released
+ * under the Apache License, Version 2.0 in 2005. It is under those terms it is reused here.
+ *
+ *
JmDNS License Notice
+ * JmDNS
+ * Changelog
+ */
diff --git a/libp2p/src/main/java/io/libp2p/protocol/autonat/AutonatProtocol.java b/libp2p/src/main/java/io/libp2p/protocol/autonat/AutonatProtocol.java
new file mode 100644
index 000000000..5425ba16a
--- /dev/null
+++ b/libp2p/src/main/java/io/libp2p/protocol/autonat/AutonatProtocol.java
@@ -0,0 +1,172 @@
+package io.libp2p.protocol.autonat;
+
+import com.google.protobuf.*;
+import io.libp2p.core.*;
+import io.libp2p.core.Stream;
+import io.libp2p.core.multiformats.*;
+import io.libp2p.core.multistream.*;
+import io.libp2p.protocol.*;
+import io.libp2p.protocol.autonat.pb.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.stream.*;
+import org.jetbrains.annotations.*;
+
+public class AutonatProtocol extends ProtobufProtocolHandler {
+
+ public static class Binding extends StrictProtocolBinding {
+ public Binding() {
+ super("/libp2p/autonat/v1.0.0", new AutonatProtocol());
+ }
+ }
+
+ public interface AutoNatController {
+ CompletableFuture rpc(Autonat.Message req);
+
+ default CompletableFuture requestDial(
+ PeerId ourId, List us) {
+ if (us.isEmpty())
+ throw new IllegalStateException("Requested autonat dial with no addresses!");
+ return rpc(Autonat.Message.newBuilder()
+ .setType(Autonat.Message.MessageType.DIAL)
+ .setDial(
+ Autonat.Message.Dial.newBuilder()
+ .setPeer(
+ Autonat.Message.PeerInfo.newBuilder()
+ .addAllAddrs(
+ us.stream()
+ .map(a -> ByteString.copyFrom(a.serialize()))
+ .collect(Collectors.toList()))
+ .setId(ByteString.copyFrom(ourId.getBytes()))))
+ .build())
+ .thenApply(msg -> msg.getDialResponse());
+ }
+ }
+
+ public static class Sender implements ProtocolMessageHandler, AutoNatController {
+ private final Stream stream;
+ private final LinkedBlockingDeque> queue =
+ new LinkedBlockingDeque<>();
+
+ public Sender(Stream stream) {
+ this.stream = stream;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Autonat.Message msg) {
+ queue.poll().complete(msg);
+ }
+
+ public CompletableFuture rpc(Autonat.Message req) {
+ CompletableFuture res = new CompletableFuture<>();
+ queue.add(res);
+ stream.writeAndFlush(req);
+ return res;
+ }
+ }
+
+ private static boolean sameIP(Multiaddr a, Multiaddr b) {
+ if (a.has(Protocol.IP4))
+ return a.getFirstComponent(Protocol.IP4).equals(b.getFirstComponent(Protocol.IP4));
+ if (a.has(Protocol.IP6))
+ return a.getFirstComponent(Protocol.IP6).equals(b.getFirstComponent(Protocol.IP6));
+ return false;
+ }
+
+ private static boolean reachableIP(Multiaddr a) {
+ try {
+ if (a.has(Protocol.IP4))
+ return InetAddress.getByName(a.getFirstComponent(Protocol.IP4).getStringValue())
+ .isReachable(1000);
+ if (a.has(Protocol.IP6))
+ return InetAddress.getByName(a.getFirstComponent(Protocol.IP6).getStringValue())
+ .isReachable(1000);
+ } catch (IOException e) {
+ }
+ return false;
+ }
+
+ public static class Receiver
+ implements ProtocolMessageHandler, AutoNatController {
+ private final Stream p2pstream;
+
+ public Receiver(Stream p2pstream) {
+ this.p2pstream = p2pstream;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Autonat.Message msg) {
+ switch (msg.getType()) {
+ case DIAL:
+ {
+ Autonat.Message.Dial dial = msg.getDial();
+ PeerId peerId = new PeerId(dial.getPeer().getId().toByteArray());
+ List requestedDials =
+ dial.getPeer().getAddrsList().stream()
+ .map(s -> Multiaddr.deserialize(s.toByteArray()))
+ .collect(Collectors.toList());
+ PeerId streamPeerId = stream.remotePeerId();
+ if (!peerId.equals(streamPeerId)) {
+ p2pstream.close();
+ return;
+ }
+
+ Multiaddr remote = stream.getConnection().remoteAddress();
+ Optional reachable =
+ requestedDials.stream()
+ .filter(a -> sameIP(a, remote))
+ .filter(a -> !a.has(Protocol.P2PCIRCUIT))
+ .filter(a -> reachableIP(a))
+ .findAny();
+ Autonat.Message.Builder resp =
+ Autonat.Message.newBuilder().setType(Autonat.Message.MessageType.DIAL_RESPONSE);
+ if (reachable.isPresent()) {
+ resp =
+ resp.setDialResponse(
+ Autonat.Message.DialResponse.newBuilder()
+ .setStatus(Autonat.Message.ResponseStatus.OK)
+ .setAddr(ByteString.copyFrom(reachable.get().serialize())));
+ } else {
+ resp =
+ resp.setDialResponse(
+ Autonat.Message.DialResponse.newBuilder()
+ .setStatus(Autonat.Message.ResponseStatus.E_DIAL_ERROR));
+ }
+ p2pstream.writeAndFlush(resp);
+ }
+ default:
+ {
+ }
+ }
+ }
+
+ public CompletableFuture rpc(Autonat.Message msg) {
+ return CompletableFuture.failedFuture(
+ new IllegalStateException("Cannot send form a receiver!"));
+ }
+ }
+
+ private static final int TRAFFIC_LIMIT = 2 * 1024;
+
+ public AutonatProtocol() {
+ super(Autonat.Message.getDefaultInstance(), TRAFFIC_LIMIT, TRAFFIC_LIMIT);
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartInitiator(@NotNull Stream stream) {
+ Sender replyPropagator = new Sender(stream);
+ stream.pushHandler(replyPropagator);
+ return CompletableFuture.completedFuture(replyPropagator);
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartResponder(@NotNull Stream stream) {
+ Receiver dialer = new Receiver(stream);
+ stream.pushHandler(dialer);
+ return CompletableFuture.completedFuture(dialer);
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitHopProtocol.java b/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitHopProtocol.java
new file mode 100644
index 000000000..be2be179d
--- /dev/null
+++ b/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitHopProtocol.java
@@ -0,0 +1,417 @@
+package io.libp2p.protocol.circuit;
+
+import com.google.protobuf.*;
+import io.libp2p.core.*;
+import io.libp2p.core.Stream;
+import io.libp2p.core.crypto.*;
+import io.libp2p.core.multiformats.*;
+import io.libp2p.core.multistream.*;
+import io.libp2p.etc.util.netty.*;
+import io.libp2p.protocol.*;
+import io.libp2p.protocol.circuit.crypto.pb.*;
+import io.libp2p.protocol.circuit.pb.*;
+import io.netty.buffer.*;
+import io.netty.channel.*;
+import io.netty.handler.codec.protobuf.*;
+import java.io.*;
+import java.nio.charset.*;
+import java.time.*;
+import java.time.Duration;
+import java.time.temporal.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+import java.util.stream.*;
+import org.jetbrains.annotations.*;
+
+public class CircuitHopProtocol extends ProtobufProtocolHandler {
+
+ private static final String HOP_HANDLER_NAME = "HOP_HANDLER";
+ private static final String STREAM_CLEARER_NAME = "STREAM_CLEARER";
+
+ public static class Binding extends StrictProtocolBinding implements HostConsumer {
+ private final CircuitHopProtocol hop;
+
+ private Binding(CircuitHopProtocol hop) {
+ super("/libp2p/circuit/relay/0.2.0/hop", hop);
+ this.hop = hop;
+ }
+
+ public Binding(RelayManager manager, CircuitStopProtocol.Binding stop) {
+ this(new CircuitHopProtocol(manager, stop));
+ }
+
+ @Override
+ public void setHost(Host us) {
+ hop.setHost(us);
+ }
+ }
+
+ private static void putUvarint(OutputStream out, long x) throws IOException {
+ while (x >= 0x80) {
+ out.write((byte) (x | 0x80));
+ x >>= 7;
+ }
+ out.write((byte) x);
+ }
+
+ public static byte[] createVoucher(
+ PrivKey priv, PeerId relay, PeerId requestor, LocalDateTime expiry) {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ try {
+ putUvarint(bout, 0x0302);
+ } catch (IOException e) {
+ }
+ byte[] typeMulticodec = bout.toByteArray();
+ byte[] payload =
+ VoucherOuterClass.Voucher.newBuilder()
+ .setRelay(ByteString.copyFrom(relay.getBytes()))
+ .setPeer(ByteString.copyFrom(requestor.getBytes()))
+ .setExpiration(expiry.toEpochSecond(ZoneOffset.UTC) * 1_000_000_000)
+ .build()
+ .toByteArray();
+ byte[] signDomain = "libp2p-relay-rsvp".getBytes(StandardCharsets.UTF_8);
+ ByteArrayOutputStream toSign = new ByteArrayOutputStream();
+ try {
+ putUvarint(toSign, signDomain.length);
+ toSign.write(signDomain);
+ putUvarint(toSign, typeMulticodec.length);
+ toSign.write(typeMulticodec);
+ putUvarint(toSign, payload.length);
+ toSign.write(payload);
+ } catch (IOException e) {
+ }
+ byte[] signature = priv.sign(toSign.toByteArray());
+ return EnvelopeOuterClass.Envelope.newBuilder()
+ .setPayloadType(ByteString.copyFrom(typeMulticodec))
+ .setPayload(ByteString.copyFrom(payload))
+ .setPublicKey(
+ EnvelopeOuterClass.PublicKey.newBuilder()
+ .setTypeValue(priv.publicKey().getKeyType().getNumber())
+ .setData(ByteString.copyFrom(priv.publicKey().raw())))
+ .setSignature(ByteString.copyFrom(signature))
+ .build()
+ .toByteArray();
+ }
+
+ public static class Reservation {
+ public final LocalDateTime expiry;
+ public final int durationSeconds;
+ public final long maxBytes;
+ public final byte[] voucher;
+ public final Multiaddr[] addrs;
+
+ public Reservation(
+ LocalDateTime expiry,
+ int durationSeconds,
+ long maxBytes,
+ byte[] voucher,
+ Multiaddr[] addrs) {
+ this.expiry = expiry;
+ this.durationSeconds = durationSeconds;
+ this.maxBytes = maxBytes;
+ this.voucher = voucher;
+ this.addrs = addrs;
+ }
+ }
+
+ public interface RelayManager {
+ boolean hasReservation(PeerId source);
+
+ Optional createReservation(PeerId requestor, Multiaddr addr);
+
+ Optional allowConnection(PeerId target, PeerId initiator);
+
+ static RelayManager limitTo(PrivKey priv, PeerId relayPeerId, int concurrent) {
+ return new RelayManager() {
+ Map reservations = new HashMap<>();
+
+ @Override
+ public synchronized boolean hasReservation(PeerId source) {
+ return reservations.containsKey(source);
+ }
+
+ @Override
+ public synchronized Optional createReservation(
+ PeerId requestor, Multiaddr addr) {
+ if (reservations.size() >= concurrent) return Optional.empty();
+ LocalDateTime now = LocalDateTime.now();
+ LocalDateTime expiry = now.plusHours(1);
+ byte[] voucher = createVoucher(priv, relayPeerId, requestor, now);
+ Reservation resv = new Reservation(expiry, 120, 4096, voucher, new Multiaddr[] {addr});
+ reservations.put(requestor, resv);
+ return Optional.of(resv);
+ }
+
+ @Override
+ public synchronized Optional allowConnection(PeerId target, PeerId initiator) {
+ return Optional.ofNullable(reservations.get(target));
+ }
+ };
+ }
+ }
+
+ public interface HopController {
+ CompletableFuture rpc(Circuit.HopMessage req);
+
+ default CompletableFuture reserve() {
+ return rpc(Circuit.HopMessage.newBuilder().setType(Circuit.HopMessage.Type.RESERVE).build())
+ .thenApply(
+ msg -> {
+ if (msg.getStatus() == Circuit.Status.OK) {
+ long expiry = msg.getReservation().getExpire();
+ return new Reservation(
+ LocalDateTime.ofEpochSecond(expiry, 0, ZoneOffset.UTC),
+ msg.getLimit().getDuration(),
+ msg.getLimit().getData(),
+ msg.getReservation().getVoucher().toByteArray(),
+ null);
+ }
+ throw new IllegalStateException(msg.getStatus().name());
+ });
+ }
+
+ CompletableFuture connect(PeerId target);
+ }
+
+ public static class HopRemover extends ChannelInitializer {
+
+ @Override
+ protected void initChannel(@NotNull Channel ch) throws Exception {
+ ch.pipeline().remove(HOP_HANDLER_NAME);
+ // also remove associated protobuf handlers
+ ch.pipeline().remove(ProtobufDecoder.class);
+ ch.pipeline().remove(ProtobufEncoder.class);
+ ch.pipeline().remove(ProtobufVarint32FrameDecoder.class);
+ ch.pipeline().remove(ProtobufVarint32LengthFieldPrepender.class);
+ ch.pipeline().remove(STREAM_CLEARER_NAME);
+ }
+ }
+
+ public static class Sender implements ProtocolMessageHandler, HopController {
+ private final Stream stream;
+ private final LinkedBlockingDeque> queue =
+ new LinkedBlockingDeque<>();
+
+ public Sender(Stream stream) {
+ this.stream = stream;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Circuit.HopMessage msg) {
+ queue.poll().complete(msg);
+ }
+
+ public CompletableFuture rpc(Circuit.HopMessage req) {
+ CompletableFuture res = new CompletableFuture<>();
+ queue.add(res);
+ stream.writeAndFlush(req);
+ return res;
+ }
+
+ @Override
+ public CompletableFuture connect(PeerId target) {
+ return rpc(Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.CONNECT)
+ .setPeer(Circuit.Peer.newBuilder().setId(ByteString.copyFrom(target.getBytes())))
+ .build())
+ .thenApply(
+ msg -> {
+ if (msg.getType() == Circuit.HopMessage.Type.STATUS
+ && msg.getStatus() == Circuit.Status.OK) {
+ // remove handler for HOP to return bare stream
+ stream.pushHandler(STREAM_CLEARER_NAME, new HopRemover());
+ return stream;
+ }
+ throw new IllegalStateException("Circuit dial returned " + msg.getStatus().name());
+ });
+ }
+ }
+
+ public static class Receiver
+ implements ProtocolMessageHandler, HopController {
+ private final Host us;
+ private final RelayManager manager;
+ private final Supplier> publicAddresses;
+ private final CircuitStopProtocol.Binding stop;
+ private final AddressBook addressBook;
+
+ public Receiver(
+ Host us,
+ RelayManager manager,
+ Supplier> publicAddresses,
+ CircuitStopProtocol.Binding stop,
+ AddressBook addressBook) {
+ this.us = us;
+ this.manager = manager;
+ this.publicAddresses = publicAddresses;
+ this.stop = stop;
+ this.addressBook = addressBook;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Circuit.HopMessage msg) {
+ switch (msg.getType()) {
+ case RESERVE:
+ {
+ PeerId requestor = stream.remotePeerId();
+ Optional reservation =
+ manager.createReservation(requestor, stream.getConnection().remoteAddress());
+ if (reservation.isEmpty()
+ || new Multiaddr(stream.getConnection().remoteAddress().toString())
+ .has(Protocol.P2PCIRCUIT)) {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.RESERVATION_REFUSED));
+ return;
+ }
+ Reservation resv = reservation.get();
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.OK)
+ .setReservation(
+ Circuit.Reservation.newBuilder()
+ .setExpire(resv.expiry.toEpochSecond(ZoneOffset.UTC))
+ .addAllAddrs(
+ publicAddresses.get().stream()
+ .map(a -> ByteString.copyFrom(a.serialize()))
+ .collect(Collectors.toList()))
+ .setVoucher(ByteString.copyFrom(resv.voucher)))
+ .setLimit(
+ Circuit.Limit.newBuilder()
+ .setDuration(resv.durationSeconds)
+ .setData(resv.maxBytes)));
+ }
+ case CONNECT:
+ {
+ PeerId target = new PeerId(msg.getPeer().getId().toByteArray());
+ if (manager.hasReservation(target)) {
+ PeerId initiator = stream.remotePeerId();
+ Optional res = manager.allowConnection(target, initiator);
+ if (res.isPresent()) {
+ Reservation resv = res.get();
+ try {
+ CircuitStopProtocol.StopController stop =
+ this.stop
+ .dial(us, target, resv.addrs)
+ .getController()
+ .orTimeout(15, TimeUnit.SECONDS)
+ .join();
+ Circuit.StopMessage reply =
+ stop.connect(initiator, resv.durationSeconds, resv.maxBytes).join();
+ if (reply.getStatus().equals(Circuit.Status.OK)) {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.OK));
+ Stream toTarget = stop.getStream();
+ Stream fromRequestor = stream;
+ // remove hop and stop handlers from streams before proxying
+ fromRequestor.pushHandler(STREAM_CLEARER_NAME, new HopRemover());
+ toTarget.pushHandler(
+ CircuitStopProtocol.STOP_REMOVER_NAME,
+ new CircuitStopProtocol.StopRemover());
+
+ // connect these streams with time + bytes enforcement
+ fromRequestor.pushHandler(new InboundTrafficLimitHandler(resv.maxBytes));
+ fromRequestor.pushHandler(
+ new TotalTimeoutHandler(
+ Duration.of(resv.durationSeconds, ChronoUnit.SECONDS)));
+ toTarget.pushHandler(new InboundTrafficLimitHandler(resv.maxBytes));
+ toTarget.pushHandler(
+ new TotalTimeoutHandler(
+ Duration.of(resv.durationSeconds, ChronoUnit.SECONDS)));
+ fromRequestor.pushHandler(new ProxyHandler(toTarget));
+ toTarget.pushHandler(new ProxyHandler(fromRequestor));
+ } else {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(reply.getStatus()));
+ }
+ } catch (Exception e) {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.CONNECTION_FAILED));
+ }
+ } else {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.RESOURCE_LIMIT_EXCEEDED));
+ }
+ } else {
+ stream.writeAndFlush(
+ Circuit.HopMessage.newBuilder()
+ .setType(Circuit.HopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.NO_RESERVATION));
+ }
+ }
+ }
+ }
+
+ @Override
+ public CompletableFuture connect(PeerId target) {
+ return CompletableFuture.failedFuture(
+ new IllegalStateException("Cannot send from a receiver!"));
+ }
+
+ public CompletableFuture rpc(Circuit.HopMessage msg) {
+ return CompletableFuture.failedFuture(
+ new IllegalStateException("Cannot send from a receiver!"));
+ }
+ }
+
+ private static class ProxyHandler extends ChannelInboundHandlerAdapter {
+
+ private final Stream target;
+
+ public ProxyHandler(Stream target) {
+ this.target = target;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof ByteBuf) {
+ target.writeAndFlush(msg);
+ }
+ }
+ }
+
+ private static final int TRAFFIC_LIMIT = 2 * 1024;
+ private final RelayManager manager;
+ private final CircuitStopProtocol.Binding stop;
+ private Host us;
+
+ public CircuitHopProtocol(RelayManager manager, CircuitStopProtocol.Binding stop) {
+ super(Circuit.HopMessage.getDefaultInstance(), TRAFFIC_LIMIT, TRAFFIC_LIMIT);
+ this.manager = manager;
+ this.stop = stop;
+ }
+
+ public void setHost(Host us) {
+ this.us = us;
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartInitiator(@NotNull Stream stream) {
+ Sender replyPropagator = new Sender(stream);
+ stream.pushHandler(
+ HOP_HANDLER_NAME, new ProtocolMessageHandlerAdapter<>(stream, replyPropagator));
+ return CompletableFuture.completedFuture(replyPropagator);
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartResponder(@NotNull Stream stream) {
+ if (us == null) throw new IllegalStateException("null Host for us!");
+ Supplier> ourpublicAddresses = () -> us.listenAddresses();
+ Receiver dialer = new Receiver(us, manager, ourpublicAddresses, stop, us.getAddressBook());
+ stream.pushHandler(HOP_HANDLER_NAME, new ProtocolMessageHandlerAdapter<>(stream, dialer));
+ return CompletableFuture.completedFuture(dialer);
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitStopProtocol.java b/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitStopProtocol.java
new file mode 100644
index 000000000..b10ee62d4
--- /dev/null
+++ b/libp2p/src/main/java/io/libp2p/protocol/circuit/CircuitStopProtocol.java
@@ -0,0 +1,157 @@
+package io.libp2p.protocol.circuit;
+
+import com.google.protobuf.*;
+import io.libp2p.core.*;
+import io.libp2p.core.multistream.*;
+import io.libp2p.protocol.*;
+import io.libp2p.protocol.circuit.pb.*;
+import io.netty.channel.*;
+import io.netty.handler.codec.protobuf.*;
+import java.util.concurrent.*;
+import org.jetbrains.annotations.*;
+
+public class CircuitStopProtocol
+ extends ProtobufProtocolHandler {
+
+ private static final String STOP_HANDLER_NAME = "STOP_HANDLER";
+ public static final String STOP_REMOVER_NAME = "STOP_REMOVER";
+
+ public static class Binding extends StrictProtocolBinding {
+ private final CircuitStopProtocol stop;
+
+ public Binding(CircuitStopProtocol stop) {
+ super("/libp2p/circuit/relay/0.2.0/stop", stop);
+ this.stop = stop;
+ }
+
+ public void setTransport(RelayTransport transport) {
+ stop.setTransport(transport);
+ }
+ }
+
+ public interface StopController {
+ CompletableFuture rpc(Circuit.StopMessage req);
+
+ Stream getStream();
+
+ default CompletableFuture connect(
+ PeerId source, int durationSeconds, long maxBytes) {
+ return rpc(
+ Circuit.StopMessage.newBuilder()
+ .setType(Circuit.StopMessage.Type.CONNECT)
+ .setPeer(Circuit.Peer.newBuilder().setId(ByteString.copyFrom(source.getBytes())))
+ .setLimit(Circuit.Limit.newBuilder().setData(maxBytes).setDuration(durationSeconds))
+ .build());
+ }
+ }
+
+ public static class Sender
+ implements ProtocolMessageHandler, StopController {
+ private final Stream stream;
+ private final LinkedBlockingDeque> queue =
+ new LinkedBlockingDeque<>();
+
+ public Sender(Stream stream) {
+ this.stream = stream;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Circuit.StopMessage msg) {
+ queue.poll().complete(msg);
+ }
+
+ public CompletableFuture rpc(Circuit.StopMessage req) {
+ CompletableFuture res = new CompletableFuture<>();
+ queue.add(res);
+ stream.writeAndFlush(req);
+ return res;
+ }
+
+ public Stream getStream() {
+ return stream;
+ }
+ }
+
+ public static class StopRemover extends ChannelInitializer {
+
+ @Override
+ protected void initChannel(@NotNull Channel ch) throws Exception {
+ ch.pipeline().remove(ProtobufDecoder.class);
+ ch.pipeline().remove(ProtobufEncoder.class);
+ ch.pipeline().remove(ProtobufVarint32FrameDecoder.class);
+ ch.pipeline().remove(ProtobufVarint32LengthFieldPrepender.class);
+ ch.pipeline().remove(STOP_HANDLER_NAME);
+ ch.pipeline().remove(STOP_REMOVER_NAME);
+ }
+ }
+
+ public static class Receiver
+ implements ProtocolMessageHandler, StopController {
+ private final Stream stream;
+ private final RelayTransport transport;
+
+ public Receiver(Stream stream, RelayTransport transport) {
+ this.stream = stream;
+ this.transport = transport;
+ }
+
+ @Override
+ public void onMessage(@NotNull Stream stream, Circuit.StopMessage msg) {
+ if (msg.getType() == Circuit.StopMessage.Type.CONNECT) {
+ PeerId remote = new PeerId(msg.getPeer().getId().toByteArray());
+ int durationSeconds = msg.getLimit().getDuration();
+ long limitBytes = msg.getLimit().getData();
+ stream.writeAndFlush(
+ Circuit.StopMessage.newBuilder()
+ .setType(Circuit.StopMessage.Type.STATUS)
+ .setStatus(Circuit.Status.OK)
+ .build());
+ // remove STOP handler from stream before upgrading
+ stream.pushHandler(STOP_REMOVER_NAME, new StopRemover());
+
+ // now upgrade connection with security and muxer protocol
+ ConnectionHandler connHandler = null; // TODO
+ RelayTransport.upgradeStream(
+ stream, false, transport.upgrader, transport, remote, connHandler);
+ }
+ }
+
+ public Stream getStream() {
+ return stream;
+ }
+
+ public CompletableFuture rpc(Circuit.StopMessage msg) {
+ return CompletableFuture.failedFuture(
+ new IllegalStateException("Cannot send form a receiver!"));
+ }
+ }
+
+ private static final int TRAFFIC_LIMIT = 2 * 1024;
+
+ private RelayTransport transport;
+
+ public CircuitStopProtocol() {
+ super(Circuit.StopMessage.getDefaultInstance(), TRAFFIC_LIMIT, TRAFFIC_LIMIT);
+ }
+
+ public void setTransport(RelayTransport transport) {
+ this.transport = transport;
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartInitiator(@NotNull Stream stream) {
+ Sender replyPropagator = new Sender(stream);
+ stream.pushHandler(
+ STOP_HANDLER_NAME, new ProtocolMessageHandlerAdapter<>(stream, replyPropagator));
+ return CompletableFuture.completedFuture(replyPropagator);
+ }
+
+ @NotNull
+ @Override
+ protected CompletableFuture onStartResponder(@NotNull Stream stream) {
+ Receiver acceptor = new Receiver(stream, transport);
+ stream.pushHandler(STOP_HANDLER_NAME, new ProtocolMessageHandlerAdapter<>(stream, acceptor));
+ return CompletableFuture.completedFuture(acceptor);
+ }
+}
diff --git a/libp2p/src/main/java/io/libp2p/protocol/circuit/HostConsumer.java b/libp2p/src/main/java/io/libp2p/protocol/circuit/HostConsumer.java
new file mode 100644
index 000000000..c3848e699
--- /dev/null
+++ b/libp2p/src/main/java/io/libp2p/protocol/circuit/HostConsumer.java
@@ -0,0 +1,8 @@
+package io.libp2p.protocol.circuit;
+
+import io.libp2p.core.*;
+
+public interface HostConsumer {
+
+ void setHost(Host us);
+}
diff --git a/libp2p/src/main/java/io/libp2p/protocol/circuit/RelayTransport.java b/libp2p/src/main/java/io/libp2p/protocol/circuit/RelayTransport.java
new file mode 100644
index 000000000..a6d44e1f8
--- /dev/null
+++ b/libp2p/src/main/java/io/libp2p/protocol/circuit/RelayTransport.java
@@ -0,0 +1,326 @@
+package io.libp2p.protocol.circuit;
+
+import io.libp2p.core.*;
+import io.libp2p.core.Stream;
+import io.libp2p.core.multiformats.*;
+import io.libp2p.core.mux.*;
+import io.libp2p.core.security.*;
+import io.libp2p.core.transport.*;
+import io.libp2p.etc.*;
+import io.libp2p.transport.*;
+import io.netty.channel.*;
+import java.time.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import java.util.function.Function;
+import java.util.stream.*;
+import kotlin.*;
+import org.jetbrains.annotations.*;
+
+public class RelayTransport implements Transport, HostConsumer {
+ private Host us;
+ private final Map listeners = new ConcurrentHashMap<>();
+ private final Map dials = new ConcurrentHashMap<>();
+ private final Function> candidateRelays;
+ private final CircuitHopProtocol.Binding hop;
+ private final CircuitStopProtocol.Binding stop;
+ public final ConnectionUpgrader upgrader;
+ private final AtomicInteger relayCount;
+ private final ScheduledExecutorService runner;
+
+ public RelayTransport(
+ CircuitHopProtocol.Binding hop,
+ CircuitStopProtocol.Binding stop,
+ ConnectionUpgrader upgrader,
+ Function> candidateRelays,
+ ScheduledExecutorService runner) {
+ this.hop = hop;
+ this.stop = stop;
+ this.upgrader = upgrader;
+ this.candidateRelays = candidateRelays;
+ this.relayCount = new AtomicInteger(0);
+ this.runner = runner;
+ }
+
+ @Override
+ public void setHost(Host us) {
+ this.us = us;
+ hop.setHost(us);
+ }
+
+ public static class CandidateRelay {
+ public final PeerId id;
+ public final List addrs;
+
+ public CandidateRelay(PeerId id, List addrs) {
+ this.id = id;
+ this.addrs = addrs;
+ }
+ }
+
+ private static class RelayState {
+ List addrs;
+ CircuitHopProtocol.HopController controller;
+ Connection conn;
+ LocalDateTime renewAfter;
+ }
+
+ public void setRelayCount(int count) {
+ relayCount.set(count);
+ }
+
+ @Override
+ public int getActiveConnections() {
+ return dials.size();
+ }
+
+ @Override
+ public int getActiveListeners() {
+ return listeners.size();
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture close() {
+ return CompletableFuture.allOf(
+ dials.values().stream().map(Stream::close).toArray(CompletableFuture[]::new))
+ .thenApply(
+ x -> {
+ dials.clear();
+ return null;
+ });
+ }
+
+ static class ConnectionOverStream implements Connection {
+ private final boolean isInitiator;
+ private final Transport transport;
+ private final Stream stream;
+ private SecureChannel.Session security;
+ private StreamMuxer.Session muxer;
+
+ public ConnectionOverStream(boolean isInitiator, Transport transport, Stream stream) {
+ this.isInitiator = isInitiator;
+ this.transport = transport;
+ this.stream = stream;
+ }
+
+ @NotNull
+ @Override
+ public Multiaddr localAddress() {
+ return stream.getConnection().localAddress().withComponent(Protocol.P2PCIRCUIT);
+ }
+
+ @NotNull
+ @Override
+ public Multiaddr remoteAddress() {
+ return stream.getConnection().remoteAddress().withComponent(Protocol.P2PCIRCUIT);
+ }
+
+ public void setSecureSession(SecureChannel.Session sec) {
+ this.security = sec;
+ }
+
+ @NotNull
+ @Override
+ public SecureChannel.Session secureSession() {
+ return security;
+ }
+
+ public void setMuxerSession(StreamMuxer.Session mux) {
+ this.muxer = mux;
+ }
+
+ @NotNull
+ @Override
+ public StreamMuxer.Session muxerSession() {
+ return muxer;
+ }
+
+ @NotNull
+ @Override
+ public Transport transport() {
+ return transport;
+ }
+
+ @Override
+ public boolean isInitiator() {
+ return isInitiator;
+ }
+
+ @Override
+ public void addHandlerBefore(
+ @NotNull String s, @NotNull String s1, @NotNull ChannelHandler channelHandler) {
+ stream.addHandlerBefore(s, s1, channelHandler);
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture close() {
+ return stream.close();
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture closeFuture() {
+ return stream.closeFuture();
+ }
+
+ @Override
+ public void pushHandler(@NotNull ChannelHandler channelHandler) {
+ stream.pushHandler(channelHandler);
+ }
+
+ @Override
+ public void pushHandler(@NotNull String s, @NotNull ChannelHandler channelHandler) {
+ stream.pushHandler(s, channelHandler);
+ }
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture dial(
+ @NotNull Multiaddr multiaddr,
+ @NotNull ConnectionHandler connHandler,
+ @Nullable ChannelVisitor channelVisitor) {
+ // first connect to relay over hop
+ List comps = multiaddr.getComponents();
+ int split = comps.indexOf(new MultiaddrComponent(Protocol.P2PCIRCUIT, null));
+ Multiaddr relay = new Multiaddr(comps.subList(0, split));
+ Multiaddr target = new Multiaddr(comps.subList(split, comps.size()));
+ CircuitHopProtocol.HopController ctr = hop.dial(us, relay).getController().join();
+ // request proxy to target
+ Stream stream = ctr.connect(target.getPeerId()).join();
+ // upgrade with sec and muxer
+ return upgradeStream(stream, true, upgrader, this, target.getPeerId(), connHandler);
+ }
+
+ public static CompletableFuture upgradeStream(
+ Stream stream,
+ boolean isInitiator,
+ ConnectionUpgrader upgrader,
+ Transport transport,
+ PeerId remote,
+ ConnectionHandler connHandler) {
+ ConnectionOverStream conn = new ConnectionOverStream(isInitiator, transport, stream);
+ CompletableFuture res = new CompletableFuture<>();
+ stream.pushHandler(
+ new ChannelInitializer<>() {
+ @Override
+ protected void initChannel(Channel channel) throws Exception {
+ channel.attr(AttributesKt.getREMOTE_PEER_ID()).set(remote);
+ channel.attr(AttributesKt.getCONNECTION()).set(conn);
+ upgrader
+ .establishSecureChannel(conn)
+ .thenCompose(
+ sess -> {
+ conn.setSecureSession(sess);
+ if (sess.getEarlyMuxer() != null) {
+ return ConnectionUpgrader.Companion.establishMuxer(
+ sess.getEarlyMuxer(), conn);
+ } else {
+ return upgrader.establishMuxer(conn);
+ }
+ })
+ .thenAccept(
+ sess -> {
+ conn.setMuxerSession(sess);
+ connHandler.handleConnection(conn);
+ res.complete(conn);
+ })
+ .exceptionally(
+ t -> {
+ res.completeExceptionally(t);
+ return null;
+ });
+ channel.pipeline().fireChannelActive();
+ }
+ });
+ return res;
+ }
+
+ @Override
+ public boolean handles(@NotNull Multiaddr multiaddr) {
+ return multiaddr.hasAny(Protocol.P2PCIRCUIT);
+ }
+
+ @Override
+ public void initialize() {
+ stop.setTransport(this);
+ // find relays and connect and reserve
+ runner.scheduleAtFixedRate(this::ensureEnoughCurrentRelays, 0, 2 * 60, TimeUnit.SECONDS);
+ }
+
+ public void ensureEnoughCurrentRelays() {
+ int active = 0;
+ // renew existing relays before finding new ones
+ Set> currentRelays = listeners.entrySet();
+ for (Map.Entry current : currentRelays) {
+ RelayState relay = current.getValue();
+ LocalDateTime now = LocalDateTime.now();
+ if (now.isBefore(relay.renewAfter)) {
+ active++;
+ } else {
+ try {
+ CircuitHopProtocol.Reservation reservation = relay.controller.reserve().join();
+ relay.renewAfter = reservation.expiry.minusMinutes(1);
+ active++;
+ } catch (Exception e) {
+ listeners.remove(current.getKey());
+ }
+ }
+ }
+ if (active >= relayCount.get()) return;
+
+ List candidates = candidateRelays.apply(us);
+ for (CandidateRelay candidate : candidates) {
+ // connect to relay and get reservation
+ CircuitHopProtocol.HopController ctr =
+ hop.dial(us, candidate.id, candidate.addrs.toArray(new Multiaddr[0]))
+ .getController()
+ .join();
+ CircuitHopProtocol.Reservation resv = ctr.reserve().join();
+ active++;
+ listeners.put(candidate.id, new RelayState());
+ if (active >= relayCount.get()) return;
+ }
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture listen(
+ @NotNull Multiaddr relayAddr,
+ @NotNull ConnectionHandler connectionHandler,
+ @Nullable ChannelVisitor channelVisitor) {
+ List components = relayAddr.getComponents();
+ Multiaddr withoutCircuit = new Multiaddr(components.subList(0, components.size() - 1));
+ CircuitHopProtocol.HopController ctr = hop.dial(us, withoutCircuit).getController().join();
+ return ctr.reserve().thenApply(res -> null);
+ }
+
+ @NotNull
+ @Override
+ public List listenAddresses() {
+ return listeners.entrySet().stream()
+ .flatMap(
+ r ->
+ r.getValue().addrs.stream()
+ .map(
+ a ->
+ a.withP2P(r.getKey())
+ .concatenated(
+ new Multiaddr(
+ List.of(
+ new MultiaddrComponent(Protocol.P2PCIRCUIT, null)))
+ .withP2P(us.getPeerId()))))
+ .collect(Collectors.toList());
+ }
+
+ @NotNull
+ @Override
+ public CompletableFuture unlisten(@NotNull Multiaddr multiaddr) {
+ RelayState relayState = listeners.get(multiaddr);
+ if (relayState == null) return CompletableFuture.completedFuture(null);
+ return relayState.conn.close();
+ }
+}
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/Connection.kt b/libp2p/src/main/kotlin/io/libp2p/core/Connection.kt
index 579b9a1a0..64eb52b64 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/Connection.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/Connection.kt
@@ -27,6 +27,7 @@ interface Connection : P2PChannel {
* Returns the local [Multiaddr] of this [Connection]
*/
fun localAddress(): Multiaddr
+
/**
* Returns the remote [Multiaddr] of this [Connection]
*/
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/Host.kt b/libp2p/src/main/kotlin/io/libp2p/core/Host.kt
index cfdb3586d..1ba4685b0 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/Host.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/Host.kt
@@ -15,14 +15,17 @@ interface Host {
* Our private key which can be used by different protocols to sign messages
*/
val privKey: PrivKey
+
/**
* Our [PeerId] which is normally derived from [privKey]
*/
val peerId: PeerId
+
/**
* [Network] implementation
*/
val network: Network
+
/**
* [AddressBook] implementation
*/
@@ -81,6 +84,8 @@ interface Host {
*/
fun addProtocolHandler(protocolBinding: ProtocolBinding)
+ fun getProtocols(): List>
+
/**
* Removes the handler added with [addProtocolHandler]
*/
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/Libp2pException.kt b/libp2p/src/main/kotlin/io/libp2p/core/Libp2pException.kt
index 31bab6068..b25269f7c 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/Libp2pException.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/Libp2pException.kt
@@ -50,6 +50,7 @@ open class NoSuchProtocolException(message: String) : Libp2pException(message)
* Indicates that the protocol is not registered at local configuration
*/
class NoSuchLocalProtocolException(message: String) : NoSuchProtocolException(message)
+
/**
* Indicates that the protocol is not known by the remote party
*/
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/crypto/Key.kt b/libp2p/src/main/kotlin/io/libp2p/core/crypto/Key.kt
index 3324c510c..7d394966d 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/crypto/Key.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/crypto/Key.kt
@@ -31,7 +31,7 @@ import java.security.SecureRandom
import crypto.pb.Crypto.PrivateKey as PbPrivateKey
import crypto.pb.Crypto.PublicKey as PbPublicKey
-enum class KEY_TYPE {
+enum class KeyType {
/**
* RSA is an enum for the supported RSA key type
@@ -56,7 +56,7 @@ enum class KEY_TYPE {
interface Key {
- val keyType: crypto.pb.Crypto.KeyType
+ val keyType: Crypto.KeyType
/**
* Bytes returns a serialized, storeable representation of this key.
@@ -124,12 +124,12 @@ abstract class PubKey(override val keyType: Crypto.KeyType) : Key {
* @param bits the number of bits desired for the key (only applicable for RSA).
*/
@JvmOverloads
-fun generateKeyPair(type: KEY_TYPE, bits: Int = 2048, random: SecureRandom = SecureRandom()): Pair {
+fun generateKeyPair(type: KeyType, bits: Int = 2048, random: SecureRandom = SecureRandom()): Pair {
return when (type) {
- KEY_TYPE.RSA -> generateRsaKeyPair(bits, random)
- KEY_TYPE.ED25519 -> generateEd25519KeyPair(random)
- KEY_TYPE.SECP256K1 -> generateSecp256k1KeyPair(random)
- KEY_TYPE.ECDSA -> generateEcdsaKeyPair(random)
+ KeyType.RSA -> generateRsaKeyPair(bits, random)
+ KeyType.ED25519 -> generateEd25519KeyPair(random)
+ KeyType.SECP256K1 -> generateSecp256k1KeyPair(random)
+ KeyType.ECDSA -> generateEcdsaKeyPair(random)
}
}
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt
index 5482b5e8e..ce1416dfd 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/dsl/Builders.kt
@@ -8,7 +8,7 @@ import io.libp2p.core.ConnectionHandler
import io.libp2p.core.Host
import io.libp2p.core.P2PChannel
import io.libp2p.core.Stream
-import io.libp2p.core.crypto.KEY_TYPE
+import io.libp2p.core.crypto.KeyType
import io.libp2p.core.crypto.PrivKey
import io.libp2p.core.crypto.generateKeyPair
import io.libp2p.core.multiformats.Multiaddr
@@ -23,6 +23,7 @@ import io.libp2p.core.security.SecureChannel
import io.libp2p.core.transport.Transport
import io.libp2p.etc.types.lazyVar
import io.libp2p.etc.types.toProtobuf
+import io.libp2p.etc.util.netty.LoggingHandlerShort
import io.libp2p.host.HostImpl
import io.libp2p.host.MemoryAddressBook
import io.libp2p.network.NetworkImpl
@@ -33,6 +34,7 @@ import io.libp2p.transport.tcp.TcpTransport
import io.netty.channel.ChannelHandler
import io.netty.handler.logging.LogLevel
import io.netty.handler.logging.LoggingHandler
+import java.util.concurrent.CopyOnWriteArrayList
typealias TransportCtor = (ConnectionUpgrader) -> Transport
typealias SecureChannelCtor = (PrivKey, List) -> SecureChannel
@@ -173,7 +175,8 @@ open class Builder {
}
}
- val muxers = muxers.map { it.createMuxer(streamMultistreamProtocol, protocols.values) }
+ val updatableProtocols: MutableList> = CopyOnWriteArrayList(protocols.values)
+ val muxers = muxers.map { it.createMuxer(streamMultistreamProtocol, updatableProtocols) }
val secureChannels = secureChannels.values.map { it(privKey, muxers) }
@@ -201,7 +204,7 @@ open class Builder {
networkImpl,
addressBook,
network.listen.map { Multiaddr(it) },
- protocols.values,
+ updatableProtocols,
broadcastConnHandler,
streamVisitors
)
@@ -217,8 +220,8 @@ class NetworkConfigBuilder {
class IdentityBuilder {
var factory: IdentityFactory? = null
- fun random() = random(KEY_TYPE.ECDSA)
- fun random(keyType: KEY_TYPE): IdentityBuilder = apply { factory = { generateKeyPair(keyType).first } }
+ fun random() = random(KeyType.ECDSA)
+ fun random(keyType: KeyType): IdentityBuilder = apply { factory = { generateKeyPair(keyType).first } }
}
class AddressBookBuilder {
@@ -239,11 +242,13 @@ class DebugBuilder {
* Could be primarily useful for security handshake debugging/monitoring
*/
val beforeSecureHandler = DebugHandlerBuilder("wire.sec.before")
+
/**
* Injects the [ChannelHandler] right after the connection cipher
* to handle plain wire messages
*/
val afterSecureHandler = DebugHandlerBuilder("wire.sec.after")
+
/**
* Injects the [ChannelHandler] right after the [StreamMuxer] pipeline handler
* It intercepts [io.libp2p.mux.MuxFrame] instances
@@ -269,6 +274,10 @@ class DebugHandlerBuilder(var name: String) {
fun addLogger(level: LogLevel, loggerName: String = name) {
addNettyHandler(LoggingHandler(loggerName, level))
}
+
+ fun addCompactLogger(level: LogLevel, loggerName: String = name) {
+ addNettyHandler(LoggingHandlerShort(loggerName, level))
+ }
}
open class Enumeration(val values: MutableList = mutableListOf()) : MutableList by values {
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multiformats/Multiaddr.kt b/libp2p/src/main/kotlin/io/libp2p/core/multiformats/Multiaddr.kt
index d918c1bfb..8bc56d621 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/multiformats/Multiaddr.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/multiformats/Multiaddr.kt
@@ -61,7 +61,11 @@ data class Multiaddr(val components: List) {
* @throws IllegalArgumentException if existing component value doesn't match [value]
*/
private fun withComponentImpl(protocol: Protocol, value: ByteArray?): Multiaddr {
- val existingComponent = getFirstComponent(protocol)
+ val existingComponent = if (has(Protocol.P2PCIRCUIT)) {
+ split { it == Protocol.P2PCIRCUIT }.get(1).getFirstComponent(protocol)
+ } else {
+ getFirstComponent(protocol)
+ }
val newComponent = MultiaddrComponent(protocol, value)
return if (existingComponent != null) {
if (!existingComponent.value.contentEquals(value)) {
diff --git a/libp2p/src/main/kotlin/io/libp2p/core/multiformats/MultiaddrDns.kt b/libp2p/src/main/kotlin/io/libp2p/core/multiformats/MultiaddrDns.kt
index 294fc5a8a..2fe118b27 100644
--- a/libp2p/src/main/kotlin/io/libp2p/core/multiformats/MultiaddrDns.kt
+++ b/libp2p/src/main/kotlin/io/libp2p/core/multiformats/MultiaddrDns.kt
@@ -17,18 +17,20 @@ class MultiaddrDns {
private val dnsProtocols = arrayOf(Protocol.DNS4, Protocol.DNS6, Protocol.DNSADDR)
fun resolve(addr: Multiaddr, resolver: Resolver = DefaultResolver): List {
- if (!addr.hasAny(*dnsProtocols))
+ if (!addr.hasAny(*dnsProtocols)) {
return listOf(addr)
+ }
val addressesToResolve = addr.split { isDnsProtocol(it) }
val resolvedAddresses = mutableListOf>()
for (address in addressesToResolve) {
val toResolve = address.filterComponents(*dnsProtocols).firstOrNull()
- val resolved = if (toResolve != null)
+ val resolved = if (toResolve != null) {
resolve(toResolve.protocol, toResolve.stringValue!!, address, resolver)
- else
+ } else {
listOf(address)
+ }
resolvedAddresses.add(resolved)
}
@@ -70,13 +72,14 @@ class MultiaddrDns {
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.1
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.2
private fun crossProduct(addressMatrix: List>): List