update to netty 4.1.105
This commit is contained in:
parent
07c6dd718a
commit
dde0ad8a11
140 changed files with 9811 additions and 88 deletions
17
build.gradle
17
build.gradle
|
@ -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')
|
||||
|
|
|
@ -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
3
gradle/compile/c.gradle
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
apply plugin: 'base'
|
||||
apply plugin: 'org.xbib.gradle.plugin.c'
|
|
@ -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'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
909
netty-channel-epoll-native/src/main/c/netty_epoll_linuxsocket.c
Normal file
909
netty-channel-epoll-native/src/main/c/netty_epoll_linuxsocket.c
Normal 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);
|
||||
}
|
|
@ -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
|
919
netty-channel-epoll-native/src/main/c/netty_epoll_native.c
Normal file
919
netty-channel-epoll-native/src/main/c/netty_epoll_native.c
Normal 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;
|
||||
}
|
||||
} while((err = errno) == EINTR);
|
||||
|
||||
return -err;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags) {
|
||||
int res = epollCtl(env, efd, EPOLL_CTL_ADD, fd, flags);
|
||||
if (res < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
static jint netty_epoll_native_epollCtlMod0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags) {
|
||||
int res = epollCtl(env, efd, EPOLL_CTL_MOD, fd, flags);
|
||||
if (res < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd, jint fd) {
|
||||
// Create an empty event to workaround a bug in older kernels which can not handle NULL.
|
||||
struct epoll_event event = { 0 };
|
||||
int res = epoll_ctl(efd, EPOLL_CTL_DEL, fd, &event);
|
||||
if (res < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jobjectArray packets, jint offset, jint len) {
|
||||
struct mmsghdr msg[len];
|
||||
struct sockaddr_storage addr[len];
|
||||
char controls[len][CMSG_SPACE(sizeof(uint16_t))];
|
||||
|
||||
socklen_t addrSize;
|
||||
int i;
|
||||
|
||||
memset(msg, 0, sizeof(msg));
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
jobject packet = (*env)->GetObjectArrayElement(env, packets, i + offset);
|
||||
jbyteArray address = (jbyteArray) (*env)->GetObjectField(env, packet, packetRecipientAddrFieldId);
|
||||
jint addrLen = (*env)->GetIntField(env, packet, packetRecipientAddrLenFieldId);
|
||||
jint packetSegmentSize = (*env)->GetIntField(env, packet, packetSegmentSizeFieldId);
|
||||
if (packetSegmentSize > 0) {
|
||||
msg[i].msg_hdr.msg_control = controls[i];
|
||||
msg[i].msg_hdr.msg_controllen = sizeof(controls[i]);
|
||||
struct cmsghdr *cm = CMSG_FIRSTHDR(&msg[i].msg_hdr);
|
||||
cm->cmsg_level = SOL_UDP;
|
||||
cm->cmsg_type = UDP_SEGMENT;
|
||||
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||
*((uint16_t *) CMSG_DATA(cm)) = packetSegmentSize;
|
||||
}
|
||||
if (addrLen != 0) {
|
||||
jint scopeId = (*env)->GetIntField(env, packet, packetRecipientScopeIdFieldId);
|
||||
jint port = (*env)->GetIntField(env, packet, packetRecipientPortFieldId);
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, ipv6, address, scopeId, port, &addr[i], &addrSize) == -1) {
|
||||
return -1;
|
||||
}
|
||||
msg[i].msg_hdr.msg_name = &addr[i];
|
||||
msg[i].msg_hdr.msg_namelen = addrSize;
|
||||
}
|
||||
|
||||
msg[i].msg_hdr.msg_iov = (struct iovec*) (intptr_t) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
|
||||
msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);
|
||||
}
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
// We directly use the syscall to prevent depending on GLIBC 2.14.
|
||||
res = syscall(SYS_sendmmsg, fd, msg, len, 0);
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static void init_packet_address(JNIEnv* env, jobject packet, struct sockaddr_storage* addr, jfieldID addrFieldId,
|
||||
jfieldID addrLenFieldId, jfieldID scopeIdFieldId, jfieldID portFieldId) {
|
||||
jbyteArray address = (jbyteArray) (*env)->GetObjectField(env, packet, addrFieldId);
|
||||
|
||||
if (addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr;
|
||||
|
||||
(*env)->SetByteArrayRegion(env, address, 0, 4, (jbyte*) &ipaddr->sin_addr.s_addr);
|
||||
(*env)->SetIntField(env, packet, addrLenFieldId, 4);
|
||||
(*env)->SetIntField(env, packet, scopeIdFieldId, 0);
|
||||
(*env)->SetIntField(env, packet, portFieldId, ntohs(ipaddr->sin_port));
|
||||
} else {
|
||||
int addrLen = netty_unix_socket_ipAddressLength(addr);
|
||||
struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr;
|
||||
|
||||
if (addrLen == 4) {
|
||||
// IPV4 mapped IPV6 address
|
||||
jbyte* addr = (jbyte*) &ip6addr->sin6_addr.s6_addr;
|
||||
(*env)->SetByteArrayRegion(env, address, 0, 4, addr + 12);
|
||||
} else {
|
||||
(*env)->SetByteArrayRegion(env, address, 0, 16, (jbyte*) &ip6addr->sin6_addr.s6_addr);
|
||||
}
|
||||
(*env)->SetIntField(env, packet, addrLenFieldId, addrLen);
|
||||
(*env)->SetIntField(env, packet, scopeIdFieldId, ip6addr->sin6_scope_id);
|
||||
(*env)->SetIntField(env, packet, portFieldId, ntohs(ip6addr->sin6_port));
|
||||
}
|
||||
}
|
||||
|
||||
static void init_packet(JNIEnv* env, jobject packet, struct msghdr* msg, int len) {
|
||||
(*env)->SetIntField(env, packet, packetCountFieldId, len);
|
||||
|
||||
init_packet_address(env, packet, (struct sockaddr_storage*) msg->msg_name, packetSenderAddrFieldId, packetSenderAddrLenFieldId, packetSenderScopeIdFieldId, packetSenderPortFieldId);
|
||||
|
||||
struct cmsghdr *cmsg = NULL;
|
||||
uint16_t gso_size = 0;
|
||||
uint16_t *gsosizeptr = NULL;
|
||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
|
||||
gsosizeptr = (uint16_t *) CMSG_DATA(cmsg);
|
||||
gso_size = *gsosizeptr;
|
||||
}
|
||||
#ifdef IP_RECVORIGDSTADDR
|
||||
else if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) {
|
||||
init_packet_address(env, packet, (struct sockaddr_storage*) CMSG_DATA(cmsg), packetRecipientAddrFieldId, packetRecipientAddrLenFieldId, packetRecipientScopeIdFieldId, packetRecipientPortFieldId);
|
||||
}
|
||||
#endif // IP_RECVORIGDSTADDR
|
||||
}
|
||||
(*env)->SetIntField(env, packet, packetSegmentSizeFieldId, gso_size);
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_recvmsg0(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jobject packet) {
|
||||
struct msghdr msg = { 0 };
|
||||
struct sockaddr_storage sock_address;
|
||||
int addrSize = sizeof(sock_address);
|
||||
// Enough space for GRO and IP_RECVORIGDSTADDR
|
||||
char control[CMSG_SPACE(sizeof(uint16_t)) + sizeof(struct sockaddr_storage)] = { 0 };
|
||||
msg.msg_name = &sock_address;
|
||||
msg.msg_namelen = (socklen_t) addrSize;
|
||||
msg.msg_iov = (struct iovec*) (intptr_t) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
|
||||
msg.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);
|
||||
msg.msg_control = control;
|
||||
msg.msg_controllen = sizeof(control);
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = recvmsg(fd, &msg, 0);
|
||||
// keep on reading if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
init_packet(env, packet, &msg, res);
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_recvmmsg0(JNIEnv* env, jclass clazz, jint fd, jboolean ipv6, jobjectArray packets, jint offset, jint len) {
|
||||
struct mmsghdr msg[len];
|
||||
memset(msg, 0, sizeof(msg));
|
||||
struct sockaddr_storage addr[len];
|
||||
int addrSize = sizeof(addr);
|
||||
memset(addr, 0, addrSize);
|
||||
int storageSize = sizeof(struct sockaddr_storage);
|
||||
char* cntrlbuf = NULL;
|
||||
|
||||
#ifdef IP_RECVORIGDSTADDR
|
||||
int readLocalAddr = 0;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_RECVORIGDSTADDR,
|
||||
&readLocalAddr, sizeof(readLocalAddr)) < 0) {
|
||||
cntrlbuf = malloc(sizeof(char) * storageSize * len);
|
||||
}
|
||||
#endif // IP_RECVORIGDSTADDR
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
jobject packet = (*env)->GetObjectArrayElement(env, packets, i + offset);
|
||||
msg[i].msg_hdr.msg_iov = (struct iovec*) (intptr_t) (*env)->GetLongField(env, packet, packetMemoryAddressFieldId);
|
||||
msg[i].msg_hdr.msg_iovlen = (*env)->GetIntField(env, packet, packetCountFieldId);
|
||||
|
||||
msg[i].msg_hdr.msg_name = addr + i;
|
||||
msg[i].msg_hdr.msg_namelen = (socklen_t) addrSize;
|
||||
|
||||
if (cntrlbuf != NULL) {
|
||||
msg[i].msg_hdr.msg_control = cntrlbuf + i * storageSize;
|
||||
msg[i].msg_hdr.msg_controllen = storageSize;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
// We directly use the syscall to prevent depending on GLIBC 2.12.
|
||||
res = syscall(SYS_recvmmsg, fd, &msg, len, 0, NULL);
|
||||
|
||||
// keep on reading if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res >= 0) {
|
||||
for (i = 0; i < res; i++) {
|
||||
jobject packet = (*env)->GetObjectArrayElement(env, packets, i + offset);
|
||||
init_packet(env, packet, &msg[i].msg_hdr, msg[i].msg_len);
|
||||
}
|
||||
}
|
||||
// Free the control message buffer if needed.
|
||||
free(cntrlbuf);
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jstring netty_epoll_native_kernelVersion(JNIEnv* env, jclass clazz) {
|
||||
struct utsname name;
|
||||
|
||||
int res = uname(&name);
|
||||
if (res == 0) {
|
||||
return (*env)->NewStringUTF(env, name.release);
|
||||
}
|
||||
netty_unix_errors_throwRuntimeExceptionErrorNo(env, "uname() failed: ", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static jboolean netty_epoll_native_isSupportingSendmmsg(JNIEnv* env, jclass clazz) {
|
||||
if (SYS_sendmmsg == -1) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (syscall(SYS_sendmmsg, -1, NULL, 0, 0) == -1) {
|
||||
if (errno == ENOSYS) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
static jboolean netty_epoll_native_isSupportingUdpSegment(JNIEnv* env, jclass clazz) {
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd == -1) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
int gso_size = 512;
|
||||
int ret = setsockopt(fd, SOL_UDP, UDP_SEGMENT, &gso_size, sizeof(gso_size));
|
||||
close(fd);
|
||||
return ret == -1 ? JNI_FALSE : JNI_TRUE;
|
||||
}
|
||||
|
||||
static jboolean netty_epoll_native_isSupportingRecvmmsg(JNIEnv* env, jclass clazz) {
|
||||
if (SYS_recvmmsg == -1) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (syscall(SYS_recvmmsg, -1, NULL, 0, 0, NULL) == -1) {
|
||||
if (errno == ENOSYS) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_tcpFastopenMode(JNIEnv* env, jclass clazz) {
|
||||
int fastopen = 0;
|
||||
getSysctlValue("/proc/sys/net/ipv4/tcp_fastopen", &fastopen);
|
||||
return fastopen;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollet(JNIEnv* env, jclass clazz) {
|
||||
return EPOLLET;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollin(JNIEnv* env, jclass clazz) {
|
||||
return EPOLLIN;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollout(JNIEnv* env, jclass clazz) {
|
||||
return EPOLLOUT;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollrdhup(JNIEnv* env, jclass clazz) {
|
||||
return EPOLLRDHUP;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_epollerr(JNIEnv* env, jclass clazz) {
|
||||
return EPOLLERR;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_sizeofEpollEvent(JNIEnv* env, jclass clazz) {
|
||||
return sizeof(struct epoll_event);
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_offsetofEpollData(JNIEnv* env, jclass clazz) {
|
||||
return offsetof(struct epoll_event, data);
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len) {
|
||||
ssize_t res;
|
||||
int err;
|
||||
loff_t off_in = (loff_t) offIn;
|
||||
loff_t off_out = (loff_t) offOut;
|
||||
|
||||
loff_t* p_off_in = off_in >= 0 ? &off_in : NULL;
|
||||
loff_t* p_off_out = off_out >= 0 ? &off_out : NULL;
|
||||
|
||||
do {
|
||||
res = splice(fd, p_off_in, fdOut, p_off_out, (size_t) len, SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
|
||||
// keep on splicing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz) {
|
||||
struct tcp_md5sig md5sig;
|
||||
|
||||
// Defensive size check
|
||||
if (sizeof(md5sig.tcpm_key) < TCP_MD5SIG_MAXKEYLEN) {
|
||||
return sizeof(md5sig.tcpm_key);
|
||||
}
|
||||
|
||||
return TCP_MD5SIG_MAXKEYLEN;
|
||||
}
|
||||
|
||||
static jint netty_epoll_native_registerUnix(JNIEnv* env, jclass clazz) {
|
||||
register_unix_called = 1;
|
||||
return netty_unix_register(env, staticPackagePrefix);
|
||||
}
|
||||
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
static const JNINativeMethod statically_referenced_fixed_method_table[] = {
|
||||
{ "epollet", "()I", (void *) netty_epoll_native_epollet },
|
||||
{ "epollin", "()I", (void *) netty_epoll_native_epollin },
|
||||
{ "epollout", "()I", (void *) netty_epoll_native_epollout },
|
||||
{ "epollrdhup", "()I", (void *) netty_epoll_native_epollrdhup },
|
||||
{ "epollerr", "()I", (void *) netty_epoll_native_epollerr },
|
||||
{ "tcpMd5SigMaxKeyLen", "()I", (void *) netty_epoll_native_tcpMd5SigMaxKeyLen },
|
||||
{ "isSupportingSendmmsg", "()Z", (void *) netty_epoll_native_isSupportingSendmmsg },
|
||||
{ "isSupportingRecvmmsg", "()Z", (void *) netty_epoll_native_isSupportingRecvmmsg },
|
||||
{ "tcpFastopenMode", "()I", (void *) netty_epoll_native_tcpFastopenMode },
|
||||
{ "kernelVersion", "()Ljava/lang/String;", (void *) netty_epoll_native_kernelVersion }
|
||||
};
|
||||
static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]);
|
||||
static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "eventFd", "()I", (void *) netty_epoll_native_eventFd },
|
||||
{ "timerFd", "()I", (void *) netty_epoll_native_timerFd },
|
||||
{ "eventFdWrite", "(IJ)V", (void *) netty_epoll_native_eventFdWrite },
|
||||
{ "eventFdRead", "(I)V", (void *) netty_epoll_native_eventFdRead },
|
||||
{ "epollCreate", "()I", (void *) netty_epoll_native_epollCreate },
|
||||
{ "epollWait0", "(IJIIIIJ)J", (void *) netty_epoll_native_epollWait0 },
|
||||
{ "epollWait", "(IJII)I", (void *) netty_epoll_native_epollWait },
|
||||
{ "epollBusyWait0", "(IJI)I", (void *) netty_epoll_native_epollBusyWait0 },
|
||||
{ "epollCtlAdd0", "(III)I", (void *) netty_epoll_native_epollCtlAdd0 },
|
||||
{ "epollCtlMod0", "(III)I", (void *) netty_epoll_native_epollCtlMod0 },
|
||||
{ "epollCtlDel0", "(II)I", (void *) netty_epoll_native_epollCtlDel0 },
|
||||
// "sendmmsg0" has a dynamic signature
|
||||
{ "sizeofEpollEvent", "()I", (void *) netty_epoll_native_sizeofEpollEvent },
|
||||
{ "offsetofEpollData", "()I", (void *) netty_epoll_native_offsetofEpollData },
|
||||
{ "splice0", "(IJIJJ)I", (void *) netty_epoll_native_splice0 },
|
||||
{ "isSupportingUdpSegment", "()Z", (void *) netty_epoll_native_isSupportingUdpSegment },
|
||||
{ "registerUnix", "()I", (void *) netty_epoll_native_registerUnix },
|
||||
|
||||
};
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
static jint dynamicMethodsTableSize() {
|
||||
return fixed_method_table_size + 3; // 3 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/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error);
|
||||
NETTY_JNI_UTIL_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error);
|
||||
dynamicMethod->name = "sendmmsg0";
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0;
|
||||
netty_jni_util_free_dynamic_name(&dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error);
|
||||
NETTY_JNI_UTIL_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error);
|
||||
dynamicMethod->name = "recvmmsg0";
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmmsg0;
|
||||
netty_jni_util_free_dynamic_name(&dynamicTypeName);
|
||||
|
||||
++dynamicMethod;
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;)I", dynamicTypeName, error);
|
||||
NETTY_JNI_UTIL_PREPEND("(IZL", dynamicTypeName, dynamicMethod->signature, error);
|
||||
dynamicMethod->name = "recvmsg0";
|
||||
dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmsg0;
|
||||
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.
|
||||
static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
|
||||
int ret = JNI_ERR;
|
||||
int staticallyRegistered = 0;
|
||||
int nativeRegistered = 0;
|
||||
int linuxsocketOnLoadCalled = 0;
|
||||
char* nettyClassName = NULL;
|
||||
jclass nativeDatagramPacketCls = NULL;
|
||||
JNINativeMethod* dynamicMethods = NULL;
|
||||
|
||||
// We must register the statically referenced methods first!
|
||||
if (netty_jni_util_register_natives(env,
|
||||
packagePrefix,
|
||||
STATICALLY_CLASSNAME,
|
||||
statically_referenced_fixed_method_table,
|
||||
statically_referenced_fixed_method_table_size) != 0) {
|
||||
goto done;
|
||||
}
|
||||
staticallyRegistered = 1;
|
||||
|
||||
// Register the methods which are not referenced by static member variables
|
||||
dynamicMethods = createDynamicMethodsTable(packagePrefix);
|
||||
if (dynamicMethods == NULL) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (netty_jni_util_register_natives(env,
|
||||
packagePrefix,
|
||||
NATIVE_CLASSNAME,
|
||||
dynamicMethods,
|
||||
dynamicMethodsTableSize()) != 0) {
|
||||
goto done;
|
||||
}
|
||||
nativeRegistered = 1;
|
||||
|
||||
if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto done;
|
||||
}
|
||||
linuxsocketOnLoadCalled = 1;
|
||||
|
||||
// Initialize this module
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket", nettyClassName, done);
|
||||
NETTY_JNI_UTIL_FIND_CLASS(env, nativeDatagramPacketCls, nettyClassName, done);
|
||||
netty_jni_util_free_dynamic_name(&nettyClassName);
|
||||
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetSenderAddrFieldId, "senderAddr", "[B", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetSenderAddrLenFieldId, "senderAddrLen", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetSenderScopeIdFieldId, "senderScopeId", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetSenderPortFieldId, "senderPort", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetRecipientAddrFieldId, "recipientAddr", "[B", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetRecipientAddrLenFieldId, "recipientAddrLen", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetRecipientScopeIdFieldId, "recipientScopeId", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetRecipientPortFieldId, "recipientPort", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetSegmentSizeFieldId, "segmentSize", "I", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetMemoryAddressFieldId, "memoryAddress", "J", done);
|
||||
NETTY_JNI_UTIL_GET_FIELD(env, nativeDatagramPacketCls, packetCountFieldId, "count", "I", done);
|
||||
|
||||
ret = NETTY_JNI_UTIL_JNI_VERSION;
|
||||
|
||||
staticPackagePrefix = packagePrefix;
|
||||
|
||||
// Check if there is an epoll_pwait2 system call and also if it works. One some systems it might be there
|
||||
// but actually is not implemented and so fail with ENOSYS.
|
||||
// See https://github.com/netty/netty/issues/12343
|
||||
if (epoll_pwait2) {
|
||||
int efd = epoll_create(1);
|
||||
if (efd != -1) {
|
||||
struct timespec ts = { 0, 0 };
|
||||
struct epoll_event ev;
|
||||
do {
|
||||
if (epoll_pwait2(efd, &ev, 1, &ts, NULL) != -1) {
|
||||
epoll_pwait2_supported = 1;
|
||||
break;
|
||||
}
|
||||
} while(errno == EINTR);
|
||||
close(efd);
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
||||
netty_jni_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
|
||||
free(nettyClassName);
|
||||
|
||||
if (ret == JNI_ERR) {
|
||||
if (staticallyRegistered == 1) {
|
||||
netty_jni_util_unregister_natives(env, packagePrefix, STATICALLY_CLASSNAME);
|
||||
}
|
||||
if (nativeRegistered == 1) {
|
||||
netty_jni_util_unregister_natives(env, packagePrefix, NATIVE_CLASSNAME);
|
||||
}
|
||||
if (linuxsocketOnLoadCalled == 1) {
|
||||
netty_epoll_linuxsocket_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
packetSenderAddrFieldId = NULL;
|
||||
packetSenderAddrLenFieldId = NULL;
|
||||
packetSenderScopeIdFieldId = NULL;
|
||||
packetSenderPortFieldId = NULL;
|
||||
packetRecipientAddrFieldId = NULL;
|
||||
packetRecipientAddrLenFieldId = NULL;
|
||||
packetRecipientScopeIdFieldId = NULL;
|
||||
packetRecipientPortFieldId = NULL;
|
||||
packetSegmentSizeFieldId = NULL;
|
||||
packetMemoryAddressFieldId = NULL;
|
||||
packetCountFieldId = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void netty_epoll_native_JNI_OnUnload(JNIEnv* env) {
|
||||
netty_epoll_linuxsocket_JNI_OnUnLoad(env, staticPackagePrefix);
|
||||
|
||||
if (register_unix_called == 1) {
|
||||
register_unix_called = 0;
|
||||
netty_unix_unregister(env, staticPackagePrefix);
|
||||
}
|
||||
|
||||
netty_jni_util_unregister_natives(env, staticPackagePrefix, STATICALLY_CLASSNAME);
|
||||
netty_jni_util_unregister_natives(env, staticPackagePrefix, NATIVE_CLASSNAME);
|
||||
|
||||
if (staticPackagePrefix != NULL) {
|
||||
free((void *) staticPackagePrefix);
|
||||
staticPackagePrefix = NULL;
|
||||
}
|
||||
|
||||
packetSenderAddrFieldId = NULL;
|
||||
packetSenderAddrLenFieldId = NULL;
|
||||
packetSenderScopeIdFieldId = NULL;
|
||||
packetSenderPortFieldId = NULL;
|
||||
packetRecipientAddrFieldId = NULL;
|
||||
packetRecipientAddrLenFieldId = NULL;
|
||||
packetRecipientScopeIdFieldId = NULL;
|
||||
packetRecipientPortFieldId = NULL;
|
||||
packetSegmentSizeFieldId = NULL;
|
||||
packetMemoryAddressFieldId = NULL;
|
||||
packetCountFieldId = NULL;
|
||||
}
|
||||
|
||||
// Invoked by the JVM when statically linked
|
||||
|
||||
// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT
|
||||
// https://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html
|
||||
|
||||
// Invoked by the JVM when statically linked
|
||||
JNIEXPORT jint JNI_OnLoad_netty_transport_native_epoll(JavaVM* vm, void* reserved) {
|
||||
return netty_jni_util_JNI_OnLoad(vm, reserved, "netty_transport_native_epoll", netty_epoll_native_JNI_OnLoad);
|
||||
}
|
||||
|
||||
// Invoked by the JVM when statically linked
|
||||
JNIEXPORT void JNI_OnUnload_netty_transport_native_epoll(JavaVM* vm, void* reserved) {
|
||||
netty_jni_util_JNI_OnUnload(vm, reserved, netty_epoll_native_JNI_OnUnload);
|
||||
}
|
||||
|
||||
#ifndef NETTY_BUILD_STATIC
|
||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
return netty_jni_util_JNI_OnLoad(vm, reserved, "netty_transport_native_epoll", netty_epoll_native_JNI_OnLoad);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
netty_jni_util_JNI_OnUnload(vm, reserved, netty_epoll_native_JNI_OnUnload);
|
||||
}
|
||||
#endif /* NETTY_BUILD_STATIC */
|
38
netty-channel-epoll-native/src/main/c/netty_epoll_vmsocket.h
Normal file
38
netty-channel-epoll-native/src/main/c/netty_epoll_vmsocket.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2023 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_VMSOCKET_H_
|
||||
#define NETTY_EPOLL_VMSOCKET_H_
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef AF_VSOCK
|
||||
#define AF_VSOCK 40
|
||||
#endif
|
||||
|
||||
struct sockaddr_vm {
|
||||
sa_family_t svm_family;
|
||||
unsigned short svm_reserved1;
|
||||
unsigned int svm_port;
|
||||
unsigned int svm_cid;
|
||||
unsigned char svm_zero[sizeof(struct sockaddr) -
|
||||
sizeof(sa_family_t) -
|
||||
sizeof(unsigned short) -
|
||||
sizeof(unsigned int) -
|
||||
sizeof(unsigned int)];
|
||||
};
|
||||
|
||||
#endif /* NETTY_EPOLL_VMSOCKET_H_ */
|
|
@ -1,4 +1,12 @@
|
|||
apply plugin: 'com.google.osdetector'
|
||||
|
||||
dependencies {
|
||||
api project(':netty-channel')
|
||||
api project(':netty-channel-unix')
|
||||
testImplementation project(':netty-testsuite')
|
||||
testImplementation project(':netty-handler')
|
||||
testImplementation testLibs.assertj
|
||||
testImplementation testLibs.rerunner.jupiter
|
||||
testRuntimeOnly project(path: ':netty-channel-epoll-native', configuration: osdetector.classifier)
|
||||
testRuntimeOnly project(path: ':netty-tcnative-boringssl-static-native', configuration: osdetector.classifier)
|
||||
}
|
||||
|
|
40
netty-channel-unix-native/CNakeFile.txt
Normal file
40
netty-channel-unix-native/CNakeFile.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(netty-channel-unix-native VERSION 4.1.104 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# jvm
|
||||
find_package(Java REQUIRED)
|
||||
# https://stackoverflow.com/questions/51047978/cmake-could-not-find-jni
|
||||
set(JAVA_AWT_LIBRARY NotNeeded)
|
||||
set(JAVA_JVM_LIBRARY NotNeeded)
|
||||
find_package(JNI REQUIRED)
|
||||
|
||||
include_directories(${JNI_INCLUDE_DIRS})
|
||||
|
||||
# force off-tree build
|
||||
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
message(FATAL_ERROR "CMake generation is not allowed within the source directory!
|
||||
Remove the CMakeCache.txt file and try again from another folder, e.g.:
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
")
|
||||
endif()
|
||||
|
||||
# default to Release build
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
|
||||
FORCE)
|
||||
endif()
|
||||
|
||||
include_directories(src/main/include)
|
||||
add_subdirectory(src/main/c)
|
10
netty-channel-unix-native/build.gradle
Normal file
10
netty-channel-unix-native/build.gradle
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
tasks.register("compile", org.xbib.gradle.plugin.c.tasks.ExtendedCCompile) {
|
||||
group = "build"
|
||||
source(layout.projectDirectory.files("src/main/c").asFileTree)
|
||||
includes(layout.projectDirectory.files("src/main/headers"))
|
||||
toolChain.set(c.localTool("12.3.1", "/usr/bin/gcc", ".o"))
|
||||
compilerArgs.addAll('-v', '-O3', '-Werror', '-Wno-attributes', '-fPIC', '-fno-omit-frame-pointer', '-Wunused-variable', '-fvisibility=hidden')
|
||||
targetPlatform.set(c.platform())
|
||||
objectFileDir.set(layout.buildDirectory.dir("libs"))
|
||||
}
|
485
netty-channel-unix-native/src/main/c/netty_jni_util.c
Normal file
485
netty-channel-unix-native/src/main/c/netty_jni_util.c
Normal file
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Copyright 2020 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:
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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
|
||||
*
|
||||
* http://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 _WIN32
|
||||
// It's important to have #define _GNU_SOURCE before any other include as otherwise it will not work.
|
||||
// See http://stackoverflow.com/questions/7296963/gnu-source-and-use-gnu
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#define MAX_DLL_PATH_LEN 2048
|
||||
#include <windows.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "netty_jni_util.h"
|
||||
|
||||
void netty_jni_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize) {
|
||||
if (dynamicMethods != NULL) {
|
||||
jint i = fixedMethodTableSize;
|
||||
for (; i < fullMethodTableSize; ++i) {
|
||||
free(dynamicMethods[i].signature);
|
||||
}
|
||||
free(dynamicMethods);
|
||||
}
|
||||
}
|
||||
|
||||
void netty_jni_util_free_dynamic_name(char** dynamicName) {
|
||||
if (dynamicName != NULL && *dynamicName != NULL) {
|
||||
free(*dynamicName);
|
||||
*dynamicName = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char* netty_jni_util_prepend(const char* prefix, const char* str) {
|
||||
if (str == NULL) {
|
||||
// If str is NULL we should just return NULL as passing NULL to strlen is undefined behavior.
|
||||
return NULL;
|
||||
}
|
||||
if (prefix == NULL) {
|
||||
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1));
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(result, str);
|
||||
return result;
|
||||
}
|
||||
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1));
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(result, prefix);
|
||||
strcat(result, str);
|
||||
return result;
|
||||
}
|
||||
|
||||
jint netty_jni_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) {
|
||||
char* nettyClassName = NULL;
|
||||
int retValue = JNI_ERR;
|
||||
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, className, nettyClassName, done);
|
||||
|
||||
jclass nativeCls = (*env)->FindClass(env, nettyClassName);
|
||||
if (nativeCls != NULL) {
|
||||
retValue = (*env)->RegisterNatives(env, nativeCls, methods, numMethods);
|
||||
}
|
||||
done:
|
||||
free(nettyClassName);
|
||||
return retValue;
|
||||
}
|
||||
|
||||
jint netty_jni_util_unregister_natives(JNIEnv* env, const char* packagePrefix, const char* className) {
|
||||
char* nettyClassName = NULL;
|
||||
int retValue = JNI_ERR;
|
||||
|
||||
NETTY_JNI_UTIL_PREPEND(packagePrefix, className, nettyClassName, done);
|
||||
|
||||
jclass nativeCls = (*env)->FindClass(env, nettyClassName);
|
||||
if (nativeCls != NULL) {
|
||||
retValue = (*env)->UnregisterNatives(env, nativeCls);
|
||||
}
|
||||
done:
|
||||
free(nettyClassName);
|
||||
return retValue;
|
||||
}
|
||||
|
||||
#ifndef NETTY_JNI_UTIL_BUILD_STATIC
|
||||
|
||||
char* netty_jni_util_rstrstr(char* s1rbegin, const char* s1rend, const char* s2) {
|
||||
if (s1rbegin == NULL || s1rend == NULL || s2 == NULL) {
|
||||
// Return NULL if any of the parameters is NULL to not risk a segfault
|
||||
return NULL;
|
||||
}
|
||||
size_t s2len = strlen(s2);
|
||||
char *s = s1rbegin - s2len;
|
||||
|
||||
for (; s >= s1rend; --s) {
|
||||
if (strncmp(s, s2, s2len) == 0) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static char* netty_jni_util_rstrchar(char* s1rbegin, const char* s1rend, const char c2) {
|
||||
if (s1rbegin == NULL || s1rend == NULL || c2 == NULL) {
|
||||
// Return NULL if any of the parameters is NULL to not risk a segfault
|
||||
return NULL;
|
||||
}
|
||||
for (; s1rbegin >= s1rend; --s1rbegin) {
|
||||
if (*s1rbegin == c2) {
|
||||
return s1rbegin;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
static char* netty_jni_util_strstr_last(const char* haystack, const char* needle) {
|
||||
if (haystack == NULL || needle == NULL) {
|
||||
// calling strstr with NULL is undefined behavior. Better just return NULL and not risk a crash.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* prevptr = NULL;
|
||||
char* ptr = (char*) haystack;
|
||||
|
||||
while ((ptr = strstr(ptr, needle)) != NULL) {
|
||||
// Just store the ptr and continue searching.
|
||||
prevptr = ptr;
|
||||
++ptr;
|
||||
}
|
||||
return prevptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* The expected format of the library name is "lib<>libname" on non windows platforms and "<>libname" on windows,
|
||||
* where the <> portion is what we will return.
|
||||
*/
|
||||
static char* parsePackagePrefix(const char* libraryPathName, const char* libname, jint* status) {
|
||||
char* packageNameEnd = netty_jni_util_strstr_last(libraryPathName, libname);
|
||||
if (packageNameEnd == NULL) {
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
// on windows there is no lib prefix so we instead look for the previous path separator or the beginning of the string.
|
||||
char* packagePrefix = netty_jni_util_rstrchar(packageNameEnd, libraryPathName, '\\');
|
||||
if (packagePrefix == NULL) {
|
||||
// The string does not have to specify a path [1].
|
||||
// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms683200(v=vs.85).aspx
|
||||
packagePrefix = libraryPathName;
|
||||
} else {
|
||||
packagePrefix += 1;
|
||||
}
|
||||
#else
|
||||
char* packagePrefix = netty_jni_util_rstrstr(packageNameEnd, libraryPathName, "lib");
|
||||
if (packagePrefix == NULL) {
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
packagePrefix += 3;
|
||||
#endif // _WIN32
|
||||
|
||||
if (packagePrefix == packageNameEnd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// packagePrefix length is > 0
|
||||
// Make a copy so we can modify the value without impacting libraryPathName.
|
||||
size_t packagePrefixLen = packageNameEnd - packagePrefix;
|
||||
char* newPackagePrefix = malloc(packagePrefixLen + 2); // +1 for trailing slash and +1 for \0
|
||||
if (newPackagePrefix == NULL) {
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Unmangle the package name, by translating:
|
||||
// - `_1` to `_`
|
||||
// - `_` to `/`
|
||||
//
|
||||
// Note that we don't unmangle `_0xxxx` because it's extremely unlikely to have a non-ASCII character
|
||||
// in a package name. For more information, see:
|
||||
// - JNI specification: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names
|
||||
// - `NativeLibraryLoader.load()` that mangles a package name.
|
||||
size_t i;
|
||||
size_t j = 0;
|
||||
for (i = 0; i < packagePrefixLen; ++i) {
|
||||
char ch = packagePrefix[i];
|
||||
if (ch != '_') {
|
||||
newPackagePrefix[j++] = ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
char nextCh = packagePrefix[i + 1];
|
||||
if (nextCh < '0' || nextCh > '9') {
|
||||
// No digit after `_`; translate to `/`.
|
||||
newPackagePrefix[j++] = '/';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextCh == '1') {
|
||||
i++;
|
||||
newPackagePrefix[j++] = '_';
|
||||
} else {
|
||||
// We don't support _0, _2 .. _9.
|
||||
fprintf(stderr,
|
||||
"FATAL: Unsupported escape pattern '_%c' in library name '%s'\n",
|
||||
nextCh, packagePrefix);
|
||||
fflush(stderr);
|
||||
free(newPackagePrefix);
|
||||
*status = JNI_ERR;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the prefix ends with `/`.
|
||||
if (newPackagePrefix[j - 1] != '/') {
|
||||
newPackagePrefix[j++] = '/';
|
||||
}
|
||||
|
||||
// Terminate with `\0`.
|
||||
newPackagePrefix[j++] = '\0';
|
||||
return newPackagePrefix;
|
||||
}
|
||||
|
||||
#endif /* NETTY_JNI_UTIL_BUILD_STATIC */
|
||||
|
||||
/* Fix missing Dl_info & dladdr in AIX
|
||||
* The code is taken from netbsd.org (src/crypto/external/bsd/openssl/dist/crypto/dso/dso_dlfcn.c)
|
||||
* except strlcpy & strlcat (those are taken from openbsd.org (src/lib/libc/string))
|
||||
*/
|
||||
#ifdef _AIX
|
||||
/*-
|
||||
* See IBM's AIX Version 7.2, Technical Reference:
|
||||
* Base Operating System and Extensions, Volume 1 and 2
|
||||
* https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
|
||||
*/
|
||||
#include <sys/ldr.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* strlcpy:
|
||||
* Copy string src to buffer dst of size dsize. At most dsize-1
|
||||
* chars will be copied. Always NUL terminates (unless dsize == 0).
|
||||
* Returns strlen(src); if retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t strlcpy(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *osrc = src;
|
||||
size_t nleft = dsize;
|
||||
|
||||
/* Copy as many bytes as will fit. */
|
||||
if (nleft != 0) {
|
||||
while (--nleft != 0) {
|
||||
if ((*dst++ = *src++) == '\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src. */
|
||||
if (nleft == 0) {
|
||||
if (dsize != 0) {
|
||||
*dst = '\0'; /* NUL-terminate dst */
|
||||
}
|
||||
while (*src++) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return src - osrc - 1; /* count does not include NUL */
|
||||
}
|
||||
|
||||
/* strlcat:
|
||||
* Appends src to string dst of size dsize (unlike strncat, dsize is the
|
||||
* full size of dst, not space left). At most dsize-1 characters
|
||||
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
|
||||
* If retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t strlcat(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *odst = dst;
|
||||
const char *osrc = src;
|
||||
size_t n = dsize;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end. */
|
||||
while (n-- != 0 && *dst != '\0') {
|
||||
dst++;
|
||||
}
|
||||
dlen = dst - odst;
|
||||
n = dsize - dlen;
|
||||
|
||||
if (n-- == 0) {
|
||||
return dlen + strlen(src);
|
||||
}
|
||||
while (*src != '\0') {
|
||||
if (n != 0) {
|
||||
*dst++ = *src;
|
||||
n--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
return dlen + src - osrc; /* count does not include NUL */
|
||||
}
|
||||
|
||||
/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
|
||||
# define DLFCN_LDINFO_SIZE 86976
|
||||
typedef struct Dl_info {
|
||||
const char *dli_fname;
|
||||
} Dl_info;
|
||||
/*
|
||||
* This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
|
||||
* address of a function, which is just located in the DATA segment instead of
|
||||
* the TEXT segment.
|
||||
*/
|
||||
static int dladdr(void *ptr, Dl_info *dl)
|
||||
{
|
||||
uintptr_t addr = (uintptr_t)ptr;
|
||||
struct ld_info *ldinfos;
|
||||
struct ld_info *next_ldi;
|
||||
struct ld_info *this_ldi;
|
||||
|
||||
if ((ldinfos = malloc(DLFCN_LDINFO_SIZE)) == NULL) {
|
||||
dl->dli_fname = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
|
||||
/*-
|
||||
* Error handling is done through errno and dlerror() reading errno:
|
||||
* ENOMEM (ldinfos buffer is too small),
|
||||
* EINVAL (invalid flags),
|
||||
* EFAULT (invalid ldinfos ptr)
|
||||
*/
|
||||
free((void *)ldinfos);
|
||||
dl->dli_fname = NULL;
|
||||
return 0;
|
||||
}
|
||||
next_ldi = ldinfos;
|
||||
|
||||
do {
|
||||
this_ldi = next_ldi;
|
||||
if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
|
||||
&& (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
|
||||
this_ldi->ldinfo_textsize)))
|
||||
|| ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
|
||||
&& (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
|
||||
this_ldi->ldinfo_datasize)))) {
|
||||
char *buffer = NULL;
|
||||
char *member = NULL;
|
||||
size_t buffer_sz;
|
||||
size_t member_len;
|
||||
|
||||
buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
|
||||
member = this_ldi->ldinfo_filename + buffer_sz;
|
||||
if ((member_len = strlen(member)) > 0) {
|
||||
buffer_sz += 1 + member_len + 1;
|
||||
}
|
||||
if ((buffer = malloc(buffer_sz)) != NULL) {
|
||||
strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
|
||||
if (member_len > 0) {
|
||||
/*
|
||||
* Need to respect a possible member name and not just
|
||||
* returning the path name in this case. See docs:
|
||||
* sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
|
||||
*/
|
||||
strlcat(buffer, "(", buffer_sz);
|
||||
strlcat(buffer, member, buffer_sz);
|
||||
strlcat(buffer, ")", buffer_sz);
|
||||
}
|
||||
dl->dli_fname = buffer;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
|
||||
this_ldi->ldinfo_next);
|
||||
}
|
||||
} while (this_ldi->ldinfo_next);
|
||||
free((void *)ldinfos);
|
||||
return dl->dli_fname != NULL;
|
||||
}
|
||||
#endif /* _AIX */
|
||||
|
||||
jint netty_jni_util_JNI_OnLoad(JavaVM* vm, void* reserved, const char* libname, jint (*load_function)(JNIEnv*, const char*)) {
|
||||
JNIEnv* env = NULL;
|
||||
if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_UTIL_JNI_VERSION) != JNI_OK) {
|
||||
fprintf(stderr, "FATAL: JNI version mismatch");
|
||||
fflush(stderr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
#ifndef NETTY_JNI_UTIL_BUILD_STATIC
|
||||
jint status = 0;
|
||||
const char* name = NULL;
|
||||
#ifndef _WIN32
|
||||
Dl_info dlinfo;
|
||||
// We need to use an address of a function that is uniquely part of this library, so choose a static
|
||||
// function. See https://github.com/netty/netty/issues/4840.
|
||||
if (!dladdr((void*) parsePackagePrefix, &dlinfo)) {
|
||||
fprintf(stderr, "FATAL: %s JNI call to dladdr failed!\n", libname);
|
||||
fflush(stderr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
name = dlinfo.dli_fname;
|
||||
#else
|
||||
HMODULE module = NULL;
|
||||
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (void*) parsePackagePrefix, &module) == 0){
|
||||
fprintf(stderr, "FATAL: %s JNI call to GetModuleHandleExA failed!\n", libname);
|
||||
fflush(stderr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// add space for \0 termination as this is not automatically included for windows XP
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx
|
||||
char dllPath[MAX_DLL_PATH_LEN + 1];
|
||||
int dllPathLen = GetModuleFileNameA(module, dllPath, MAX_DLL_PATH_LEN);
|
||||
if (dllPathLen == 0) {
|
||||
fprintf(stderr, "FATAL: %s JNI call to GetModuleFileNameA failed!\n", libname);
|
||||
fflush(stderr);
|
||||
return JNI_ERR;
|
||||
} else {
|
||||
// ensure we null terminate as this is not automatically done on windows xp
|
||||
dllPath[dllPathLen] = '\0';
|
||||
}
|
||||
|
||||
name = dllPath;
|
||||
#endif
|
||||
char* packagePrefix = parsePackagePrefix(name, libname, &status);
|
||||
|
||||
if (status == JNI_ERR) {
|
||||
fprintf(stderr, "FATAL: %s encountered unexpected library path: %s\n", name, libname);
|
||||
fflush(stderr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
#else
|
||||
char* packagePrefix = NULL;
|
||||
#endif /* NETTY_JNI_UTIL_BUILD_STATIC */
|
||||
|
||||
jint ret = load_function(env, packagePrefix);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void netty_jni_util_JNI_OnUnload(JavaVM* vm, void* reserved, void (*unload_function)(JNIEnv*)) {
|
||||
JNIEnv* env = NULL;
|
||||
if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_UTIL_JNI_VERSION) != JNI_OK) {
|
||||
fprintf(stderr, "FATAL: JNI version mismatch");
|
||||
fflush(stderr);
|
||||
// Something is wrong but nothing we can do about this :(
|
||||
return;
|
||||
}
|
||||
unload_function(env);
|
||||
}
|
87
netty-channel-unix-native/src/main/c/netty_unix.c
Normal file
87
netty-channel-unix-native/src/main/c/netty_unix.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
#include "netty_unix_jni.h"
|
||||
#include "netty_unix.h"
|
||||
#include "netty_unix_buffer.h"
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "netty_unix_limits.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "netty_unix_util.h"
|
||||
|
||||
// IMPORTANT: If you add any NETTY_JNI_UTIL_LOAD_CLASS or NETTY_JNI_UTIL_FIND_CLASS calls you also need to update
|
||||
// Unix to reflect that.
|
||||
jint netty_unix_register(JNIEnv* env, const char* packagePrefix) {
|
||||
int limitsOnLoadCalled = 0;
|
||||
int errorsOnLoadCalled = 0;
|
||||
int filedescriptorOnLoadCalled = 0;
|
||||
int socketOnLoadCalled = 0;
|
||||
int bufferOnLoadCalled = 0;
|
||||
|
||||
// Load all c modules that we depend upon
|
||||
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto error;
|
||||
}
|
||||
limitsOnLoadCalled = 1;
|
||||
|
||||
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto error;
|
||||
}
|
||||
errorsOnLoadCalled = 1;
|
||||
|
||||
if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto error;
|
||||
}
|
||||
filedescriptorOnLoadCalled = 1;
|
||||
|
||||
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto error;
|
||||
}
|
||||
socketOnLoadCalled = 1;
|
||||
|
||||
if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
|
||||
goto error;
|
||||
}
|
||||
bufferOnLoadCalled = 1;
|
||||
|
||||
return NETTY_JNI_UTIL_JNI_VERSION;
|
||||
error:
|
||||
if (limitsOnLoadCalled == 1) {
|
||||
netty_unix_limits_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
if (errorsOnLoadCalled == 1) {
|
||||
netty_unix_errors_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
if (filedescriptorOnLoadCalled == 1) {
|
||||
netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
if (socketOnLoadCalled == 1) {
|
||||
netty_unix_socket_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
if (bufferOnLoadCalled == 1) {
|
||||
netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
void netty_unix_unregister(JNIEnv* env, const char* packagePrefix) {
|
||||
netty_unix_limits_JNI_OnUnLoad(env, packagePrefix);
|
||||
netty_unix_errors_JNI_OnUnLoad(env, packagePrefix);
|
||||
netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix);
|
||||
netty_unix_socket_JNI_OnUnLoad(env, packagePrefix);
|
||||
netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue