1// Copyright 2003-2005 Arthur van Hoff, Rick Blair 2// Licensed under Apache License version 2.0 3// Original license LGPL 4 5package javax.jmdns.impl; 6 7import java.io.IOException; 8import java.net.DatagramPacket; 9import java.net.Inet4Address; 10import java.net.Inet6Address; 11import java.net.InetAddress; 12import java.net.NetworkInterface; 13import java.net.UnknownHostException; 14import java.util.ArrayList; 15import java.util.Collection; 16import java.util.List; 17import java.util.logging.Level; 18import java.util.logging.Logger; 19 20import javax.jmdns.NetworkTopologyDiscovery; 21import javax.jmdns.impl.constants.DNSConstants; 22import javax.jmdns.impl.constants.DNSRecordClass; 23import javax.jmdns.impl.constants.DNSRecordType; 24import javax.jmdns.impl.constants.DNSState; 25import 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 */ 32public 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