View Javadoc

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 }