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