1 /* 2 * Copyright (c) 2017, 2020, 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 <sys/un.h> 27 #include <sys/types.h> 28 #include <string.h> 29 #include <errno.h> 30 #include <unistd.h> 31 32 #include <jni.h> 33 #include <netinet/tcp.h> 34 #include <netinet/in.h> 35 #include "jni_util.h" 36 #include "jdk_net_LinuxSocketOptions.h" 37 38 #ifndef SO_INCOMING_NAPI_ID 39 #define SO_INCOMING_NAPI_ID 56 40 #endif 41 42 static void handleError(JNIEnv *env, jint rv, const char *errmsg) { 43 if (rv < 0) { 44 if (errno == ENOPROTOOPT) { 45 JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", 46 "unsupported socket option"); 47 } else { 48 JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg); 49 } 50 } 51 } 52 53 static jint socketOptionSupported(jint level, jint optname) { 54 jint one = 1; 55 jint rv, s; 56 socklen_t sz = sizeof (one); 57 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 58 if (s < 0) { 59 return 0; 60 } 61 rv = getsockopt(s, level, optname, (void *) &one, &sz); 62 if (rv != 0 && errno == ENOPROTOOPT) { 63 rv = 0; 64 } else { 65 rv = 1; 66 } 67 close(s); 68 return rv; 69 } 70 71 /* 72 * Declare library specific JNI_Onload entry if static build 73 */ 74 DEF_STATIC_JNI_OnLoad 75 76 /* 77 * Class: jdk_net_LinuxSocketOptions 78 * Method: setQuickAck 79 * Signature: (II)V 80 */ 81 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setQuickAck0 82 (JNIEnv *env, jobject unused, jint fd, jboolean on) { 83 int optval; 84 int rv; 85 optval = (on ? 1 : 0); 86 rv = setsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &optval, sizeof (optval)); 87 handleError(env, rv, "set option TCP_QUICKACK failed"); 88 } 89 90 /* 91 * Class: jdk_net_LinuxSocketOptions 92 * Method: getQuickAck 93 * Signature: (I)Z; 94 */ 95 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0 96 (JNIEnv *env, jobject unused, jint fd) { 97 int on; 98 socklen_t sz = sizeof (on); 99 int rv = getsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &on, &sz); 100 handleError(env, rv, "get option TCP_QUICKACK failed"); 101 return on != 0; 102 } 103 104 /* 105 * Class: jdk_net_LinuxSocketOptions 106 * Method: quickAckSupported 107 * Signature: ()Z 108 */ 109 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_quickAckSupported0 110 (JNIEnv *env, jobject unused) { 111 return socketOptionSupported(SOL_SOCKET, TCP_QUICKACK); 112 } 113 114 /* 115 * Class: jdk_net_LinuxSocketOptions 116 * Method: getSoPeerCred0 117 * Signature: (I[I)I 118 */ 119 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_getSoPeerCred0 120 (JNIEnv *env, jclass clazz, jint fd, jintArray result) { 121 122 int rv; 123 struct ucred cred; 124 socklen_t len = sizeof(cred); 125 int *rr = (int *)(*env)->GetIntArrayElements(env, result, NULL); 126 127 if ((rv=getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len)) < 0) { 128 handleError(env, rv, "get SO_PEERCRED failed"); 129 } else { 130 if ((int)cred.uid == -1) { 131 handleError(env, -1, "get SO_PEERCRED failed"); 132 } else { 133 rr[0] = cred.uid; 134 rr[1] = cred.gid; 135 } 136 } 137 (*env)->ReleaseIntArrayElements(env, result, rr, 0); 138 } 139 140 /* 141 * Class: jdk_net_LinuxSocketOptions 142 * Method: keepAliveOptionsSupported0 143 * Signature: ()Z 144 */ 145 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_keepAliveOptionsSupported0 146 (JNIEnv *env, jobject unused) { 147 return socketOptionSupported(SOL_TCP, TCP_KEEPIDLE) && socketOptionSupported(SOL_TCP, TCP_KEEPCNT) 148 && socketOptionSupported(SOL_TCP, TCP_KEEPINTVL); 149 } 150 151 /* 152 * Class: jdk_net_LinuxSocketOptions 153 * Method: setTcpkeepAliveProbes0 154 * Signature: (II)V 155 */ 156 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpkeepAliveProbes0 157 (JNIEnv *env, jobject unused, jint fd, jint optval) { 158 jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, sizeof (optval)); 159 handleError(env, rv, "set option TCP_KEEPCNT failed"); 160 } 161 162 /* 163 * Class: jdk_net_LinuxSocketOptions 164 * Method: setTcpKeepAliveTime0 165 * Signature: (II)V 166 */ 167 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpKeepAliveTime0 168 (JNIEnv *env, jobject unused, jint fd, jint optval) { 169 jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, sizeof (optval)); 170 handleError(env, rv, "set option TCP_KEEPIDLE failed"); 171 } 172 173 /* 174 * Class: jdk_net_LinuxSocketOptions 175 * Method: setTcpKeepAliveIntvl0 176 * Signature: (II)V 177 */ 178 JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setTcpKeepAliveIntvl0 179 (JNIEnv *env, jobject unused, jint fd, jint optval) { 180 jint rv = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, sizeof (optval)); 181 handleError(env, rv, "set option TCP_KEEPINTVL failed"); 182 } 183 184 /* 185 * Class: jdk_net_LinuxSocketOptions 186 * Method: getTcpkeepAliveProbes0 187 * Signature: (I)I; 188 */ 189 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpkeepAliveProbes0 190 (JNIEnv *env, jobject unused, jint fd) { 191 jint optval, rv; 192 socklen_t sz = sizeof (optval); 193 rv = getsockopt(fd, SOL_TCP, TCP_KEEPCNT, &optval, &sz); 194 handleError(env, rv, "get option TCP_KEEPCNT failed"); 195 return optval; 196 } 197 198 /* 199 * Class: jdk_net_LinuxSocketOptions 200 * Method: getTcpKeepAliveTime0 201 * Signature: (I)I; 202 */ 203 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveTime0 204 (JNIEnv *env, jobject unused, jint fd) { 205 jint optval, rv; 206 socklen_t sz = sizeof (optval); 207 rv = getsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &optval, &sz); 208 handleError(env, rv, "get option TCP_KEEPIDLE failed"); 209 return optval; 210 } 211 212 /* 213 * Class: jdk_net_LinuxSocketOptions 214 * Method: getTcpKeepAliveIntvl0 215 * Signature: (I)I; 216 */ 217 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveIntvl0 218 (JNIEnv *env, jobject unused, jint fd) { 219 jint optval, rv; 220 socklen_t sz = sizeof (optval); 221 rv = getsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &optval, &sz); 222 handleError(env, rv, "get option TCP_KEEPINTVL failed"); 223 return optval; 224 } 225 226 /* 227 * Class: jdk_net_LinuxSocketOptions 228 * Method: incomingNapiIdSupported0 229 * Signature: ()Z; 230 */ 231 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_incomingNapiIdSupported0 232 (JNIEnv *env, jobject unused) { 233 return socketOptionSupported(SOL_SOCKET, SO_INCOMING_NAPI_ID); 234 } 235 236 /* 237 * Class: jdk_net_LinuxSocketOptions 238 * Method: getIncomingNapiId0 239 * Signature: (I)I; 240 */ 241 JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getIncomingNapiId0 242 (JNIEnv *env, jobject unused, jint fd) { 243 jint optval, rv; 244 socklen_t sz = sizeof (optval); 245 rv = getsockopt(fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &optval, &sz); 246 handleError(env, rv, "get option SO_INCOMING_NAPI_ID failed"); 247 return optval; 248 }