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          */
HostInfoState(JmDNSImpl dns)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      */
newHostInfo(InetAddress address, JmDNSImpl dns, String jmdnsName)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 
loopbackAddress()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 
HostInfo(final InetAddress address, final String name, final JmDNSImpl dns)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 
getName()135     public String getName() {
136         return _name;
137     }
138 
getInetAddress()139     public InetAddress getInetAddress() {
140         return _address;
141     }
142 
getInet4Address()143     Inet4Address getInet4Address() {
144         if (this.getInetAddress() instanceof Inet4Address) {
145             return (Inet4Address) _address;
146         }
147         return null;
148     }
149 
getInet6Address()150     Inet6Address getInet6Address() {
151         if (this.getInetAddress() instanceof Inet6Address) {
152             return (Inet6Address) _address;
153         }
154         return null;
155     }
156 
getInterface()157     public NetworkInterface getInterface() {
158         return _interfaze;
159     }
160 
conflictWithRecord(DNSRecord.Address record)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 
incrementHostName()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 
shouldIgnorePacket(DatagramPacket packet)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 
getDNSAddressRecord(DNSRecordType type, boolean unique, int ttl)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 
getDNS4AddressRecord(boolean unique, int ttl)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 
getDNS6AddressRecord(boolean unique, int ttl)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 
getDNSReverseAddressRecord(DNSRecordType type, boolean unique, int ttl)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 
getDNS4ReverseAddressRecord(boolean unique, int ttl)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 
getDNS6ReverseAddressRecord(boolean unique, int ttl)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
toString()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 
answers(boolean unique, int ttl)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
getDns()287     public JmDNSImpl getDns() {
288         return this._state.getDns();
289     }
290 
291     /**
292      * {@inheritDoc}
293      */
294     @Override
advanceState(DNSTask task)295     public boolean advanceState(DNSTask task) {
296         return this._state.advanceState(task);
297     }
298 
299     /**
300      * {@inheritDoc}
301      */
302     @Override
removeAssociationWithTask(DNSTask task)303     public void removeAssociationWithTask(DNSTask task) {
304         this._state.removeAssociationWithTask(task);
305     }
306 
307     /**
308      * {@inheritDoc}
309      */
310     @Override
revertState()311     public boolean revertState() {
312         return this._state.revertState();
313     }
314 
315     /**
316      * {@inheritDoc}
317      */
318     @Override
associateWithTask(DNSTask task, DNSState state)319     public void associateWithTask(DNSTask task, DNSState state) {
320         this._state.associateWithTask(task, state);
321     }
322 
323     /**
324      * {@inheritDoc}
325      */
326     @Override
isAssociatedWithTask(DNSTask task, DNSState state)327     public boolean isAssociatedWithTask(DNSTask task, DNSState state) {
328         return this._state.isAssociatedWithTask(task, state);
329     }
330 
331     /**
332      * {@inheritDoc}
333      */
334     @Override
cancelState()335     public boolean cancelState() {
336         return this._state.cancelState();
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     @Override
closeState()343     public boolean closeState() {
344         return this._state.closeState();
345     }
346 
347     /**
348      * {@inheritDoc}
349      */
350     @Override
recoverState()351     public boolean recoverState() {
352         return this._state.recoverState();
353     }
354 
355     /**
356      * {@inheritDoc}
357      */
358     @Override
isProbing()359     public boolean isProbing() {
360         return this._state.isProbing();
361     }
362 
363     /**
364      * {@inheritDoc}
365      */
366     @Override
isAnnouncing()367     public boolean isAnnouncing() {
368         return this._state.isAnnouncing();
369     }
370 
371     /**
372      * {@inheritDoc}
373      */
374     @Override
isAnnounced()375     public boolean isAnnounced() {
376         return this._state.isAnnounced();
377     }
378 
379     /**
380      * {@inheritDoc}
381      */
382     @Override
isCanceling()383     public boolean isCanceling() {
384         return this._state.isCanceling();
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     @Override
isCanceled()391     public boolean isCanceled() {
392         return this._state.isCanceled();
393     }
394 
395     /**
396      * {@inheritDoc}
397      */
398     @Override
isClosing()399     public boolean isClosing() {
400         return this._state.isClosing();
401     }
402 
403     /**
404      * {@inheritDoc}
405      */
406     @Override
isClosed()407     public boolean isClosed() {
408         return this._state.isClosed();
409     }
410 
411     /**
412      * {@inheritDoc}
413      */
414     @Override
waitForAnnounced(long timeout)415     public boolean waitForAnnounced(long timeout) {
416         return _state.waitForAnnounced(timeout);
417     }
418 
419     /**
420      * {@inheritDoc}
421      */
422     @Override
waitForCanceled(long timeout)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