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