1/*
2 * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.net;
27
28import java.io.FileDescriptor;
29import java.io.IOException;
30import java.util.Set;
31
32/**
33 * Abstract datagram and multicast socket implementation base class.
34 * @author Pavani Diwanji
35 * @since  1.1
36 */
37
38public abstract class DatagramSocketImpl implements SocketOptions {
39
40    /**
41     * The local port number.
42     */
43    protected int localPort;
44
45    /**
46     * The file descriptor object.
47     */
48    protected FileDescriptor fd;
49
50    /**
51     * The DatagramSocket or MulticastSocket
52     * that owns this impl
53     */
54    DatagramSocket socket;
55
56    void setDatagramSocket(DatagramSocket socket) {
57        this.socket = socket;
58    }
59
60    DatagramSocket getDatagramSocket() {
61        return socket;
62    }
63
64    int dataAvailable() {
65        // default impl returns zero, which disables the calling
66        // functionality
67        return 0;
68    }
69
70    /**
71     * Creates a datagram socket.
72     * @exception SocketException if there is an error in the
73     * underlying protocol, such as a TCP error.
74     */
75    protected abstract void create() throws SocketException;
76
77    /**
78     * Binds a datagram socket to a local port and address.
79     * @param lport the local port
80     * @param laddr the local address
81     * @exception SocketException if there is an error in the
82     * underlying protocol, such as a TCP error.
83     */
84    protected abstract void bind(int lport, InetAddress laddr) throws SocketException;
85
86    /**
87     * Sends a datagram packet. The packet contains the data and the
88     * destination address to send the packet to.
89     * @param p the packet to be sent.
90     * @exception IOException if an I/O exception occurs while sending the
91     * datagram packet.
92     * @exception  PortUnreachableException may be thrown if the socket is connected
93     * to a currently unreachable destination. Note, there is no guarantee that
94     * the exception will be thrown.
95     */
96    protected abstract void send(DatagramPacket p) throws IOException;
97
98    /**
99     * Connects a datagram socket to a remote destination. This associates the remote
100     * address with the local socket so that datagrams may only be sent to this destination
101     * and received from this destination. This may be overridden to call a native
102     * system connect.
103     *
104     * <p>If the remote destination to which the socket is connected does not
105     * exist, or is otherwise unreachable, and if an ICMP destination unreachable
106     * packet has been received for that address, then a subsequent call to
107     * send or receive may throw a PortUnreachableException.
108     * Note, there is no guarantee that the exception will be thrown.
109     * @param address the remote InetAddress to connect to
110     * @param port the remote port number
111     * @exception   SocketException may be thrown if the socket cannot be
112     * connected to the remote destination
113     * @since 1.4
114     */
115    protected void connect(InetAddress address, int port) throws SocketException {}
116
117    /**
118     * Disconnects a datagram socket from its remote destination.
119     * @since 1.4
120     */
121    protected void disconnect() {}
122
123    /**
124     * Peek at the packet to see who it is from. Updates the specified {@code InetAddress}
125     * to the address which the packet came from.
126     * @param i an InetAddress object
127     * @return the port number which the packet came from.
128     * @exception IOException if an I/O exception occurs
129     * @exception  PortUnreachableException may be thrown if the socket is connected
130     *       to a currently unreachable destination. Note, there is no guarantee that the
131     *       exception will be thrown.
132     */
133    protected abstract int peek(InetAddress i) throws IOException;
134
135    /**
136     * Peek at the packet to see who it is from. The data is copied into the specified
137     * {@code DatagramPacket}. The data is returned,
138     * but not consumed, so that a subsequent peekData/receive operation
139     * will see the same data.
140     * @param p the Packet Received.
141     * @return the port number which the packet came from.
142     * @exception IOException if an I/O exception occurs
143     * @exception  PortUnreachableException may be thrown if the socket is connected
144     *       to a currently unreachable destination. Note, there is no guarantee that the
145     *       exception will be thrown.
146     * @since 1.4
147     */
148    protected abstract int peekData(DatagramPacket p) throws IOException;
149    /**
150     * Receive the datagram packet.
151     * @param p the Packet Received.
152     * @exception IOException if an I/O exception occurs
153     * while receiving the datagram packet.
154     * @exception  PortUnreachableException may be thrown if the socket is connected
155     *       to a currently unreachable destination. Note, there is no guarantee that the
156     *       exception will be thrown.
157     */
158    protected abstract void receive(DatagramPacket p) throws IOException;
159
160    /**
161     * Set the TTL (time-to-live) option.
162     * @param ttl a byte specifying the TTL value
163     *
164     * @deprecated use setTimeToLive instead.
165     * @exception IOException if an I/O exception occurs while setting
166     * the time-to-live option.
167     * @see #getTTL()
168     */
169    @Deprecated
170    protected abstract void setTTL(byte ttl) throws IOException;
171
172    /**
173     * Retrieve the TTL (time-to-live) option.
174     *
175     * @exception IOException if an I/O exception occurs
176     * while retrieving the time-to-live option
177     * @deprecated use getTimeToLive instead.
178     * @return a byte representing the TTL value
179     * @see #setTTL(byte)
180     */
181    @Deprecated
182    protected abstract byte getTTL() throws IOException;
183
184    /**
185     * Set the TTL (time-to-live) option.
186     * @param ttl an {@code int} specifying the time-to-live value
187     * @exception IOException if an I/O exception occurs
188     * while setting the time-to-live option.
189     * @see #getTimeToLive()
190     */
191    protected abstract void setTimeToLive(int ttl) throws IOException;
192
193    /**
194     * Retrieve the TTL (time-to-live) option.
195     * @exception IOException if an I/O exception occurs
196     * while retrieving the time-to-live option
197     * @return an {@code int} representing the time-to-live value
198     * @see #setTimeToLive(int)
199     */
200    protected abstract int getTimeToLive() throws IOException;
201
202    /**
203     * Join the multicast group.
204     * @param inetaddr multicast address to join.
205     * @exception IOException if an I/O exception occurs
206     * while joining the multicast group.
207     */
208    protected abstract void join(InetAddress inetaddr) throws IOException;
209
210    /**
211     * Leave the multicast group.
212     * @param inetaddr multicast address to leave.
213     * @exception IOException if an I/O exception occurs
214     * while leaving the multicast group.
215     */
216    protected abstract void leave(InetAddress inetaddr) throws IOException;
217
218    /**
219     * Join the multicast group.
220     * @param mcastaddr address to join.
221     * @param netIf specifies the local interface to receive multicast
222     *        datagram packets
223     * @throws IOException if an I/O exception occurs while joining
224     * the multicast group
225     * @since 1.4
226     */
227    protected abstract void joinGroup(SocketAddress mcastaddr,
228                                      NetworkInterface netIf)
229        throws IOException;
230
231    /**
232     * Leave the multicast group.
233     * @param mcastaddr address to leave.
234     * @param netIf specified the local interface to leave the group at
235     * @throws IOException if an I/O exception occurs while leaving
236     * the multicast group
237     * @since 1.4
238     */
239    protected abstract void leaveGroup(SocketAddress mcastaddr,
240                                       NetworkInterface netIf)
241        throws IOException;
242
243    /**
244     * Close the socket.
245     */
246    protected abstract void close();
247
248    /**
249     * Gets the local port.
250     * @return an {@code int} representing the local port value
251     */
252    protected int getLocalPort() {
253        return localPort;
254    }
255
256    /**
257     * Gets the datagram socket file descriptor.
258     * @return a {@code FileDescriptor} object representing the datagram socket
259     * file descriptor
260     */
261    protected FileDescriptor getFileDescriptor() {
262        return fd;
263    }
264
265    /**
266     * Called to set a socket option.
267     *
268     * @param <T> The type of the socket option value
269     * @param name The socket option
270     *
271     * @param value The value of the socket option. A value of {@code null}
272     *              may be valid for some options.
273     *
274     * @throws UnsupportedOperationException if the DatagramSocketImpl does not
275     *         support the option
276     *
277     * @throws NullPointerException if name is {@code null}
278     * @throws IOException if an I/O problem occurs while attempting to set the option
279     * @since 9
280     */
281    protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
282        if (name == StandardSocketOptions.SO_SNDBUF) {
283            setOption(SocketOptions.SO_SNDBUF, value);
284        } else if (name == StandardSocketOptions.SO_RCVBUF) {
285            setOption(SocketOptions.SO_RCVBUF, value);
286        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
287            setOption(SocketOptions.SO_REUSEADDR, value);
288        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
289            supportedOptions().contains(name)) {
290            setOption(SocketOptions.SO_REUSEPORT, value);
291        } else if (name == StandardSocketOptions.IP_TOS) {
292            setOption(SocketOptions.IP_TOS, value);
293        } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
294            (getDatagramSocket() instanceof MulticastSocket)) {
295            setOption(SocketOptions.IP_MULTICAST_IF2, value);
296        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
297            (getDatagramSocket() instanceof MulticastSocket)) {
298            if (! (value instanceof Integer)) {
299                throw new IllegalArgumentException("not an integer");
300            }
301            setTimeToLive((Integer)value);
302        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
303            (getDatagramSocket() instanceof MulticastSocket)) {
304            setOption(SocketOptions.IP_MULTICAST_LOOP, value);
305        } else {
306            throw new UnsupportedOperationException("unsupported option");
307        }
308    }
309
310    /**
311     * Called to get a socket option.
312     *
313     * @return the socket option
314     * @param <T> The type of the socket option value
315     * @param name The socket option
316     *
317     * @throws UnsupportedOperationException if the DatagramSocketImpl does not
318     *         support the option
319     *
320     * @throws NullPointerException if name is {@code null}
321     * @throws IOException if an I/O problem occurs while attempting to set the option
322     *
323     * @since 9
324     */
325    @SuppressWarnings("unchecked")
326    protected <T> T getOption(SocketOption<T> name) throws IOException {
327        if (name == StandardSocketOptions.SO_SNDBUF) {
328            return (T) getOption(SocketOptions.SO_SNDBUF);
329        } else if (name == StandardSocketOptions.SO_RCVBUF) {
330            return (T) getOption(SocketOptions.SO_RCVBUF);
331        } else if (name == StandardSocketOptions.SO_REUSEADDR) {
332            return (T) getOption(SocketOptions.SO_REUSEADDR);
333        } else if (name == StandardSocketOptions.SO_REUSEPORT &&
334            supportedOptions().contains(name)) {
335            return (T) getOption(SocketOptions.SO_REUSEPORT);
336        } else if (name == StandardSocketOptions.IP_TOS) {
337            return (T) getOption(SocketOptions.IP_TOS);
338        } else if (name == StandardSocketOptions.IP_MULTICAST_IF &&
339            (getDatagramSocket() instanceof MulticastSocket)) {
340            return (T) getOption(SocketOptions.IP_MULTICAST_IF2);
341        } else if (name == StandardSocketOptions.IP_MULTICAST_TTL &&
342            (getDatagramSocket() instanceof MulticastSocket)) {
343            Integer ttl = getTimeToLive();
344            return (T)ttl;
345        } else if (name == StandardSocketOptions.IP_MULTICAST_LOOP &&
346            (getDatagramSocket() instanceof MulticastSocket)) {
347            return (T) getOption(SocketOptions.IP_MULTICAST_LOOP);
348        } else {
349            throw new UnsupportedOperationException("unsupported option");
350        }
351    }
352
353    private static final Set<SocketOption<?>> dgSocketOptions;
354
355    private static final Set<SocketOption<?>> mcSocketOptions;
356
357    static {
358        dgSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
359                                 StandardSocketOptions.SO_RCVBUF,
360                                 StandardSocketOptions.SO_REUSEADDR,
361                                 StandardSocketOptions.IP_TOS);
362
363        mcSocketOptions = Set.of(StandardSocketOptions.SO_SNDBUF,
364                                 StandardSocketOptions.SO_RCVBUF,
365                                 StandardSocketOptions.SO_REUSEADDR,
366                                 StandardSocketOptions.IP_TOS,
367                                 StandardSocketOptions.IP_MULTICAST_IF,
368                                 StandardSocketOptions.IP_MULTICAST_TTL,
369                                 StandardSocketOptions.IP_MULTICAST_LOOP);
370    }
371
372    /**
373     * Returns a set of SocketOptions supported by this impl
374     * and by this impl's socket (DatagramSocket or MulticastSocket)
375     *
376     * @return a Set of SocketOptions
377     *
378     * @since 9
379     */
380    protected Set<SocketOption<?>> supportedOptions() {
381        if (getDatagramSocket() instanceof MulticastSocket) {
382            return mcSocketOptions;
383        } else {
384            return dgSocketOptions;
385        }
386    }
387}
388