1 /*
   2  * Copyright (c) 2017, 2018 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 #include <sys/socket.h>
  26 #include <string.h>
  27 #include <errno.h>
  28 #include <unistd.h>
  29 
  30 #include <jni.h>
  31 #include <netinet/tcp.h>
  32 #include <netinet/in.h>
  33 #include "jni_util.h"
  34 
  35 /*
  36  * Class:     jdk_net_LinuxSocketOptions
  37  * Method:    setQuickAck
  38  * Signature: (II)V
  39  */
  40 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setQuickAck0
  41 (JNIEnv *env, jobject unused, jint fd, jboolean on) {
  42     int optval;
  43     int rv;
  44     optval = (on ? 1 : 0);
  45     rv = setsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &optval, sizeof (optval));
  46     if (rv < 0) {
  47         if (errno == ENOPROTOOPT) {
  48             JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
  49                             "unsupported socket option");
  50         } else {
  51             JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
  52                                         "set option TCP_QUICKACK failed");
  53         }
  54     }
  55 }
  56 
  57 /*
  58  * Class:     jdk_net_LinuxSocketOptions
  59  * Method:    getQuickAck
  60  * Signature: (I)Z;
  61  */
  62 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
  63 (JNIEnv *env, jobject unused, jint fd) {
  64     int on;
  65     socklen_t sz = sizeof (on);
  66     int rv = getsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &on, &sz);
  67     if (rv < 0) {
  68         if (errno == ENOPROTOOPT) {
  69             JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
  70                             "unsupported socket option");
  71         } else {
  72             JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
  73                                         "get option TCP_QUICKACK failed");
  74         }
  75     }
  76     return on != 0;
  77 }
  78 
  79 /*
  80  * Class:     jdk_net_LinuxSocketOptions
  81  * Method:    quickAckSupported
  82  * Signature: ()Z
  83  */
  84 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_quickAckSupported0
  85 (JNIEnv *env, jobject unused) {
  86     int one = 1;
  87     int rv, s;
  88     s = socket(PF_INET, SOCK_STREAM, 0);
  89     if (s < 0) {
  90         return JNI_FALSE;
  91     }
  92     rv = setsockopt(s, SOL_SOCKET, TCP_QUICKACK, (void *) &one, sizeof (one));
  93     if (rv != 0 && errno == ENOPROTOOPT) {
  94         rv = JNI_FALSE;
  95     } else {
  96         rv = JNI_TRUE;
  97     }
  98     close(s);
  99     return rv;
 100 }
 101 
 102 static jint socketOptionSupported(jint sockopt) {
 103     jint one = 1;
 104     jint rv, s;
 105     s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 106     if (s < 0) {
 107         return 0;
 108     }
 109     rv = setsockopt(s, SOL_TCP, sockopt, (void *) &one, sizeof (one));
 110     if (rv != 0 && errno == ENOPROTOOPT) {
 111         rv = 0;
 112     } else {
 113         rv = 1;
 114     }
 115     close(s);
 116     return rv;
 117 }
 118 
 119 static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
 120     if (rv < 0) {
 121         if (errno == ENOPROTOOPT) {
 122             JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
 123                     "unsupported socket option");
 124         } else {
 125             JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);
 126         }
 127     }
 128 }
 129 
 130 /*
 131  * Class:     jdk_net_LinuxSocketOptions
 132  * Method:    keepAliveOptionsSupported0
 133  * Signature: ()Z
 134  */
 135 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_keepAliveOptionsSupported0
 136 (JNIEnv *env, jobject unused) {
 137     return socketOptionSupported(TCP_KEEPIDLE) && socketOptionSupported(TCP_KEEPCNT)
 138             && socketOptionSupported(TCP_KEEPINTVL);
 139 }
 140 
 141 /*
 142  * Class:     jdk_net_LinuxSocketOptions
 143  * Method:    setTcpkeepAliveProbes0
 144  * Signature: (II)V
 145  */
 146 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpkeepAliveProbes0
 147 (JNIEnv *env, jobject unused, jint fd, jint optval) {
 148     jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof (optval));
 149     handleError(env, rv, "set option TCP_KEEPCNT failed");
 150 }
 151 
 152 /*
 153  * Class:     jdk_net_LinuxSocketOptions
 154  * Method:    setTcpKeepAliveTime0
 155  * Signature: (II)V
 156  */
 157 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpKeepAliveTime0
 158 (JNIEnv *env, jobject unused, jint fd, jint optval) {
 159     jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof (optval));
 160     handleError(env, rv, "set option TCP_KEEPIDLE failed");
 161 }
 162 
 163 /*
 164  * Class:     jdk_net_LinuxSocketOptions
 165  * Method:    setTcpKeepAliveIntvl0
 166  * Signature: (II)V
 167  */
 168 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpKeepAliveIntvl0
 169 (JNIEnv *env, jobject unused, jint fd, jint optval) {
 170     jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof (optval));
 171     handleError(env, rv, "set option TCP_KEEPINTVL failed");
 172 }
 173 
 174 /*
 175  * Class:     jdk_net_LinuxSocketOptions
 176  * Method:    getTcpkeepAliveProbes0
 177  * Signature: (I)I;
 178  */
 179 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpkeepAliveProbes0
 180 (JNIEnv *env, jobject unused, jint fd) {
 181     jint optval, rv;
 182     socklen_t sz = sizeof (optval);
 183     rv = getsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, &sz);
 184     handleError(env, rv, "get option TCP_KEEPCNT failed");
 185     return optval;
 186 }
 187 
 188 /*
 189  * Class:     jdk_net_LinuxSocketOptions
 190  * Method:    getTcpKeepAliveTime0
 191  * Signature: (I)I;
 192  */
 193 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveTime0
 194 (JNIEnv *env, jobject unused, jint fd) {
 195     jint optval, rv;
 196     socklen_t sz = sizeof (optval);
 197     rv = getsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, &sz);
 198     handleError(env, rv, "get option TCP_KEEPIDLE failed");
 199     return optval;
 200 }
 201 
 202 /*
 203  * Class:     jdk_net_LinuxSocketOptions
 204  * Method:    getTcpKeepAliveIntvl0
 205  * Signature: (I)I;
 206  */
 207 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveIntvl0
 208 (JNIEnv *env, jobject unused, jint fd) {
 209     jint optval, rv;
 210     socklen_t sz = sizeof (optval);
 211     rv = getsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, &sz);
 212     handleError(env, rv, "get option TCP_KEEPINTVL failed");
 213     return optval;
 214 }