1 /* 2 * Copyright (c) 2014, 2016, 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 static jboolean quickack_available(); 58 59 /* 60 * Class: sun_net_ExtendedOptionsImpl 61 * Method: init 62 * Signature: ()V 63 */ 64 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init 65 (JNIEnv *env, jclass UNUSED) 66 { 67 static int initialized = 0; 68 jclass c; 69 70 /* Global class references */ 71 72 if (initialized) { 73 return; 74 } 75 76 c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status"); 77 CHECK_NULL(c); 78 sf_status_class = (*env)->NewGlobalRef(env, c); 79 CHECK_NULL(sf_status_class); 80 81 /* int "fd" field of java.io.FileDescriptor */ 82 83 c = (*env)->FindClass(env, "java/io/FileDescriptor"); 84 CHECK_NULL(c); 85 sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I"); 86 CHECK_NULL(sf_fd_fdID); 87 88 89 /* SocketFlow fields */ 90 91 c = (*env)->FindClass(env, "jdk/net/SocketFlow"); 92 CHECK_NULL(c); 93 94 /* status */ 95 96 sf_status = (*env)->GetFieldID(env, c, "status", 97 "Ljdk/net/SocketFlow$Status;"); 98 CHECK_NULL(sf_status); 99 100 /* priority */ 101 102 sf_priority = (*env)->GetFieldID(env, c, "priority", "I"); 103 CHECK_NULL(sf_priority); 104 105 /* bandwidth */ 106 107 sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J"); 108 CHECK_NULL(sf_bandwidth); 109 110 /* Initialize the static enum values */ 111 112 sfs_NOSTATUS = getEnumField(env, "NO_STATUS"); 113 CHECK_NULL(sfs_NOSTATUS); 114 sfs_OK = getEnumField(env, "OK"); 115 CHECK_NULL(sfs_OK); 116 sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION"); 117 CHECK_NULL(sfs_NOPERMISSION); 118 sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED"); 119 CHECK_NULL(sfs_NOTCONNECTED); 120 sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED"); 121 CHECK_NULL(sfs_NOTSUPPORTED); 122 sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED"); 123 CHECK_NULL(sfs_ALREADYCREATED); 124 sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS"); 125 CHECK_NULL(sfs_INPROGRESS); 126 sfs_OTHER = getEnumField(env, "OTHER"); 127 CHECK_NULL(sfs_OTHER); 128 initialized = JNI_TRUE; 129 } 130 131 static jobject getEnumField(JNIEnv *env, char *name) 132 { 133 jobject f; 134 jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name, 135 "Ljdk/net/SocketFlow$Status;"); 136 CHECK_NULL_RETURN(fID, NULL); 137 138 f = (*env)->GetStaticObjectField(env, sf_status_class, fID); 139 CHECK_NULL_RETURN(f, NULL); 140 f = (*env)->NewGlobalRef(env, f); 141 CHECK_NULL_RETURN(f, NULL); 142 return f; 143 } 144 145 /* 146 * Returns a java.lang.Boolean based on 'b' 147 */ 148 static jobject createBoolean(JNIEnv *env, int b) { 149 static jclass b_class; 150 static jmethodID b_ctrID; 151 152 if (b_class == NULL) { 153 jclass c = (*env)->FindClass(env, "java/lang/Boolean"); 154 CHECK_NULL_RETURN(c, NULL); 155 b_ctrID = (*env)->GetMethodID(env, c, "<init>", "(Z)V"); 156 CHECK_NULL_RETURN(b_ctrID, NULL); 157 b_class = (*env)->NewGlobalRef(env, c); 158 CHECK_NULL_RETURN(b_class, NULL); 159 } 160 161 return ((*env)->NewObject(env, b_class, b_ctrID, (jboolean) (b != 0))); 162 } 163 164 /* 165 * Retrieve the int file-descriptor from a public socket type object. 166 * Gets impl, then the FileDescriptor from the impl, and then the fd 167 * from that. 168 */ 169 static int getFD(JNIEnv *env, jobject fileDesc) { 170 return (*env)->GetIntField(env, fileDesc, sf_fd_fdID); 171 } 172 173 /** 174 * Sets the status field of a SocketFlow to one of the 175 * canned enum values 176 */ 177 static void setStatus (JNIEnv *env, jobject obj, int errval) 178 { 179 switch (errval) { 180 case 0: /* OK */ 181 (*env)->SetObjectField(env, obj, sf_status, sfs_OK); 182 break; 183 case EPERM: 184 (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION); 185 break; 186 case ENOTCONN: 187 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED); 188 break; 189 case EOPNOTSUPP: 190 (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED); 191 break; 192 case EALREADY: 193 (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED); 194 break; 195 case EINPROGRESS: 196 (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS); 197 break; 198 default: 199 (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER); 200 break; 201 } 202 } 203 204 #ifdef __solaris__ 205 206 /* 207 * Class: sun_net_ExtendedOptionsImpl 208 * Method: setFlowOption 209 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 210 */ 211 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 212 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 213 { 214 int fd = getFD(env, fileDesc); 215 216 if (fd < 0) { 217 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 218 return; 219 } else { 220 sock_flow_props_t props; 221 jlong bandwidth; 222 int rv; 223 224 jint priority = (*env)->GetIntField(env, flow, sf_priority); 225 memset(&props, 0, sizeof(props)); 226 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 227 228 if (priority != jdk_net_SocketFlow_UNSET) { 229 props.sfp_mask |= SFP_PRIORITY; 230 props.sfp_priority = priority; 231 } 232 bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth); 233 if (bandwidth > -1) { 234 props.sfp_mask |= SFP_MAXBW; 235 props.sfp_maxbw = (uint64_t) bandwidth; 236 } 237 rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 238 if (rv < 0) { 239 if (errno == ENOPROTOOPT) { 240 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 241 "unsupported socket option"); 242 } else if (errno == EACCES || errno == EPERM) { 243 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 244 "Permission denied"); 245 } else { 246 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 247 "set option SO_FLOW_SLA failed"); 248 } 249 return; 250 } 251 setStatus(env, flow, props.sfp_status); 252 } 253 } 254 255 /* 256 * Class: sun_net_ExtendedOptionsImpl 257 * Method: getFlowOption 258 * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V 259 */ 260 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 261 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 262 { 263 int fd = getFD(env, fileDesc); 264 265 if (fd < 0) { 266 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 267 return; 268 } else { 269 sock_flow_props_t props; 270 int status; 271 socklen_t sz = sizeof(props); 272 273 int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); 274 if (rv < 0) { 275 if (errno == ENOPROTOOPT) { 276 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 277 "unsupported socket option"); 278 } else if (errno == EACCES || errno == EPERM) { 279 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 280 "Permission denied"); 281 } else { 282 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 283 "set option SO_FLOW_SLA failed"); 284 } 285 return; 286 } 287 /* first check status to see if flow exists */ 288 status = props.sfp_status; 289 setStatus(env, flow, status); 290 if (status == 0) { /* OK */ 291 /* can set the other fields now */ 292 if (props.sfp_mask & SFP_PRIORITY) { 293 (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); 294 } 295 if (props.sfp_mask & SFP_MAXBW) { 296 (*env)->SetLongField(env, flow, sf_bandwidth, 297 (jlong)props.sfp_maxbw); 298 } 299 } 300 } 301 } 302 303 static jboolean flowsupported; 304 static jboolean flowsupported_set = JNI_FALSE; 305 306 static jboolean flowSupported0() 307 { 308 /* Do a simple dummy call, and try to figure out from that */ 309 sock_flow_props_t props; 310 int rv, s; 311 if (flowsupported_set) { 312 return flowsupported; 313 } 314 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 315 if (s < 0) { 316 flowsupported = JNI_FALSE; 317 flowsupported_set = JNI_TRUE; 318 return JNI_FALSE; 319 } 320 memset(&props, 0, sizeof(props)); 321 props.sfp_version = SOCK_FLOW_PROP_VERSION1; 322 props.sfp_mask |= SFP_PRIORITY; 323 props.sfp_priority = SFP_PRIO_NORMAL; 324 rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); 325 if (rv != 0 && errno == ENOPROTOOPT) { 326 rv = JNI_FALSE; 327 } else { 328 rv = JNI_TRUE; 329 } 330 close(s); 331 flowsupported = rv; 332 flowsupported_set = JNI_TRUE; 333 return flowsupported; 334 } 335 336 #else /* __solaris__ */ 337 338 /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ 339 340 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption 341 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 342 { 343 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 344 "unsupported socket option"); 345 } 346 347 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption 348 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) 349 { 350 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 351 "unsupported socket option"); 352 } 353 354 static jboolean flowSupported0() { 355 return JNI_FALSE; 356 } 357 358 #endif /* __solaris__ */ 359 360 #ifdef __linux__ 361 362 /* 363 * Class: sun_net_ExtendedOptionsImpl 364 * Method: setQuickAckOption 365 * Signature: (Ljava/io/FileDescriptor;Ljava/lang/Object;)V 366 */ 367 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setQuickAckOption 368 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) { 369 int fd = getFD(env, fileDesc); 370 int optval; 371 if (fd < 0) { 372 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 373 } else { 374 int rv; 375 optval = (on ? 1 : 0); 376 rv = setsockopt(fd, SOL_SOCKET, SO_QUICKACK, &optval, sizeof (optval)); 377 if (rv < 0) { 378 if (errno == ENOPROTOOPT) { 379 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 380 "unsupported socket option"); 381 } else { 382 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 383 "set option SO_REUSEPORT failed"); 384 } 385 } 386 } 387 } 388 389 /* 390 * Class: sun_net_ExtendedOptionsImpl 391 * Method: getQuickAckOption 392 * Signature: (Ljava/io/FileDescriptor;)Ljava/lang/Object; 393 */ 394 JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getQuickAckOption 395 (JNIEnv *env, jclass UNUSED, jobject fileDesc) 396 { 397 int fd = getFD(env, fileDesc); 398 if (fd < 0) { 399 NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); 400 return JNI_FALSE; 401 } else { 402 int on; 403 socklen_t sz = sizeof(on); 404 int rv = getsockopt(fd, SOL_SOCKET, SO_QUICKACK, &on, &sz); 405 if (rv < 0) { 406 if (errno == ENOPROTOOPT) { 407 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 408 "unsupported socket option"); 409 } else { 410 NET_ERROR(env, JNU_JAVANETPKG "SocketException", 411 "setoption SO_QUICKACK failed"); 412 } 413 } 414 return createBoolean(env, on); 415 } 416 } 417 418 static jboolean isQuickAckAvailable; 419 static jboolean isQuickAckAvailableChecked = JNI_FALSE; 420 static jboolean quickack_available() { 421 int one = 1; 422 int rv, s; 423 if (isQuickAckAvailableChecked) { 424 return isQuickAckAvailable; 425 } 426 s = socket(PF_INET, SOCK_STREAM, 0); 427 if (s < 0) { 428 isQuickAckAvailable = JNI_FALSE; 429 isQuickAckAvailableChecked = JNI_TRUE; 430 return JNI_FALSE; 431 } 432 rv = setsockopt(s, SOL_SOCKET, SO_QUICKACK, (void *) &one, sizeof (one)); 433 isQuickAckAvailableChecked = JNI_TRUE; 434 if (rv != 0 && errno == ENOPROTOOPT) { 435 isQuickAckAvailable = JNI_FALSE; 436 } else { 437 isQuickAckAvailable = JNI_TRUE; 438 } 439 close(s); 440 return isQuickAckAvailable; 441 } 442 443 #else 444 445 JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setQuickAckOption 446 (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) 447 { 448 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 449 "unsupported socket option"); 450 } 451 452 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_getQuickAckOption 453 (JNIEnv *env, jclass UNUSED, jobject fileDesc) 454 { 455 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 456 "unsupported socket option"); 457 return JNI_FALSE; 458 } 459 460 static jboolean quickack_available() { 461 return JNI_FALSE; 462 } 463 464 #endif /* __linux__ */ 465 466 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported 467 (JNIEnv *env, jclass UNUSED) 468 { 469 return flowSupported0(); 470 } 471 472 JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_isQuickAckAvailable0 473 (JNIEnv *env, jclass UNUSED) 474 { 475 return quickack_available(); 476 }