update to netty 4.1.105

This commit is contained in:
Jörg Prante 2024-01-22 08:39:20 +01:00
parent 07c6dd718a
commit dde0ad8a11
140 changed files with 9811 additions and 88 deletions

View file

@ -4,6 +4,7 @@ plugins {
id 'signing'
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
id "com.google.osdetector" version "1.7.3"
id 'org.xbib.gradle.plugin.c' version '3.1.0'
}
wrapper {
@ -11,8 +12,6 @@ wrapper {
distributionType = Wrapper.DistributionType.BIN
}
apply plugin: 'com.google.osdetector'
ext {
user = 'joerg'
name = 'netty'
@ -30,11 +29,17 @@ ext {
organizationUrl = 'https://xbib.org'
}
apply plugin: 'com.google.osdetector'
subprojects {
apply from: rootProject.file('gradle/repositories/maven.gradle')
apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/publish/maven.gradle')
if (it.name.endsWith('-native')) {
apply from: rootProject.file('gradle/compile/c.gradle')
} else {
apply from: rootProject.file('gradle/repositories/maven.gradle')
apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/publish/maven.gradle')
}
}
apply from: rootProject.file('gradle/publish/sonatype.gradle')
apply from: rootProject.file('gradle/publish/forgejo.gradle')

View file

@ -1,3 +1,3 @@
group = org.xbib.netty
name = netty
version = 4.1.104
version = 4.1.105.0

3
gradle/compile/c.gradle Normal file
View file

@ -0,0 +1,3 @@
apply plugin: 'base'
apply plugin: 'org.xbib.gradle.plugin.c'

View file

@ -1,21 +1,12 @@
apply plugin: 'com.google.osdetector'
dependencies {
testImplementation project(':netty-channel-unix')
testImplementation project(':netty-channel-epoll')
testImplementation project(':netty-testsuite')
testImplementation project(':netty-handler')
testImplementation testLibs.assertj
testImplementation testLibs.rerunner.jupiter
testRuntimeOnly project(path: ':netty-tcnative-boringssl-static', configuration: osdetector.classifier)
}
task nettyEpollLinuxX8664(type: Jar) {
archiveBaseName.set('netty-channel-epoll-native')
destinationDirectory.set(project.layout.buildDirectory.dir('libs'))
archiveBaseName.set(project.name + '-' + project.version)
archiveExtension.set('jar')
archiveClassifier.set('linux-x86_64')
version rootProject.version
from (sourceSets.main.output) {
from (project.layout.projectDirectory.dir('src/main/resources')) {
include 'META-INF/native/libnetty_transport_native_epoll_x86_64.so'
}
}
@ -25,7 +16,13 @@ configurations {
'linux-x86_64' {
canBeConsumed = true
canBeResolved = false
extendsFrom runtimeOnly
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, JavaVersion.current().majorVersion.toInteger())
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, 'linux-x86_64'))
}
}
}

View file

