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.Closeable;
21import java.io.FileDescriptor;
22import java.io.IOException;
23import java.nio.channels.DatagramChannel;
24import libcore.io.ErrnoException;
25import libcore.io.IoBridge;
26import libcore.io.Libcore;
27import static libcore.io.OsConstants.*;
28
29/**
30 * This class implements a UDP socket for sending and receiving {@code
31 * DatagramPacket}. A {@code DatagramSocket} object can be used for both
32 * endpoints of a connection for a packet delivery service.
33 *
34 * @see DatagramPacket
35 * @see DatagramSocketImplFactory
36 */
37public class DatagramSocket implements Closeable {
38
39    DatagramSocketImpl impl;
40
41    InetAddress address;
42
43    int port = -1;
44
45    static DatagramSocketImplFactory factory;
46
47    boolean isBound = false;
48
49    private boolean isConnected = false;
50
51    private SocketException pendingConnectException;
52
53    private boolean isClosed = false;
54
55    private Object lock = new Object();
56
57    /**
58     * Constructs a UDP datagram socket which is bound to any available port on
59     * the localhost.
60     *
61     * @throws SocketException
62     *             if an error occurs while creating or binding the socket.
63     */
64    public DatagramSocket() throws SocketException {
65        this(0);
66    }
67
68    /**
69     * Constructs a UDP datagram socket which is bound to the specific port
70     * {@code aPort} on the localhost. Valid values for {@code aPort} are
71     * between 0 and 65535 inclusive.
72     *
73     * @param aPort
74     *            the port to bind on the localhost.
75     * @throws SocketException
76     *             if an error occurs while creating or binding the socket.
77     */
78    public DatagramSocket(int aPort) throws SocketException {
79        checkPort(aPort);
80        createSocket(aPort, Inet4Address.ANY);
81    }
82
83    /**
84     * Constructs a UDP datagram socket which is bound to the specific local
85     * address {@code addr} on port {@code aPort}. Valid values for {@code
86     * aPort} are between 0 and 65535 inclusive.
87     *
88     * @param aPort
89     *            the port to bind on the localhost.
90     * @param addr
91     *            the address to bind on the localhost.
92     * @throws SocketException
93     *             if an error occurs while creating or binding the socket.
94     */
95    public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
96        checkPort(aPort);
97        createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
98    }
99
100    private void checkPort(int aPort) {
101        if (aPort < 0 || aPort > 65535) {
102            throw new IllegalArgumentException("Port out of range: " + aPort);
103        }
104    }
105
106    /**
107     * Closes this UDP datagram socket and all possibly associated channels.
108     */
109    // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is
110    // noted as not being synchronized.
111    public void close() {
112        isClosed = true;
113        impl.close();
114    }
115
116    /**
117     * Disconnects this UDP datagram socket from the remote host. This method
118     * called on an unconnected socket does nothing.
119     */
120    public void disconnect() {
121        if (isClosed() || !isConnected()) {
122            return;
123        }
124        impl.disconnect();
125        address = null;
126        port = -1;
127        isConnected = false;
128    }
129
130    synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
131        impl = factory != null ? factory.createDatagramSocketImpl()
132                : new PlainDatagramSocketImpl();
133        impl.create();
134        try {
135            impl.bind(aPort, addr);
136            isBound = true;
137        } catch (SocketException e) {
138            close();
139            throw e;
140        }
141    }
142
143    /**
144     * Gets the {@code InetAddress} instance representing the remote address to
145     * which this UDP datagram socket is connected.
146     *
147     * @return the remote address this socket is connected to or {@code null} if
148     *         this socket is not connected.
149     */
150    public InetAddress getInetAddress() {
151        return address;
152    }
153
154    /**
155     * Returns the local address to which this socket is bound,
156     * or {@code null} if this socket is closed.
157     */
158    public InetAddress getLocalAddress() {
159        try {
160            return IoBridge.getSocketLocalAddress(impl.fd);
161        } catch (SocketException ex) {
162            return null;
163        }
164    }
165
166    /**
167     * Gets the local port which this socket is bound to.
168     *
169     * @return the local port of this socket or {@code -1} if this socket is
170     *         closed and {@code 0} if it is unbound.
171     */
172    public int getLocalPort() {
173        if (isClosed()) {
174            return -1;
175        }
176        if (!isBound()) {
177            return 0;
178        }
179        return impl.getLocalPort();
180    }
181
182    /**
183     * Gets the remote port which this socket is connected to.
184     *
185     * @return the remote port of this socket. The return value {@code -1}
186     *         indicates that this socket is not connected.
187     */
188    public int getPort() {
189        return port;
190    }
191
192    /**
193     * Indicates whether this socket is multicast or not.
194     *
195     * @return the return value is always {@code false}.
196     */
197    boolean isMulticastSocket() {
198        return false;
199    }
200
201    /**
202     * Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}.
203     */
204    public synchronized int getReceiveBufferSize() throws SocketException {
205        checkOpen();
206        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
207    }
208
209    /**
210     * Returns this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
211     */
212    public synchronized int getSendBufferSize() throws SocketException {
213        checkOpen();
214        return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
215    }
216
217    /**
218     * Gets the socket {@link SocketOptions#SO_TIMEOUT receive timeout}.
219     *
220     * @throws SocketException
221     *                if an error occurs while getting the option value.
222     */
223    public synchronized int getSoTimeout() throws SocketException {
224        checkOpen();
225        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
226    }
227
228    /**
229     * Receives a packet from this socket and stores it in the argument {@code
230     * pack}. All fields of {@code pack} must be set according to the data
231     * received. If the received data is longer than the packet buffer size it
232     * is truncated. This method blocks until a packet is received or a timeout
233     * has expired.
234     *
235     * @param pack
236     *            the {@code DatagramPacket} to store the received data.
237     * @throws IOException
238     *                if an error occurs while receiving the packet.
239     */
240    public synchronized void receive(DatagramPacket pack) throws IOException {
241        checkOpen();
242        ensureBound();
243        if (pack == null) {
244            throw new NullPointerException("pack == null");
245        }
246        if (pendingConnectException != null) {
247            throw new SocketException("Pending connect failure", pendingConnectException);
248        }
249        pack.resetLengthForReceive();
250        impl.receive(pack);
251    }
252
253    /**
254     * Sends a packet over this socket.
255     *
256     * @param pack
257     *            the {@code DatagramPacket} which has to be sent.
258     * @throws IOException
259     *                if an error occurs while sending the packet.
260     */
261    public void send(DatagramPacket pack) throws IOException {
262        checkOpen();
263        ensureBound();
264
265        InetAddress packAddr = pack.getAddress();
266        if (address != null) { // The socket is connected
267            if (packAddr != null) {
268                if (!address.equals(packAddr) || port != pack.getPort()) {
269                    throw new IllegalArgumentException("Packet address mismatch with connected address");
270                }
271            } else {
272                pack.setAddress(address);
273                pack.setPort(port);
274            }
275        } else {
276            // not connected so the target address is not allowed to be null
277            if (packAddr == null) {
278                throw new NullPointerException("Destination address is null");
279            }
280        }
281        impl.send(pack);
282    }
283
284    /**
285     * Sets the network interface used by this socket.  Any packets sent
286     * via this socket are transmitted via the specified interface.  Any
287     * packets received by this socket will come from the specified
288     * interface.  Broadcast datagrams received on this interface will
289     * be processed by this socket. This corresponds to Linux's SO_BINDTODEVICE.
290     *
291     * @hide used by GoogleTV for DHCP
292     */
293    public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
294        if (netInterface == null) {
295            throw new NullPointerException("netInterface == null");
296        }
297        try {
298            Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
299        } catch (ErrnoException errnoException) {
300            throw errnoException.rethrowAsSocketException();
301        }
302    }
303
304    /**
305     * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
306     */
307    public synchronized void setSendBufferSize(int size) throws SocketException {
308        if (size < 1) {
309            throw new IllegalArgumentException("size < 1");
310        }
311        checkOpen();
312        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
313    }
314
315    /**
316     * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
317     */
318    public synchronized void setReceiveBufferSize(int size) throws SocketException {
319        if (size < 1) {
320            throw new IllegalArgumentException("size < 1");
321        }
322        checkOpen();
323        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
324    }
325
326    /**
327     * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket.
328     * This receive timeout defines the period the socket will block waiting to
329     * receive data before throwing an {@code InterruptedIOException}. The value
330     * {@code 0} (default) is used to set an infinite timeout. To have effect
331     * this option must be set before the blocking method was called.
332     *
333     * @param timeout the timeout in milliseconds or 0 for no timeout.
334     * @throws SocketException
335     *                if an error occurs while setting the option.
336     */
337    public synchronized void setSoTimeout(int timeout) throws SocketException {
338        if (timeout < 0) {
339            throw new IllegalArgumentException("timeout < 0");
340        }
341        checkOpen();
342        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
343    }
344
345    /**
346     * Sets the socket implementation factory. This may only be invoked once
347     * over the lifetime of the application. This factory is used to create
348     * a new datagram socket implementation.
349     *
350     * @param fac
351     *            the socket factory to use.
352     * @throws IOException
353     *                if the factory has already been set.
354     * @see DatagramSocketImplFactory
355     */
356    public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
357            throws IOException {
358        if (factory != null) {
359            throw new SocketException("Factory already set");
360        }
361        factory = fac;
362    }
363
364    /**
365     * Constructs a new {@code DatagramSocket} using the specific datagram
366     * socket implementation {@code socketImpl}. The created {@code
367     * DatagramSocket} will not be bound.
368     *
369     * @param socketImpl
370     *            the DatagramSocketImpl to use.
371     */
372    protected DatagramSocket(DatagramSocketImpl socketImpl) {
373        if (socketImpl == null) {
374            throw new NullPointerException("socketImpl == null");
375        }
376        impl = socketImpl;
377    }
378
379    /**
380     * Constructs a new {@code DatagramSocket} bound to the host/port specified
381     * by the {@code SocketAddress} {@code localAddr} or an unbound {@code
382     * DatagramSocket} if the {@code SocketAddress} is {@code null}.
383     *
384     * @param localAddr
385     *            the local machine address and port to bind to.
386     * @throws IllegalArgumentException
387     *             if the SocketAddress is not supported
388     * @throws SocketException
389     *             if a problem occurs creating or binding the socket.
390     */
391    public DatagramSocket(SocketAddress localAddr) throws SocketException {
392        if (localAddr != null) {
393            if (!(localAddr instanceof InetSocketAddress)) {
394                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
395                        localAddr.getClass());
396            }
397            checkPort(((InetSocketAddress) localAddr).getPort());
398        }
399        impl = factory != null ? factory.createDatagramSocketImpl()
400                : new PlainDatagramSocketImpl();
401        impl.create();
402        if (localAddr != null) {
403            try {
404                bind(localAddr);
405            } catch (SocketException e) {
406                close();
407                throw e;
408            }
409        }
410        // SocketOptions.SO_BROADCAST is set by default for DatagramSocket
411        setBroadcast(true);
412    }
413
414    void checkOpen() throws SocketException {
415        if (isClosed()) {
416            throw new SocketException("Socket is closed");
417        }
418    }
419
420    private void ensureBound() throws SocketException {
421        if (!isBound()) {
422            impl.bind(0, Inet4Address.ANY);
423            isBound = true;
424        }
425    }
426
427    /**
428     * Binds this socket to the local address and port specified by {@code
429     * localAddr}. If this value is {@code null} any free port on a valid local
430     * address is used.
431     *
432     * @param localAddr
433     *            the local machine address and port to bind on.
434     * @throws IllegalArgumentException
435     *             if the SocketAddress is not supported
436     * @throws SocketException
437     *             if the socket is already bound or a problem occurs during
438     *             binding.
439     */
440    public void bind(SocketAddress localAddr) throws SocketException {
441        checkOpen();
442        int localPort = 0;
443        InetAddress addr = Inet4Address.ANY;
444        if (localAddr != null) {
445            if (!(localAddr instanceof InetSocketAddress)) {
446                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
447                        localAddr.getClass());
448            }
449            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
450            addr = inetAddr.getAddress();
451            if (addr == null) {
452                throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
453            }
454            localPort = inetAddr.getPort();
455            checkPort(localPort);
456        }
457        impl.bind(localPort, addr);
458        isBound = true;
459    }
460
461    /**
462     * Connects this datagram socket to the address and port specified by {@code peer}.
463     * Future calls to {@link #send} will use this as the default target, and {@link #receive}
464     * will only accept packets from this source.
465     *
466     * @throws SocketException if an error occurs.
467     */
468    public void connect(SocketAddress peer) throws SocketException {
469        if (peer == null) {
470            throw new IllegalArgumentException("peer == null");
471        }
472
473        if (!(peer instanceof InetSocketAddress)) {
474            throw new IllegalArgumentException("peer not an InetSocketAddress: " + peer.getClass());
475        }
476
477        InetSocketAddress isa = (InetSocketAddress) peer;
478        if (isa.getAddress() == null) {
479            throw new SocketException("Host is unresolved: " + isa.getHostName());
480        }
481
482        synchronized (lock) {
483            checkOpen();
484            ensureBound();
485
486            this.address = isa.getAddress();
487            this.port = isa.getPort();
488            this.isConnected = true;
489
490            impl.connect(address, port);
491        }
492    }
493
494    /**
495     * Connects this datagram socket to the specific {@code address} and {@code port}.
496     * Future calls to {@link #send} will use this as the default target, and {@link #receive}
497     * will only accept packets from this source.
498     *
499     * <p>Beware: because it can't throw, this method silently ignores failures.
500     * Use {@link #connect(SocketAddress)} instead.
501     */
502    public void connect(InetAddress address, int port) {
503        if (address == null) {
504            throw new IllegalArgumentException("address == null");
505        }
506        try {
507            connect(new InetSocketAddress(address, port));
508        } catch (SocketException connectException) {
509            // TODO: or just use SneakyThrow? There's a clear API bug here.
510            pendingConnectException = connectException;
511        }
512    }
513
514    /**
515     * Returns true if this socket is bound to a local address. See {@link #bind}.
516     */
517    public boolean isBound() {
518        return isBound;
519    }
520
521    /**
522     * Returns true if this datagram socket is connected to a remote address. See {@link #connect}.
523     */
524    public boolean isConnected() {
525        return isConnected;
526    }
527
528    /**
529     * Returns the {@code SocketAddress} this socket is connected to, or null for an unconnected
530     * socket.
531     */
532    public SocketAddress getRemoteSocketAddress() {
533        if (!isConnected()) {
534            return null;
535        }
536        return new InetSocketAddress(getInetAddress(), getPort());
537    }
538
539    /**
540     * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket.
541     */
542    public SocketAddress getLocalSocketAddress() {
543        if (!isBound()) {
544            return null;
545        }
546        return new InetSocketAddress(getLocalAddress(), getLocalPort());
547    }
548
549    /**
550     * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option
551     * has to be enabled if more than one UDP socket wants to be bound to the
552     * same address. That could be needed for receiving multicast packets.
553     * <p>
554     * There is an undefined behavior if this option is set after the socket is
555     * already bound.
556     *
557     * @param reuse
558     *            the socket option value to enable or disable this option.
559     * @throws SocketException
560     *             if the socket is closed or the option could not be set.
561     */
562    public void setReuseAddress(boolean reuse) throws SocketException {
563        checkOpen();
564        impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
565    }
566
567    /**
568     * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}.
569     *
570     * @return {@code true} if the option is enabled, {@code false} otherwise.
571     * @throws SocketException
572     *             if the socket is closed or the option is invalid.
573     */
574    public boolean getReuseAddress() throws SocketException {
575        checkOpen();
576        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue();
577    }
578
579    /**
580     * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option
581     * must be enabled to send broadcast messages.
582     *
583     * @param broadcast
584     *            the socket option value to enable or disable this option.
585     * @throws SocketException
586     *             if the socket is closed or the option could not be set.
587     */
588    public void setBroadcast(boolean broadcast) throws SocketException {
589        checkOpen();
590        impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast));
591    }
592
593    /**
594     * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}.
595     *
596     * @return {@code true} if the option is enabled, {@code false} otherwise.
597     * @throws SocketException
598     *             if the socket is closed or the option is invalid.
599     */
600    public boolean getBroadcast() throws SocketException {
601        checkOpen();
602        return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST)).booleanValue();
603    }
604
605    /**
606     * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket.
607     *
608     * @throws SocketException
609     *             if the socket is closed or the option could not be set.
610     */
611    public void setTrafficClass(int value) throws SocketException {
612        checkOpen();
613        if (value < 0 || value > 255) {
614            throw new IllegalArgumentException("Value doesn't fit in an unsigned byte: " + value);
615        }
616        impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
617    }
618
619    /**
620     * Returns this socket's {@see SocketOptions#IP_TOS} setting.
621     *
622     * @throws SocketException
623     *             if the socket is closed or the option is invalid.
624     */
625    public int getTrafficClass() throws SocketException {
626        checkOpen();
627        return (Integer) impl.getOption(SocketOptions.IP_TOS);
628    }
629
630    /**
631     * Gets the state of this socket.
632     *
633     * @return {@code true} if the socket is closed, {@code false} otherwise.
634     */
635    public boolean isClosed() {
636        return isClosed;
637    }
638
639    /**
640     * Returns this socket's {@code DatagramChannel}, if one exists. A channel is
641     * available only if this socket wraps a channel. (That is, you can go from a
642     * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.)
643     * In practice, this means that the socket must have been created by
644     * {@link java.nio.channels.DatagramChannel#open}.
645     */
646    public DatagramChannel getChannel() {
647        return null;
648    }
649
650    /**
651     * @hide internal use only
652     */
653    public final FileDescriptor getFileDescriptor$() {
654        return impl.fd;
655    }
656}
657