1/*
2 * Copyright (c) 2014, 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 jdk.net;
27
28import java.io.FileDescriptor;
29import java.net.SocketException;
30import java.net.SocketOption;
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33import java.util.Collections;
34import java.util.Set;
35import jdk.internal.misc.JavaIOFileDescriptorAccess;
36import jdk.internal.misc.SharedSecrets;
37
38/**
39 * Defines extended socket options, beyond those defined in
40 * {@link java.net.StandardSocketOptions}. These options may be platform
41 * specific.
42 *
43 * @since 1.8
44 */
45public final class ExtendedSocketOptions {
46
47    private static class ExtSocketOption<T> implements SocketOption<T> {
48        private final String name;
49        private final Class<T> type;
50        ExtSocketOption(String name, Class<T> type) {
51            this.name = name;
52            this.type = type;
53        }
54        @Override public String name() { return name; }
55        @Override public Class<T> type() { return type; }
56        @Override public String toString() { return name; }
57    }
58
59    private ExtendedSocketOptions() { }
60
61    /**
62     * Service level properties. When a security manager is installed,
63     * setting or getting this option requires a {@link NetworkPermission}
64     * {@code ("setOption.SO_FLOW_SLA")} or {@code "getOption.SO_FLOW_SLA"}
65     * respectively.
66     */
67    public static final SocketOption<SocketFlow> SO_FLOW_SLA = new
68        ExtSocketOption<SocketFlow>("SO_FLOW_SLA", SocketFlow.class);
69
70
71    private static final PlatformSocketOptions platformSocketOptions =
72            PlatformSocketOptions.get();
73
74    private static final boolean flowSupported =
75            platformSocketOptions.flowSupported();
76
77    private static final Set<SocketOption<?>> extendedOptions = options();
78
79    static Set<SocketOption<?>> options() {
80        if (flowSupported)
81            return Set.of(SO_FLOW_SLA);
82        else
83            return Collections.<SocketOption<?>>emptySet();
84    }
85
86    static {
87        // Registers the extended socket options with the base module.
88        sun.net.ext.ExtendedSocketOptions.register(
89                new sun.net.ext.ExtendedSocketOptions(extendedOptions) {
90
91            @Override
92            public void setOption(FileDescriptor fd,
93                                  SocketOption<?> option,
94                                  Object value)
95                throws SocketException
96            {
97                SecurityManager sm = System.getSecurityManager();
98                if (sm != null)
99                    sm.checkPermission(new NetworkPermission("setOption." + option.name()));
100
101                if (fd == null || !fd.valid())
102                    throw new SocketException("socket closed");
103
104                if (option == SO_FLOW_SLA) {
105                    assert flowSupported;
106                    SocketFlow flow = checkValueType(value, option.type());
107                    setFlowOption(fd, flow);
108                } else {
109                    throw new InternalError("Unexpected option " + option);
110                }
111            }
112
113            @Override
114            public Object getOption(FileDescriptor fd,
115                                    SocketOption<?> option)
116                throws SocketException
117            {
118                SecurityManager sm = System.getSecurityManager();
119                if (sm != null)
120                    sm.checkPermission(new NetworkPermission("getOption." + option.name()));
121
122                if (fd == null || !fd.valid())
123                    throw new SocketException("socket closed");
124
125                if (option == SO_FLOW_SLA) {
126                    assert flowSupported;
127                    SocketFlow flow = SocketFlow.create();
128                    getFlowOption(fd, flow);
129                    return flow;
130                } else {
131                    throw new InternalError("Unexpected option " + option);
132                }
133            }
134        });
135    }
136
137    @SuppressWarnings("unchecked")
138    private static <T> T checkValueType(Object value, Class<?> type) {
139        if (!type.isAssignableFrom(value.getClass())) {
140            String s = "Found: " + value.getClass() + ", Expected: " + type;
141            throw new IllegalArgumentException(s);
142        }
143        return (T) value;
144    }
145
146    private static final JavaIOFileDescriptorAccess fdAccess =
147            SharedSecrets.getJavaIOFileDescriptorAccess();
148
149    private static void setFlowOption(FileDescriptor fd, SocketFlow f)
150        throws SocketException
151    {
152        int status = platformSocketOptions.setFlowOption(fdAccess.get(fd),
153                                                         f.priority(),
154                                                         f.bandwidth());
155        f.status(status);  // augment the given flow with the status
156    }
157
158    private static void getFlowOption(FileDescriptor fd, SocketFlow f)
159        throws SocketException
160    {
161        int status = platformSocketOptions.getFlowOption(fdAccess.get(fd), f);
162        f.status(status);  // augment the given flow with the status
163    }
164
165    static class PlatformSocketOptions {
166
167        protected PlatformSocketOptions() {}
168
169        @SuppressWarnings("unchecked")
170        private static PlatformSocketOptions newInstance(String cn) {
171            Class<PlatformSocketOptions> c;
172            try {
173                c = (Class<PlatformSocketOptions>)Class.forName(cn);
174                return c.getConstructor(new Class<?>[] { }).newInstance();
175            } catch (ReflectiveOperationException x) {
176                throw new AssertionError(x);
177            }
178        }
179
180        private static PlatformSocketOptions create() {
181            String osname = AccessController.doPrivileged(
182                    new PrivilegedAction<String>() {
183                        public String run() {
184                            return System.getProperty("os.name");
185                        }
186                    });
187            if ("SunOS".equals(osname))
188                return newInstance("jdk.net.SolarisSocketOptions");
189            return new PlatformSocketOptions();
190        }
191
192        private static final PlatformSocketOptions instance = create();
193
194        static PlatformSocketOptions get() {
195            return instance;
196        }
197
198        int setFlowOption(int fd, int priority, long bandwidth)
199            throws SocketException
200        {
201            throw new UnsupportedOperationException("unsupported socket option");
202        }
203
204        int getFlowOption(int fd, SocketFlow f) throws SocketException {
205            throw new UnsupportedOperationException("unsupported socket option");
206        }
207
208        boolean flowSupported() {
209            return false;
210        }
211    }
212}
213