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.io.IOException;
21import java.io.ObjectInputStream;
22
23/**
24 * This class represents a socket endpoint described by a IP address and a port
25 * number. It is a concrete implementation of {@code SocketAddress} for IP.
26 */
27public class InetSocketAddress extends SocketAddress {
28
29    private static final long serialVersionUID = 5076001401234631237L;
30
31    // Exactly one of hostname or addr should be set.
32    private final InetAddress addr;
33    private final String hostname;
34    private final int port;
35
36    /**
37     * @hide internal use only
38     */
39    public InetSocketAddress() {
40        // These will be filled in the native implementation of recvfrom.
41        this.addr = null;
42        this.hostname = null;
43        this.port = -1;
44    }
45
46    /**
47     * Creates a socket endpoint with the given port number {@code port} and
48     * no specified address. The range for valid port numbers is between 0 and
49     * 65535 inclusive.
50     *
51     * @param port
52     *            the specified port number to which this socket is bound.
53     */
54    public InetSocketAddress(int port) {
55        this((InetAddress) null, port);
56    }
57
58    /**
59     * Creates a socket endpoint with the given port number {@code port} and
60     * {@code address}. The range for valid port numbers is between 0 and 65535
61     * inclusive. If {@code address} is {@code null} this socket is bound to the
62     * IPv4 wildcard address.
63     *
64     * @param port
65     *            the specified port number to which this socket is bound.
66     * @param address
67     *            the specified address to which this socket is bound.
68     */
69    public InetSocketAddress(InetAddress address, int port) {
70        if (port < 0 || port > 65535) {
71            throw new IllegalArgumentException("port=" + port);
72        }
73        this.addr = (address == null) ? Inet4Address.ANY : address;
74        this.hostname = null;
75        this.port = port;
76    }
77
78    /**
79     * Creates a socket endpoint with the given port number {@code port} and the
80     * hostname {@code host}. The hostname is tried to be resolved and cannot be
81     * {@code null}. The range for valid port numbers is between 0 and 65535
82     * inclusive.
83     *
84     * @param port
85     *            the specified port number to which this socket is bound.
86     * @param host
87     *            the specified hostname to which this socket is bound.
88     */
89    public InetSocketAddress(String host, int port) {
90        this(host, port, true);
91    }
92
93    /*
94     * Internal constructor for InetSocketAddress(String, int) and
95     * createUnresolved(String, int);
96     */
97    InetSocketAddress(String hostname, int port, boolean needResolved) {
98        if (hostname == null || port < 0 || port > 65535) {
99            throw new IllegalArgumentException("host=" + hostname + ", port=" + port);
100        }
101
102        InetAddress addr = null;
103        if (needResolved) {
104            try {
105                addr = InetAddress.getByName(hostname);
106                hostname = null;
107            } catch (UnknownHostException ignored) {
108            }
109        }
110        this.addr = addr;
111        this.hostname = hostname;
112        this.port = port;
113    }
114
115    /**
116     * Creates an {@code InetSocketAddress} without trying to resolve the
117     * hostname into an {@code InetAddress}. The address field is marked as
118     * unresolved.
119     *
120     * @param host
121     *            the specified hostname to which this socket is bound.
122     * @param port
123     *            the specified port number to which this socket is bound.
124     * @return the created InetSocketAddress instance.
125     * @throws IllegalArgumentException
126     *             if the hostname {@code host} is {@code null} or the port is
127     *             not in the range between 0 and 65535.
128     */
129    public static InetSocketAddress createUnresolved(String host, int port) {
130        return new InetSocketAddress(host, port, false);
131    }
132
133    /**
134     * Gets the port number of this socket.
135     *
136     * @return the socket endpoint port number.
137     */
138    public final int getPort() {
139        return port;
140    }
141
142    /**
143     * Gets the address of this socket.
144     *
145     * @return the socket endpoint address.
146     */
147    public final InetAddress getAddress() {
148        return addr;
149    }
150
151    /**
152     * Returns the hostname, doing a reverse lookup on the {@code InetAddress} if no
153     * hostname string was provided at construction time.
154     */
155    public final String getHostName() {
156        return (addr != null) ? addr.getHostName() : hostname;
157    }
158
159    /**
160     * Returns the hostname if known, or the result of {@code InetAddress.getHostAddress}.
161     * Unlike {@link #getHostName}, this method will never cause a DNS lookup.
162     * @since 1.7
163     * @hide 1.7 - remember to add a link in the getHostName documentation!
164     */
165    public final String getHostString() {
166        return (hostname != null) ? hostname : addr.getHostAddress();
167    }
168
169    /**
170     * Returns whether this socket address is unresolved or not.
171     *
172     * @return {@code true} if this socket address is unresolved, {@code false}
173     *         otherwise.
174     */
175    public final boolean isUnresolved() {
176        return addr == null;
177    }
178
179    /**
180     * Returns a string containing the address (or the hostname for an
181     * unresolved {@code InetSocketAddress}) and port number.
182     * For example: {@code "www.google.com/74.125.224.115:80"} or {@code "/127.0.0.1:80"}.
183     */
184    @Override public String toString() {
185        return ((addr != null) ? addr.toString() : hostname) + ":" + port;
186    }
187
188    /**
189     * Compares two socket endpoints and returns true if they are equal. Two
190     * socket endpoints are equal if the IP address or the hostname of both are
191     * equal and they are bound to the same port.
192     *
193     * @param socketAddr
194     *            the object to be tested for equality.
195     * @return {@code true} if this socket and the given socket object {@code
196     *         socketAddr} are equal, {@code false} otherwise.
197     */
198    @Override
199    public final boolean equals(Object socketAddr) {
200        if (this == socketAddr) {
201            return true;
202        }
203        if (!(socketAddr instanceof InetSocketAddress)) {
204            return false;
205        }
206        InetSocketAddress iSockAddr = (InetSocketAddress) socketAddr;
207
208        // check the ports as we always need to do this
209        if (port != iSockAddr.port) {
210            return false;
211        }
212
213        // we only use the hostnames in the comparison if the addrs were not
214        // resolved
215        if ((addr == null) && (iSockAddr.addr == null)) {
216            return hostname.equals(iSockAddr.hostname);
217        }
218
219        // addrs were resolved so use them for the comparison
220        if (addr == null) {
221            // if we are here we know iSockAddr is not null so just return
222            // false
223            return false;
224        }
225        return addr.equals(iSockAddr.addr);
226    }
227
228    @Override
229    public final int hashCode() {
230        if (addr == null) {
231            return hostname.hashCode() + port;
232        }
233        return addr.hashCode() + port;
234    }
235
236    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
237        stream.defaultReadObject();
238    }
239}
240