// Copyright 2003-2005 Arthur van Hoff, Rick Blair // Licensed under Apache License version 2.0 // Original license LGPL package javax.jmdns.impl; import java.io.IOException; import java.net.DatagramPacket; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.jmdns.NetworkTopologyDiscovery; import javax.jmdns.impl.constants.DNSConstants; import javax.jmdns.impl.constants.DNSRecordClass; import javax.jmdns.impl.constants.DNSRecordType; import javax.jmdns.impl.constants.DNSState; import javax.jmdns.impl.tasks.DNSTask; import android.util.Log; /** * HostInfo information on the local host to be able to cope with change of addresses. * * @author Pierre Frisch, Werner Randelshofer */ public class HostInfo implements DNSStatefulObject { private static Logger logger = Logger.getLogger(HostInfo.class.getName()); protected String _name; protected InetAddress _address; protected NetworkInterface _interfaze; private final HostInfoState _state; private final static class HostInfoState extends DNSStatefulObject.DefaultImplementation { private static final long serialVersionUID = -8191476803620402088L; /** * @param dns */ public HostInfoState(JmDNSImpl dns) { super(); this.setDns(dns); } } /** * @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) { Log.i("mDNS","newHostInfo"); HostInfo localhost = null; String aName = ""; InetAddress addr = address; Log.i("mDNS","addr:"+addr); try { if (addr == null) { String ip = System.getProperty("net.mdns.interface"); Log.i("mDNS","ip"+ip); if (ip != null) { addr = InetAddress.getByName(ip); } else { addr = InetAddress.getLocalHost(); Log.i("mDNS","addr:"+addr); if (addr.isLoopbackAddress()) { // Find local address that isn't a loopback address InetAddress[] addresses = NetworkTopologyDiscovery.Factory.getInstance().getInetAddresses(); if (addresses.length > 0) { addr = addresses[0]; } } } aName = addr.getHostName(); if (addr.isLoopbackAddress()) { logger.warning("Could not find any address beside the loopback."); } } else { 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.log(Level.WARNING, "Could not intialize 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. aName = aName.replace('.', '-'); aName += ".local."; localhost = new HostInfo(addr, aName, dns); return localhost; } private static InetAddress loopbackAddress() { try { return InetAddress.getByName(null); } catch (UnknownHostException exception) { return null; } } /** * This is used to create a unique name for the host name. */ private int hostNameCount; private HostInfo(final InetAddress address, final String name, final JmDNSImpl dns) { super(); this._state = new HostInfoState(dns); this._address = address; this._name = name; if (address != null) { try { _interfaze = NetworkInterface.getByInetAddress(address); } catch (Exception exception) { logger.log(Level.SEVERE, "LocalHostInfo() exception ", exception); } } } public String getName() { return _name; } public InetAddress getInetAddress() { return _address; } Inet4Address getInet4Address() { if (this.getInetAddress() instanceof Inet4Address) { return (Inet4Address) _address; } return null; } Inet6Address getInet6Address() { if (this.getInetAddress() instanceof Inet6Address) { return (Inet6Address) _address; } return null; } public NetworkInterface getInterface() { return _interfaze; } public boolean conflictWithRecord(DNSRecord.Address record) { DNSRecord.Address hostAddress = this.getDNSAddressRecord(record.getRecordType(), record.isUnique(), DNSConstants.DNS_TTL); if (hostAddress != null) { return hostAddress.sameType(record) && hostAddress.sameName(record) && (!hostAddress.sameValue(record)); } return false; } synchronized String incrementHostName() { hostNameCount++; int plocal = _name.indexOf(".local."); int punder = _name.lastIndexOf('-'); _name = _name.substring(0, (punder == -1 ? plocal : punder)) + "-" + hostNameCount + ".local."; return _name; } boolean shouldIgnorePacket(DatagramPacket packet) { boolean result = false; if (this.getInetAddress() != null) { InetAddress from = packet.getAddress(); if (from != null) { 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; } DNSRecord.Address getDNSAddressRecord(DNSRecordType type, boolean unique, int ttl) { switch (type) { case TYPE_A: return this.getDNS4AddressRecord(unique, ttl); case TYPE_A6: case TYPE_AAAA: return this.getDNS6AddressRecord(unique, ttl); default: } return null; } private DNSRecord.Address getDNS4AddressRecord(boolean unique, int ttl) { if ((this.getInetAddress() instanceof Inet4Address) || ((this.getInetAddress() instanceof Inet6Address) && (((Inet6Address) this.getInetAddress()).isIPv4CompatibleAddress()))) { return new DNSRecord.IPv4Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress()); } return null; } private DNSRecord.Address getDNS6AddressRecord(boolean unique, int ttl) { if (this.getInetAddress() instanceof Inet6Address) { return new DNSRecord.IPv6Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress()); } return null; } DNSRecord.Pointer getDNSReverseAddressRecord(DNSRecordType type, boolean unique, int ttl) { switch (type) { case TYPE_A: return this.getDNS4ReverseAddressRecord(unique, ttl); case TYPE_A6: case TYPE_AAAA: return this.getDNS6ReverseAddressRecord(unique, ttl); default: } return null; } private DNSRecord.Pointer getDNS4ReverseAddressRecord(boolean unique, int ttl) { if (this.getInetAddress() instanceof Inet4Address) { return new DNSRecord.Pointer(this.getInetAddress().getHostAddress() + ".in-addr.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName()); } if ((this.getInetAddress() instanceof Inet6Address) && (((Inet6Address) this.getInetAddress()).isIPv4CompatibleAddress())) { byte[] rawAddress = this.getInetAddress().getAddress(); String address = (rawAddress[12] & 0xff) + "." + (rawAddress[13] & 0xff) + "." + (rawAddress[14] & 0xff) + "." + (rawAddress[15] & 0xff); return new DNSRecord.Pointer(address + ".in-addr.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName()); } return null; } private DNSRecord.Pointer getDNS6ReverseAddressRecord(boolean unique, int ttl) { if (this.getInetAddress() instanceof Inet6Address) { return new DNSRecord.Pointer(this.getInetAddress().getHostAddress() + ".ip6.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName()); } return null; } @Override public String toString() { StringBuilder buf = new StringBuilder(1024); buf.append("local host info["); buf.append(getName() != null ? getName() : "no name"); buf.append(", "); buf.append(getInterface() != null ? getInterface().getDisplayName() : "???"); buf.append(":"); buf.append(getInetAddress() != null ? getInetAddress().getHostAddress() : "no address"); buf.append(", "); buf.append(_state); buf.append("]"); return buf.toString(); } public Collection<DNSRecord> answers(boolean unique, int ttl) { List<DNSRecord> list = new ArrayList<DNSRecord>(); DNSRecord answer = this.getDNS4AddressRecord(unique, ttl); if (answer != null) { list.add(answer); } answer = this.getDNS6AddressRecord(unique, ttl); if (answer != null) { list.add(answer); } return list; } /** * {@inheritDoc} */ @Override public JmDNSImpl getDns() { return this._state.getDns(); } /** * {@inheritDoc} */ @Override public boolean advanceState(DNSTask task) { return this._state.advanceState(task); } /** * {@inheritDoc} */ @Override public void removeAssociationWithTask(DNSTask task) { this._state.removeAssociationWithTask(task); } /** * {@inheritDoc} */ @Override public boolean revertState() { return this._state.revertState(); } /** * {@inheritDoc} */ @Override public void associateWithTask(DNSTask task, DNSState state) { this._state.associateWithTask(task, state); } /** * {@inheritDoc} */ @Override public boolean isAssociatedWithTask(DNSTask task, DNSState state) { return this._state.isAssociatedWithTask(task, state); } /** * {@inheritDoc} */ @Override public boolean cancelState() { return this._state.cancelState(); } /** * {@inheritDoc} */ @Override public boolean closeState() { return this._state.closeState(); } /** * {@inheritDoc} */ @Override public boolean recoverState() { return this._state.recoverState(); } /** * {@inheritDoc} */ @Override public boolean isProbing() { return this._state.isProbing(); } /** * {@inheritDoc} */ @Override public boolean isAnnouncing() { return this._state.isAnnouncing(); } /** * {@inheritDoc} */ @Override public boolean isAnnounced() { return this._state.isAnnounced(); } /** * {@inheritDoc} */ @Override public boolean isCanceling() { return this._state.isCanceling(); } /** * {@inheritDoc} */ @Override public boolean isCanceled() { return this._state.isCanceled(); } /** * {@inheritDoc} */ @Override public boolean isClosing() { return this._state.isClosing(); } /** * {@inheritDoc} */ @Override public boolean isClosed() { return this._state.isClosed(); } /** * {@inheritDoc} */ @Override public boolean waitForAnnounced(long timeout) { return _state.waitForAnnounced(timeout); } /** * {@inheritDoc} */ @Override public boolean waitForCanceled(long timeout) { if (_address == null) { // No need to wait this was never announced. return true; } return _state.waitForCanceled(timeout); } }