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 26 #include <jni.h> 27 #include <string.h> 28 29 #include "net_util.h" 30 #include "jdk_net_SocketFlow.h" 31 32 static jclass sf_status_class; /* Status enum type */ 33 34 static jfieldID sf_status; 35 static jfieldID sf_priority; 36 static jfieldID sf_bandwidth; 37 38 static jfieldID sf_fd_fdID; /* FileDescriptor.fd */ 39 40 /* References to the literal enum values */ 41 42 static jobject sfs_NOSTATUS; 43 static jobject sfs_OK; 44 static jobject sfs_NOPERMISSION; 45 static jobject sfs_NOTCONNECTED; 46 static jobject sfs_NOTSUPPORTED; 47 static jobject sfs_ALREADYCREATED; 48 static jobject sfs_INPROGRESS; 49 static jobject sfs_OTHER; 50 51 static jobject getEnumField(JNIEnv *env, char *name); 52 static void setStatus(JNIEnv *env, jobject obj, int errval); 53 54 /* OS specific code is implemented in these three functions */ 55 56 static jboolean flowSupported0() ; 57 58 /* 59 * Class: sun_net_ExtendedOptionsImpl 60 * Method: init 61 * Signature: ()V 62 */ 63 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init 64 (JNIEnv *env, jclass UNUSED) { 65 66 static int initialized = 0; 67 jclass c; 68 69 /* Global class references */ 70 71 if (initialized) { 72 return; 73 } 74 75 c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status"); 76 CHECK_NULL(c); 77 sf_status_class = (*env)->NewGlobalRef(env, c); 78 CHECK_NULL(sf_status_class); 79 80 /* int "fd" field of java.io.FileDescriptor */ 81 82 c = (*env)->FindClass(env, "java/io/FileDescriptor"); 83 CHECK_NULL(c); 84 sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I"); 85 CHECK_NULL(sf_fd_fdID); 86 87 88 /* SocketFlow fields */ 89 90 c = (*env)->FindClass(env, "jdk/net/SocketFlow"); 91 92 /* status */ 93 94 sf_status = (*env)->GetFieldID(env, c, "status", 95 "Ljdk/net/SocketFlow$Status;"); 96 CHECK_NULL(sf_status); 97 98 /* priority */ 99 100 sf_priority = (*env)->GetFieldID(env, c, "priority", "I"); 101 CHECK_NULL(sf_priority); 102 103 /* bandwidth */ 104 105 sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J"); 106 CHECK_NULL(sf_bandwidth); 107 108 /* Initialize the static enum values */ 109 110 sfs_NOSTATUS = getEnumField(env, "NO_STATUS"); 111 CHECK_NULL(sfs_NOSTATUS); 112 sfs_OK = getEnumField(env, "OK"); 113 CHECK_NULL(sfs_OK); 114 sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION"); 115 CHECK_NULL(sfs_NOPERMISSION); 116 sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED"); 117 CHECK_NULL(sfs_NOTCONNECTED); 118 sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED"); 119 CHECK_NULL(sfs_NOTSUPPORTED); 120 sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED"); 121 CHECK_NULL(sfs_ALREADYCREATED); 122 sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS"); 123 CHECK_NULL(sfs_INPROGRESS); 124 sfs_OTHER = getEnumField(env, "OTHER"); 125 CHECK_NULL(sfs_OTHER); 126 initialized = JNI_TRUE; 127 } 128 129 static jobject getEnumField(JNIEnv *env, char *name) { 130 jobject f; 131 jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name, 132 "Ljdk/net/SocketFlow$Status;"); 133 CHECK_NULL_RETURN(fID, NULL); 134 135 f = (*env)->GetStaticObjectField(env, sf_status_class, fID); 136 CHECK_NULL_RETURN(f, NULL); 137 f = (*env)->NewGlobalRef(env, f); 138 CHECK_NULL_RETURN(f, NULL); 139 return f; 140 } 141 142 /* 143 * Retrieve the int file-descriptor from a public socket type object. 144 * Gets impl, then the FileDescriptor from the impl, and then the fd 145 * from that. 146 */ 147 static int getFD(JNIEnv *env, jobject fileDesc) { 148 return (*env)->GetIntField(env, fileDesc, sf_fd_fdID); 149 } 150 151 /** 152 * Sets the status field of a SocketFlow to one of the 153 * canned enum values 154 */ 155 static void setStatus (JNIEnv *env, jobject obj, int errval) { 156 switch (errval) { 157 case 0: /* OK */ 158 (*env)->SetObjectField(env, obj, sf_status, sfs_OK); 159 break; 160 case EPERM: 161 (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION); 162 break; 163 case ENOTCONN: 164 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED); 165 break; 166 case EOPNOTSUPP: 167 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED); 168 break; 169 case EALREADY: 170 (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED); 171 break; 172 case EINPROGRESS: 173 (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS); 174 break; 175 default: 176 (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER); 177 break; 178 } 179 } 180 181 #ifdef __solaris__ 182 183 /* 184 * Class: sun_net_ExtendedOptionsImpl 185 * Method: setFlowOption 186 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 187 */ 188 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 189 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { 190 int fd = getFD(env, fileDesc); 191 192 if (fd < 0) { 193 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 194 return; 195 } else { 196 sock_flow_props_t props; 197 jlong bandwidth; 198 int rv; 199 200 jint priority = (*env)->GetIntField(env, flow, sf_priority); 201 memset(&props, 0, sizeof(props)); 202 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 203 204 if (priority != jdk_net_SocketFlow_UNSET) { 205 props.sfp_mask |= SFP_PRIORITY; 206 props.sfp_priority = priority; 207 } 208 bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth); 209 if (bandwidth > -1) { 210 props.sfp_mask |= SFP_MAXBW; 211 props.sfp_maxbw = (uint64_t) bandwidth; 212 } 213 rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 214 if (rv < 0) { 215 if (errno == ENOPROTOOPT) { 216 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 217 "unsupported socket option"); 218 } else { 219 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 220 "set option SO_FLOW_SLA failed"); 221 } 222 return; 223 } 224 setStatus(env, flow, props.sfp_status); 225 } 226 } 227 228 /* 229 * Class: sun_net_ExtendedOptionsImpl 230 * Method: getFlowOption 231 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 232 */ 233 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 234 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { 235 int fd = getFD(env, fileDesc); 236 237 if (fd < 0) { 238 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 239 return; 240 } else { 241 sock_flow_props_t props; 242 int status; 243 socklen_t sz = sizeof(props); 244 245 int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); 246 if (rv < 0) { 247 if (errno == ENOPROTOOPT) { 248 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 249 "unsupported socket option"); 250 } else { 251 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 252 "set option SO_FLOW_SLA failed"); 253 } 254 return; 255 } 256 /* first check status to see if flow exists */ 257 status = props.sfp_status; 258 setStatus(env, flow, status); 259 if (status == 0) { /* OK */ 260 /* can set the other fields now */ 261 if (props.sfp_mask & SFP_PRIORITY) { 262 (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); 263 } 264 if (props.sfp_mask & SFP_MAXBW) { 265 (*env)->SetLongField(env, flow, sf_bandwidth, 266 (jlong)props.sfp_maxbw); 267 } 268 } 269 } 270 } 271 272 static jboolean flowsupported; 273 static jboolean flowsupported_set = JNI_FALSE; 274 275 static jboolean flowSupported0() { 276 /* Do a simple dummy call, and try to figure out from that */ 277 sock_flow_props_t props; 278 int rv, s; 279 if (flowsupported_set) { 280 return flowsupported; 281 } 282 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 283 if (s < 0) { 284 flowsupported = JNI_FALSE; 285 flowsupported_set = JNI_TRUE; 286 return JNI_FALSE; 287 } 288 memset(&props, 0, sizeof(props)); 289 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 290 props.sfp_mask |= SFP_PRIORITY; 291 props.sfp_priority = SFP_PRIO_NORMAL; 292 rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 293 if (rv != 0 && errno == ENOPROTOOPT) { 294 rv = JNI_FALSE; 295 } else { 296 rv = JNI_TRUE; 297 } 298 close(s); 299 flowsupported = rv; 300 flowsupported_set = JNI_TRUE; 301 return flowsupported; 302 } 303 304 #else /* __solaris__ */ 305 306 /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ 307 308 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 309 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { 310 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 311 "unsupported socket option"); 312 } 313 314 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 315 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { 316 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 317 "unsupported socket option"); 318 } 319 320 static jboolean flowSupported0() { 321 return JNI_FALSE; 322 } 323 324 #endif /* __solaris__ */ 325 326 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported 327 (JNIEnv *env, jclass UNUSED) { 328 return flowSupported0(); 329 }