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 }