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 { 131 jobject f; 132 jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name, 133 "Ljdk/net/SocketFlow$Status;"); 134 CHECK_NULL_RETURN(fID, NULL); 135 136 f = (*env)->GetStaticObjectField(env, sf_status_class, fID); 137 CHECK_NULL_RETURN(f, NULL); 138 f = (*env)->NewGlobalRef(env, f); 139 CHECK_NULL_RETURN(f, NULL); 140 return f; 141 } 142 143 /* 144 * Retrieve the int file-descriptor from a public socket type object. 145 * Gets impl, then the FileDescriptor from the impl, and then the fd 146 * from that. 147 */ 148 static int getFD(JNIEnv *env, jobject fileDesc) { 149 return (*env)->GetIntField(env, fileDesc, sf_fd_fdID); 150 } 151 152 /** 153 * Sets the status field of a SocketFlow to one of the 154 * canned enum values 155 */ 156 static void setStatus (JNIEnv *env, jobject obj, int errval) 157 { 158 switch (errval) { 159 case 0: /* OK */ 160 (*env)->SetObjectField(env, obj, sf_status, sfs_OK); 161 break; 162 case EPERM: 163 (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION); 164 break; 165 case ENOTCONN: 166 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED); 167 break; 168 case EOPNOTSUPP: 169 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED); 170 break; 171 case EALREADY: 172 (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED); 173 break; 174 case EINPROGRESS: 175 (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS); 176 break; 177 default: 178 (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER); 179 break; 180 } 181 } 182 183 #ifdef __solaris__ 184 185 /* 186 * Class: sun_net_ExtendedOptionsImpl 187 * Method: setFlowOption 188 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 189 */ 190 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 191 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 192 { 193 int fd = getFD(env, fileDesc); 194 195 if (fd < 0) { 196 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 197 return; 198 } else { 199 sock_flow_props_t props; 200 jlong bandwidth; 201 int rv; 202 203 jint priority = (*env)->GetIntField(env, flow, sf_priority); 204 memset(&props, 0, sizeof(props)); 205 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 206 207 if (priority != jdk_net_SocketFlow_UNSET) { 208 props.sfp_mask |= SFP_PRIORITY; 209 props.sfp_priority = priority; 210 } 211 bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth); 212 if (bandwidth > -1) { 213 props.sfp_mask |= SFP_MAXBW; 214 props.sfp_maxbw = (uint64_t) bandwidth; 215 } 216 rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 217 if (rv < 0) { 218 if (errno == ENOPROTOOPT) { 219 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 220 "unsupported socket option"); 221 } else { 222 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 223 "set option SO_FLOW_SLA failed"); 224 } 225 return; 226 } 227 setStatus(env, flow, props.sfp_status); 228 } 229 } 230 231 /* 232 * Class: sun_net_ExtendedOptionsImpl 233 * Method: getFlowOption 234 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 235 */ 236 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 237 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 238 { 239 int fd = getFD(env, fileDesc); 240 241 if (fd < 0) { 242 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 243 return; 244 } else { 245 sock_flow_props_t props; 246 int status; 247 socklen_t sz = sizeof(props); 248 249 int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); 250 if (rv < 0) { 251 if (errno == ENOPROTOOPT) { 252 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 253 "unsupported socket option"); 254 } else { 255 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 256 "set option SO_FLOW_SLA failed"); 257 } 258 return; 259 } 260 /* first check status to see if flow exists */ 261 status = props.sfp_status; 262 setStatus(env, flow, status); 263 if (status == 0) { /* OK */ 264 /* can set the other fields now */ 265 if (props.sfp_mask & SFP_PRIORITY) { 266 (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); 267 } 268 if (props.sfp_mask & SFP_MAXBW) { 269 (*env)->SetLongField(env, flow, sf_bandwidth, 270 (jlong)props.sfp_maxbw); 271 } 272 } 273 } 274 } 275 276 static jboolean flowsupported; 277 static jboolean flowsupported_set = JNI_FALSE; 278 279 static jboolean flowSupported0() 280 { 281 /* Do a simple dummy call, and try to figure out from that */ 282 sock_flow_props_t props; 283 int rv, s; 284 if (flowsupported_set) { 285 return flowsupported; 286 } 287 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 288 if (s < 0) { 289 flowsupported = JNI_FALSE; 290 flowsupported_set = JNI_TRUE; 291 return JNI_FALSE; 292 } 293 memset(&props, 0, sizeof(props)); 294 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 295 props.sfp_mask |= SFP_PRIORITY; 296 props.sfp_priority = SFP_PRIO_NORMAL; 297 rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 298 if (rv != 0 && errno == ENOPROTOOPT) { 299 rv = JNI_FALSE; 300 } else { 301 rv = JNI_TRUE; 302 } 303 close(s); 304 flowsupported = rv; 305 flowsupported_set = JNI_TRUE; 306 return flowsupported; 307 } 308 309 #else /* __solaris__ */ 310 311 /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ 312 313 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 314 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 315 { 316 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 317 "unsupported socket option"); 318 } 319 320 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 321 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 322 { 323 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 324 "unsupported socket option"); 325 } 326 327 static jboolean flowSupported0() { 328 return JNI_FALSE; 329 } 330 331 #endif /* __solaris__ */ 332 333 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported 334 (JNIEnv *env, jclass UNUSED) 335 { 336 return flowSupported0(); 337 }