1 /*
2  * Copyright (c) 1996, 2018, 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 package java.net;
26 
27 import java.io.FileDescriptor;
28 import java.io.IOException;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Set;
32 
33 import sun.net.ResourceManager;
34 import sun.security.action.GetPropertyAction;
35 
36 /**
37  * Abstract datagram and multicast socket implementation base class.
38  * Note: This is not a public class, so that applets cannot call
39  * into the implementation directly and hence cannot bypass the
40  * security checks present in the DatagramSocket and MulticastSocket
41  * classes.
42  *
43  * @author Pavani Diwanji
44  */
45 
46 abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
47 {
48     /* timeout value for receive() */
49     int timeout = 0;
50     boolean connected = false;
51     private int trafficClass = 0;
52     protected InetAddress connectedAddress = null;
53     private int connectedPort = -1;
54 
55     private static final String os =
56             GetPropertyAction.privilegedGetProperty("os.name");
57 
58     /**
59      * flag set if the native connect() call not to be used
60      */
61     private static final boolean connectDisabled = os.contains("OS X");
62 
63     /**
64      * Load net library into runtime.
65      */
66     static {
java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<>() { public Void run() { System.loadLibrary(R); return null; } })67         java.security.AccessController.doPrivileged(
68             new java.security.PrivilegedAction<>() {
69                 public Void run() {
70                     System.loadLibrary("net");
71                     return null;
72                 }
73             });
74     }
75 
76     private static volatile boolean checkedReusePort;
77     private static volatile boolean isReusePortAvailable;
78 
79     /**
80      * Tells whether SO_REUSEPORT is supported.
81      */
isReusePortAvailable()82     static boolean isReusePortAvailable() {
83         if (!checkedReusePort) {
84             isReusePortAvailable = isReusePortAvailable0();
85             checkedReusePort = true;
86         }
87         return isReusePortAvailable;
88     }
89 
90     /**
91      * Returns a set of SocketOptions supported by this impl and by this impl's
92      * socket (Socket or ServerSocket)
93      *
94      * @return a Set of SocketOptions
95      */
96     @Override
supportedOptions()97     protected Set<SocketOption<?>> supportedOptions() {
98         Set<SocketOption<?>> options;
99         if (isReusePortAvailable()) {
100             options = new HashSet<>();
101             options.addAll(super.supportedOptions());
102             options.add(StandardSocketOptions.SO_REUSEPORT);
103             options = Collections.unmodifiableSet(options);
104         } else {
105             options = super.supportedOptions();
106         }
107         return options;
108     }
109 
110     /**
111      * Creates a datagram socket
112      */
create()113     protected synchronized void create() throws SocketException {
114         ResourceManager.beforeUdpCreate();
115         fd = new FileDescriptor();
116         try {
117             datagramSocketCreate();
118             SocketCleanable.register(fd);
119         } catch (SocketException ioe) {
120             ResourceManager.afterUdpClose();
121             fd = null;
122             throw ioe;
123         }
124     }
125 
126     /**
127      * Binds a datagram socket to a local port.
128      */
bind(int lport, InetAddress laddr)129     protected synchronized void bind(int lport, InetAddress laddr)
130         throws SocketException {
131         bind0(lport, laddr);
132     }
133 
bind0(int lport, InetAddress laddr)134     protected abstract void bind0(int lport, InetAddress laddr)
135         throws SocketException;
136 
137     /**
138      * Sends a datagram packet. The packet contains the data and the
139      * destination address to send the packet to.
140      * @param p the packet to be sent.
141      */
send(DatagramPacket p)142     protected abstract void send(DatagramPacket p) throws IOException;
143 
144     /**
145      * Connects a datagram socket to a remote destination. This associates the remote
146      * address with the local socket so that datagrams may only be sent to this destination
147      * and received from this destination.
148      * @param address the remote InetAddress to connect to
149      * @param port the remote port number
150      */
connect(InetAddress address, int port)151     protected void connect(InetAddress address, int port) throws SocketException {
152         connect0(address, port);
153         connectedAddress = address;
154         connectedPort = port;
155         connected = true;
156     }
157 
158     /**
159      * Disconnects a previously connected socket. Does nothing if the socket was
160      * not connected already.
161      */
disconnect()162     protected void disconnect() {
163         disconnect0(connectedAddress.holder().getFamily());
164         connected = false;
165         connectedAddress = null;
166         connectedPort = -1;
167     }
168 
169     /**
170      * Peek at the packet to see who it is from.
171      * @param i the address to populate with the sender address
172      */
peek(InetAddress i)173     protected abstract int peek(InetAddress i) throws IOException;
peekData(DatagramPacket p)174     protected abstract int peekData(DatagramPacket p) throws IOException;
175     /**
176      * Receive the datagram packet.
177      * @param p the packet to receive into
178      */
receive(DatagramPacket p)179     protected synchronized void receive(DatagramPacket p)
180         throws IOException {
181         receive0(p);
182     }
183 
receive0(DatagramPacket p)184     protected abstract void receive0(DatagramPacket p)
185         throws IOException;
186 
187     /**
188      * Set the TTL (time-to-live) option.
189      * @param ttl TTL to be set.
190      */
setTimeToLive(int ttl)191     protected abstract void setTimeToLive(int ttl) throws IOException;
192 
193     /**
194      * Get the TTL (time-to-live) option.
195      */
getTimeToLive()196     protected abstract int getTimeToLive() throws IOException;
197 
198     /**
199      * Set the TTL (time-to-live) option.
200      * @param ttl TTL to be set.
201      */
202     @Deprecated
setTTL(byte ttl)203     protected abstract void setTTL(byte ttl) throws IOException;
204 
205     /**
206      * Get the TTL (time-to-live) option.
207      */
208     @Deprecated
getTTL()209     protected abstract byte getTTL() throws IOException;
210 
211     /**
212      * Join the multicast group.
213      * @param inetaddr multicast address to join.
214      */
join(InetAddress inetaddr)215     protected void join(InetAddress inetaddr) throws IOException {
216         join(inetaddr, null);
217     }
218 
219     /**
220      * Leave the multicast group.
221      * @param inetaddr multicast address to leave.
222      */
leave(InetAddress inetaddr)223     protected void leave(InetAddress inetaddr) throws IOException {
224         leave(inetaddr, null);
225     }
226     /**
227      * Join the multicast group.
228      * @param mcastaddr multicast address to join.
229      * @param netIf specifies the local interface to receive multicast
230      *        datagram packets
231      * @throws  IllegalArgumentException if mcastaddr is null or is a
232      *          SocketAddress subclass not supported by this socket
233      * @since 1.4
234      */
235 
joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)236     protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
237         throws IOException {
238         if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
239             throw new IllegalArgumentException("Unsupported address type");
240         join(((InetSocketAddress)mcastaddr).getAddress(), netIf);
241     }
242 
join(InetAddress inetaddr, NetworkInterface netIf)243     protected abstract void join(InetAddress inetaddr, NetworkInterface netIf)
244         throws IOException;
245 
246     /**
247      * Leave the multicast group.
248      * @param mcastaddr  multicast address to leave.
249      * @param netIf specified the local interface to leave the group at
250      * @throws  IllegalArgumentException if mcastaddr is null or is a
251      *          SocketAddress subclass not supported by this socket
252      * @since 1.4
253      */
leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)254     protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
255         throws IOException {
256         if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
257             throw new IllegalArgumentException("Unsupported address type");
258         leave(((InetSocketAddress)mcastaddr).getAddress(), netIf);
259     }
260 
leave(InetAddress inetaddr, NetworkInterface netIf)261     protected abstract void leave(InetAddress inetaddr, NetworkInterface netIf)
262         throws IOException;
263 
264     /**
265      * Close the socket.
266      */
close()267     protected void close() {
268         if (fd != null) {
269             SocketCleanable.unregister(fd);
270             datagramSocketClose();
271             ResourceManager.afterUdpClose();
272             fd = null;
273         }
274     }
275 
isClosed()276     protected boolean isClosed() {
277         return (fd == null) ? true : false;
278     }
279 
280     /**
281      * set a value - since we only support (setting) binary options
282      * here, o must be a Boolean
283      */
284 
setOption(int optID, Object o)285      public void setOption(int optID, Object o) throws SocketException {
286          if (isClosed()) {
287              throw new SocketException("Socket Closed");
288          }
289          switch (optID) {
290             /* check type safety b4 going native.  These should never
291              * fail, since only java.Socket* has access to
292              * PlainSocketImpl.setOption().
293              */
294          case SO_TIMEOUT:
295              if (o == null || !(o instanceof Integer)) {
296                  throw new SocketException("bad argument for SO_TIMEOUT");
297              }
298              int tmp = ((Integer) o).intValue();
299              if (tmp < 0)
300                  throw new IllegalArgumentException("timeout < 0");
301              timeout = tmp;
302              return;
303          case IP_TOS:
304              if (o == null || !(o instanceof Integer)) {
305                  throw new SocketException("bad argument for IP_TOS");
306              }
307              trafficClass = ((Integer)o).intValue();
308              break;
309          case SO_REUSEADDR:
310              if (o == null || !(o instanceof Boolean)) {
311                  throw new SocketException("bad argument for SO_REUSEADDR");
312              }
313              break;
314          case SO_BROADCAST:
315              if (o == null || !(o instanceof Boolean)) {
316                  throw new SocketException("bad argument for SO_BROADCAST");
317              }
318              break;
319          case SO_BINDADDR:
320              throw new SocketException("Cannot re-bind Socket");
321          case SO_RCVBUF:
322          case SO_SNDBUF:
323              if (o == null || !(o instanceof Integer) ||
324                  ((Integer)o).intValue() < 0) {
325                  throw new SocketException("bad argument for SO_SNDBUF or " +
326                                            "SO_RCVBUF");
327              }
328              break;
329          case IP_MULTICAST_IF:
330              if (o == null || !(o instanceof InetAddress))
331                  throw new SocketException("bad argument for IP_MULTICAST_IF");
332              break;
333          case IP_MULTICAST_IF2:
334              if (o == null || !(o instanceof NetworkInterface))
335                  throw new SocketException("bad argument for IP_MULTICAST_IF2");
336              break;
337          case IP_MULTICAST_LOOP:
338              if (o == null || !(o instanceof Boolean))
339                  throw new SocketException("bad argument for IP_MULTICAST_LOOP");
340              break;
341          case SO_REUSEPORT:
342              if (o == null || !(o instanceof Boolean)) {
343                  throw new SocketException("bad argument for SO_REUSEPORT");
344              }
345              if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
346                  throw new UnsupportedOperationException("unsupported option");
347              }
348              break;
349          default:
350              throw new SocketException("invalid option: " + optID);
351          }
352          socketSetOption(optID, o);
353      }
354 
355     /*
356      * get option's state - set or not
357      */
358 
getOption(int optID)359     public Object getOption(int optID) throws SocketException {
360         if (isClosed()) {
361             throw new SocketException("Socket Closed");
362         }
363 
364         Object result;
365 
366         switch (optID) {
367             case SO_TIMEOUT:
368                 result = timeout;
369                 break;
370 
371             case IP_TOS:
372                 result = socketGetOption(optID);
373                 if ( ((Integer)result).intValue() == -1) {
374                     result = trafficClass;
375                 }
376                 break;
377 
378             case SO_BINDADDR:
379             case IP_MULTICAST_IF:
380             case IP_MULTICAST_IF2:
381             case SO_RCVBUF:
382             case SO_SNDBUF:
383             case IP_MULTICAST_LOOP:
384             case SO_REUSEADDR:
385             case SO_BROADCAST:
386                 result = socketGetOption(optID);
387                 break;
388 
389             case SO_REUSEPORT:
390                 if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
391                     throw new UnsupportedOperationException("unsupported option");
392                 }
393                 result = socketGetOption(optID);
394                 break;
395 
396             default:
397                 throw new SocketException("invalid option: " + optID);
398         }
399 
400         return result;
401     }
402 
datagramSocketCreate()403     protected abstract void datagramSocketCreate() throws SocketException;
datagramSocketClose()404     protected abstract void datagramSocketClose();
socketSetOption(int opt, Object val)405     protected abstract void socketSetOption(int opt, Object val)
406         throws SocketException;
socketGetOption(int opt)407     protected abstract Object socketGetOption(int opt) throws SocketException;
408 
connect0(InetAddress address, int port)409     protected abstract void connect0(InetAddress address, int port) throws SocketException;
disconnect0(int family)410     protected abstract void disconnect0(int family);
411 
nativeConnectDisabled()412     protected boolean nativeConnectDisabled() {
413         return connectDisabled;
414     }
415 
dataAvailable()416     abstract int dataAvailable();
isReusePortAvailable0()417     private static native boolean isReusePortAvailable0();
418 }
419