HostInfo.java revision 3742d9db8b6edb10627b0f89336cca5249f1d15a
1d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant// Copyright 2003-2005 Arthur van Hoff, Rick Blair
2d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant// Licensed under Apache License version 2.0
3d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant// Original license LGPL
4d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
5d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantpackage javax.jmdns.impl;
6d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
7d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.io.IOException;
8d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.DatagramPacket;
9d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.Inet4Address;
10d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.Inet6Address;
11d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.InetAddress;
12d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.NetworkInterface;
13d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.net.UnknownHostException;
14d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport java.util.ArrayList;
157d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clowimport java.util.Collection;
167d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clowimport java.util.List;
177d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clowimport java.util.logging.Level;
187d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clowimport java.util.logging.Logger;
19d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
20d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport javax.jmdns.NetworkTopologyDiscovery;
21d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport javax.jmdns.impl.constants.DNSConstants;
22d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport javax.jmdns.impl.constants.DNSRecordClass;
23d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport javax.jmdns.impl.constants.DNSRecordType;
24d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantimport javax.jmdns.impl.constants.DNSState;
251b92188a82b01e76ac6e8ad5f997293c2a078adcMarshall Clowimport javax.jmdns.impl.tasks.DNSTask;
26d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
27d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant/**
28d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant * HostInfo information on the local host to be able to cope with change of addresses.
29d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant *
30d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant * @author Pierre Frisch, Werner Randelshofer
31d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant */
32d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnantpublic class HostInfo implements DNSStatefulObject {
33d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    private static Logger       logger = Logger.getLogger(HostInfo.class.getName());
34d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
35d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    protected String            _name;
36d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
37d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    protected InetAddress       _address;
38d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
397d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow    protected NetworkInterface  _interfaze;
407d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow
417d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow    private final HostInfoState _state;
427d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow
437d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow    private final static class HostInfoState extends DNSStatefulObject.DefaultImplementation {
447d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow
457d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow        private static final long serialVersionUID = -8191476803620402088L;
467d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow
477d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow        /**
487d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow         * @param dns
497d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow         */
507d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow        public HostInfoState(JmDNSImpl dns) {
517d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow            super();
52d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant            this.setDns(dns);
53d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant        }
54d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
55d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    }
56d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant
57d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    /**
58d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     * @param address
59d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     *            IP address to bind
60d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     * @param dns
61d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     *            JmDNS instance
62d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     * @param jmdnsName
63d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     *            JmDNS name
64d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     * @return new HostInfo
65d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant     */
66d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant    public static HostInfo newHostInfo(InetAddress address, JmDNSImpl dns, String jmdnsName) {
67d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant        HostInfo localhost = null;
68d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant        String aName = "";
69d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant        InetAddress addr = address;
70d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant        try {
71d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant            if (addr == null) {
72d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant                String ip = System.getProperty("net.mdns.interface");
737d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                if (ip != null) {
747d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                    addr = InetAddress.getByName(ip);
757d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                } else {
767d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                    addr = InetAddress.getLocalHost();
77d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant                    if (addr.isLoopbackAddress()) {
787d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                        // Find local address that isn't a loopback address
79d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant                        InetAddress[] addresses = NetworkTopologyDiscovery.Factory.getInstance().getInetAddresses();
807d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                        if (addresses.length > 0) {
817d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                            addr = addresses[0];
827d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                        }
837d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                    }
847d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                }
857d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                aName = addr.getHostName();
867d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                if (addr.isLoopbackAddress()) {
877d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                    logger.warning("Could not find any address beside the loopback.");
887d914d1bfffac32da13a44871fc17b8ba3ade57aMarshall Clow                }
89d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant            } else {
90d1d27a4afaba2e3b708afd73f1533b7f8530008bHoward Hinnant                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