/* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include #include #include "net_util.h" #include "jdk_net_SocketFlow.h" static jclass sf_status_class; /* Status enum type */ static jfieldID sf_status; static jfieldID sf_priority; static jfieldID sf_bandwidth; static jfieldID sf_fd_fdID; /* FileDescriptor.fd */ /* References to the literal enum values */ static jobject sfs_NOSTATUS; static jobject sfs_OK; static jobject sfs_NOPERMISSION; static jobject sfs_NOTCONNECTED; static jobject sfs_NOTSUPPORTED; static jobject sfs_ALREADYCREATED; static jobject sfs_INPROGRESS; static jobject sfs_OTHER; static jobject getEnumField(JNIEnv *env, char *name); static void setStatus(JNIEnv *env, jobject obj, int errval); /* OS specific code is implemented in these three functions */ static jboolean flowSupported0() ; static jboolean reuseportSupported0() ; /* * Class: sun_net_ExtendedOptionsImpl * Method: init * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init (JNIEnv *env, jclass UNUSED) { static int initialized = 0; jclass c; /* Global class references */ if (initialized) { return; } c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status"); CHECK_NULL(c); sf_status_class = (*env)->NewGlobalRef(env, c); CHECK_NULL(sf_status_class); /* int "fd" field of java.io.FileDescriptor */ c = (*env)->FindClass(env, "java/io/FileDescriptor"); CHECK_NULL(c); sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I"); CHECK_NULL(sf_fd_fdID); /* SocketFlow fields */ c = (*env)->FindClass(env, "jdk/net/SocketFlow"); CHECK_NULL(c); /* status */ sf_status = (*env)->GetFieldID(env, c, "status", "Ljdk/net/SocketFlow$Status;"); CHECK_NULL(sf_status); /* priority */ sf_priority = (*env)->GetFieldID(env, c, "priority", "I"); CHECK_NULL(sf_priority); /* bandwidth */ sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J"); CHECK_NULL(sf_bandwidth); /* Initialize the static enum values */ sfs_NOSTATUS = getEnumField(env, "NO_STATUS"); CHECK_NULL(sfs_NOSTATUS); sfs_OK = getEnumField(env, "OK"); CHECK_NULL(sfs_OK); sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION"); CHECK_NULL(sfs_NOPERMISSION); sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED"); CHECK_NULL(sfs_NOTCONNECTED); sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED"); CHECK_NULL(sfs_NOTSUPPORTED); sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED"); CHECK_NULL(sfs_ALREADYCREATED); sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS"); CHECK_NULL(sfs_INPROGRESS); sfs_OTHER = getEnumField(env, "OTHER"); CHECK_NULL(sfs_OTHER); initialized = JNI_TRUE; } static jobject getEnumField(JNIEnv *env, char *name) { jobject f; jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name, "Ljdk/net/SocketFlow$Status;"); CHECK_NULL_RETURN(fID, NULL); f = (*env)->GetStaticObjectField(env, sf_status_class, fID); CHECK_NULL_RETURN(f, NULL); f = (*env)->NewGlobalRef(env, f); CHECK_NULL_RETURN(f, NULL); return f; } /* * Returns a java.lang.Boolean based on 'b' */ static jobject createBoolean(JNIEnv *env, int b) { static jclass b_class; static jmethodID b_ctrID; if (b_class == NULL) { jclass c = (*env)->FindClass(env, "java/lang/Boolean"); CHECK_NULL_RETURN(c, NULL); b_ctrID = (*env)->GetMethodID(env, c, "", "(Z)V"); CHECK_NULL_RETURN(b_ctrID, NULL); b_class = (*env)->NewGlobalRef(env, c); CHECK_NULL_RETURN(b_class, NULL); } return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) ); } /* * Retrieve the int file-descriptor from a public socket type object. * Gets impl, then the FileDescriptor from the impl, and then the fd * from that. */ static int getFD(JNIEnv *env, jobject fileDesc) { return (*env)->GetIntField(env, fileDesc, sf_fd_fdID); } /** * Sets the status field of a SocketFlow to one of the * canned enum values */ static void setStatus (JNIEnv *env, jobject obj, int errval) { switch (errval) { case 0: /* OK */ (*env)->SetObjectField(env, obj, sf_status, sfs_OK); break; case EPERM: (*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION); break; case ENOTCONN: (*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED); break; case EOPNOTSUPP: (*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED); break; case EALREADY: (*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED); break; case EINPROGRESS: (*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS); break; default: (*env)->SetObjectField(env, obj, sf_status, sfs_OTHER); break; } } #ifdef __solaris__ /* * Class: sun_net_ExtendedOptionsImpl * Method: setFlowOption * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { int fd = getFD(env, fileDesc); if (fd < 0) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); return; } else { sock_flow_props_t props; jlong bandwidth; int rv; jint priority = (*env)->GetIntField(env, flow, sf_priority); memset(&props, 0, sizeof(props)); props.sfp_version = SOCK_FLOW_PROP_VERSION1; if (priority != jdk_net_SocketFlow_UNSET) { props.sfp_mask |= SFP_PRIORITY; props.sfp_priority = priority; } bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth); if (bandwidth > -1) { props.sfp_mask |= SFP_MAXBW; props.sfp_maxbw = (uint64_t) bandwidth; } rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); if (rv < 0) { if (errno == ENOPROTOOPT) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } else if (errno == EACCES || errno == EPERM) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "Permission denied"); } else { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "set option SO_FLOW_SLA failed"); } return; } setStatus(env, flow, props.sfp_status); } } /* * Class: sun_net_ExtendedOptionsImpl * Method: getFlowOption * Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { int fd = getFD(env, fileDesc); if (fd < 0) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); return; } else { sock_flow_props_t props; int status; socklen_t sz = sizeof(props); int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz); if (rv < 0) { if (errno == ENOPROTOOPT) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } else if (errno == EACCES || errno == EPERM) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "Permission denied"); } else { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "set option SO_FLOW_SLA failed"); } return; } /* first check status to see if flow exists */ status = props.sfp_status; setStatus(env, flow, status); if (status == 0) { /* OK */ /* can set the other fields now */ if (props.sfp_mask & SFP_PRIORITY) { (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority); } if (props.sfp_mask & SFP_MAXBW) { (*env)->SetLongField(env, flow, sf_bandwidth, (jlong)props.sfp_maxbw); } } } } static jboolean flowsupported; static jboolean flowsupported_set = JNI_FALSE; static jboolean flowSupported0() { /* Do a simple dummy call, and try to figure out from that */ sock_flow_props_t props; int rv, s; if (flowsupported_set) { return flowsupported; } s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { flowsupported = JNI_FALSE; flowsupported_set = JNI_TRUE; return JNI_FALSE; } memset(&props, 0, sizeof(props)); props.sfp_version = SOCK_FLOW_PROP_VERSION1; props.sfp_mask |= SFP_PRIORITY; props.sfp_priority = SFP_PRIO_NORMAL; rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props)); if (rv != 0 && errno == ENOPROTOOPT) { rv = JNI_FALSE; } else { rv = JNI_TRUE; } close(s); flowsupported = rv; flowsupported_set = JNI_TRUE; return flowsupported; } #else /* __solaris__ */ /* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } static jboolean flowSupported0() { return JNI_FALSE; } #endif /* __solaris__ */ #ifdef __linux__ /* * Class: sun_net_ExtendedOptionsImpl * Method: setReusePortOption * Signature: (Ljava/io/FileDescriptor;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) { int fd = getFD(env, fileDesc); int optval; if (fd < 0) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); return; } else { int rv; optval = (on ? 1 : 0); rv = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); if (rv < 0) { if (errno == ENOPROTOOPT) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } else { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "set option SO_REUSEPORT failed"); } return; } } } /* * Class: sun_net_ExtendedOptionsImpl * Method: getReusePortOption * Signature: (Ljava/io/FileDescriptor;)Ljava/lang/Object; */ JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption (JNIEnv *env, jclass UNUSED, jobject fileDesc) { int fd = getFD(env, fileDesc); if (fd < 0) { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); return JNI_FALSE; } else { int on; socklen_t sz = sizeof(on); int rv = getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, &sz); if (rv < 0) { if (errno == ENOPROTOOPT) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } else { NET_ERROR(env, JNU_JAVANETPKG "SocketException", "set option SO_REUSEPORT failed"); } } printf("getReusePortOption returns %d\n", on); return createBoolean(env, on); } } static jboolean reuseportsupported; static jboolean reuseportsupported_set = JNI_FALSE; static jboolean reuseportSupported0() { /* Do a simple dummy call, and try to figure out from that */ int one = 1; int rv, s; if (reuseportsupported_set) { return reuseportsupported; } s = socket(PF_INET, SOCK_STREAM, 0); if (s < 0) { reuseportsupported = JNI_FALSE; reuseportsupported_set = JNI_TRUE; return JNI_FALSE; } rv = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(one)); if (rv != 0 && errno == ENOPROTOOPT) { rv = JNI_FALSE; } else { rv = JNI_TRUE; } close(s); reuseportsupported = rv; reuseportsupported_set = JNI_TRUE; return reuseportsupported; } #else /* __linux__ */ /* Non Linux. Functionality is not supported. So, throw UnsupportedOpExc */ JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); } JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption (JNIEnv *env, jclass UNUSED, jobject fileDesc) { JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", "unsupported socket option"); return JNI_FALSE; } static jboolean reuseportSupported0() { return JNI_FALSE; } #endif /* __linux__ */ JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported (JNIEnv *env, jclass UNUSED) { return flowSupported0(); } JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_reuseportSupported (JNIEnv *env, jclass UNUSED) { return reuseportSupported0(); }