update to netty 4.1.105
This commit is contained in:
parent
07c6dd718a
commit
dde0ad8a11
140 changed files with 9811 additions and 88 deletions
|
@ -4,6 +4,7 @@ plugins {
|
||||||
id 'signing'
|
id 'signing'
|
||||||
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
|
id "io.github.gradle-nexus.publish-plugin" version "2.0.0-rc-1"
|
||||||
id "com.google.osdetector" version "1.7.3"
|
id "com.google.osdetector" version "1.7.3"
|
||||||
|
id 'org.xbib.gradle.plugin.c' version '3.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
|
@ -11,8 +12,6 @@ wrapper {
|
||||||
distributionType = Wrapper.DistributionType.BIN
|
distributionType = Wrapper.DistributionType.BIN
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.osdetector'
|
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
user = 'joerg'
|
user = 'joerg'
|
||||||
name = 'netty'
|
name = 'netty'
|
||||||
|
@ -30,11 +29,17 @@ ext {
|
||||||
organizationUrl = 'https://xbib.org'
|
organizationUrl = 'https://xbib.org'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.google.osdetector'
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
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/repositories/maven.gradle')
|
||||||
apply from: rootProject.file('gradle/compile/java.gradle')
|
apply from: rootProject.file('gradle/compile/java.gradle')
|
||||||
apply from: rootProject.file('gradle/test/junit5.gradle')
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
apply from: rootProject.file('gradle/publish/maven.gradle')
|
apply from: rootProject.file('gradle/publish/maven.gradle')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
apply from: rootProject.file('gradle/publish/sonatype.gradle')
|
apply from: rootProject.file('gradle/publish/sonatype.gradle')
|
||||||
apply from: rootProject.file('gradle/publish/forgejo.gradle')
|
apply from: rootProject.file('gradle/publish/forgejo.gradle')
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
group = org.xbib.netty
|
group = org.xbib.netty
|
||||||
name = 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) {
|
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')
|
archiveClassifier.set('linux-x86_64')
|
||||||
version rootProject.version
|
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'
|
include 'META-INF/native/libnetty_transport_native_epoll_x86_64.so'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +16,13 @@ configurations {
|
||||||
'linux-x86_64' {
|
'linux-x86_64' {
|
||||||
canBeConsumed = true
|
canBeConsumed = true
|
||||||
canBeResolved = false
|
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 {
|
dependencies {
|
||||||
api project(':netty-channel')
|
api project(':netty-channel')
|
||||||
api project(':netty-channel-unix')
|
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