NetworkInterface.java revision b5fc5ecd3fe5315fc2756c0c25adc458cc8c8d91
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.util.Arrays;
21import java.util.Enumeration;
22import java.util.LinkedHashMap;
23import java.util.List;
24import java.util.Map;
25import java.util.Vector;
26
27import org.apache.harmony.luni.util.Msg;
28
29/**
30 * This class is used to represent a network interface of the local device. An
31 * interface is defined by its address and a platform dependent name. The class
32 * provides methods to get all information about the available interfaces of the
33 * system or to identify the local interface of a joined multicast group.
34 */
35public final class NetworkInterface extends Object {
36
37    private static final int CHECK_CONNECT_NO_PORT = -1;
38
39    static final int NO_INTERFACE_INDEX = 0;
40
41    static final int UNSET_INTERFACE_INDEX = -1;
42
43    private String name;
44
45    private String displayName;
46
47    InetAddress addresses[];
48
49    // The interface index is a positive integer which is non-negative. Where
50    // value is zero then we do not have an index for the interface (which
51    // occurs in systems which only support IPV4)
52    private int interfaceIndex;
53
54    private int hashCode;
55
56    // BEGIN android-changed: we pay this extra complexity on the Java side
57    // in return for vastly simpler native code.
58    private static native InterfaceAddress[] getInterfaceAddresses() throws SocketException;
59
60    private static NetworkInterface[] getNetworkInterfacesImpl() throws SocketException {
61        Map<String, NetworkInterface> networkInterfaces = new LinkedHashMap<String, NetworkInterface>();
62        for (InterfaceAddress ia : getInterfaceAddresses()) {
63            if (ia != null) { // The array may contain harmless null elements.
64                String name = ia.name;
65                NetworkInterface ni = networkInterfaces.get(name);
66                if (ni == null) {
67                    ni = new NetworkInterface(name, name, new InetAddress[] { ia.address }, ia.index);
68                    networkInterfaces.put(name, ni);
69                } else {
70                    ni.addInterfaceAddress(ia.address);
71                }
72            }
73        }
74        return networkInterfaces.values().toArray(new NetworkInterface[networkInterfaces.size()]);
75    }
76
77    private void addInterfaceAddress(InetAddress address) {
78        InetAddress[] newAddresses = new InetAddress[addresses.length + 1];
79        System.arraycopy(addresses, 0, newAddresses, 0, addresses.length);
80        newAddresses[addresses.length] = address;
81        addresses = newAddresses;
82    }
83    // END android-changed
84
85    /**
86     * This constructor is used by the native method in order to construct the
87     * NetworkInterface objects in the array that it returns.
88     *
89     * @param name
90     *            internal name associated with the interface.
91     * @param displayName
92     *            a user interpretable name for the interface.
93     * @param addresses
94     *            the Internet addresses associated with the interface.
95     * @param interfaceIndex
96     *            an index for the interface. Only set for platforms that
97     *            support IPV6.
98     */
99    NetworkInterface(String name, String displayName, InetAddress addresses[],
100            int interfaceIndex) {
101        this.name = name;
102        this.displayName = displayName;
103        this.addresses = addresses;
104        this.interfaceIndex = interfaceIndex;
105    }
106
107    /**
108     * Returns the index for the network interface. Unless the system supports
109     * IPV6 this will be 0.
110     *
111     * @return the index
112     */
113    int getIndex() {
114        return interfaceIndex;
115    }
116
117    /**
118     * Returns the first address for the network interface. This is used in the
119     * natives when we need one of the addresses for the interface and any one
120     * will do
121     *
122     * @return the first address if one exists, otherwise null.
123     */
124    InetAddress getFirstAddress() {
125        if ((addresses != null) && (addresses.length >= 1)) {
126            return addresses[0];
127        }
128        return null;
129    }
130
131    /**
132     * Gets the name associated with this network interface.
133     *
134     * @return the name of this {@code NetworkInterface} instance.
135     */
136    public String getName() {
137        return name;
138    }
139
140    /**
141     * Gets a list of addresses bound to this network interface.
142     *
143     * @return the address list of the represented network interface.
144     */
145    public Enumeration<InetAddress> getInetAddresses() {
146        /*
147         * create new vector from which Enumeration to be returned can be
148         * generated set the initial capacity to be the number of addresses for
149         * the network interface which is the maximum required size
150         */
151
152        /*
153         * return an empty enumeration if there are no addresses associated with
154         * the interface
155         */
156        if (addresses == null) {
157            return new Vector<InetAddress>(0).elements();
158        }
159
160        /*
161         * for those configuration that support the security manager we only
162         * return addresses for which checkConnect returns true
163         */
164        Vector<InetAddress> accessibleAddresses = new Vector<InetAddress>(
165                addresses.length);
166
167        /*
168         * get the security manager. If one does not exist just return the full
169         * list
170         */
171        SecurityManager security = System.getSecurityManager();
172        if (security == null) {
173            return (new Vector<InetAddress>(Arrays.asList(addresses)))
174                    .elements();
175        }
176
177        /*
178         * ok security manager exists so check each address and return those
179         * that pass
180         */
181        for (InetAddress element : addresses) {
182            if (security != null) {
183                try {
184                    /*
185                     * since we don't have a port in this case we pass in
186                     * NO_PORT
187                     */
188                    security.checkConnect(element.getHostName(),
189                            CHECK_CONNECT_NO_PORT);
190                    accessibleAddresses.add(element);
191                } catch (SecurityException e) {
192                }
193            }
194        }
195
196        Enumeration<InetAddress> theAccessibleElements = accessibleAddresses
197                .elements();
198        if (theAccessibleElements.hasMoreElements()) {
199            return accessibleAddresses.elements();
200        }
201
202        return new Vector<InetAddress>(0).elements();
203    }
204
205    /**
206     * Gets the human-readable name associated with this network interface.
207     *
208     * @return the display name of this network interface or the name if the
209     *         display name is not available.
210     */
211    public String getDisplayName() {
212        /*
213         * we should return the display name unless it is blank in this case
214         * return the name so that something is displayed.
215         */
216        if (!(displayName.equals(""))) { //$NON-NLS-1$
217            return displayName;
218        }
219        return name;
220    }
221
222    /**
223     * Gets the specific network interface according to a given name.
224     *
225     * @param interfaceName
226     *            the name to identify the searched network interface.
227     * @return the network interface with the specified name if one exists or
228     *         {@code null} otherwise.
229     * @throws SocketException
230     *             if an error occurs while getting the network interface
231     *             information.
232     * @throws NullPointerException
233     *             if the given interface's name is {@code null}.
234     */
235    public static NetworkInterface getByName(String interfaceName)
236            throws SocketException {
237
238        if (interfaceName == null) {
239            throw new NullPointerException(Msg.getString("K0330")); //$NON-NLS-1$
240        }
241
242        /*
243         * get the list of interfaces, and then loop through the list to look
244         * for one with a matching name
245         */
246        Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
247        if (interfaces != null) {
248            while (interfaces.hasMoreElements()) {
249                NetworkInterface netif = interfaces.nextElement();
250                if (netif.getName().equals(interfaceName)) {
251                    return netif;
252                }
253            }
254        }
255        return null;
256    }
257
258    /**
259     * Gets the specific network interface according to the given address.
260     *
261     * @param address
262     *            the address to identify the searched network interface.
263     * @return the network interface with the specified address if one exists or
264     *         {@code null} otherwise.
265     * @throws SocketException
266     *             if an error occurs while getting the network interface
267     *             information.
268     * @throws NullPointerException
269     *             if the given interface address is invalid.
270     */
271    public static NetworkInterface getByInetAddress(InetAddress address)
272            throws SocketException {
273
274        if (address == null) {
275            throw new NullPointerException(Msg.getString("K0331")); //$NON-NLS-1$
276        }
277
278        /*
279         * get the list of interfaces, and then loop through the list. For each
280         * interface loop through the associated set of internet addresses and
281         * see if one matches. If so return that network interface
282         */
283        Enumeration<NetworkInterface> interfaces = getNetworkInterfaces();
284        if (interfaces != null) {
285            while (interfaces.hasMoreElements()) {
286                NetworkInterface netif = interfaces.nextElement();
287                /*
288                 * to be compatible use the raw addresses without any security
289                 * filtering
290                 */
291                // Enumeration netifAddresses = netif.getInetAddresses();
292                if ((netif.addresses != null) && (netif.addresses.length != 0)) {
293                    Enumeration<InetAddress> netifAddresses = (new Vector<InetAddress>(
294                            Arrays.asList(netif.addresses))).elements();
295                    if (netifAddresses != null) {
296                        while (netifAddresses.hasMoreElements()) {
297                            if (address.equals(netifAddresses.nextElement())) {
298                                return netif;
299                            }
300                        }
301                    }
302                }
303            }
304        }
305        return null;
306    }
307
308    /**
309     * Gets a list of all network interfaces available on the local system or
310     * {@code null} if no interface is available.
311     *
312     * @return the list of {@code NetworkInterface} instances representing the
313     *         available interfaces.
314     * @throws SocketException
315     *             if an error occurs while getting the network interface
316     *             information.
317     */
318    public static Enumeration<NetworkInterface> getNetworkInterfaces()
319            throws SocketException {
320        NetworkInterface[] interfaces = getNetworkInterfacesImpl();
321        if (interfaces == null) {
322            return null;
323        }
324
325        for (NetworkInterface netif : interfaces) {
326            // Ensure that current NetworkInterface is bound to at least
327            // one InetAddress before processing
328            if (netif.addresses != null) {
329                for (InetAddress addr : netif.addresses) {
330                    if (16 == addr.ipaddress.length) {
331                        if (addr.isLinkLocalAddress()
332                                || addr.isSiteLocalAddress()) {
333                            ((Inet6Address) addr).scopedIf = netif;
334                            ((Inet6Address) addr).ifname = netif.name;
335                            ((Inet6Address) addr).scope_ifname_set = true;
336                        }
337                    }
338                }
339            }
340        }
341
342        return (new Vector<NetworkInterface>(Arrays.asList(interfaces)))
343                .elements();
344    }
345
346    /**
347     * Compares the specified object to this {@code NetworkInterface} and
348     * returns whether they are equal or not. The object must be an instance of
349     * {@code NetworkInterface} with the same name, {@code displayName} and list
350     * of network interfaces to be equal.
351     *
352     * @param obj
353     *            the object to compare with this instance.
354     * @return {@code true} if the specified object is equal to this {@code
355     *         NetworkInterface}, {@code false} otherwise.
356     * @see #hashCode()
357     */
358    @Override
359    public boolean equals(Object obj) {
360        // Return true if it is the exact same object.
361        if (obj == this) {
362            return true;
363        }
364
365        // Ensure it is the right type.
366        if (!(obj instanceof NetworkInterface)) {
367            return false;
368        }
369
370        /*
371         * Make sure that some simple checks pass. If the name is not the same
372         * then we are sure it is not the same one. We don't check the hashcode
373         * as it is generated from the name which we check
374         */
375        NetworkInterface netif = (NetworkInterface) obj;
376
377        if (netif.getIndex() != interfaceIndex) {
378            return false;
379        }
380
381        if (!(name.equals("")) && (!netif.getName().equals(name))) { //$NON-NLS-1$
382            return false;
383        }
384
385        if ((name.equals("")) && (!netif.getName().equals(displayName))) { //$NON-NLS-1$
386            return false;
387        }
388
389        // Now check that the collection of internet addresses are equal.
390        Enumeration<InetAddress> netifAddresses = netif.getInetAddresses();
391        Enumeration<InetAddress> localifAddresses = getInetAddresses();
392
393        // Check for both null (same), or one null (not same).
394        if (netifAddresses == null) {
395            return localifAddresses == null;
396        }
397        if (localifAddresses == null) {
398            return false;
399        }
400
401        // Both are not null, check InetAddress elements.
402        while (netifAddresses.hasMoreElements()
403                && localifAddresses.hasMoreElements()) {
404            if (!(localifAddresses.nextElement()).equals(
405                    netifAddresses.nextElement())) {
406                return false;
407            }
408        }
409
410        /*
411         * Now make sure that they had the same number of addresses, if not they
412         * are not the same interface.
413         */
414        return !netifAddresses.hasMoreElements()
415                && !localifAddresses.hasMoreElements();
416    }
417
418    /**
419     * Gets the hashcode for this {@code NetworkInterface} instance. Since the
420     * name should be unique for each network interface the hashcode is
421     * generated using this name.
422     *
423     * @return the hashcode value for this {@code NetworkInterface} instance.
424     */
425    @Override
426    public int hashCode() {
427        if (hashCode == 0) {
428            hashCode = name.hashCode();
429        }
430        return hashCode;
431    }
432
433    /**
434     * Gets a string containing a concise, human-readable description of this
435     * network interface.
436     *
437     * @return the textual representation for this network interface.
438     */
439    @Override
440    public String toString() {
441        StringBuilder string = new StringBuilder(25);
442        string.append("["); //$NON-NLS-1$
443        string.append(name);
444        string.append("]["); //$NON-NLS-1$
445        string.append(displayName);
446        // BEGIN android-added: the RI shows this, and it's useful for IPv6 users.
447        string.append("]["); //$NON-NLS-1$
448        string.append(interfaceIndex);
449        // END android-added
450        string.append("]"); //$NON-NLS-1$
451
452        /*
453         * get the addresses through this call to make sure we only reveal those
454         * that we should
455         */
456        Enumeration<InetAddress> theAddresses = getInetAddresses();
457        if (theAddresses != null) {
458            while (theAddresses.hasMoreElements()) {
459                InetAddress nextAddress = theAddresses.nextElement();
460                string.append("["); //$NON-NLS-1$
461                string.append(nextAddress.toString());
462                string.append("]"); //$NON-NLS-1$
463            }
464        }
465        return string.toString();
466    }
467}
468