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 }