Home | History | Annotate | Download | only in impl
      1 // Copyright 2003-2005 Arthur van Hoff, Rick Blair
      2 // Licensed under Apache License version 2.0
      3 // Original license LGPL
      4 
      5 package javax.jmdns.impl;
      6 
      7 import java.io.IOException;
      8 import java.net.DatagramPacket;
      9 import java.net.Inet4Address;
     10 import java.net.Inet6Address;
     11 import java.net.InetAddress;
     12 import java.net.NetworkInterface;
     13 import java.net.UnknownHostException;
     14 import java.util.ArrayList;
     15 import java.util.Collection;
     16 import java.util.List;
     17 import java.util.logging.Level;
     18 import java.util.logging.Logger;
     19 
     20 import javax.jmdns.NetworkTopologyDiscovery;
     21 import javax.jmdns.impl.constants.DNSConstants;
     22 import javax.jmdns.impl.constants.DNSRecordClass;
     23 import javax.jmdns.impl.constants.DNSRecordType;
     24 import javax.jmdns.impl.constants.DNSState;
     25 import javax.jmdns.impl.tasks.DNSTask;
     26 
     27 /**
     28  * HostInfo information on the local host to be able to cope with change of addresses.
     29  *
     30  * @author Pierre Frisch, Werner Randelshofer
     31  */
     32 public class HostInfo implements DNSStatefulObject {
     33     private static Logger       logger = Logger.getLogger(HostInfo.class.getName());
     34 
     35     protected String            _name;
     36 
     37     protected InetAddress       _address;
     38 
     39     protected NetworkInterface  _interfaze;
     40 
     41     private final HostInfoState _state;
     42 
     43     private final static class HostInfoState extends DNSStatefulObject.DefaultImplementation {
     44 
     45         private static final long serialVersionUID = -8191476803620402088L;
     46 
     47         /**
     48          * @param dns
     49          */
     50         public HostInfoState(JmDNSImpl dns) {
     51             super();
     52             this.setDns(dns);
     53         }
     54 
     55     }
     56 
     57     /**
     58      * @param address
     59      *            IP address to bind
     60      * @param dns
     61      *            JmDNS instance
     62      * @param jmdnsName
     63      *            JmDNS name
     64      * @return new HostInfo
     65      */
     66     public static HostInfo newHostInfo(InetAddress address, JmDNSImpl dns, String jmdnsName) {
     67         HostInfo localhost = null;
     68         String aName = "";
     69         InetAddress addr = address;
     70         try {
     71             if (addr == null) {
     72                 String ip = System.getProperty("net.mdns.interface");
     73                 if (ip != null) {
     74                     addr = InetAddress.getByName(ip);
     75                 } else {
     76                     addr = InetAddress.getLocalHost();
     77                     if (addr.isLoopbackAddress()) {
     78                         // Find local address that isn't a loopback address
     79                         InetAddress[] addresses = NetworkTopologyDiscovery.Factory.getInstance().getInetAddresses();
     80                         if (addresses.length > 0) {
     81                             addr = addresses[0];
     82                         }
     83                     }
     84                 }
     85                 aName = addr.getHostName();
     86                 if (addr.isLoopbackAddress()) {
     87                     logger.warning("Could not find any address beside the loopback.");
     88                 }
     89             } else {
     90                 aName = addr.getHostName();
     91             }
     92             if (aName.contains("in-addr.arpa") || (aName.equals(addr.getHostAddress()))) {
     93                 aName = ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : addr.getHostAddress());
     94             }
     95         } catch (final IOException e) {
     96             logger.log(Level.WARNING, "Could not intialize the host network interface on " + address + "because of an error: " + e.getMessage(), e);
     97             // This is only used for running unit test on Debian / Ubuntu
     98             addr = loopbackAddress();
     99             aName = ((jmdnsName != null) && (jmdnsName.length() > 0) ? jmdnsName : "computer");
    100         }
    101         // A host name with "." is illegal. so strip off everything and append .local.
    102         aName = aName.replace('.', '-');
    103         aName += ".local.";
    104         localhost = new HostInfo(addr, aName, dns);
    105         return localhost;
    106     }
    107 
    108     private static InetAddress loopbackAddress() {
    109         try {
    110             return InetAddress.getByName(null);
    111         } catch (UnknownHostException exception) {
    112             return null;
    113         }
    114     }
    115 
    116     /**
    117      * This is used to create a unique name for the host name.
    118      */
    119     private int hostNameCount;
    120 
    121     private HostInfo(final InetAddress address, final String name, final JmDNSImpl dns) {
    122         super();
    123         this._state = new HostInfoState(dns);
    124         this._address = address;
    125         this._name = name;
    126         if (address != null) {
    127             try {
    128                 _interfaze = NetworkInterface.getByInetAddress(address);
    129             } catch (Exception exception) {
    130                 logger.log(Level.SEVERE, "LocalHostInfo() exception ", exception);
    131             }
    132         }
    133     }
    134 
    135     public String getName() {
    136         return _name;
    137     }
    138 
    139     public InetAddress getInetAddress() {
    140         return _address;
    141     }
    142 
    143     Inet4Address getInet4Address() {
    144         if (this.getInetAddress() instanceof Inet4Address) {
    145             return (Inet4Address) _address;
    146         }
    147         return null;
    148     }
    149 
    150     Inet6Address getInet6Address() {
    151         if (this.getInetAddress() instanceof Inet6Address) {
    152             return (Inet6Address) _address;
    153         }
    154         return null;
    155     }
    156 
    157     public NetworkInterface getInterface() {
    158         return _interfaze;
    159     }
    160 
    161     public boolean conflictWithRecord(DNSRecord.Address record) {
    162         DNSRecord.Address hostAddress = this.getDNSAddressRecord(record.getRecordType(), record.isUnique(), DNSConstants.DNS_TTL);
    163         if (hostAddress != null) {
    164             return hostAddress.sameType(record) && hostAddress.sameName(record) && (!hostAddress.sameValue(record));
    165         }
    166         return false;
    167     }
    168 
    169     synchronized String incrementHostName() {
    170         hostNameCount++;
    171         int plocal = _name.indexOf(".local.");
    172         int punder = _name.lastIndexOf('-');
    173         _name = _name.substring(0, (punder == -1 ? plocal : punder)) + "-" + hostNameCount + ".local.";
    174         return _name;
    175     }
    176 
    177     boolean shouldIgnorePacket(DatagramPacket packet) {
    178         boolean result = false;
    179         if (this.getInetAddress() != null) {
    180             InetAddress from = packet.getAddress();
    181             if (from != null) {
    182                 if (from.isLinkLocalAddress() && (!this.getInetAddress().isLinkLocalAddress())) {
    183                     // Ignore linklocal packets on regular interfaces, unless this is
    184                     // also a linklocal interface. This is to avoid duplicates. This is
    185                     // a terrible hack caused by the lack of an API to get the address
    186                     // of the interface on which the packet was received.
    187                     result = true;
    188                 }
    189                 if (from.isLoopbackAddress() && (!this.getInetAddress().isLoopbackAddress())) {
    190                     // Ignore loopback packets on a regular interface unless this is also a loopback interface.
    191                     result = true;
    192                 }
    193             }
    194         }
    195         return result;
    196     }
    197 
    198     DNSRecord.Address getDNSAddressRecord(DNSRecordType type, boolean unique, int ttl) {
    199         switch (type) {
    200             case TYPE_A:
    201                 return this.getDNS4AddressRecord(unique, ttl);
    202             case TYPE_A6:
    203             case TYPE_AAAA:
    204                 return this.getDNS6AddressRecord(unique, ttl);
    205             default:
    206         }
    207         return null;
    208     }
    209 
    210     private DNSRecord.Address getDNS4AddressRecord(boolean unique, int ttl) {
    211         if ((this.getInetAddress() instanceof Inet4Address) || ((this.getInetAddress() instanceof Inet6Address) && (((Inet6Address) this.getInetAddress()).isIPv4CompatibleAddress()))) {
    212             return new DNSRecord.IPv4Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress());
    213         }
    214         return null;
    215     }
    216 
    217     private DNSRecord.Address getDNS6AddressRecord(boolean unique, int ttl) {
    218         if (this.getInetAddress() instanceof Inet6Address) {
    219             return new DNSRecord.IPv6Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress());
    220         }
    221         return null;
    222     }
    223 
    224     DNSRecord.Pointer getDNSReverseAddressRecord(DNSRecordType type, boolean unique, int ttl) {
    225         switch (type) {
    226             case TYPE_A:
    227                 return this.getDNS4ReverseAddressRecord(unique, ttl);
    228             case TYPE_A6:
    229             case TYPE_AAAA:
    230                 return this.getDNS6ReverseAddressRecord(unique, ttl);
    231             default:
    232         }
    233         return null;
    234     }
    235 
    236     private DNSRecord.Pointer getDNS4ReverseAddressRecord(boolean unique, int ttl) {
    237         if (this.getInetAddress() instanceof Inet4Address) {
    238             return new DNSRecord.Pointer(this.getInetAddress().getHostAddress() + ".in-addr.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName());
    239         }
    240         if ((this.getInetAddress() instanceof Inet6Address) && (((Inet6Address) this.getInetAddress()).isIPv4CompatibleAddress())) {
    241             byte[] rawAddress = this.getInetAddress().getAddress();
    242             String address = (rawAddress[12] & 0xff) + "." + (rawAddress[13] & 0xff) + "." + (rawAddress[14] & 0xff) + "." + (rawAddress[15] & 0xff);
    243             return new DNSRecord.Pointer(address + ".in-addr.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName());
    244         }
    245         return null;
    246     }
    247 
    248     private DNSRecord.Pointer getDNS6ReverseAddressRecord(boolean unique, int ttl) {
    249         if (this.getInetAddress() instanceof Inet6Address) {
    250             return new DNSRecord.Pointer(this.getInetAddress().getHostAddress() + ".ip6.arpa.", DNSRecordClass.CLASS_IN, unique, ttl, this.getName());
    251         }
    252         return null;
    253     }
    254 
    255     @Override
    256     public String toString() {
    257         StringBuilder buf = new StringBuilder(1024);
    258         buf.append("local host info[");
    259         buf.append(getName() != null ? getName() : "no name");
    260         buf.append(", ");
    261         buf.append(getInterface() != null ? getInterface().getDisplayName() : "???");
    262         buf.append(":");
    263         buf.append(getInetAddress() != null ? getInetAddress().getHostAddress() : "no address");
    264         buf.append(", ");
    265         buf.append(_state);
    266         buf.append("]");
    267         return buf.toString();
    268     }
    269 
    270     public Collection<DNSRecord> answers(boolean unique, int ttl) {
    271         List<DNSRecord> list = new ArrayList<DNSRecord>();
    272         DNSRecord answer = this.getDNS4AddressRecord(unique, ttl);
    273         if (answer != null) {
    274             list.add(answer);
    275         }
    276         answer = this.getDNS6AddressRecord(unique, ttl);
    277         if (answer != null) {
    278             list.add(answer);
    279         }
    280         return list;
    281     }
    282 
    283     /**
    284      * {@inheritDoc}
    285      */
    286     @Override
    287     public JmDNSImpl getDns() {
    288         return this._state.getDns();
    289     }
    290 
    291     /**
    292      * {@inheritDoc}
    293      */
    294     @Override
    295     public boolean advanceState(DNSTask task) {
    296         return this._state.advanceState(task);
    297     }
    298 
    299     /**
    300      * {@inheritDoc}
    301      */
    302     @Override
    303     public void removeAssociationWithTask(DNSTask task) {
    304         this._state.removeAssociationWithTask(task);
    305     }
    306 
    307     /**
    308      * {@inheritDoc}
    309      */
    310     @Override
    311     public boolean revertState() {
    312         return this._state.revertState();
    313     }
    314 
    315     /**
    316      * {@inheritDoc}
    317      */
    318     @Override
    319     public void associateWithTask(DNSTask task, DNSState state) {
    320         this._state.associateWithTask(task, state);
    321     }
    322 
    323     /**
    324      * {@inheritDoc}
    325      */
    326     @Override
    327     public boolean isAssociatedWithTask(DNSTask task, DNSState state) {
    328         return this._state.isAssociatedWithTask(task, state);
    329     }
    330 
    331     /**
    332      * {@inheritDoc}
    333      */
    334     @Override
    335     public boolean cancelState() {
    336         return this._state.cancelState();
    337     }
    338 
    339     /**
    340      * {@inheritDoc}
    341      */
    342     @Override
    343     public boolean closeState() {
    344         return this._state.closeState();
    345     }
    346 
    347     /**
    348      * {@inheritDoc}
    349      */
    350     @Override
    351     public boolean recoverState() {
    352         return this._state.recoverState();
    353     }
    354 
    355     /**
    356      * {@inheritDoc}
    357      */
    358     @Override
    359     public boolean isProbing() {
    360         return this._state.isProbing();
    361     }
    362 
    363     /**
    364      * {@inheritDoc}
    365      */
    366     @Override
    367     public boolean isAnnouncing() {
    368         return this._state.isAnnouncing();
    369     }
    370 
    371     /**
    372      * {@inheritDoc}
    373      */
    374     @Override
    375     public boolean isAnnounced() {
    376         return this._state.isAnnounced();
    377     }
    378 
    379     /**
    380      * {@inheritDoc}
    381      */
    382     @Override
    383     public boolean isCanceling() {
    384         return this._state.isCanceling();
    385     }
    386 
    387     /**
    388      * {@inheritDoc}
    389      */
    390     @Override
    391     public boolean isCanceled() {
    392         return this._state.isCanceled();
    393     }
    394 
    395     /**
    396      * {@inheritDoc}
    397      */
    398     @Override
    399     public boolean isClosing() {
    400         return this._state.isClosing();
    401     }
    402 
    403     /**
    404      * {@inheritDoc}
    405      */
    406     @Override
    407     public boolean isClosed() {
    408         return this._state.isClosed();
    409     }
    410 
    411     /**
    412      * {@inheritDoc}
    413      */
    414     @Override
    415     public boolean waitForAnnounced(long timeout) {
    416         return _state.waitForAnnounced(timeout);
    417     }
    418 
    419     /**
    420      * {@inheritDoc}
    421      */
    422     @Override
    423     public boolean waitForCanceled(long timeout) {
    424         if (_address == null) {
    425             // No need to wait this was never announced.
    426             return true;
    427         }
    428         return _state.waitForCanceled(timeout);
    429     }
    430 
    431 }
    432