NetworkInterface.java revision 0d93c38cc3c7a5001aece8a18cafc6d1fc7551f3
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.ArrayList;
21import java.util.Collections;
22import java.util.Enumeration;
23import java.util.LinkedHashMap;
24import java.util.LinkedList;
25import java.util.List;
26import java.util.Map;
27
28/**
29 * This class is used to represent a network interface of the local device. An
30 * interface is defined by its address and a platform dependent name. The class
31 * provides methods to get all information about the available interfaces of the
32 * system or to identify the local interface of a joined multicast group.
33 */
34public final class NetworkInterface extends Object {
35
36    private static final int CHECK_CONNECT_NO_PORT = -1;
37
38    static final int NO_INTERFACE_INDEX = 0;
39    static final int UNSET_INTERFACE_INDEX = -1;
40
41    private final String name;
42    private final String displayName;
43    private final List<InterfaceAddress> interfaceAddresses = new LinkedList<InterfaceAddress>();
44
45    private final List<InetAddress> addresses = new LinkedList<InetAddress>();
46
47    // The interface index is a positive integer which is non-negative. Where
48    // value is zero then we do not have an index for the interface (which
49    // occurs in systems which only support IPV4)
50    private int interfaceIndex;
51
52    private NetworkInterface parent = null;
53
54    private final List<NetworkInterface> children = new LinkedList<NetworkInterface>();
55
56    private static NetworkInterface[] getNetworkInterfacesImpl() throws SocketException {
57        Map<String, NetworkInterface> networkInterfaces = new LinkedHashMap<String, NetworkInterface>();
58        for (InterfaceAddress ia : getAllInterfaceAddressesImpl()) {
59            if (ia != null) { // The array may contain harmless null elements.
60                String name = ia.name;
61                NetworkInterface ni = networkInterfaces.get(name);
62                if (ni == null) {
63                    ni = new NetworkInterface(name, name, new InetAddress[] { ia.address }, ia.index);
64                    ni.interfaceAddresses.add(ia);
65                    networkInterfaces.put(name, ni);
66                } else {
67                    ni.addresses.add(ia.address);
68                    ni.interfaceAddresses.add(ia);
69                }
70            }
71        }
72        return networkInterfaces.values().toArray(new NetworkInterface[networkInterfaces.size()]);
73    }
74    private static native InterfaceAddress[] getAllInterfaceAddressesImpl() throws SocketException;
75
76    /**
77     * This constructor is used by the native method in order to construct the
78     * NetworkInterface objects in the array that it returns.
79     *
80     * @param name
81     *            internal name associated with the interface.
82     * @param displayName
83     *            a user interpretable name for the interface.
84     * @param addresses
85     *            the Internet addresses associated with the interface.
86     * @param interfaceIndex
87     *            an index for the interface. Only set for platforms that
88     *            support IPV6.
89     */
90    NetworkInterface(String name, String displayName, InetAddress[] addresses,
91            int interfaceIndex) {
92        this.name = name;
93        this.displayName = displayName;
94        this.interfaceIndex = interfaceIndex;
95        if (addresses != null) {
96            for (InetAddress address : addresses) {
97                this.addresses.add(address);
98            }
99        }
100    }
101
102    /**
103     * Returns the index for the network interface. Unless the system supports
104     * IPV6 this will be 0.
105     *
106     * @return the index
107     */
108    int getIndex() {
109        return interfaceIndex;
110    }
111
112    /**
113     * Returns the first address for the network interface. This is used in the
114     * natives when we need one of the addresses for the interface and any one
115     * will do
116     *
117     * @return the first address if one exists, otherwise null.
118     */
119    InetAddress getFirstAddress() {
120        if (addresses.size() >= 1) {
121            return addresses.get(0);
122        }
123        return null;
124    }
125
126    /**
127     * Gets the name associated with this network interface.
128     *
129     * @return the name of this {@code NetworkInterface} instance.
130     */
131    public String getName() {
132        return name;
133    }
134
135    /**
136     * Gets a list of addresses bound to this network interface.
137     *
138     * @return the address list of the represented network interface.
139     */
140    public Enumeration<InetAddress> getInetAddresses() {
141        SecurityManager sm = System.getSecurityManager();
142        if (sm == null || addresses.isEmpty()) {
143            return Collections.enumeration(addresses);
144        }
145        // TODO: Android should ditch SecurityManager and the associated pollution.
146        List<InetAddress> result = new ArrayList<InetAddress>(addresses.size());
147        for (InetAddress address : addresses) {
148            try {
149                sm.checkConnect(address.getHostName(), CHECK_CONNECT_NO_PORT);
150            } catch (SecurityException e) {
151                continue;
152            }
153            result.add(address);
154        }
155        return Collections.enumeration(result);
156    }
157
158    /**
159     * Gets the human-readable name associated with this network interface.
160     *
161     * @return the display name of this network interface or the name if the
162     *         display name is not available.
163     */
164    public String getDisplayName() {
165        /*
166         * we should return the display name unless it is blank in this case
167         * return the name so that something is displayed.
168         */
169        return displayName.isEmpty() ? name : displayName;
170    }
171
172    /**
173     * Gets the specific network interface according to a given name.
174     *
175     * @param interfaceName
176     *            the name to identify the searched network interface.
177     * @return the network interface with the specified name if one exists or
178     *         {@code null} otherwise.
179     * @throws SocketException
180     *             if an error occurs while getting the network interface
181     *             information.
182     * @throws NullPointerException
183     *             if the given interface's name is {@code null}.
184     */
185    public static NetworkInterface getByName(String interfaceName) throws SocketException {
186        if (interfaceName == null) {
187            throw new NullPointerException();
188        }
189        for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
190            if (networkInterface.name.equals(interfaceName)) {
191                return networkInterface;
192            }
193        }
194        return null;
195    }
196
197    /**
198     * Gets the specific network interface according to the given address.
199     *
200     * @param address
201     *            the address to identify the searched network interface.
202     * @return the network interface with the specified address if one exists or
203     *         {@code null} otherwise.
204     * @throws SocketException
205     *             if an error occurs while getting the network interface
206     *             information.
207     * @throws NullPointerException
208     *             if the given interface address is invalid.
209     */
210    public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException {
211        if (address == null) {
212            throw new NullPointerException("address == null");
213        }
214        for (NetworkInterface networkInterface : getNetworkInterfacesList()) {
215            if (networkInterface.addresses.contains(address)) {
216                return networkInterface;
217            }
218        }
219        return null;
220    }
221
222    /**
223     * Gets a list of all network interfaces available on the local system or
224     * {@code null} if no interface is available.
225     *
226     * @return the list of {@code NetworkInterface} instances representing the
227     *         available interfaces.
228     * @throws SocketException
229     *             if an error occurs while getting the network interface
230     *             information.
231     */
232    public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException {
233        return Collections.enumeration(getNetworkInterfacesList());
234    }
235
236    private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
237        NetworkInterface[] interfaces = getNetworkInterfacesImpl();
238
239        for (NetworkInterface netif : interfaces) {
240            // Ensure that current NetworkInterface is bound to at least
241            // one InetAddress before processing
242            for (InetAddress addr : netif.addresses) {
243                if (addr.ipaddress.length == 16) {
244                    if (addr.isLinkLocalAddress() || addr.isSiteLocalAddress()) {
245                        ((Inet6Address) addr).scopedIf = netif;
246                        ((Inet6Address) addr).ifname = netif.name;
247                        ((Inet6Address) addr).scope_ifname_set = true;
248                    }
249                }
250            }
251        }
252
253        List<NetworkInterface> result = new ArrayList<NetworkInterface>();
254        boolean[] peeked = new boolean[interfaces.length];
255        for (int counter = 0; counter < interfaces.length; counter++) {
256            // If this interface has been touched, continue.
257            if (peeked[counter]) {
258                continue;
259            }
260            int counter2 = counter;
261            // Checks whether the following interfaces are children.
262            for (; counter2 < interfaces.length; counter2++) {
263                if (peeked[counter2]) {
264                    continue;
265                }
266                if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) {
267                    // Tagged as peeked
268                    peeked[counter2] = true;
269                    interfaces[counter].children.add(interfaces[counter2]);
270                    interfaces[counter2].parent = interfaces[counter];
271                    interfaces[counter].addresses.addAll(interfaces[counter2].addresses);
272                }
273            }
274            // Tagged as peeked
275            result.add(interfaces[counter]);
276            peeked[counter] = true;
277        }
278        return result;
279    }
280
281    /**
282     * Compares the specified object to this {@code NetworkInterface} and
283     * returns whether they are equal or not. The object must be an instance of
284     * {@code NetworkInterface} with the same name, {@code displayName} and list
285     * of network interfaces to be equal.
286     *
287     * @param obj
288     *            the object to compare with this instance.
289     * @return {@code true} if the specified object is equal to this {@code
290     *         NetworkInterface}, {@code false} otherwise.
291     * @see #hashCode()
292     */
293    @Override
294    public boolean equals(Object obj) {
295        if (obj == this) {
296            return true;
297        }
298        if (!(obj instanceof NetworkInterface)) {
299            return false;
300        }
301        NetworkInterface rhs = (NetworkInterface) obj;
302        // TODO: should the order of the addresses matter (we use List.equals)?
303        return interfaceIndex == rhs.interfaceIndex &&
304                name.equals(rhs.name) && displayName.equals(rhs.displayName) &&
305                addresses.equals(rhs.addresses);
306    }
307
308    /**
309     * Returns the hash code for this {@code NetworkInterface}. Since the
310     * name should be unique for each network interface the hash code is
311     * generated using this name.
312     */
313    @Override
314    public int hashCode() {
315        return name.hashCode();
316    }
317
318    /**
319     * Gets a string containing a concise, human-readable description of this
320     * network interface.
321     *
322     * @return the textual representation for this network interface.
323     */
324    @Override
325    public String toString() {
326        StringBuilder string = new StringBuilder(25);
327        string.append("[");
328        string.append(name);
329        string.append("][");
330        string.append(displayName);
331        string.append("][");
332        string.append(interfaceIndex);
333        string.append("]");
334
335        /*
336         * get the addresses through this call to make sure we only reveal those
337         * that we should
338         */
339        Enumeration<InetAddress> theAddresses = getInetAddresses();
340        if (theAddresses != null) {
341            while (theAddresses.hasMoreElements()) {
342                InetAddress nextAddress = theAddresses.nextElement();
343                string.append("[");
344                string.append(nextAddress.toString());
345                string.append("]");
346            }
347        }
348        return string.toString();
349    }
350
351    /**
352     * Returns a List the InterfaceAddresses for this network interface.
353     * <p>
354     * If there is a security manager, its checkConnect method is called with
355     * the InetAddress for each InterfaceAddress. Only InterfaceAddresses where
356     * the checkConnect doesn't throw a SecurityException will be returned.
357     *
358     * @return a List of the InterfaceAddresses for this network interface.
359     * @since 1.6
360     */
361    public List<InterfaceAddress> getInterfaceAddresses() {
362        SecurityManager sm = System.getSecurityManager();
363        if (sm == null) {
364            return Collections.unmodifiableList(interfaceAddresses);
365        }
366        // TODO: Android should ditch SecurityManager and the associated pollution.
367        List<InterfaceAddress> result = new ArrayList<InterfaceAddress>(interfaceAddresses.size());
368        for (InterfaceAddress ia : interfaceAddresses) {
369            try {
370                sm.checkConnect(ia.getAddress().getHostName(), CHECK_CONNECT_NO_PORT);
371            } catch (SecurityException e) {
372                continue;
373            }
374            result.add(ia);
375        }
376        return result;
377    }
378
379    /**
380     * Returns an {@code Enumeration} of all the sub-interfaces of this network interface.
381     * Sub-interfaces are also known as virtual interfaces.
382     * <p>
383     * For example, {@code eth0:1} would be a sub-interface of {@code eth0}.
384     *
385     * @return an Enumeration of all the sub-interfaces of this network interface
386     * @since 1.6
387     */
388    public Enumeration<NetworkInterface> getSubInterfaces() {
389        return Collections.enumeration(children);
390    }
391
392    /**
393     * Returns the parent NetworkInterface of this interface if this is a
394     * sub-interface, or null if it's a physical (non virtual) interface.
395     *
396     * @return the NetworkInterface this interface is attached to.
397     * @since 1.6
398     */
399    public NetworkInterface getParent() {
400        return parent;
401    }
402
403    /**
404     * Returns true if this network interface is up.
405     *
406     * @return true if the interface is up.
407     * @throws SocketException if an I/O error occurs.
408     * @since 1.6
409     */
410    public boolean isUp() throws SocketException {
411        if (addresses.isEmpty()) {
412            return false;
413        }
414        return isUpImpl(name);
415    }
416    private static native boolean isUpImpl(String n) throws SocketException;
417
418    /**
419     * Returns true if this network interface is a loopback interface.
420     *
421     * @return true if the interface is a loopback interface.
422     * @throws SocketException if an I/O error occurs.
423     * @since 1.6
424     */
425    public boolean isLoopback() throws SocketException {
426        if (addresses.isEmpty()) {
427            return false;
428        }
429        return isLoopbackImpl(name);
430    }
431    private static native boolean isLoopbackImpl(String n) throws SocketException;
432
433    /**
434     * Returns true if this network interface is a point-to-point interface.
435     * (For example, a PPP connection using a modem.)
436     *
437     * @return true if the interface is point-to-point.
438     * @throws SocketException if an I/O error occurs.
439     * @since 1.6
440     */
441    public boolean isPointToPoint() throws SocketException {
442        if (addresses.isEmpty()) {
443            return false;
444        }
445        return isPointToPointImpl(name);
446    }
447    private static native boolean isPointToPointImpl(String n) throws SocketException;
448
449    /**
450     * Returns true if this network interface supports multicast.
451     *
452     * @throws SocketException if an I/O error occurs.
453     * @since 1.6
454     */
455    public boolean supportsMulticast() throws SocketException {
456        if (addresses.isEmpty()) {
457            return false;
458        }
459        return supportsMulticastImpl(name);
460    }
461    private static native boolean supportsMulticastImpl(String n) throws SocketException;
462
463    /**
464     * Returns the hardware address of the interface, if it has one, and the
465     * user has the necessary privileges to access the address.
466     *
467     * @return a byte array containing the address or null if the address
468     *         doesn't exist or is not accessible.
469     * @throws SocketException if an I/O error occurs.
470     * @since 1.6
471     */
472    public byte[] getHardwareAddress() throws SocketException {
473        if (addresses.isEmpty()) {
474            return new byte[0];
475        }
476        return getHardwareAddressImpl(name);
477    }
478    private static native byte[] getHardwareAddressImpl(String n) throws SocketException;
479
480    /**
481     * Returns the Maximum Transmission Unit (MTU) of this interface.
482     *
483     * @return the value of the MTU for the interface.
484     * @throws SocketException if an I/O error occurs.
485     * @since 1.6
486     */
487    public int getMTU() throws SocketException {
488        if (addresses.isEmpty()) {
489            return 0;
490        }
491        return getMTUImpl(name);
492    }
493    private static native int getMTUImpl(String n) throws SocketException;
494
495    /**
496     * Returns true if this interface is a virtual interface (also called
497     * a sub-interface). Virtual interfaces are, on some systems, interfaces
498     * created as a child of a physical interface and given different settings
499     * (like address or MTU). Usually the name of the interface will the name of
500     * the parent followed by a colon (:) and a number identifying the child,
501     * since there can be several virtual interfaces attached to a single
502     * physical interface.
503     *
504     * @return true if this interface is a virtual interface.
505     * @since 1.6
506     */
507    public boolean isVirtual() {
508        return parent != null;
509    }
510}
511