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 if (errno == EACCES || errno == EPERM) { 222 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 223 "Permission denied"); 224 } else { 225 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 226 "set option SO_FLOW_SLA failed"); 227 } 228 return; 229 } 230 setStatus(env, flow, props.sfp_status); 231 } 232 } 233 234 /* 235 * Class: sun_net_ExtendedOptionsImpl 236 * Method: getFlowOption 237 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 238 */ 239 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 240 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 241 { 242 int fd = getFD(env, fileDesc); 243 244 if (fd < 0) { 245 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 246 return; 247 } else { 248 sock_flow_props_t props; 249 int status; 250 socklen_t sz = sizeof(props); 251 252 int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); 253 if (rv < 0) { 254 if (errno == ENOPROTOOPT) { 255 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 256 "unsupported socket option"); 257 } else if (errno == EACCES || errno == EPERM) { 258 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 259 "Permission denied"); 260 } else { 261 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 262 "set option SO_FLOW_SLA failed"); 263 } 264 return; 265 } 266 /* first check status to see if flow exists */ 267 status = props.sfp_status; 268 setStatus(env, flow, status); 269 if (status == 0) { /* OK */ 270 /* can set the other fields now */ 271 if (props.sfp_mask & SFP_PRIORITY) { 272 (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); 273 } 274 if (props.sfp_mask & SFP_MAXBW) { 275 (*env)->SetLongField(env, flow, sf_bandwidth, 276 (jlong)props.sfp_maxbw); 277 } 278 } 279 } 280 } 281 282 static jboolean flowsupported; 283 static jboolean flowsupported_set = JNI_FALSE; 284 285 static jboolean flowSupported0() 286 { 287 /* Do a simple dummy call, and try to figure out from that */ 288 sock_flow_props_t props; 289 int rv, s; 290 if (flowsupported_set) { 291 return flowsupported; 292 } 293 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 294 if (s < 0) { 295 flowsupported = JNI_FALSE; 296 flowsupported_set = JNI_TRUE; 297 return JNI_FALSE; 298 } 299 memset(&props, 0, sizeof(props)); 300 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 301 props.sfp_mask |= SFP_PRIORITY; 302 props.sfp_priority = SFP_PRIO_NORMAL; 303 rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 304 if (rv != 0 && errno == ENOPROTOOPT) { 305 rv = JNI_FALSE; 306 } else { 307 rv = JNI_TRUE; 308 } 309 close(s); 310 flowsupported = rv; 311 flowsupported_set = JNI_TRUE; 312 return flowsupported; 313 } 314 315 #else /* __solaris__ */ 316 317 /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ 318 319 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 320 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 321 { 322 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 323 "unsupported socket option"); 324 } 325 326 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 327 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 328 { 329 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 330 "unsupported socket option"); 331 } 332 333 static jboolean flowSupported0() { 334 return JNI_FALSE; 335 } 336 337 #endif /* __solaris__ */ 338 339 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported 340 (JNIEnv *env, jclass UNUSED) 341 { 342 return flowSupported0(); 343 }