@ -0,0 +1,909 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/*
* Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined
* (before including any header files) in order to obtain the
* definition of the ucred structure. See <a href=https://linux.die.net/man/7/unix>
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/udp.h> // SOL_UDP
#include <sys/sendfile.h>
#include <linux/tcp.h> // TCP_NOTSENT_LOWAT is a linux specific define
#include "netty_epoll_linuxsocket.h"
#include "netty_epoll_vmsocket.h"
#include "netty_unix_errors.h"
#include "netty_unix_filedescriptor.h"
#include "netty_unix_jni.h"
#include "netty_unix_socket.h"
#include "netty_unix_util.h"
#define LINUXSOCKET_CLASSNAME "io/netty/channel/epoll/LinuxSocket"
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 23
#endif
// TCP_FASTOPEN_CONNECT is defined in linux 4.11. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN_CONNECT
#define TCP_FASTOPEN_CONNECT 30
#endif
// TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile.
#ifndef TCP_NOTSENT_LOWAT
#define TCP_NOTSENT_LOWAT 25
#endif
// SO_BUSY_POLL is defined in linux 3.11. We define this here so older kernels can compile.
#ifndef SO_BUSY_POLL
#define SO_BUSY_POLL 46
#endif
// UDP_GRO is defined in linux 5. We define this here so older kernels can compile.
#ifndef UDP_GRO
#define UDP_GRO 104
#endif
static jweak peerCredentialsClassWeak = NULL;
static jmethodID peerCredentialsMethodId = NULL;
static jfieldID fileChannelFieldId = NULL;
static jfieldID transferredFieldId = NULL;
static jfieldID fdFieldId = NULL;
static jfieldID fileDescriptorFieldId = NULL;
// JNI Registered Methods Begin
static jint netty_epoll_linuxsocket_newVSockStreamFd(JNIEnv* env, jclass clazz) {
int fd = netty_unix_socket_nonBlockingSocket(AF_VSOCK, SOCK_STREAM, 0);
if (fd == -1) {
return -errno;
}
return fd;
}
static jint netty_epoll_linuxsocket_bindVSock(JNIEnv* env, jclass clazz, jint fd, jint cid, jint port) {
struct sockaddr_vm addr;
memset(&addr, 0, sizeof(struct sockaddr_vm));
addr.svm_family = AF_VSOCK;
addr.svm_port = port;
addr.svm_cid = cid;
int res = bind(fd, (struct sockaddr*) &addr, sizeof(struct sockaddr_vm));
if (res == -1) {
return -errno;
}
return res;
}
static jint netty_epoll_linuxsocket_connectVSock(JNIEnv* env, jclass clazz, jint fd, jint cid, jint port) {
struct sockaddr_vm addr;
memset(&addr, 0, sizeof(struct sockaddr_vm));
addr.svm_family = AF_VSOCK;
addr.svm_port = port;
addr.svm_cid = cid;
int res;
int err;
do {
res = connect(fd, (struct sockaddr*) &addr, sizeof(struct sockaddr_vm));
} while (res == -1 && ((err = errno) == EINTR));
if (res == -1) {
return -errno;
}
return res;
}
static jbyteArray createVSockAddressArray(JNIEnv* env, const struct sockaddr_vm* addr) {
jbyteArray bArray = (*env)->NewByteArray(env, 8);
if (bArray == NULL) {
return NULL;
}
unsigned int cid = (addr->svm_cid);
unsigned int port = (addr->svm_port);
unsigned char a[4];
a[0] = cid >> 24;
a[1] = cid >> 16;
a[2] = cid >> 8;
a[3] = cid;
(*env)->SetByteArrayRegion(env, bArray, 0, 4, (jbyte*) &a);
a[0] = port >> 24;
a[1] = port >> 16;
a[2] = port >> 8;
a[3] = port;
(*env)->SetByteArrayRegion(env, bArray, 4, 4, (jbyte*) &a);
return bArray;
}
static jbyteArray netty_epoll_linuxsocket_remoteVSockAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_vm addr = { 0 };
socklen_t len = sizeof(addr);
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createVSockAddressArray(env, &addr);
}
static jbyteArray netty_epoll_linuxsocket_localVSockAddress(JNIEnv* env, jclass clazz, jint fd) {
struct sockaddr_vm addr = { 0 };
socklen_t len = sizeof(addr);
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
return createVSockAddressArray(env, &addr);
}
static void netty_epoll_linuxsocket_setTimeToLive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_TTL, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setIpMulticastLoop(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jint optval) {
if (ipv6 == JNI_TRUE) {
u_int val = (u_int) optval;
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val));
} else {
u_char val = (u_char) optval;
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val));
}
}
static void netty_epoll_linuxsocket_setInterface(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
struct sockaddr_storage interfaceAddr;
socklen_t interfaceAddrSize;
struct sockaddr_in* interfaceIpAddr;
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
if (ipv6 == JNI_TRUE) {
if (interfaceIndex == -1) {
netty_unix_errors_throwIOException(env, "Unable to find network index");
return;
}
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceIndex, sizeof(interfaceIndex));
} else {
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr");
return;
}
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
}
}
static void netty_epoll_linuxsocket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpQuickAck(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpFastOpen(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setIpTransparent(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_IP, IP_TRANSPARENT, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setIpRecvOrigDestAddr(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_setSoBusyPoll(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval));
}
static void netty_epoll_linuxsocket_joinGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
struct sockaddr_storage groupAddr;
socklen_t groupAddrSize;
struct sockaddr_storage interfaceAddr;
socklen_t interfaceAddrSize;
struct sockaddr_in* groupIpAddr;
struct sockaddr_in* interfaceIpAddr;
struct ip_mreq mreq;
struct sockaddr_in6* groupIp6Addr;
struct ipv6_mreq mreq6;
memset(&groupAddr, 0, sizeof(groupAddr));
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
return;
}
switch (groupAddr.ss_family) {
case AF_INET:
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddr");
return;
}
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
groupIpAddr = (struct sockaddr_in*) &groupAddr;
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
break;
case AF_INET6:
if (interfaceIndex == -1) {
netty_unix_errors_throwIOException(env, "Unable to find network index");
return;
}
mreq6.ipv6mr_interface = interfaceIndex;
groupIp6Addr = (struct sockaddr_in6*) &groupAddr;
memcpy(&mreq6.ipv6mr_multiaddr, &groupIp6Addr->sin6_addr, sizeof(groupIp6Addr->sin6_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
break;
default:
netty_unix_errors_throwIOException(env, "Address family not supported");
break;
}
}
static void netty_epoll_linuxsocket_joinSsmGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex, jbyteArray sourceAddress) {
struct sockaddr_storage groupAddr;
socklen_t groupAddrSize;
struct sockaddr_storage interfaceAddr;
socklen_t interfaceAddrSize;
struct sockaddr_storage sourceAddr;
socklen_t sourceAddrSize;
struct sockaddr_in* groupIpAddr;
struct sockaddr_in* interfaceIpAddr;
struct sockaddr_in* sourceIpAddr;
struct ip_mreq_source mreq;
struct group_source_req mreq6;
memset(&groupAddr, 0, sizeof(groupAddr));
memset(&sourceAddr, 0, sizeof(sourceAddr));
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
return;
}
if (netty_unix_socket_initSockaddr(env, ipv6, sourceAddress, scopeId, 0, &sourceAddr, &sourceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for sourceAddress");
return;
}
switch (groupAddr.ss_family) {
case AF_INET:
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
return;
}
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
groupIpAddr = (struct sockaddr_in*) &groupAddr;
sourceIpAddr = (struct sockaddr_in*) &sourceAddr;
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
memcpy(&mreq.imr_sourceaddr, &sourceIpAddr->sin_addr, sizeof(sourceIpAddr->sin_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mreq, sizeof(mreq));
break;
case AF_INET6:
if (interfaceIndex == -1) {
netty_unix_errors_throwIOException(env, "Unable to find network index");
return;
}
mreq6.gsr_group = groupAddr;
mreq6.gsr_interface = interfaceIndex;
mreq6.gsr_source = sourceAddr;
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, &mreq6, sizeof(mreq6));
break;
default:
netty_unix_errors_throwIOException(env, "Address family not supported");
break;
}
}
static void netty_epoll_linuxsocket_leaveGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex) {
struct sockaddr_storage groupAddr;
socklen_t groupAddrSize;
struct sockaddr_storage interfaceAddr;
socklen_t interfaceAddrSize;
struct sockaddr_in* groupIpAddr;
struct sockaddr_in* interfaceIpAddr;
struct ip_mreq mreq;
struct sockaddr_in6* groupIp6Addr;
struct ipv6_mreq mreq6;
memset(&groupAddr, 0, sizeof(groupAddr));
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
return;
}
switch (groupAddr.ss_family) {
case AF_INET:
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
return;
}
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
groupIpAddr = (struct sockaddr_in*) &groupAddr;
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
break;
case AF_INET6:
if (interfaceIndex == -1) {
netty_unix_errors_throwIOException(env, "Unable to find network index");
return;
}
mreq6.ipv6mr_interface = interfaceIndex;
groupIp6Addr = (struct sockaddr_in6*) &groupAddr;
memcpy(&mreq6.ipv6mr_multiaddr, &groupIp6Addr->sin6_addr, sizeof(groupIp6Addr->sin6_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6));
break;
default:
netty_unix_errors_throwIOException(env, "Address family not supported");
break;
}
}
static void netty_epoll_linuxsocket_leaveSsmGroup(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray groupAddress, jbyteArray interfaceAddress, jint scopeId, jint interfaceIndex, jbyteArray sourceAddress) {
struct sockaddr_storage groupAddr;
socklen_t groupAddrSize;
struct sockaddr_storage interfaceAddr;
socklen_t interfaceAddrSize;
struct sockaddr_storage sourceAddr;
socklen_t sourceAddrSize;
struct sockaddr_in* groupIpAddr;
struct sockaddr_in* interfaceIpAddr;
struct sockaddr_in* sourceIpAddr;
struct ip_mreq_source mreq;
struct group_source_req mreq6;
memset(&groupAddr, 0, sizeof(groupAddr));
memset(&sourceAddr, 0, sizeof(sourceAddr));
memset(&interfaceAddr, 0, sizeof(interfaceAddr));
if (netty_unix_socket_initSockaddr(env, ipv6, groupAddress, scopeId, 0, &groupAddr, &groupAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for groupAddress");
return;
}
if (netty_unix_socket_initSockaddr(env, ipv6, sourceAddress, scopeId, 0, &sourceAddr, &sourceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for sourceAddress");
return;
}
switch (groupAddr.ss_family) {
case AF_INET:
if (netty_unix_socket_initSockaddr(env, ipv6, interfaceAddress, scopeId, 0, &interfaceAddr, &interfaceAddrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr for interfaceAddress");
return;
}
interfaceIpAddr = (struct sockaddr_in*) &interfaceAddr;
groupIpAddr = (struct sockaddr_in*) &groupAddr;
sourceIpAddr = (struct sockaddr_in*) &sourceAddr;
memcpy(&mreq.imr_multiaddr, &groupIpAddr->sin_addr, sizeof(groupIpAddr->sin_addr));
memcpy(&mreq.imr_interface, &interfaceIpAddr->sin_addr, sizeof(interfaceIpAddr->sin_addr));
memcpy(&mreq.imr_sourceaddr, &sourceIpAddr->sin_addr, sizeof(sourceIpAddr->sin_addr));
netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &mreq, sizeof(mreq));
break;
case AF_INET6:
if (interfaceIndex == -1) {
netty_unix_errors_throwIOException(env, "Unable to find network index");
return;
}
mreq6.gsr_group = groupAddr;
mreq6.gsr_interface = interfaceIndex;
mreq6.gsr_source = sourceAddr;
netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, MCAST_LEAVE_SOURCE_GROUP, &mreq6, sizeof(mreq6));
break;
default:
netty_unix_errors_throwIOException(env, "Address family not supported");
break;
}
}
static void netty_epoll_linuxsocket_setTcpMd5Sig(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jbyteArray address, jint scopeId, jbyteArray key) {
struct sockaddr_storage addr;
socklen_t addrSize;
memset(&addr, 0, sizeof(addr));
if (netty_unix_socket_initSockaddr(env, ipv6, address, scopeId, 0, &addr, &addrSize) == -1) {
netty_unix_errors_throwIOException(env, "Could not init sockaddr");
return;
}
struct tcp_md5sig md5sig;
memset(&md5sig, 0, sizeof(md5sig));
md5sig.tcpm_addr.ss_family = addr.ss_family;
struct sockaddr_in* ipaddr;
struct sockaddr_in6* ip6addr;
switch (addr.ss_family) {
case AF_INET:
ipaddr = (struct sockaddr_in*) &addr;
memcpy(&((struct sockaddr_in *) &md5sig.tcpm_addr)->sin_addr, &ipaddr->sin_addr, sizeof(ipaddr->sin_addr));
break;
case AF_INET6:
ip6addr = (struct sockaddr_in6*) &addr;
memcpy(&((struct sockaddr_in6 *) &md5sig.tcpm_addr)->sin6_addr, &ip6addr->sin6_addr, sizeof(ip6addr->sin6_addr));
break;
}
if (key != NULL) {
md5sig.tcpm_keylen = (*env)->GetArrayLength(env, key);
(*env)->GetByteArrayRegion(env, key, 0, md5sig.tcpm_keylen, (void *) &md5sig.tcpm_key);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
return;
}
}
if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) < 0) {
netty_unix_errors_throwIOExceptionErrorNo(env, "setsockopt() failed: ", errno);
}
}
static int netty_epoll_linuxsocket_getInterface(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6) {
if (ipv6 == JNI_TRUE) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
} else {
struct in_addr optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &optval, sizeof(optval)) == -1) {
return -1;
}
return ntohl(optval.s_addr);
}
}
static jint netty_epoll_linuxsocket_getTimeToLive(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_TTL, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getIpMulticastLoop(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6) {
if (ipv6 == JNI_TRUE) {
u_int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &optval, sizeof(optval)) == -1) {
return -1;
}
return (jint) optval;
} else {
u_char optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &optval, sizeof(optval)) == -1) {
return -1;
}
return (jint) optval;
}
}
static jint netty_epoll_linuxsocket_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_isIpTransparent(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_IP, IP_TRANSPARENT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_isIpRecvOrigDestAddr(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static void netty_epoll_linuxsocket_getTcpInfo(JNIEnv* env, jclass clazz, jint fd, jlongArray array) {
struct tcp_info tcp_info;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
return;
}
jlong cArray[32];
// Expand to 64 bits, then cast away unsigned-ness.
cArray[0] = (jlong) (uint64_t) tcp_info.tcpi_state;
cArray[1] = (jlong) (uint64_t) tcp_info.tcpi_ca_state;
cArray[2] = (jlong) (uint64_t) tcp_info.tcpi_retransmits;
cArray[3] = (jlong) (uint64_t) tcp_info.tcpi_probes;
cArray[4] = (jlong) (uint64_t) tcp_info.tcpi_backoff;
cArray[5] = (jlong) (uint64_t) tcp_info.tcpi_options;
cArray[6] = (jlong) (uint64_t) tcp_info.tcpi_snd_wscale;
cArray[7] = (jlong) (uint64_t) tcp_info.tcpi_rcv_wscale;
cArray[8] = (jlong) (uint64_t) tcp_info.tcpi_rto;
cArray[9] = (jlong) (uint64_t) tcp_info.tcpi_ato;
cArray[10] = (jlong) (uint64_t) tcp_info.tcpi_snd_mss;
cArray[11] = (jlong) (uint64_t) tcp_info.tcpi_rcv_mss;
cArray[12] = (jlong) (uint64_t) tcp_info.tcpi_unacked;
cArray[13] = (jlong) (uint64_t) tcp_info.tcpi_sacked;
cArray[14] = (jlong) (uint64_t) tcp_info.tcpi_lost;
cArray[15] = (jlong) (uint64_t) tcp_info.tcpi_retrans;
cArray[16] = (jlong) (uint64_t) tcp_info.tcpi_fackets;
cArray[17] = (jlong) (uint64_t) tcp_info.tcpi_last_data_sent;
cArray[18] = (jlong) (uint64_t) tcp_info.tcpi_last_ack_sent;
cArray[19] = (jlong) (uint64_t) tcp_info.tcpi_last_data_recv;
cArray[20] = (jlong) (uint64_t) tcp_info.tcpi_last_ack_recv;
cArray[21] = (jlong) (uint64_t) tcp_info.tcpi_pmtu;
cArray[22] = (jlong) (uint64_t) tcp_info.tcpi_rcv_ssthresh;
cArray[23] = (jlong) (uint64_t) tcp_info.tcpi_rtt;
cArray[24] = (jlong) (uint64_t) tcp_info.tcpi_rttvar;
cArray[25] = (jlong) (uint64_t) tcp_info.tcpi_snd_ssthresh;
cArray[26] = (jlong) (uint64_t) tcp_info.tcpi_snd_cwnd;
cArray[27] = (jlong) (uint64_t) tcp_info.tcpi_advmss;
cArray[28] = (jlong) (uint64_t) tcp_info.tcpi_reordering;
cArray[29] = (jlong) (uint64_t) tcp_info.tcpi_rcv_rtt;
cArray[30] = (jlong) (uint64_t) tcp_info.tcpi_rcv_space;
cArray[31] = (jlong) (uint64_t) tcp_info.tcpi_total_retrans;
(*env)->SetLongArrayRegion(env, array, 0, 32, cArray);
}
static jint netty_epoll_linuxsocket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getSoBusyPoll(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BUSY_POLL, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_epoll_linuxsocket_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jobject netty_epoll_linuxsocket_getPeerCredentials(JNIEnv *env, jclass clazz, jint fd) {
struct ucred credentials;
jclass peerCredentialsClass = NULL;
if(netty_unix_socket_getOption(env,fd, SOL_SOCKET, SO_PEERCRED, &credentials, sizeof (credentials)) == -1) {
return NULL;
}
jintArray gids = (*env)->NewIntArray(env, 1);
(*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.gid);
NETTY_JNI_UTIL_NEW_LOCAL_FROM_WEAK(env, peerCredentialsClass, peerCredentialsClassWeak, error);
jobject creds = (*env)->NewObject(env, peerCredentialsClass, peerCredentialsMethodId, credentials.pid, credentials.uid, gids);
NETTY_JNI_UTIL_DELETE_LOCAL(env, peerCredentialsClass);
return creds;
error:
return NULL;
}
static jint netty_epoll_linuxsocket_isUdpGro(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, SOL_UDP, UDP_GRO, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static void netty_epoll_linuxsocket_setUdpGro(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, SOL_UDP, UDP_GRO, &optval, sizeof(optval));
}
static jlong netty_epoll_linuxsocket_sendFile(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) {
jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId);
if (fileChannel == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get DefaultFileRegion.file");
return -1;
}
jobject fileDescriptor = (*env)->GetObjectField(env, fileChannel, fileDescriptorFieldId);
if (fileDescriptor == NULL) {
netty_unix_errors_throwRuntimeException(env, "failed to get FileChannelImpl.fd");
return -1;
}
jint srcFd = (*env)->GetIntField(env, fileDescriptor, fdFieldId);
if (srcFd == -1) {
netty_unix_errors_throwRuntimeException(env, "failed to get FileDescriptor.fd");
return -1;
}
ssize_t res;
off_t offset = base_off + off;
int err;
do {
res = sendfile(fd, srcFd, &offset, (size_t) len);
} while (res == -1 && ((err = errno) == EINTR));
if (res < 0) {
return -err;
}
if (res > 0) {
// update the transferred field in DefaultFileRegion
(*env)->SetLongField(env, fileRegion, transferredFieldId, off + res);
}
return res;
}
// JNI Registered Methods End
// JNI Method Registration Table Begin
static const JNINativeMethod fixed_method_table[] = {
{ "newVSockStreamFd", "()I", (void *) netty_epoll_linuxsocket_newVSockStreamFd },
{ "bindVSock", "(III)I", (void *) netty_epoll_linuxsocket_bindVSock },
{ "connectVSock", "(III)I", (void *) netty_epoll_linuxsocket_connectVSock },
{ "remoteVSockAddress", "(I)[B", (void *) netty_epoll_linuxsocket_remoteVSockAddress },
{ "localVSockAddress", "(I)[B", (void *) netty_epoll_linuxsocket_localVSockAddress },
{ "setTimeToLive", "(II)V", (void *) netty_epoll_linuxsocket_setTimeToLive },
{ "getTimeToLive", "(I)I", (void *) netty_epoll_linuxsocket_getTimeToLive },
{ "setInterface", "(IZ[BII)V", (void *) netty_epoll_linuxsocket_setInterface },
{ "getInterface", "(IZ)I", (void *) netty_epoll_linuxsocket_getInterface },
{ "setIpMulticastLoop", "(IZI)V", (void * ) netty_epoll_linuxsocket_setIpMulticastLoop },
{ "getIpMulticastLoop", "(IZ)I", (void * ) netty_epoll_linuxsocket_getIpMulticastLoop },
{ "setTcpCork", "(II)V", (void *) netty_epoll_linuxsocket_setTcpCork },
{ "setSoBusyPoll", "(II)V", (void *) netty_epoll_linuxsocket_setSoBusyPoll },
{ "setTcpQuickAck", "(II)V", (void *) netty_epoll_linuxsocket_setTcpQuickAck },
{ "setTcpDeferAccept", "(II)V", (void *) netty_epoll_linuxsocket_setTcpDeferAccept },
{ "setTcpNotSentLowAt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpNotSentLowAt },
{ "isTcpCork", "(I)I", (void *) netty_epoll_linuxsocket_isTcpCork },
{ "getSoBusyPoll", "(I)I", (void *) netty_epoll_linuxsocket_getSoBusyPoll },
{ "getTcpDeferAccept", "(I)I", (void *) netty_epoll_linuxsocket_getTcpDeferAccept },
{ "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpNotSentLowAt },
{ "isTcpQuickAck", "(I)I", (void *) netty_epoll_linuxsocket_isTcpQuickAck },
{ "setTcpFastOpen", "(II)V", (void *) netty_epoll_linuxsocket_setTcpFastOpen },
{ "setTcpKeepIdle", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIdle },
{ "setTcpKeepIntvl", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIntvl },
{ "setTcpKeepCnt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepCnt },
{ "setTcpUserTimeout", "(II)V", (void *) netty_epoll_linuxsocket_setTcpUserTimeout },
{ "setIpFreeBind", "(II)V", (void *) netty_epoll_linuxsocket_setIpFreeBind },
{ "setIpTransparent", "(II)V", (void *) netty_epoll_linuxsocket_setIpTransparent },
{ "setIpRecvOrigDestAddr", "(II)V", (void *) netty_epoll_linuxsocket_setIpRecvOrigDestAddr },
{ "getTcpKeepIdle", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepIdle },
{ "getTcpKeepIntvl", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepIntvl },
{ "getTcpKeepCnt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpKeepCnt },
{ "getTcpUserTimeout", "(I)I", (void *) netty_epoll_linuxsocket_getTcpUserTimeout },
{ "isIpFreeBind", "(I)I", (void *) netty_epoll_linuxsocket_isIpFreeBind },
{ "isIpTransparent", "(I)I", (void *) netty_epoll_linuxsocket_isIpTransparent },
{ "isIpRecvOrigDestAddr", "(I)I", (void *) netty_epoll_linuxsocket_isIpRecvOrigDestAddr },
{ "getTcpInfo", "(I[J)V", (void *) netty_epoll_linuxsocket_getTcpInfo },
{ "setTcpMd5Sig", "(IZ[BI[B)V", (void *) netty_epoll_linuxsocket_setTcpMd5Sig },
{ "joinGroup", "(IZ[B[BII)V", (void *) netty_epoll_linuxsocket_joinGroup },
{ "joinSsmGroup", "(IZ[B[BII[B)V", (void *) netty_epoll_linuxsocket_joinSsmGroup },
{ "leaveGroup", "(IZ[B[BII)V", (void *) netty_epoll_linuxsocket_leaveGroup },
{ "leaveSsmGroup", "(IZ[B[BII[B)V", (void *) netty_epoll_linuxsocket_leaveSsmGroup },
{ "isUdpGro", "(I)I", (void *) netty_epoll_linuxsocket_isUdpGro },
{ "setUdpGro", "(II)V", (void *) netty_epoll_linuxsocket_setUdpGro }
// "sendFile" has a dynamic signature
};
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
static jint dynamicMethodsTableSize() {
return fixed_method_table_size + 2; // 2 is for the dynamic method signatures.
}
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
char* dynamicTypeName = NULL;
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
JNINativeMethod* dynamicMethods = malloc(size);
if (dynamicMethods == NULL) {
return NULL;
}
memset(dynamicMethods, 0, size);
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error);
NETTY_JNI_UTIL_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "getPeerCredentials";
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
netty_jni_util_free_dynamic_name(&dynamicTypeName);
++dynamicMethod;
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error);
NETTY_JNI_UTIL_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error);
dynamicMethod->name = "sendFile";
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile;
netty_jni_util_free_dynamic_name(&dynamicTypeName);
return dynamicMethods;
error:
free(dynamicTypeName);
netty_jni_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
return NULL;
}
// JNI Method Registration Table End
// IMPORTANT: If you add any NETTY_JNI_UTIL_LOAD_CLASS or NETTY_JNI_UTIL_FIND_CLASS calls you also need to update
// Native to reflect that.
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR;
char* nettyClassName = NULL;
jclass fileRegionCls = NULL;
jclass fileChannelCls = NULL;
jclass fileDescriptorCls = NULL;
jclass peerCredentialsClass = NULL;
// Register the methods which are not referenced by static member variables
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) {
goto done;
}
if (netty_jni_util_register_natives(env,
packagePrefix,
LINUXSOCKET_CLASSNAME,
dynamicMethods,
dynamicMethodsTableSize()) != 0) {
goto done;
}
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done);
NETTY_JNI_UTIL_LOAD_CLASS_WEAK(env, peerCredentialsClassWeak, nettyClassName, done);
NETTY_JNI_UTIL_NEW_LOCAL_FROM_WEAK(env, peerCredentialsClass, peerCredentialsClassWeak, done);
netty_jni_util_free_dynamic_name(&nettyClassName);
NETTY_JNI_UTIL_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "<init>", "(II[I)V", done);
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done);
NETTY_JNI_UTIL_FIND_CLASS(env, fileRegionCls, nettyClassName, done);
netty_jni_util_free_dynamic_name(&nettyClassName);
NETTY_JNI_UTIL_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done);
NETTY_JNI_UTIL_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done);
NETTY_JNI_UTIL_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done);
NETTY_JNI_UTIL_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done);
NETTY_JNI_UTIL_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done);
NETTY_JNI_UTIL_TRY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I");
if (fdFieldId == NULL) {
// Android uses a different field name, let's try it.
NETTY_JNI_UTIL_GET_FIELD(env, fileDescriptorCls, fdFieldId, "descriptor", "I", done);
}
ret = NETTY_JNI_UTIL_JNI_VERSION;
done:
netty_jni_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
free(nettyClassName);
NETTY_JNI_UTIL_DELETE_LOCAL(env, peerCredentialsClass);
return ret;
}
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) {
NETTY_JNI_UTIL_UNLOAD_CLASS_WEAK(env, peerCredentialsClassWeak);
netty_jni_util_unregister_natives(env, packagePrefix, LINUXSOCKET_CLASSNAME);
}

View file

@ -0,0 +1,26 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef NETTY_EPOLL_LINUXSOCKET_H_
#define NETTY_EPOLL_LINUXSOCKET_H_
#include <jni.h>
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix);
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix);
#endif

View file

@ -0,0 +1,919 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#define _GNU_SOURCE
#include <jni.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <stddef.h>
#include <limits.h>
#include <inttypes.h>
#include <link.h>
#include <time.h>
// Needed to be able to use syscalls directly and so not depend on newer GLIBC versions
#include <linux/net.h>
#include <sys/syscall.h>
// Needed for UDP_SEGMENT
#include <netinet/udp.h>
#include "netty_epoll_linuxsocket.h"
#include "netty_unix_buffer.h"
#include "netty_unix_errors.h"
#include "netty_unix_filedescriptor.h"
#include "netty_unix_jni.h"
#include "netty_unix_limits.h"
#include "netty_unix_socket.h"
#include "netty_unix_util.h"
#include "netty_unix.h"
// Add define if NETTY_BUILD_STATIC is defined so it is picked up in netty_jni_util.c
#ifdef NETTY_BUILD_STATIC
#define NETTY_JNI_UTIL_BUILD_STATIC
#endif
#define STATICALLY_CLASSNAME "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods"
#define NATIVE_CLASSNAME "io/netty/channel/epoll/Native"
// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 23
#endif
// Allow to compile on systems with older kernels.
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
// UDP_GRO is defined in linux 5. We define this here so older kernels can compile.
#ifndef UDP_GRO
#define UDP_GRO 104
#endif
#ifdef IP_RECVORIGDSTADDR
#if !defined(SOL_IP) && defined(IPPROTO_IP)
#define SOL_IP IPPROTO_IP
#endif /* !SOL_IP && IPPROTO_IP */
#endif // IP_RECVORIGDSTADDR
// optional
extern int epoll_create1(int flags) __attribute__((weak));
extern int epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, const struct timespec *timeout, const sigset_t *sigmask) __attribute__((weak));
#ifndef __USE_GNU
struct mmsghdr {
struct msghdr msg_hdr; /* Message header */
unsigned int msg_len; /* Number of bytes transmitted */
};
#endif
// All linux syscall numbers are stable so this is safe.
#ifndef SYS_recvmmsg
// Only support SYS_recvmmsg for __x86_64__ / __i386__ for now
#if defined(__x86_64__)
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_64.tbl
#define SYS_recvmmsg 299
#elif defined(__i386__)
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_32.tbl
#define SYS_recvmmsg 337
#elif defined(__loongarch64)
// See https://github.com/torvalds/linux/blob/v6.1/include/uapi/asm-generic/unistd.h
#define SYS_recvmmsg 243
#else
#define SYS_recvmmsg -1
#endif
#endif // SYS_recvmmsg
#ifndef SYS_sendmmsg
// Only support SYS_sendmmsg for __x86_64__ / __i386__ for now
#if defined(__x86_64__)
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_64.tbl
#define SYS_sendmmsg 307
#elif defined(__i386__)
// See https://github.com/torvalds/linux/blob/v5.4/arch/x86/entry/syscalls/syscall_32.tbl
#define SYS_sendmmsg 345
#elif defined(__loongarch64)
// See https://github.com/torvalds/linux/blob/v6.1/include/uapi/asm-generic/unistd.h
#define SYS_sendmmsg 269
#else
#define SYS_sendmmsg -1
#endif
#endif // SYS_sendmmsg
// Those are initialized in the init(...) method and cached for performance reasons
static jfieldID packetSenderAddrFieldId = NULL;
static jfieldID packetSenderAddrLenFieldId = NULL;
static jfieldID packetSenderScopeIdFieldId = NULL;
static jfieldID packetSenderPortFieldId = NULL;
static jfieldID packetRecipientAddrFieldId = NULL;
static jfieldID packetRecipientAddrLenFieldId = NULL;
static jfieldID packetRecipientScopeIdFieldId = NULL;
static jfieldID packetRecipientPortFieldId = NULL;
static jfieldID packetSegmentSizeFieldId = NULL;
static jfieldID packetMemoryAddressFieldId = NULL;
static jfieldID packetCountFieldId = NULL;
static const char* staticPackagePrefix = NULL;
static int register_unix_called = 0;
static int epoll_pwait2_supported = 0;
// util methods
static int getSysctlValue(const char * property, int* returnValue) {
int rc = -1;
FILE *fd=fopen(property, "r");
if (fd != NULL) {
char buf[32] = {0x0};
if (fgets(buf, 32, fd) != NULL) {
*returnValue = atoi(buf);
rc = 0;
}
fclose(fd);
}
return rc;
}
static inline jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags) {
uint32_t events = flags;
struct epoll_event ev = {
.data.fd = fd,
.events = events
};
return epoll_ctl(efd, op, fd, &ev);
}
// JNI Registered Methods Begin
static jint netty_epoll_native_eventFd(JNIEnv* env, jclass clazz) {
jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (eventFD < 0) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd() failed: ", errno);
}
return eventFD;
}
static jint netty_epoll_native_timerFd(JNIEnv* env, jclass clazz) {
jint timerFD = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (timerFD < 0) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_create() failed: ", errno);
}
return timerFD;
}
static void netty_epoll_native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value) {
uint64_t val;
for (;;) {
jint ret = eventfd_write(fd, (eventfd_t) value);
if (ret < 0) {
// We need to read before we can write again, let's try to read and then write again and if this
// fails we will bail out.
//
// See https://man7.org/linux/man-pages/man2/eventfd.2.html.
if (errno == EAGAIN) {
if (eventfd_read(fd, &val) == 0 || errno == EAGAIN) {
// Try again
continue;
}
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_read(...) failed: ", errno);
} else {
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write(...) failed: ", errno);
}
}
break;
}
}
static void netty_epoll_native_eventFdRead(JNIEnv* env, jclass clazz, jint fd) {
uint64_t eventfd_t;
if (eventfd_read(fd, &eventfd_t) != 0) {
// something is serious wrong
netty_unix_errors_throwRuntimeException(env, "eventfd_read() failed");
}
}
static jint netty_epoll_native_epollCreate(JNIEnv* env, jclass clazz) {
jint efd;
if (epoll_create1) {
efd = epoll_create1(EPOLL_CLOEXEC);
} else {
// size will be ignored anyway but must be positive
efd = epoll_create(126);
}
if (efd < 0) {
int err = errno;
if (epoll_create1) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create1() failed: ", err);
} else {
netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create() failed: ", err);
}
return efd;
}
if (!epoll_create1) {
if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) {
int err = errno;
close(efd);
netty_unix_errors_throwChannelExceptionErrorNo(env, "fcntl() failed: ", err);
return err;
}
}
return efd;
}
static jint netty_epoll_native_epollWait(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len, jint timeout) {
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
int result, err;
do {
result = epoll_wait(efd, ev, len, timeout);
if (result >= 0) {
return result;
}
} while((err = errno) == EINTR);
return -err;
}
// This needs to be consistent with Native.java
#define EPOLL_WAIT_RESULT(V, ARM_TIMER) ((jlong) ((uint64_t) ((uint32_t) V) << 32 | ARM_TIMER))
static jlong netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len, jint timerFd, jint tvSec, jint tvNsec, jlong millisThreshold) {
// only reschedule the timer if there is a newer event.
// -1 is a special value used by EpollEventLoop.
uint32_t armTimer = millisThreshold <= 0 ? 1 : 0;
if (tvSec != ((jint) -1) && tvNsec != ((jint) -1)) {
if (millisThreshold > 0 && (tvSec != 0 || tvNsec != 0)) {
// Let's try to reduce the syscalls as much as possible as timerfd_settime(...) can be expensive:
// See https://github.com/netty/netty/issues/11695
if (epoll_pwait2_supported == 1) {
// We have epoll_pwait2(...) and it is supported, this means we can just pass in the itimerspec directly and not need an
// extra syscall even for very small timeouts.
struct timespec ts = { tvSec, tvNsec };
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
int result, err;
do {
result = epoll_pwait2(efd, ev, len, &ts, NULL);
if (result >= 0) {
return EPOLL_WAIT_RESULT(result, armTimer);
}
} while((err = errno) == EINTR);
return EPOLL_WAIT_RESULT(-err, armTimer);
}
int millis = tvNsec / 1000000;
// Check if we can reduce the syscall overhead by just use epoll_wait. This is done in cases when we can
// tolerate some "drift".
if (tvNsec == 0 ||
// Let's use the threshold to accept that we may be not 100 % accurate and ignore anything that
// is smaller then 1 ms.
millis >= millisThreshold ||
tvSec > 0) {
millis += tvSec * 1000;
int result = netty_epoll_native_epollWait(env, clazz, efd, address, len, millis);
return EPOLL_WAIT_RESULT(result, armTimer);
}
}
struct itimerspec ts;
memset(&ts.it_interval, 0, sizeof(struct timespec));
ts.it_value.tv_sec = tvSec;
ts.it_value.tv_nsec = tvNsec;
if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) {
netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno);
return -1;
}
armTimer = 1;
}
int result = netty_epoll_native_epollWait(env, clazz, efd, address, len, -1);
return EPOLL_WAIT_RESULT(result, armTimer);
}
static inline void cpu_relax() {
#if defined(__x86_64__)
asm volatile("pause\n": : :"memory");
#elif defined(__aarch64__)
asm volatile("isb\n": : :"memory");
#elif defined(__riscv) && defined(__riscv_zihintpause)
asm volatile("pause\n": : :"memory");
#endif
}
static jint netty_epoll_native_epollBusyWait0(JNIEnv* env, jclass clazz, jint efd, jlong address, jint len) {
struct epoll_event *ev = (struct epoll_event*) (intptr_t) address;
int result, err;
// Zeros = poll (aka return immediately).
do {
result = epoll_wait(efd, ev, len, 0);
if (result == 0) {
// Since we're always polling epoll_wait with no timeout,
// signal CPU that we're in a busy loop
cpu_relax();
}
if (result >= 0) {
return result;
} <