diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 63cb347c1..60a9806fb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,5 +1,5 @@ --- -name: Bug report +name: Bug report / request for enhancement about: Create a report to help us improve junixsocket title: '' labels: '' @@ -8,7 +8,7 @@ assignees: '' --- **Describe the bug** -A clear and concise description of what the bug is. +A clear and concise description of what the bug is or how you'd like to see junixsocket be improved. **To Reproduce** Steps to reproduce the behavior: @@ -22,15 +22,15 @@ A clear and concise description of what you expected to happen. **Output/Screenshots** If applicable, add console output/screenshots to help explain your problem. -**Environment (please complete the following information):** - - OS: [e.g., Linux] - - Distribution [e.g., Alpine] - - Version [e.g., 3.13.0] -Please also add the output of `java -jar junixsocket-selftest-x.y.z-SNAPSHOT-jar-with-dependencies.jar` (x.y.z being the latest version, e.g., 2.3.3). — The selftest jar is available from the junixsocket-dist package in the Release section. +**Please make sure to test the problem still occurs on the latest version of junixsocket** +Please add the output of the selftest from the latest version available, for example: -**Notes** -Add any other context about the problem here. Please link/attach any source code that is useful to diagnose the issue. +`java -jar junixsocket-selftest-2.7.0-jar-with-dependencies.jar` + +The selftest is available from the [Release section](https://github.com/kohlschutter/junixsocket/releases) on GitHub. -**Lastly, please make sure to test the problem still occurs on the latest version of junixsocket** All minor version updates (e.g., 2.4.x -> 2.5.x) are supposed to be backwards compatible. If you find that this isn't the case, please mention it in your report. Thank you! + +**Notes** +Add any other context about the problem here. Please link/attach any source code that is useful to diagnose the issue. diff --git a/.github/workflows/codeql-analysis-c.yml b/.github/workflows/codeql-analysis-c.yml deleted file mode 100644 index a5e642ac9..000000000 --- a/.github/workflows/codeql-analysis-c.yml +++ /dev/null @@ -1,60 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL C" - -on: - push: - branches: [ "main", "dev", "wip" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "main", "dev", "wip" ] - schedule: - - cron: '39 16 * * 3' - -jobs: - analyze: - name: Analyze C - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: true - matrix: - language: [ 'cpp' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - run: | - echo "Build junixsocket-native with Maven (no tests)" - mvn clean install -Pcodeql -pl junixsocket-native -Dignorant - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/codeql-analysis-java.yml b/.github/workflows/codeql-analysis.yml similarity index 87% rename from .github/workflows/codeql-analysis-java.yml rename to .github/workflows/codeql-analysis.yml index 94cee34e2..e2e5b8f60 100644 --- a/.github/workflows/codeql-analysis-java.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,7 +9,7 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL Java" +name: "CodeQL" on: push: @@ -22,7 +22,7 @@ on: jobs: analyze: - name: Analyze Java + name: Analyze runs-on: ubuntu-latest permissions: actions: read @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: true matrix: - language: [ 'java' ] + language: [ 'cpp', 'java' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support @@ -40,6 +40,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + cache: 'maven' + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -54,7 +61,7 @@ jobs: - run: | echo "Build junixsocket with Maven (no tests)" - mvn clean install -Pcodeql -Pcodeql-skip-c + mvn clean install -Pcodeql -Pstrict -Puse-snapshots - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 930253cae..988056e0f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ interpolated-pom.xml .checkstyle hs_err_pid*.log .idea +/*.dll +/*.so +/jux.bat +infer-out +javacore.*.txt diff --git a/NOTICE b/NOTICE index b9b917486..cfae8c6e7 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ junixsocket -Copyright 2009-2022 Christian Kohlschütter +Copyright 2009-2023 Christian Kohlschütter Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 9c6307400..b6498310c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,23 @@ +![junixsocket logo](https://user-images.githubusercontent.com/822690/246675372-d1775152-5f5e-4576-8f3d-8445779ea584.png) + +[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/kohlschutter/junixsocket/codeql-analysis.yml?cacheSeconds=60)](https://github.com/kohlschutter/junixsocket/actions/workflows/codeql-analysis.yml) [![Last commit on main](https://img.shields.io/github/last-commit/kohlschutter/junixsocket/main)](https://github.com/kohlschutter/junixsocket/commits/main) [![Maven Central version](https://img.shields.io/maven-central/v/com.kohlschutter.junixsocket/junixsocket)](https://search.maven.org/artifact/com.kohlschutter.junixsocket/junixsocket) [![Apache 2.0 Licensed](https://img.shields.io/github/license/kohlschutter/junixsocket)](https://github.com/kohlschutter/junixsocket/blob/main/NOTICE) + +**Users of junixsocket are strongly advised to upgrade to version 2.7.0 or newer ([changelog](https://kohlschutter.github.io/junixsocket/changelog.html))** + # junixsocket junixsocket is a Java/JNI library that allows the use of [Unix Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket) (AF_UNIX sockets), and -other address/protocol families (such as [AF_TIPC](http://tipc.io/) and AF_VSOCK), from Java. +other address/protocol families (such as [AF_TIPC](http://tipc.io/), AF_VSOCK, and AF_SYSTEM), +from Java. ## Unix sockets API, in Java, AF. * *junixsocket* is the most complete implementation of AF_UNIX sockets for the Java ecosystem. -* Supports other socket types, such as TIPC (on Linux) and VSOCK (on Linux, and certain macOS VMs), as well! +* Supports other socket types, such as TIPC (on Linux), VSOCK (on Linux, and certain macOS VMs), and + AF_SYSTEM (on macOS) as well! * Comes with pre-built native libraries for most operating systems and platforms, including - macOS, Linux, Windows, Solaris, FreeBSD, NetBSD, OpenBSD, DragonFlyBSD, AIX, IBM i. + macOS, Linux, Android, Windows, Solaris, FreeBSD, NetBSD, OpenBSD, DragonFlyBSD, AIX, IBM i. * Additionally, you can build and run junixsocket natively on IBM z/OS (experimental). * Supports all Java versions since Java 8* * Supports both the Java Socket API and NIO (`java.net.Socket`, `java.net.SocketChannel`, etc.) @@ -28,7 +36,7 @@ other address/protocol families (such as [AF_TIPC](http://tipc.io/) and AF_VSOCK * Provides a selftest package with 100+ tests to ensure compatibility with any target platform. * Apache 2.0 licensed. -`*` (Tested up to Java 19; support for Java 7 was dropped in version 2.5.0). +`*` (Tested up to Java 22; support for Java 7 was dropped in version 2.5.0). ## Quick links @@ -39,10 +47,13 @@ other address/protocol families (such as [AF_TIPC](http://tipc.io/) and AF_VSOCK - Sockets (`org.newsclub.net.unix.demo`) - RMI over Unix Sockets (`org.newsclub.net.unix.demo.rmi` and `org.newsclub.net.unix.demo.rmi.services`) - MySQL over Unix Sockets (`org.newsclub.net.mysql.demo`) + - Apache Mina (`org.newsclub.net.unix.demo.mina`) + - Netty (`org.newsclub.net.unix.demo.netty`) * [API Javadocs](https://kohlschutter.github.io/junixsocket/apidocs/) * [Unix Domain Socket Reference](https://kohlschutter.github.io/junixsocket/unixsockets.html) * [TIPC documentation](https://kohlschutter.github.io/junixsocket/junixsocket-tipc/index.html) * [VSOCK documentation](https://kohlschutter.github.io/junixsocket/junixsocket-vsock/index.html) + * [AF_SYSTEM documentation](https://kohlschutter.github.io/junixsocket/junixsocket-darwin/index.html) ## Licensing @@ -66,23 +77,28 @@ java -jar junixsocket-selftest-VERSION-jar-with-dependencies.jar To include the core junixsocket functionality in your project, add the following Maven dependency -> **NOTE** Since version 2.4.0, `junixsocket-core` is POM-only (that's why you need to specify `pom`) +> **NOTE** Since version 2.4.0, `junixsocket-core` is POM-only (that's why you need to specify +`pom`) ``` com.kohlschutter.junixsocket junixsocket-core - 2.6.1 + 2.7.1 pom ``` -While you should definitely pin your dependency to a specific version, you are very much encouraged to always update to the most recent version. Check back frequently. +While you should definitely pin your dependency to a specific version, you are very much encouraged +to keep updating to the most recent version. Check back frequently. -For more, optional packages (RMI, MySQL, Jetty, TIPC, VSOCK, server, GraalVM, etc.) and Gradle instructions see -[here](https://kohlschutter.github.io/junixsocket/dependency.html) +For more, optional packages (RMI, MySQL, Jetty, TIPC, VSOCK, server, Darwin, GraalVM, etc.) and +Gradle instructions see [here](https://kohlschutter.github.io/junixsocket/dependency.html) -If you're testing a `-SNAPSHOT` version, make sure that the Sonatype snapshot repository is enabled in your POM: +## Snapshot builds for testing + +When you're testing a `-SNAPSHOT` version, make sure that the Sonatype snapshot repository is +enabled in your POM: ``` @@ -98,4 +114,15 @@ If you're testing a `-SNAPSHOT` version, make sure that the Sonatype snapshot re ``` -> **NOTE** Never rely on -SNAPSHOT builds. They can break any time. +To update to the latest SNAPSHOT (which is currently not being built for every commit), +run the following command from within your own project: + +``` +mvn -U dependency:resolve +``` + +or (for Gradle) + +``` +./gradlew refreshVersions +``` diff --git a/junixsocket-codecoverage/jacoco-extra/.gitignore b/junixsocket-codecoverage/jacoco-extra/.gitignore new file mode 100644 index 000000000..f6a7da6fc --- /dev/null +++ b/junixsocket-codecoverage/jacoco-extra/.gitignore @@ -0,0 +1,2 @@ +lib/ +*.zip diff --git a/junixsocket-codecoverage/jacoco-extra/README b/junixsocket-codecoverage/jacoco-extra/README new file mode 100644 index 000000000..cb1e87cbb --- /dev/null +++ b/junixsocket-codecoverage/jacoco-extra/README @@ -0,0 +1,25 @@ +Place additional jacoco *.exec files here to improve code coverage reporting. + +Make sure to delete all outdated *.exec files first. + +Collect code coverage results by running junixsocket-selftest on all supported +target platforms and configurations. Make sure to test in environments with +certain features enabled/disabled and configured differently (e.g., TIPC +enabled but not configured). + +Run the following command to collect these *.exec files on a target system: + +./run-selftest-coverage.sh +(this may download the JaCoCo agent jar if it's not already there) + +The test files will be stored under junixsocket-codecoverage/target. + +Depending on your setup (shared directory or not), you may now need to +aggregate these *.exec files. + +Then, on the machine aggregating the results, run + +./rerun-aggregate-coverage.sh + +which will set up the aggregate report under +junixsocket-codecoverage/target/site/jacoco-aggregate/index.html diff --git a/junixsocket-codecoverage/jacoco-extra/rerun-aggregate-coverage.sh b/junixsocket-codecoverage/jacoco-extra/rerun-aggregate-coverage.sh new file mode 100755 index 000000000..e57d43ae8 --- /dev/null +++ b/junixsocket-codecoverage/jacoco-extra/rerun-aggregate-coverage.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd "$(dirname $0)"/../.. +mvn verify -Pstrict -Prelease -rf :junixsocket-common -P \!code-quality,\!documentation -DskipTests diff --git a/junixsocket-codecoverage/jacoco-extra/run-selftest-coverage.sh b/junixsocket-codecoverage/jacoco-extra/run-selftest-coverage.sh new file mode 100755 index 000000000..7bbac9c79 --- /dev/null +++ b/junixsocket-codecoverage/jacoco-extra/run-selftest-coverage.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +cd $(dirname "$0") + +[[ -n "$JACOCO_VERSION" ]] || JACOCO_VERSION=0.8.10 + +if [[ ! -e "lib/jacocoagent.jar" ]]; then + wget "https://search.maven.org/remotecontent?filepath=org/jacoco/jacoco/${JACOCO_VERSION}/jacoco-${JACOCO_VERSION}.zip" -O jacoco.zip + + unzip jacoco.zip lib/jacocoagent.jar +fi + +mkdir -p ../target + +java \ +-javaagent:./lib/jacocoagent.jar=destfile=../target/$(uuidgen).exec,excludes='org/apache/maven/**:org/junit/**:org/apiguardian/**:sun/**' \ +-jar ../../junixsocket-selftest/target/junixsocket-selftest-*-jar-with-dependencies.jar diff --git a/junixsocket-codecoverage/pom.xml b/junixsocket-codecoverage/pom.xml index 20ba3ea87..552b803b3 100644 --- a/junixsocket-codecoverage/pom.xml +++ b/junixsocket-codecoverage/pom.xml @@ -1,12 +1,14 @@ - + 4.0.0 junixsocket-codecoverage pom com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-codecoverage diff --git a/junixsocket-common/pom.xml b/junixsocket-common/pom.xml index 7d4bd1784..4021406e8 100644 --- a/junixsocket-common/pom.xml +++ b/junixsocket-common/pom.xml @@ -6,7 +6,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-common @@ -25,6 +25,7 @@ -h ${kohlschutter.project.base.directory}/junixsocket-native/src/main/c + ${xlint.compiler.arg} @@ -55,6 +56,28 @@ + + + + with-native-custom + + + !junixsocket.native-custom.skip + + + + + com.kohlschutter.junixsocket + junixsocket-native-custom + ${project.version} + test + default + true + + + + + com.kohlschutter.junixsocket @@ -62,13 +85,5 @@ ${project.version} test - - com.kohlschutter.junixsocket - junixsocket-native-custom - ${project.version} - test - default - true - diff --git a/junixsocket-common/src/main/java-unresolved-properties/org/newsclub/net/unix/BuildProperties.java b/junixsocket-common/src/main/java-unresolved-properties/org/newsclub/net/unix/BuildProperties.java new file mode 100644 index 000000000..cae2b86e5 --- /dev/null +++ b/junixsocket-common/src/main/java-unresolved-properties/org/newsclub/net/unix/BuildProperties.java @@ -0,0 +1,55 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Properties that are populated upon build time. + * + * Also see {@code src/main/unresolved-java/.../BuildProperties.java} + * + * @author Christian Kohlschütter + */ +final class BuildProperties { + private static final Map MAP; + + static { + Map map = new LinkedHashMap<>(); + + map.put("project.version", "${project.version}"); + map.put("git.build.version", "${git.build.version}"); // junixsocket version + map.put("git.commit.id.abbrev", "${git.commit.id.abbrev}"); + map.put("git.commit.id.describe", "${git.commit.id.abbrev}"); + map.put("git.commit.id.full", "${git.commit.id.full}"); + map.put("git.commit.time", "${git.commit.time}"); + map.put("git.dirty", "${git.dirty}"); + + MAP = Collections.unmodifiableMap(map); + } + + private BuildProperties() { + throw new IllegalStateException("No instances"); + } + + static Map getBuildProperties() { + return MAP; + } +} diff --git a/junixsocket-common/src/main/java/module-info.java b/junixsocket-common/src/main/java/module-info.java index 849cfa966..b4101a0c4 100644 --- a/junixsocket-common/src/main/java/module-info.java +++ b/junixsocket-common/src/main/java/module-info.java @@ -1,11 +1,12 @@ /** * The common junixsocket classes. */ +// NOPMD -- https://github.com/pmd/pmd/issues/4620 module org.newsclub.net.unix { exports org.newsclub.net.unix; requires java.base; requires static java.rmi; - requires static com.kohlschutter.annotations.compiletime; + requires static transitive com.kohlschutter.annotations.compiletime; requires static org.eclipse.jdt.annotation; } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamily.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamily.java index 61649530b..5bd0bae68 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamily.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamily.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ /** * Describes an address family supported by junixsocket. - * + * * @param The corresponding {@link AFSocketAddress} subclass. * @author Christian Kohlschütter */ @@ -179,7 +179,7 @@ String getJuxInetAddressSuffix() { /** * Registers an address family. - * + * * @param The supported address type. * @param juxString The sockaddr_* identifier as registered in native code. * @param addressClass The supported address subclass. @@ -196,7 +196,7 @@ public static synchronized AFAddressFamily regist } if (af.addressClassname != null && !addressClass.getName().equals(af.addressClassname)) { throw new IllegalStateException("Unexpected classname for address family " + juxString + ": " - + addressClass.getName()); + + addressClass.getName() + "; expected: " + af.addressClassname); } if (af.addressConstructor != null || af.addressClass != null) { throw new IllegalStateException("Already registered: " + juxString); @@ -226,7 +226,7 @@ public static synchronized AFAddressFamily regist /** * Registers an implementation. - * + * * @param The supported address type. * @param juxString The sockaddr_* identifier as registered in native code. * @param addressFamily The supported address family as registered via @@ -267,6 +267,8 @@ AFSocketImplExtensions initImplExtensions(AncillaryDataSupport ancillaryDataS return (AFSocketImplExtensions) new AFTIPCSocketImplExtensions(ancillaryDataSupport); case NativeUnixSocket.DOMAIN_VSOCK: return (AFSocketImplExtensions) new AFVSOCKSocketImplExtensions(ancillaryDataSupport); + case NativeUnixSocket.DOMAIN_SYSTEM: + return (AFSocketImplExtensions) new AFSYSTEMSocketImplExtensions(ancillaryDataSupport); default: throw new UnsupportedOperationException(); } @@ -274,7 +276,7 @@ AFSocketImplExtensions initImplExtensions(AncillaryDataSupport ancillaryDataS /** * Creates a new, unconnected, unbound socket compatible with this socket address. - * + * * @return The socket instance. * @throws IOException on error. */ @@ -288,7 +290,7 @@ public AFSocket newSocket() throws IOException { /** * Creates a new, unconnected, unbound server socket compatible with this socket address. - * + * * @return The server socket instance. * @throws IOException on error. */ @@ -302,7 +304,7 @@ public AFServerSocket newServerSocket() throws IOException { /** * Creates a new, unconnected, unbound {@link SocketChannel} compatible with this socket address. - * + * * @return The socket instance. * @throws IOException on error. */ @@ -313,7 +315,7 @@ public AFSocketChannel newSocketChannel() throws IOException { /** * Creates a new, unconnected, unbound {@link ServerSocketChannel} compatible with this socket * address. - * + * * @return The socket instance. * @throws IOException on error. */ @@ -330,10 +332,10 @@ AFSocketAddress parseURI(URI u, int overridePort) throws SocketException { /** * Returns the set of supported URI schemes that can be parsed to some {@link AFSocketAddress}. - * + * * The set is dependent on which {@link AFSocketAddress} implementations are registered with * junixsocket. - * + * * @return The set of supported URI schemes. */ public static synchronized Set uriSchemes() { @@ -344,7 +346,7 @@ public static synchronized Set uriSchemes() { /** * Returns the {@link SelectorProvider} associated with this address family, or {@code null} if no * such instance is registered. - * + * * @return The {@link SelectorProvider}. * @throws IllegalStateException on error. */ @@ -357,7 +359,7 @@ public synchronized SelectorProvider getSelectorProvider() { } try { selectorProvider = (SelectorProvider) Class.forName(selectorProviderClassname).getMethod( - "provider", new Class[0]).invoke(null); + "provider", new Class[0]).invoke(null); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException | RuntimeException e) { throw new IllegalStateException("Cannot instantiate selector provider for " diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamilyConfig.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamilyConfig.java index 88f4a5290..89bf97128 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamilyConfig.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFAddressFamilyConfig.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * The implementation-specifics for a given address family implementation. - * + * * @param The supported address type. * @author Christian Kohlschütter * @see AFSocketAddressConfig @@ -41,63 +41,63 @@ protected AFAddressFamilyConfig() { /** * Returns the implementation's {@link Socket} class. - * + * * @return The implementation's {@link Socket} class. */ protected abstract Class> socketClass(); /** * Returns the implementation's {@link Socket} constructor. - * + * * @return The implementation's {@link Socket} constructor. */ protected abstract AFSocket.Constructor socketConstructor(); /** * Returns the implementation's {@link ServerSocket} class. - * + * * @return The implementation's {@link ServerSocket} class. */ protected abstract Class> serverSocketClass(); /** * Returns the implementation's {@link ServerSocket} constructor. - * + * * @return The implementation's {@link ServerSocket} constructor. */ protected abstract AFServerSocket.Constructor serverSocketConstructor(); /** * Returns the implementation's {@link SocketChannel} class. - * + * * @return The implementation's {@link SocketChannel} class.. */ protected abstract Class> socketChannelClass(); /** * Returns the implementation's {@link ServerSocketChannel} class. - * + * * @return The implementation's {@link ServerSocketChannel} class. */ protected abstract Class> serverSocketChannelClass(); /** * Returns the implementation's {@link DatagramSocket} class. - * + * * @return The implementation's {@link DatagramSocket} class. */ protected abstract Class> datagramSocketClass(); /** * Returns the implementation's {@link DatagramSocket} constructor. - * + * * @return The implementation's {@link DatagramSocket} constructor. */ protected abstract AFDatagramSocket.Constructor datagramSocketConstructor(); /** * Returns the implementation's {@link DatagramChannel} class. - * + * * @return The implementation's {@link DatagramChannel} class. */ protected abstract Class> datagramChannelClass(); diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFCore.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFCore.java index 0b67705ad..22c474551 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFCore.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFCore.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * The core functionality of file descriptor based I/O. - * + * * @author Christian Kohlschütter */ class AFCore extends CleanableState { @@ -79,8 +79,9 @@ boolean isClosed() { } void doClose() throws IOException { - NativeUnixSocket.close(fd); - closed.set(true); + if (closed.compareAndSet(false, true)) { + NativeUnixSocket.close(fd); + } } FileDescriptor validFdOrException() throws SocketException { @@ -116,33 +117,50 @@ int read(ByteBuffer dst, ByteBuffer socketAddressBuffer, int options) throws IOE } FileDescriptor fdesc = validFdOrException(); + int dstPos = dst.position(); + ByteBuffer buf; - if (dst.isDirect()) { + int pos; + + boolean direct = dst.isDirect(); + if (direct) { buf = dst; + pos = dstPos; } else { buf = getThreadLocalDirectByteBuffer(remaining); remaining = Math.min(remaining, buf.remaining()); + pos = buf.position(); } if (!blocking) { options |= NativeUnixSocket.OPT_NON_BLOCKING; } - int pos = dst.position(); - int count = NativeUnixSocket.receive(fdesc, buf, pos, remaining, socketAddressBuffer, options, ancillaryDataSupport, 0); if (count == -1) { return count; } - if (buf != dst) { // NOPMD - buf.limit(count); - dst.put(buf); - } else { + + if (direct) { if (count < 0) { throw new IllegalStateException(); } dst.position(pos + count); + } else { + int oldLimit = buf.limit(); + if (count < oldLimit) { + buf.limit(count); + } + try { + while (buf.hasRemaining()) { + dst.put(buf); + } + } finally { + if (count < oldLimit) { + buf.limit(oldLimit); + } + } } return count; } @@ -176,32 +194,29 @@ int write(ByteBuffer src, SocketAddress target, int options) throws IOException } int pos = src.position(); - boolean isDirect = src.isDirect(); ByteBuffer buf; + int bufPos; if (isDirect) { buf = src; + bufPos = pos; } else { buf = getThreadLocalDirectByteBuffer(remaining); remaining = Math.min(remaining, buf.remaining()); - // Java 16: buf.put(buf.position(), src, src.position(), Math.min(buf.limit(), src.limit())); - int limit = src.limit(); - if (limit > buf.limit()) { - src.limit(buf.limit()); - buf.put(src); - src.limit(limit); - } else { + bufPos = buf.position(); + + while (src.hasRemaining() && buf.hasRemaining()) { buf.put(src); } - buf.position(0); + buf.position(bufPos); } if (datagramMode) { options |= NativeUnixSocket.OPT_DGRAM_MODE; } - int written = NativeUnixSocket.send(fdesc, buf, pos, remaining, addressTo, addressToLen, + int written = NativeUnixSocket.send(fdesc, buf, bufPos, remaining, addressTo, addressToLen, options, ancillaryDataSupport); src.position(pos + written); @@ -210,11 +225,11 @@ int write(ByteBuffer src, SocketAddress target, int options) throws IOException /** * Returns a per-thread reusable byte buffer for a given capacity. - * + * * If a thread-local buffer currently uses a smaller capacity, the buffer is replaced by a larger * one. If the capacity exceeds a configurable maximum, a new direct buffer is allocated but not * cached (i.e., the previously cached one is kept but not immediately returned to the caller). - * + * * @param capacity The desired capacity. * @return A byte buffer satisfying the requested capacity. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramChannel.java index 665580039..087c2ced9 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,8 +37,9 @@ /** * A {@link DatagramChannel} implementation that works with junixsocket. - * + * * @author Christian Kohlschütter + * @param The supported address type. */ public abstract class AFDatagramChannel extends DatagramChannel implements AFSomeSocket, AFSocketExtensions { @@ -46,7 +47,7 @@ public abstract class AFDatagramChannel extends Datag /** * Creates a new {@link AFDatagramChannel} instance. - * + * * @param selectorProvider The corresponding {@link SelectorProvider}. * @param socket The corresponding {@link Socket}. */ @@ -57,7 +58,7 @@ protected AFDatagramChannel(AFSelectorProvider selectorProvider, AFDatagramSo /** * Returns the corresponding {@link Socket}. - * + * * @return The socket. */ protected final AFDatagramSocket getAFSocket() { @@ -91,7 +92,7 @@ public final AFDatagramSocket socket() { /** * Returns the binding state of the socket. - * + * * @return true if the socket successfully bound to an address */ public final boolean isBound() { @@ -209,7 +210,7 @@ public final AFDatagramChannel setOption(SocketOption name, T value) if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - afSocket.getAFImpl().setOption(optionId.intValue(), value); + afSocket.getAFImpl().setOption(optionId, value); } return this; } @@ -224,7 +225,7 @@ public final T getOption(SocketOption name) throws IOException { if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - return (T) afSocket.getAFImpl().getOption(optionId.intValue()); + return (T) afSocket.getAFImpl().getOption(optionId); } } @@ -244,10 +245,10 @@ public final FileDescriptor getFileDescriptor() throws IOException { /** * Checks if this {@link DatagramSocket}'s bound filename should be removed upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}. */ public final boolean isDeleteOnClose() { @@ -256,10 +257,10 @@ public final boolean isDeleteOnClose() { /** * Enables/disables deleting this {@link DatagramSocket}'s bound filename upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @param b Enabled if {@code true}. */ public final void setDeleteOnClose(boolean b) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocket.java index d35194d23..c349d4232 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,23 +36,25 @@ /** * A {@link DatagramSocket} implementation that works with junixsocket. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @author Christian Kohlschütter */ -public abstract class AFDatagramSocket extends DatagramSocket implements - AFSomeSocket, AFSocketExtensions { +public abstract class AFDatagramSocket extends DatagramSocketShim + implements AFSomeSocket, AFSocketExtensions { private static final InetSocketAddress WILDCARD_ADDRESS = new InetSocketAddress(0); private final AFDatagramSocketImpl impl; private final AncillaryDataSupport ancillaryDataSupport; private final AtomicBoolean created = new AtomicBoolean(false); private final AtomicBoolean deleteOnClose = new AtomicBoolean(true); + + @SuppressWarnings("this-escape") private final AFDatagramChannel channel = newChannel(); /** * Creates a new {@link AFDatagramSocket} instance. - * + * * @param impl The corresponding {@link SocketImpl} class. * @throws IOException on error. */ @@ -64,30 +66,30 @@ protected AFDatagramSocket(final AFDatagramSocketImpl impl) throws IOExceptio /** * Creates a new {@link DatagramChannel} that is associated with this socket. - * + * * @return The channel. */ protected abstract AFDatagramChannel newChannel(); /** - * Returns the {@link AncillaryDataSupport} instance. - * + * Returns the {@code AncillaryDataSupport} instance. + * * @return The instance. */ - protected final AncillaryDataSupport getAncillaryDataSupport() { + final AncillaryDataSupport getAncillaryDataSupport() { return ancillaryDataSupport; } /** * A reference to the constructor of an {@link AFDatagramSocket} subclass. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. */ @FunctionalInterface public interface Constructor { /** * Constructs a new {@link DatagramSocket} instance. - * + * * @param fd The file descriptor. * @return The new instance. * @throws IOException on error. @@ -97,7 +99,7 @@ public interface Constructor { /** * Returns the {@link AFSocketAddress} type supported by this socket. - * + * * @return The supported {@link AFSocketAddress}. */ protected final Class socketAddressClass() { @@ -119,7 +121,7 @@ protected static final AFDatagramSocket newInstan /** * Creates a new {@link AFDatagramSocket}. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @param constructor The supplying constructor. * @param fdObj The file descriptor. @@ -169,12 +171,12 @@ public final void connect(InetAddress address, int port) { /** * Reads the next received packet without actually removing it from the queue. - * + * * In other words, once a packet is received, calling this method multiple times in a row will not * have further effects on the packet contents. - * + * * This call still blocks until at least one packet has been received and added to the queue. - * + * * @param p The packet. * @throws IOException on error. */ @@ -328,10 +330,10 @@ public final synchronized void bind(SocketAddress addr) throws SocketException { /** * Checks if this {@link AFDatagramSocket}'s bound filename should be removed upon * {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}. */ public final boolean isDeleteOnClose() { @@ -340,10 +342,10 @@ public final boolean isDeleteOnClose() { /** * Enables/disables deleting this {@link AFDatagramSocket}'s bound filename upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @param b Enabled if {@code true}. */ public final void setDeleteOnClose(boolean b) { @@ -407,7 +409,7 @@ public final void receive(DatagramPacket p) throws IOException { /** * Returns the address family supported by this implementation. - * + * * @return The family. */ protected final AFAddressFamily addressFamily() { @@ -416,7 +418,7 @@ protected final AFAddressFamily addressFamily() { /** * Returns the internal helper instance for address-specific extensions. - * + * * @return The helper instance. * @throws UnsupportedOperationException if such extensions are not supported for this address * type. @@ -424,4 +426,32 @@ protected final AFAddressFamily addressFamily() { protected AFSocketImplExtensions getImplExtensions() { return getAFImpl(false).getImplExtensions(); } + + /** + * Returns the value of a junixsocket socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @return The value of the socket option. + * @throws IOException on error. + */ + @Override + public T getOption(AFSocketOption name) throws IOException { + return getAFImpl().getCore().getOption(name); + } + + /** + * Sets the value of a socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @param value The value of the socket option. + * @return this DatagramSocket. + * @throws IOException on error. + */ + @Override + public DatagramSocket setOption(AFSocketOption name, T value) throws IOException { + getAFImpl().getCore().setOption(name, value); + return this; + } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocketImpl.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocketImpl.java index b1320085f..8c87473a5 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocketImpl.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFDatagramSocketImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ /** * A {@link DatagramSocketImpl} implemented by junixsocket. - * + * * @param The associated address type. * @author Christian Kohlschütter */ @@ -56,12 +56,13 @@ public abstract class AFDatagramSocketImpl extends /** * Constructs a new {@link AFDatagramSocketImpl} using the given {@link FileDescriptor} (or null * to create a new one). - * + * * @param addressFamily The address family. * @param fd The file descriptor, or {@code null}. * @param socketType The socket type. * @throws IOException on error. */ + @SuppressWarnings("this-escape") protected AFDatagramSocketImpl(AFAddressFamily<@NonNull A> addressFamily, FileDescriptor fd, AFSocketType socketType) throws IOException { super(); @@ -114,7 +115,7 @@ protected final void disconnect() { connected.set(false); this.remotePort = 0; } catch (IOException e) { - e.printStackTrace(); + StackTraceUtil.printStackTrace(e); } } @@ -207,6 +208,7 @@ protected final void send(DatagramPacket p) throws IOException { sendToBuf = AFSocketAddress.SOCKETADDRESS_BUFFER_TL.get(); sendToBufLen = NativeUnixSocket.bytesToSockAddr(getAddressFamily().getDomain(), sendToBuf, addrBytes); + sendToBuf.position(0); if (sendToBufLen == -1) { throw new SocketException("Unsupported domain"); } @@ -219,6 +221,8 @@ protected final void send(DatagramPacket p) throws IOException { ByteBuffer datagramPacketBuffer = core.getThreadLocalDirectByteBuffer(len); datagramPacketBuffer.clear(); datagramPacketBuffer.put(p.getData(), p.getOffset(), p.getLength()); + datagramPacketBuffer.flip(); + NativeUnixSocket.send(fdesc, datagramPacketBuffer, 0, len, sendToBuf, sendToBufLen, /* NativeUnixSocket.OPT_NON_BLOCKING | */ NativeUnixSocket.OPT_DGRAM_MODE, ancillaryDataSupport); @@ -236,11 +240,13 @@ protected final int peekData(DatagramPacket p) throws IOException { } @Override + @Deprecated protected final byte getTTL() throws IOException { return (byte) (getTimeToLive() & 0xFF); } @Override + @Deprecated protected final void setTTL(byte ttl) throws IOException { // ignored } @@ -375,7 +381,7 @@ final void updatePorts(int local, int remote) { /** * Returns the address family supported by this implementation. - * + * * @return The family. */ protected final AFAddressFamily<@NonNull A> getAddressFamily() { @@ -384,7 +390,7 @@ final void updatePorts(int local, int remote) { /** * Returns the internal helper instance for address-specific extensions. - * + * * @return The helper instance. * @throws UnsupportedOperationException if such extensions are not supported for this address * type. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java index 311e428d6..7bf8c375b 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInetAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,6 @@ */ package org.newsclub.net.unix; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; import java.io.UnsupportedEncodingException; import java.net.DatagramPacket; import java.net.Inet4Address; @@ -36,56 +33,21 @@ /** * A workaround to create an {@link InetAddress} for an {@link AFSocketAddress}. - * + * * {@link DatagramPacket} internally requires InetAddress compatibility. Even if it pretends to * accept {@link SocketAddress}es, it refuses anything other than {@link InetSocketAddress} * and then even stores host and port separately. - * + * * This implementation deserializes a specially crafted {@link InetAddress} with a hostname that * encodes the raw bytes of an {@link AFSocketAddress}. We do this because the deserialization code * path does not attempt DNS resolution (which would fail one way or another). - * + * * The hostnames we use end with ".junixsocket", to distinguish them from regular hostnames. - * + * * @author Christian Kohlschütter */ class AFInetAddress { - private static final byte[] SERIALIZED_INET_ADDRESS_START = { - (byte) 0xac, (byte) 0xed, // STREAM_MAGIC - 0x00, 0x05, // STREAM_VERSION - 0x73, // TC_OBJECT - 0x72, // TC_CLASSDESC, - 0x00, 0x14, // length - 'j', 'a', 'v', 'a', '.', 'n', 'e', 't', '.', // - 'I', 'n', 'e', 't', 'A', 'd', 'd', 'r', 'e', 's', 's', // "java.net.InetAddress" - - 0x2d, (byte) 0x9b, 0x57, (byte) 0xaf, (byte) 0x9f, (byte) 0xe3, (byte) 0xeb, (byte) 0xdb, // - // serialVersionUID for java.net.InetAddress - - 0x03, // classDescFlags SC_WRITE_METHOD | SC_SERIALIZABLE - 0x00, 0x03, // fieldCount (3) - 'I', // int field - 0x00, 0x07, // length (7) - 'a', 'd', 'd', 'r', 'e', 's', 's', // "address" - 'I', // int field - 0x00, 0x06, // length (6) - 'f', 'a', 'm', 'i', 'l', 'y', // "family" - 'L', // Object field - 0x00, 0x08, // length (8) - 'h', 'o', 's', 't', 'N', 'a', 'm', 'e', // "hostName" - 0x74, // (className1) TC_STRING - 0x00, 0x12, // length (18) - 'L', 'j', 'a', 'v', 'a', '/', 'l', 'a', 'n', 'g', '/', // - 'O', 'b', 'j', 'e', 'c', 't', ';', // "Ljava/lang/Object;" - 0x78, // TC_ENDBLOCKDATA, - 0x70, // (superClassDesc) TC_NULL - 0x7f, 0x00, 0x00, (byte) 0xaf, // "address" value: (int) 127.0.0.175 (0x7f0000af) - 0x00, 0x00, 0x00, 0x01, // "family" value: (int) 1 (IPv4) - 0x74, // "hostName" value is a TC_STRING - 0x00, // high-byte of string length (always 0 in our case) - // low-byte of string length and string itself will be appended later - // followed by 0x78 (TC_ENDBLOCKDATA) - }; + private static final byte[] LOCAL_AF = {0x7f, 0, 0, (byte) 0xaf}; private static final char PREFIX = '['; private static final String MARKER_HEX_ENCODING = "%%"; @@ -94,12 +56,12 @@ class AFInetAddress { /** * Encodes a junixsocket socketAddress into a string that is (somewhat) guaranteed to not be * resolved by java.net code. - * + * * Implementation detail: The "[" prefix (with the corresponding "]" suffix missing from the * input) should cause an early {@link UnknownHostException} be thrown, which is caught within * {@link InetSocketAddress#InetSocketAddress(String, int)}, causing the hostname be marked as * "unresolved" (without an address set). - * + * * @param socketAddress The socket address. * @return A string, to be used when calling * {@link InetSocketAddress#InetSocketAddress(String, int)}, etc. @@ -140,8 +102,8 @@ static final String createUnresolvedHostname(byte[] socketAddress, AFAddressFami * Creates an InetAddress that is considered "resolved" internally (using a static loopback * address), without actually having to resolve the address via DNS, thus still carrying the * "hostname" field containing a hostname as returned by - * {@link #createUnresolvedHostname(byte[])}. - * + * {@link #createUnresolvedHostname(byte[],AFAddressFamily)}. + * * @param socketAddress The socket address. * @return The {@link InetAddress}. */ @@ -151,21 +113,15 @@ static final InetAddress wrapAddress(byte[] socketAddress, AFAddressFamily af return null; } - byte[] bytes = createUnresolvedHostname(socketAddress, af).getBytes(StandardCharsets.UTF_8); + String hostname = createUnresolvedHostname(socketAddress, af); + byte[] bytes = hostname.getBytes(StandardCharsets.UTF_8); if (bytes.length > 255) { throw new IllegalStateException("junixsocket address is too long to wrap as InetAddress"); } - byte[] serializedData = new byte[SERIALIZED_INET_ADDRESS_START.length + 1 + bytes.length + 1]; - System.arraycopy(SERIALIZED_INET_ADDRESS_START, 0, serializedData, 0, - SERIALIZED_INET_ADDRESS_START.length); - serializedData[SERIALIZED_INET_ADDRESS_START.length] = (byte) bytes.length; - System.arraycopy(bytes, 0, serializedData, SERIALIZED_INET_ADDRESS_START.length + 1, - bytes.length); - serializedData[serializedData.length - 1] = 0x78; // TC_ENDBLOCKDATA - - try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedData))) { - return (InetAddress) ois.readObject(); - } catch (ClassNotFoundException | IOException e) { + + try { + return InetAddress.getByAddress(hostname, LOCAL_AF); + } catch (UnknownHostException e) { throw new IllegalStateException(e); } } @@ -182,7 +138,7 @@ static final byte[] unwrapAddress(InetAddress addr, AFAddressFamily af) try { return unwrapAddress(hostname, af); } catch (IllegalArgumentException e) { - throw (SocketException)new SocketException("Unsupported address").initCause(e); + throw (SocketException) new SocketException("Unsupported address").initCause(e); } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInputStream.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInputStream.java index dc2423e4d..56dd45d0a 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInputStream.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFInputStream.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * An {@link InputStream} for {@link AFSocket}, etc. - * + * * @author Christian Kohlschütter */ public abstract class AFInputStream extends InputStream implements FileDescriptorAccess { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFOutputStream.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFOutputStream.java index 531cbbb1f..9764aeae1 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFOutputStream.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFOutputStream.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * An {@link OutputStream} for {@link AFSocket}, etc. - * + * * @author Christian Kohlschütter */ public abstract class AFOutputStream extends OutputStream implements FileDescriptorAccess { @@ -37,11 +37,11 @@ public abstract class AFOutputStream extends OutputStream implements FileDescrip * Reads all bytes from the given input stream and writes the bytes to this output stream in the * order that they are read. On return, this input stream will be at end of stream. This method * does not close either stream. - * + * * This method effectively is the reverse notation of * {@link InputStream#transferTo(OutputStream)}, which may or may not be optimized for * {@link AFSocket}s. - * + * * @param in The {@link InputStream} to transfer from. * @return The number of bytes transferred. * @throws IOException on error. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFPipe.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFPipe.java index 15f733eee..9b37b8645 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFPipe.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFPipe.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * A {@link Pipe}, natively implemented. - * + * * @author Christian Kohlschütter */ public final class AFPipe extends Pipe implements Closeable { @@ -75,7 +75,7 @@ FileDescriptor sinkFD() { @Override public void close() throws IOException { - try { + try { // NOPMD.UseTryWithResources source().close(); } finally { sink().close(); @@ -172,7 +172,7 @@ public FileDescriptor getFileDescriptor() throws IOException { /** * Returns the options bitmask that is to be passed to native receive/send calls. - * + * * @return The options. */ int getOptions() { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketAddress.java new file mode 100644 index 000000000..9678cce2c --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketAddress.java @@ -0,0 +1,407 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * An {@link AFSocketAddress} for AF_SYSTEM sockets. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMSocketAddress extends AFSocketAddress { + private static final long serialVersionUID = 1L; // do not change! + + private static AFAddressFamily afSystem; + private static final String SELECTOR_PROVIDER_CLASS = + "org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider"; + + /** + * The AF_SYSTEM system address. + * + * @author Christian Kohlschütter + */ + @NonNullByDefault + public static final class SysAddr extends NamedInteger { + private static final long serialVersionUID = 1L; + + /** + * The sysaddr AF_SYS_CONTROL, also known as AF_SYS_KERN_CONTROL. + */ + public static final SysAddr AF_SYS_CONTROL; + + private static final @NonNull SysAddr[] VALUES = init(new @NonNull SysAddr[] { + AF_SYS_CONTROL = new SysAddr("AF_SYS_CONTROL", 2) // + }); + + private SysAddr(int id) { + super(id); + } + + private SysAddr(String name, int id) { + super(name, id); + } + + /** + * Returns a {@link SysAddr} for the given custom value. + * + * @param v The value. + * @return The {@link SysAddr} object. + */ + public static SysAddr ofValue(int v) { + return ofValue(VALUES, SysAddr::new, v); + } + } + + private AFSYSTEMSocketAddress(int port, final byte[] socketAddress, ByteBuffer nativeAddress) + throws SocketException { + super(port, socketAddress, nativeAddress, addressFamily()); + } + + private static AFSYSTEMSocketAddress newAFSocketAddress(int port, final byte[] socketAddress, + ByteBuffer nativeAddress) throws SocketException { + return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, addressFamily(), + AFSYSTEMSocketAddress::new); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e., + * referring to a particular socket instance instead of a service address). A Java-only "IP port + * number" is stored along the instance for compatibility reasons. + * + * @param javaPort The emulated "port" number (not part of AF_SYSTEM). + * @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL) + * @param id Controller unique identifier + * @param unit Developer private unit number, 0 means "unspecified". + * @return A corresponding {@link AFSYSTEMSocketAddress} instance. + * @throws SocketException if the operation fails. + */ + public static AFSYSTEMSocketAddress ofSysAddrIdUnit(int javaPort, SysAddr sysAddr, int id, + int unit) throws SocketException { + return resolveAddress(toBytes(sysAddr, id, unit), javaPort, addressFamily()); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} that refers to a given AF_SYSTEM socket address (i.e., + * referring to a particular socket instance instead of a service address). + * + * @param sysAddr 16-bit system address (e.g., AF_SYS_KERNCONTROL) + * @param id Controller unique identifier + * @param unit Developer private unit number + * @return A corresponding {@link AFSYSTEMSocketAddress} instance. + * @throws SocketException if the operation fails. + */ + public static AFSYSTEMSocketAddress ofSysAddrIdUnit(SysAddr sysAddr, int id, int unit) + throws SocketException { + return ofSysAddrIdUnit(0, sysAddr, id, unit); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} that encodes the + * byte sequence of an AF_SYSTEM socket address, like those returned by {@link #wrapAddress()}. + * + * @param address The "special" {@link InetAddress}. + * @param port The port (use 0 for "none"). + * @return The {@link AFSYSTEMSocketAddress} instance. + * @throws SocketException if the operation fails, for example when an unsupported address is + * specified. + */ + public static AFSYSTEMSocketAddress unwrap(InetAddress address, int port) throws SocketException { + return AFSocketAddress.unwrap(address, port, addressFamily()); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} given a special {@link InetAddress} hostname that + * encodes the byte sequence of an AF_SYSTEM socket address, like those returned by + * {@link #wrapAddress()}. + * + * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}. + * @param port The port (use 0 for "none"). + * @return The {@link AFSYSTEMSocketAddress} instance. + * @throws SocketException if the operation fails, for example when an unsupported address is + * specified. + */ + public static AFSYSTEMSocketAddress unwrap(String hostname, int port) throws SocketException { + return AFSocketAddress.unwrap(hostname, port, addressFamily()); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} given a generic {@link SocketAddress}. + * + * @param address The address to unwrap. + * @return The {@link AFSYSTEMSocketAddress} instance. + * @throws SocketException if the operation fails, for example when an unsupported address is + * specified. + */ + public static AFSYSTEMSocketAddress unwrap(SocketAddress address) throws SocketException { + Objects.requireNonNull(address); + if (!isSupportedAddress(address)) { + throw new SocketException("Unsupported address"); + } + return (AFSYSTEMSocketAddress) address; + } + + @Override + public String toString() { + int port = getPort(); + + byte[] bytes = getBytes(); + if (bytes.length != (8 * 4)) { + return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port) + ";UNKNOWN" + "]"; + } + + ByteBuffer bb = ByteBuffer.wrap(bytes); + SysAddr sysAddr = SysAddr.ofValue(bb.getInt()); + int id = bb.getInt(); + int unit = bb.getInt(); + + return getClass().getName() + "[" + (port == 0 ? "" : "port=" + port + ";") + sysAddr + ";id=" + + id + ";unit=" + unit + "]"; + } + + /** + * Returns the "SysAddr" part of the address. + * + * @return The SysAddr part. + */ + public @NonNull SysAddr getSysAddr() { + ByteBuffer bb = ByteBuffer.wrap(getBytes()); + return SysAddr.ofValue(bb.getInt(0)); + } + + /** + * Returns the "id" part of the address. + * + * @return The id part. + */ + public int getId() { + ByteBuffer bb = ByteBuffer.wrap(getBytes()); + return bb.getInt(4); + } + + /** + * Returns the "unit" part of the address. + * + * @return The unit part. + */ + public int getUnit() { + ByteBuffer bb = ByteBuffer.wrap(getBytes()); + return bb.getInt(8); + } + + @Override + public boolean hasFilename() { + return false; + } + + @Override + public File getFile() throws FileNotFoundException { + throw new FileNotFoundException("no file"); + } + + /** + * Checks if an {@link InetAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}. + * + * @param addr The instance to check. + * @return {@code true} if so. + * @see #wrapAddress() + * @see #unwrap(InetAddress, int) + */ + public static boolean isSupportedAddress(InetAddress addr) { + return AFSocketAddress.isSupportedAddress(addr, addressFamily()); + } + + /** + * Checks if a {@link SocketAddress} can be unwrapped to an {@link AFSYSTEMSocketAddress}. + * + * @param addr The instance to check. + * @return {@code true} if so. + * @see #unwrap(InetAddress, int) + */ + public static boolean isSupportedAddress(SocketAddress addr) { + return (addr instanceof AFSYSTEMSocketAddress); + } + + @SuppressWarnings("cast") + private static byte[] toBytes(SysAddr sysAddr, int id, int unit) { + ByteBuffer bb = ByteBuffer.allocate(8 * 4); + bb.putInt(sysAddr.value()); + bb.putInt(id); + bb.putInt(unit); + bb.putInt(0); + bb.putInt(0); + bb.putInt(0); + bb.putInt(0); + bb.putInt(0); + return (byte[]) bb.flip().array(); + } + + /** + * Returns the corresponding {@link AFAddressFamily}. + * + * @return The address family instance. + */ + @SuppressWarnings("null") + public static synchronized AFAddressFamily addressFamily() { + if (afSystem == null) { + afSystem = AFAddressFamily.registerAddressFamily("system", // + AFSYSTEMSocketAddress.class, new AFSocketAddressConfig() { + + private final AFSocketAddressConstructor addrConstr = + isUseDeserializationForInit() ? AFSYSTEMSocketAddress::newAFSocketAddress + : AFSYSTEMSocketAddress::new; + + @Override + protected AFSYSTEMSocketAddress parseURI(URI u, int port) throws SocketException { + return AFSYSTEMSocketAddress.of(u, port); + } + + @Override + protected AFSocketAddressConstructor addressConstructor() { + return addrConstr; + } + + @Override + protected String selectorProviderClassname() { + return SELECTOR_PROVIDER_CLASS; + } + + @Override + protected Set uriSchemes() { + return new HashSet<>(Arrays.asList("afsystem")); + } + }); + try { + Class.forName(SELECTOR_PROVIDER_CLASS); + } catch (ClassNotFoundException e) { + // ignore + } + } + return afSystem; + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible. + * + * @param uri The URI. + * @return The address. + * @throws SocketException if the operation fails. + */ + @SuppressWarnings("PMD.ShortMethodName") + public static AFSYSTEMSocketAddress of(URI uri) throws SocketException { + return of(uri, -1); + } + + /** + * Returns an {@link AFSYSTEMSocketAddress} for the given URI, if possible. + * + * @param uri The URI. + * @param overridePort The port to forcibly use, or {@code -1} for "don't override". + * @return The address. + * @throws SocketException if the operation fails. + */ + @SuppressWarnings({ + "PMD.CognitiveComplexity", "PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength", + "PMD.NcssCount", "PMD.NPathComplexity", "PMD.ShortMethodName"}) + public static AFSYSTEMSocketAddress of(URI uri, int overridePort) throws SocketException { + switch (uri.getScheme()) { + case "afsystem": + break; + default: + throw new SocketException("Unsupported URI scheme: " + uri.getScheme()); + } + + String host; + if ((host = uri.getHost()) == null || host.isEmpty()) { + String ssp = uri.getSchemeSpecificPart(); + if (ssp == null || !ssp.startsWith("//")) { + throw new SocketException("Unsupported URI: " + uri); + } + ssp = ssp.substring(2); + int i = ssp.indexOf('/'); + host = i == -1 ? ssp : ssp.substring(0, i); + if (host.isEmpty()) { + throw new SocketException("Unsupported URI: " + uri); + } + } + + ByteBuffer bb = ByteBuffer.allocate(8 * 4); + for (String p : host.split("\\.")) { + int v; + try { + v = Integer.parseUnsignedInt(p); + } catch (NumberFormatException e) { + throw (SocketException) new SocketException("Unsupported URI: " + uri).initCause(e); + } + bb.putInt(v); + } + bb.flip(); + if (bb.remaining() > 8 * 4) { + throw new SocketException("Unsupported URI: " + uri); + } + /* SysAddr sa = */ SysAddr.ofValue(bb.getInt()); + /* int id = */ bb.getInt(); + /* int unit = */ bb.getInt(); + + while (bb.remaining() > 0) { + if (bb.getInt() != 0) { + throw new SocketException("Unsupported URI: " + uri); + } + } + + return + + resolveAddress(bb.array(), uri.getPort(), addressFamily()); + } + + @Override + @SuppressWarnings({"PMD.CognitiveComplexity", "PMD.CompareObjectsWithEquals"}) + public URI toURI(String scheme, URI template) throws IOException { + switch (scheme) { + case "afsystem": + break; + default: + return super.toURI(scheme, template); + } + + ByteBuffer bb = ByteBuffer.wrap(getBytes()); + StringBuilder sb = new StringBuilder(); + while (bb.remaining() > 0) { + sb.append(Integer.toUnsignedString(bb.getInt())); + if (bb.remaining() > 0) { + sb.append('.'); + } + } + + return new HostAndPort(sb.toString(), getPort()).toURI(scheme, template); + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketImplExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketImplExtensions.java new file mode 100644 index 000000000..95f605c52 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSYSTEMSocketImplExtensions.java @@ -0,0 +1,48 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.Objects; + +/** + * AF_SYSTEM-specific code that resides in the native library. To be used by {@code AFSystemSocket} + * and {@code AFSystemDatagramSocket} only. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMSocketImplExtensions implements + AFSocketImplExtensions { + + @SuppressWarnings("PMD.UnusedFormalParameter") + AFSYSTEMSocketImplExtensions(AncillaryDataSupport ancillaryDataSupport) { + } + + /** + * Retrieves the kernel control ID given a kernel control name. + * + * @param fd The socket file descriptor. + * @param name The control name + * @return The control Id. + * @throws IOException on error. + */ + public int getKernelControlId(FileDescriptor fd, String name) throws IOException { + return NativeUnixSocket.systemResolveCtlId(fd, Objects.requireNonNull(name)); + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectionKey.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectionKey.java index a70352a4f..e5db4dc2d 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectionKey.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectionKey.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,10 +66,18 @@ public boolean isValid() { return !hasOpInvalid() && !cancelled.get() && chann.isOpen() && sel.isOpen(); } + boolean isCancelled() { + return cancelled.get(); + } + boolean hasOpInvalid() { return (opsReady & OP_INVALID) != 0; } + boolean isSelected() { + return readyOps() != 0; + } + @Override public void cancel() { sel.remove(this); diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelector.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelector.java index fa4c56040..1507a648f 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelector.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelector.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,12 +28,11 @@ import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.AbstractSelector; -import java.util.AbstractSet; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; final class AFSelector extends AbstractSelector { private final AFPipe selectorPipe; @@ -42,12 +41,16 @@ final class AFSelector extends AbstractSelector { private final ByteBuffer pipeMsgWakeUp = ByteBuffer.allocate(1); private final ByteBuffer pipeMsgReceiveBuffer = ByteBuffer.allocateDirect(256); - private final Set keysRegistered = new HashSet<>(); + private final Set keysRegistered = ConcurrentHashMap.newKeySet(); + private final Set keysRegisteredPublic = Collections.unmodifiableSet( + keysRegistered); + + private final Set selectedKeysSet = new HashSet<>(); + private final Set selectedKeysPublic = new UngrowableSet<>(selectedKeysSet); private PollFd pollFd = null; - private final Set keysSelected = new HashSet<>(); - protected AFSelector(AFSelectorProvider provider) throws IOException { + AFSelector(AFSelectorProvider provider) throws IOException { super(provider); this.selectorPipe = AFUNIXSelectorProvider.getInstance().openSelectablePipe(); @@ -66,16 +69,12 @@ protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object at @Override public Set keys() { - synchronized (this) { - return Collections.unmodifiableSet(new HashSet<>(keysRegistered)); - } + return keysRegisteredPublic; } @Override public Set selectedKeys() { - synchronized (keysSelected) { - return new SelectionKeySet(keysSelected); - } + return selectedKeysPublic; } @Override @@ -111,9 +110,7 @@ private int select0(int timeout) throws IOException { throw new ClosedSelectorException(); } pfd = pollFd = initPollFd(pollFd); - synchronized (keysSelected) { - keysSelected.clear(); - } + selectedKeysSet.clear(); } int num; try { @@ -123,6 +120,7 @@ private int select0(int timeout) throws IOException { end(); } synchronized (this) { + selectedKeysSet.clear(); pfd = pollFd; if (pfd != null) { AFSelectionKey[] keys = pfd.keys; @@ -139,11 +137,9 @@ private int select0(int timeout) throws IOException { } if (num > 0) { consumeAllBytesAfterPoll(); - setOpsReady(); - } - synchronized (keysSelected) { - return keysSelected.size(); + setOpsReady(pfd); // updates keysSelected and numKeysSelected } + return selectedKeysSet.size(); } } @@ -181,32 +177,30 @@ private synchronized void consumeAllBytesAfterPoll() throws IOException { } } - private synchronized void setOpsReady() { - if (pollFd == null) { - return; - } - for (int i = 1; i < pollFd.rops.length; i++) { - int rops = pollFd.rops[i]; - if (rops == 0) { - continue; - } - AFSelectionKey key = pollFd.keys[i]; - key.setOpsReady(rops); - synchronized (keysSelected) { - keysSelected.add(key); + private synchronized void setOpsReady(PollFd pfd) { + if (pfd != null) { + for (int i = 1; i < pfd.rops.length; i++) { + int rops = pfd.rops[i]; + AFSelectionKey key = pfd.keys[i]; + key.setOpsReady(rops); + if (rops != 0 && key.isValid()) { + selectedKeysSet.add(key); + } } } } - @SuppressWarnings("resource") + @SuppressWarnings({"resource", "PMD.CognitiveComplexity"}) private PollFd initPollFd(PollFd existingPollFd) throws IOException { synchronized (this) { for (Iterator it = keysRegistered.iterator(); it.hasNext();) { AFSelectionKey key = it.next(); - if (!key.getAFCore().fd.valid()) { + if (!key.getAFCore().fd.valid() || key.hasOpInvalid()) { key.cancelNoRemove(); it.remove(); existingPollFd = null; + } else { + key.setOpsReady(0); } } @@ -230,7 +224,14 @@ private PollFd initPollFd(PollFd existingPollFd) throws IOException { } } - int size = keysRegistered.size() + 1; + int keysToPoll = keysRegistered.size(); + for (AFSelectionKey key : keysRegistered) { + if (!key.isValid()) { + keysToPoll--; + } + } + + int size = keysToPoll + 1; FileDescriptor[] fds = new FileDescriptor[size]; int[] ops = new int[size]; @@ -240,6 +241,9 @@ private PollFd initPollFd(PollFd existingPollFd) throws IOException { int i = 1; for (AFSelectionKey key : keysRegistered) { + if (!key.isValid()) { + continue; + } keys[i] = key; fds[i] = key.getAFCore().fd; ops[i] = key.interestOps(); @@ -279,20 +283,17 @@ public Selector wakeup() { } } } - } catch (IOException e) { + } catch (IOException e) { // NOPMD.ExceptionAsFlowControl // FIXME throw as runtimeexception? - e.printStackTrace(); + StackTraceUtil.printStackTrace(e); } } return this; } synchronized void remove(AFSelectionKey key) { + selectedKeysSet.remove(key); deregister(key); - keysRegistered.remove(key); - synchronized (keysSelected) { - keysSelected.remove(key); - } pollFd = null; } @@ -340,95 +341,4 @@ static final class PollFd { this.rops = new int[ops.length]; } } - - private static final class SelectionKeySet extends AbstractSet { - private final Set keys; - - SelectionKeySet(Set keys) { - super(); - this.keys = keys; - } - - @Override - public boolean add(SelectionKey e) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(Object o) { - synchronized (keys) { - return keys.remove(o); - } - } - - @Override - public Iterator iterator() { - final Set copy; - synchronized (keys) { - copy = new HashSet<>(keys); - } - Iterator it = copy.iterator(); - return new Iterator() { - SelectionKey current = null; - - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public SelectionKey next() { - current = it.next(); - return current; - } - - @Override - public void remove() { - if (current != null) { - SelectionKeySet.this.remove(current); - } - } - }; - } - - @Override - public int size() { - synchronized (keys) { - return keys.size(); - } - } - - @Override - public boolean isEmpty() { - synchronized (keys) { - return keys.isEmpty(); - } - } - - @Override - public boolean contains(Object o) { - synchronized (keys) { - return keys.contains(o); - } - } - - @Override - public boolean containsAll(Collection c) { - synchronized (keys) { - return super.containsAll(c); - } - } - - @Override - public void clear() { - synchronized (keys) { - keys.clear(); - } - } - } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectorProvider.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectorProvider.java index 5fb4a4c16..7b5ffa8c8 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectorProvider.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSelectorProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.ProtocolFamily; import java.net.SocketAddress; +import java.nio.channels.DatagramChannel; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.SelectorProvider; @@ -27,7 +28,7 @@ /** * Service-provider class for junixsocket selectors and selectable channels. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. */ public abstract class AFSelectorProvider extends SelectorProvider { @@ -41,7 +42,7 @@ protected AFSelectorProvider() { /** * Constructs a new pipe. - * + * * @param selectable {@code true} if the pipe should be selectable. * @return The pipe. * @throws IOException on error. @@ -52,7 +53,7 @@ private AFPipe newPipe(boolean selectable) throws IOException { /** * Constructs a new socket pair from two sockets. - * + * * @param The type of the pair. * @param s1 Some socket, the first one. * @param s2 Some socket, the second one. @@ -62,7 +63,7 @@ private AFPipe newPipe(boolean selectable) throws IOException { /** * Constructs a new socket. - * + * * @return The socket instance. * @throws IOException on error. */ @@ -70,21 +71,21 @@ private AFPipe newPipe(boolean selectable) throws IOException { /** * Returns the protocol family supported by this implementation. - * + * * @return The protocol family. */ protected abstract ProtocolFamily protocolFamily(); /** * Returns the address family supported by this implementation. - * + * * @return The address family. */ protected abstract AFAddressFamily<@NonNull A> addressFamily(); /** * Returns the domain ID for the supported protocol, as specified by {@link NativeUnixSocket}. - * + * * @return The domain ID. */ protected final int domainId() { @@ -93,7 +94,7 @@ protected final int domainId() { /** * Opens a socket pair of interconnected channels. - * + * * @return The new channel pair. * @throws IOException on error. */ @@ -113,18 +114,30 @@ public AFSocketPair> openSocketChannelPair() throws /** * Opens a socket pair of interconnected datagram channels. - * + * * @return The new channel pair. * @throws IOException on error. */ - @SuppressWarnings("resource") public AFSocketPair> openDatagramChannelPair() throws IOException { + return openDatagramChannelPair(AFSocketType.SOCK_DGRAM); + } + + /** + * Opens a socket pair of interconnected {@link DatagramChannel}s, using the given + * {@link AFSocketType}. + * + * @param type The socket type. + * @return The new channel pair. + * @throws IOException on error. + */ + @SuppressWarnings("resource") + public AFSocketPair> openDatagramChannelPair(AFSocketType type) + throws IOException { ProtocolFamily pf = protocolFamily(); AFDatagramChannel s1 = openDatagramChannel(pf); AFDatagramChannel s2 = openDatagramChannel(pf); - NativeUnixSocket.socketPair(domainId(), NativeUnixSocket.SOCK_STREAM, s1.getAFCore().fd, s2 - .getAFCore().fd); + NativeUnixSocket.socketPair(domainId(), type.getId(), s1.getAFCore().fd, s2.getAFCore().fd); s1.socket().internalDummyBind(); s2.socket().internalDummyBind(); @@ -137,6 +150,15 @@ public AFSocketPair> openDatagramChannelPair() th @Override public abstract AFDatagramChannel openDatagramChannel() throws IOException; + /** + * Opens a {@link DatagramChannel} using the given socket type. + * + * @param type The socket type. + * @return the new channel + * @throws IOException on error. + */ + public abstract AFDatagramChannel openDatagramChannel(AFSocketType type) throws IOException; + @Override public AFDatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { if (!protocolFamily().name().equals(family.name())) { @@ -152,7 +174,7 @@ public final AFPipe openPipe() throws IOException { /** * Opens a pipe with support for selectors. - * + * * @return The new pipe * @throws IOException on error. */ @@ -185,7 +207,7 @@ public AFSocketChannel openSocketChannel() throws IOException { /** * Opens a socket channel connected to the given {@link SocketAddress}. - * + * * @param sa The socket address to connect to. * @return The new channel * @throws IOException on error. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocket.java index d99fb104a..6d0c14aac 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,15 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; +import java.net.InetAddress; import java.net.ServerSocket; import java.net.SocketAddress; import java.net.SocketException; +import java.net.SocketOption; +import java.net.SocketOptions; import java.nio.channels.IllegalBlockingModeException; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.jdt.annotation.NonNull; @@ -34,10 +39,11 @@ /** * The server part of a junixsocket socket. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @author Christian Kohlschütter */ +@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CouplingBetweenObjects"}) public abstract class AFServerSocket extends ServerSocket implements FileDescriptorAccess { private final AFSocketImpl implementation; @@ -45,18 +51,20 @@ public abstract class AFServerSocket extends ServerSo private final Closeables closeables = new Closeables(); private final AtomicBoolean created = new AtomicBoolean(false); private final AtomicBoolean deleteOnClose = new AtomicBoolean(true); + + @SuppressWarnings("this-escape") private final AFServerSocketChannel channel = newChannel(); private @Nullable SocketAddressFilter bindFilter; /** * The constructor of the concrete subclass. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. */ public interface Constructor { /** * Creates a new {@link AFServerSocket} instance. - * + * * @param fd The file descriptor. * @return The new instance. * @throws IOException on error. @@ -67,7 +75,7 @@ public interface Constructor { /** * Constructs a new, unconnected instance. - * + * * @throws IOException if the operation fails. */ protected AFServerSocket() throws IOException { @@ -76,28 +84,30 @@ protected AFServerSocket() throws IOException { /** * Constructs a new instance, optionally associated with the given file descriptor. - * + * * @param fdObj The file descriptor, or {@code null}. * @throws IOException if the operation fails. */ + @SuppressWarnings({"this-escape", "PMD.ConstructorCallsOverridableMethod"}) protected AFServerSocket(FileDescriptor fdObj) throws IOException { super(); + this.implementation = newImpl(fdObj); NativeUnixSocket.initServerImpl(this, implementation); - setReuseAddress(true); + getAFImpl().setOption(SocketOptions.SO_REUSEADDR, true); } /** * Creates a new AFServerSocketChannel for this socket. - * + * * @return The new instance. */ protected abstract AFServerSocketChannel newChannel(); /** * Creates a new AFSocketImpl. - * + * * @param fdObj The file descriptor. * @return The new instance. * @throws IOException on error. @@ -106,7 +116,7 @@ protected AFServerSocket(FileDescriptor fdObj) throws IOException { /** * Creates a new AFServerSocket instance, using the given subclass constructor. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @param instanceSupplier The subclass constructor. * @return The new instance. @@ -119,7 +129,7 @@ protected static AFServerSocket newInstance( /** * Creates a new AFServerSocket instance, using the given subclass constructor. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @param instanceSupplier The subclass constructor. * @param fdObj The file descriptor. @@ -163,7 +173,7 @@ protected static AFServerSocket newInstance( /** * Returns a new {@link ServerSocket} that is bound to the given {@link AFSocketAddress}. - * + * * @param instanceSupplier The constructor of the concrete subclass. * @param addr The socket file to bind to. * @param The concrete {@link AFSocketAddress} that is supported by this type. @@ -179,7 +189,7 @@ protected static AFServerSocket bindOn( /** * Returns a new {@link ServerSocket} that is bound to the given {@link AFSocketAddress}. - * + * * @param instanceSupplier The constructor of the concrete subclass. * @param addr The socket file to bind to. * @param deleteOnClose If {@code true}, the socket file (if the address points to a file) will be @@ -199,7 +209,7 @@ protected static AFServerSocket bindOn( /** * Returns a new, unbound {@link ServerSocket} that will always bind to the given * address, regardless of any socket address used in a call to bind. - * + * * @param instanceSupplier The constructor of the concrete subclass. * @param forceAddr The address to use. * @param The concrete {@link AFSocketAddress} that is supported by this type. @@ -216,7 +226,7 @@ protected static AFServerSocket forceBindOn( * Forces the address to be used for any subsequent call to {@link #bind(SocketAddress)} to be the * given one, regardless of what'll be passed to {@link #bind(SocketAddress, int)}, but doesn't * bind yet. - * + * * @param endpoint The forced endpoint address. * @return This {@link AFServerSocket}. */ @@ -264,7 +274,7 @@ public final void bind(SocketAddress endpoint, int backlog) throws IOException { } } setBoundEndpoint(getAFImpl().getLocalSocketAddress()); - if (boundEndpoint == null) { + if (boundEndpoint0() == null) { setBoundEndpoint(endpointCast); } @@ -277,7 +287,7 @@ public final void bind(SocketAddress endpoint, int backlog) throws IOException { @Override public final boolean isBound() { - return boundEndpoint != null && implementation.getFD().valid(); + return boundEndpoint0() != null && implementation.getFD().valid(); } @Override @@ -323,7 +333,7 @@ AFSocket accept1(boolean throwOnFail) throws IOException { /** * Returns a new {@link AFSocket} instance. - * + * * @return The new instance. * @throws IOException on error. */ @@ -331,7 +341,7 @@ AFSocket accept1(boolean throwOnFail) throws IOException { @Override public String toString() { - return getClass().getSimpleName() + "[" + (isBound() ? boundEndpoint : "unbound") + "]"; + return getClass().getSimpleName() + "[" + (isBound() ? boundEndpoint0() : "unbound") + "]"; } @Override @@ -381,7 +391,7 @@ && isDeleteOnClose()) { /** * Registers a {@link Closeable} that should be closed when this socket is closed. - * + * * @param closeable The closeable. */ public final void addCloseable(Closeable closeable) { @@ -390,7 +400,7 @@ public final void addCloseable(Closeable closeable) { /** * Unregisters a previously registered {@link Closeable}. - * + * * @param closeable The closeable. */ public final void removeCloseable(Closeable closeable) { @@ -399,7 +409,7 @@ public final void removeCloseable(Closeable closeable) { /** * Checks whether everything is setup to support junixsocket sockets. - * + * * @return {@code true} if supported. */ public static boolean isSupported() { @@ -409,18 +419,25 @@ public static boolean isSupported() { @Override @SuppressFBWarnings("EI_EXPOSE_REP") public final @Nullable A getLocalSocketAddress() { - if (boundEndpoint == null) { - setBoundEndpoint(getAFImpl().getLocalSocketAddress()); + @Nullable + A ep = boundEndpoint0(); + if (ep == null) { + ep = getAFImpl().getLocalSocketAddress(); + setBoundEndpoint(ep); } + return ep; + } + + private synchronized @Nullable A boundEndpoint0() { return boundEndpoint; } /** * Checks if the local socket address returned by {@link #getLocalSocketAddress()} is still valid. - * + * * The address is no longer valid if the server socket has been closed, {@code null}, or another * server socket has been bound on that address. - * + * * @return {@code true} iff still valid. */ public boolean isLocalSocketAddressValid() { @@ -435,7 +452,7 @@ public boolean isLocalSocketAddressValid() { return addr.equals(getAFImpl().getLocalSocketAddress()); } - final void setBoundEndpoint(@Nullable A addr) { + final synchronized void setBoundEndpoint(@Nullable A addr) { this.boundEndpoint = addr; int port; if (addr == null) { @@ -448,10 +465,10 @@ final void setBoundEndpoint(@Nullable A addr) { @Override public final int getLocalPort() { - if (boundEndpoint == null) { + if (boundEndpoint0() == null) { setBoundEndpoint(getAFImpl().getLocalSocketAddress()); } - if (boundEndpoint == null) { + if (boundEndpoint0() == null) { return -1; } else { return getAFImpl().getLocalPort1(); @@ -460,10 +477,10 @@ public final int getLocalPort() { /** * Checks if this {@link AFServerSocket}'s file should be removed upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}. */ public final boolean isDeleteOnClose() { @@ -473,10 +490,10 @@ public final boolean isDeleteOnClose() { /** * Enables/disables deleting this {@link AFServerSocket}'s file (or other resource type) upon * {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @param b Enabled if {@code true}. */ public final void setDeleteOnClose(boolean b) { @@ -486,6 +503,7 @@ public final void setDeleteOnClose(boolean b) { final AFSocketImpl getAFImpl() { if (created.compareAndSet(false, true)) { try { + getAFImpl().create(true); getSoTimeout(); // trigger create via java.net.Socket } catch (IOException e) { // ignore @@ -507,7 +525,7 @@ public final FileDescriptor getFileDescriptor() throws IOException { /** * Returns the address family supported by this implementation. - * + * * @return The family. */ protected final AFAddressFamily addressFamily() { @@ -517,9 +535,9 @@ protected final AFAddressFamily addressFamily() { /** * Sets the hook for any subsequent call to {@link #bind(SocketAddress)} and * {@link #bind(SocketAddress, int)} to be the given function. - * + * * The function can monitor calls or even alter the endpoint address. - * + * * @param hook The function that gets called for each {@code bind} call. * @return This instance. */ @@ -527,4 +545,117 @@ public final AFServerSocket bindHook(SocketAddressFilter hook) { this.bindFilter = hook; return this; } + + @Override + public void bind(SocketAddress endpoint) throws IOException { + bind(endpoint, 50); + } + + @Override + public InetAddress getInetAddress() { + if (!isBound()) { + return null; + } else { + return getAFImpl().getInetAddress(); + } + } + + @Override + public synchronized void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) { + throw new IllegalArgumentException("receive buffer size must be a positive number"); + } + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + getAFImpl().setOption(SocketOptions.SO_RCVBUF, size); + } + + @Override + public synchronized int getReceiveBufferSize() throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + int result = 0; + Object o = getAFImpl().getOption(SocketOptions.SO_RCVBUF); + if (o instanceof Number) { + result = ((Number) o).intValue(); + } + return result; + } + + @Override + @SuppressWarnings("UnsynchronizedOverridesSynchronized" /* errorprone */) + public void setSoTimeout(int timeout) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + if (timeout < 0) { + throw new IllegalArgumentException("timeout < 0"); + } + getAFImpl().setOption(SocketOptions.SO_TIMEOUT, timeout); + } + + @Override + @SuppressWarnings("UnsynchronizedOverridesSynchronized" /* errorprone */) + public int getSoTimeout() throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + Object o = getAFImpl().getOption(SocketOptions.SO_TIMEOUT); + /* extra type safety */ + if (o instanceof Number) { + return ((Number) o).intValue(); + } else { + return 0; + } + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + getAFImpl().setOption(SocketOptions.SO_REUSEADDR, on); + } + + @Override + public boolean getReuseAddress() throws SocketException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + return ((Boolean) (getAFImpl().getOption(SocketOptions.SO_REUSEADDR))); + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + } + + @SuppressWarnings({"all", "MissingOverride" /* errorprone */}) + public T getOption(SocketOption name) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + return getAFImpl().getOption(name); + } + + @SuppressWarnings({"all", "MissingOverride" /* errorprone */}) + public ServerSocket setOption(SocketOption name, T value) throws IOException { + Objects.requireNonNull(name); + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + getAFImpl().setOption(name, value); + return this; + } + + @SuppressWarnings("all") + public Set> supportedOptions() { + return getAFImpl().supportedOptions(); + } + + // NOTE: We shall re-implement all methods defined in ServerSocket that internally call getImpl() + // and call getAFImpl() here. This is not strictly necessary for environments where we can + // override "impl"; however it's the right thing to do. } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketChannel.java index 87dd4c5a9..65f00c2de 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ /** * A selectable channel for stream-oriented listening sockets. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @author Christian Kohlschütter */ @@ -43,11 +43,11 @@ public abstract class AFServerSocketChannel extends S /** * Creates a new {@link AFServerSocketChannel} instance. - * + * * @param socket The corresponding {@link ServerSocket}. * @param sp The corresponding {@link SelectorProvider}. */ - @SuppressWarnings("null") + @SuppressWarnings("all") // null protected AFServerSocketChannel(AFServerSocket socket, AFSelectorProvider sp) { super(sp); this.afSocket = Objects.requireNonNull(socket); @@ -65,7 +65,7 @@ public T getOption(SocketOption name) throws IOException { if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - return (T) afSocket.getAFImpl().getOption(optionId.intValue()); + return (T) afSocket.getAFImpl().getOption(optionId); } } @@ -79,7 +79,7 @@ public AFServerSocketChannel setOption(SocketOption name, T value) thr if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - afSocket.getAFImpl().setOption(optionId.intValue(), value); + afSocket.getAFImpl().setOption(optionId, value); } return this; } @@ -116,10 +116,10 @@ public final AFSocketAddress getLocalAddress() throws IOException { /** * Checks if the local socket address returned by {@link #getLocalAddress()} is still valid. - * + * * The address is no longer valid if the server socket has been closed, {@code null}, or another * server socket has been bound on that address. - * + * * @return {@code true} iff still valid. */ public final boolean isLocalSocketAddressValid() { @@ -147,10 +147,10 @@ public final FileDescriptor getFileDescriptor() throws IOException { /** * Checks if this {@link AFServerSocketChannel}'s file should be removed upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @return {@code true} if an attempt is made to delete the socket file upon {@link #close()}. */ public final boolean isDeleteOnClose() { @@ -160,10 +160,10 @@ public final boolean isDeleteOnClose() { /** * Enables/disables deleting this {@link AFServerSocketChannel}'s file (or other resource type) * upon {@link #close()}. - * + * * Deletion is not guaranteed, especially when not supported (e.g., addresses in the abstract * namespace). - * + * * @param b Enabled if {@code true}. */ public final void setDeleteOnClose(boolean b) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketConnector.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketConnector.java new file mode 100644 index 000000000..7f669cb7d --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFServerSocketConnector.java @@ -0,0 +1,41 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.IOException; + +/** + * Some connector that is able to create {@link AFServerSocket}s bound to a given + * {@link AFSocketAddress}. + * + * @param The address type to bind to. + * @param The address type for the returned server socket (which should either be identical to + * {@code A} or {@link AFSocketAddress} to indicate that this could be any socket). + * @author Christian Kohlschütter + * @see AFSocketConnector + */ +public interface AFServerSocketConnector { + /** + * Creates an {@link AFServerSocket} bound to the given address. + * + * @param addr The address to bind to. + * @return The bound socket. + * @throws IOException on error. + */ + AFServerSocket bind(A addr) throws IOException; +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocket.java index 6df312e17..4a4bc08bb 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,10 +33,11 @@ /** * junixsocket's base implementation of a {@link Socket}. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @author Christian Kohlschütter */ +@SuppressWarnings("PMD.CouplingBetweenObjects") public abstract class AFSocket extends Socket implements AFSomeSocket, AFSocketExtensions { static final String PROP_LIBRARY_DISABLE_CAPABILITY_PREFIX = @@ -47,20 +48,22 @@ public abstract class AFSocket extends Socket impleme @SuppressWarnings("PMD.MutableStaticState") static String loadedLibrary; // set by NativeLibraryLoader - private static final int CAPABILITIES = initCapabilities(); + private static Integer capabilitiesValue = null; private final AFSocketImpl impl; private final AFSocketAddressFromHostname afh; private final Closeables closeables = new Closeables(); private final AtomicBoolean created = new AtomicBoolean(false); + + @SuppressWarnings("this-escape") private final AFSocketChannel channel = newChannel(); private @Nullable SocketAddressFilter connectFilter; /** * Creates a new {@link AFSocket} instance. - * + * * @param impl The corresponding {@link SocketImpl} class. * @param afh The conversion helper to get a socket address from an encoded hostname. * @throws SocketException on error. @@ -74,7 +77,7 @@ protected AFSocket(final AFSocketImpl impl, AFSocketAddressFromHostname af /** * Returns the {@link AFSocketAddress} type supported by this socket. - * + * * @return The supported {@link AFSocketAddress}. */ protected final Class socketAddressClass() { @@ -83,21 +86,21 @@ protected final Class socketAddressClass() { /** * Creates a new {@link AFSocketChannel} for this socket. - * + * * @return The new instance. */ protected abstract AFSocketChannel newChannel(); /** * The reference to the constructor of an {@link AFSocket} subclass. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. */ @FunctionalInterface public interface Constructor { /** * Constructs a new {@link AFSocket} subclass instance. - * + * * @param fdObj The file descriptor. * @param factory The socket factory instance. * @return The instance. @@ -141,12 +144,12 @@ static AFSocket newInstance(Constructor constr /** * Creates a new, unbound {@link AFSocket}. - * + * * This "default" implementation is a bit "lenient" with respect to the specification. - * + * * In particular, we may ignore calls to {@link Socket#getTcpNoDelay()} and * {@link Socket#setTcpNoDelay(boolean)}. - * + * * @param The corresponding address type. * @param constr The implementation's {@link AFSocket} constructor * @param factory The corresponding socket factory, or {@code null}. @@ -166,7 +169,7 @@ protected static final AFSocket newInstance(Const /** * Creates a new {@link AFSocket} and connects it to the given {@link AFSocketAddress}. - * + * * @param The corresponding address type. * @param constr The implementation's {@link AFSocket} constructor * @param addr The address to connect to. @@ -183,7 +186,7 @@ protected static final AFSocket newInstance(Const /** * Creates a new {@link AFSocket} and connects it to the given {@link AFSocketAddress} using the * default implementation suited for that address type. - * + * * @param The corresponding address type. * @param addr The address to connect to. * @return A new, connected socket. @@ -198,7 +201,7 @@ public static final AFSocket connectTo(@NonNull A /** * Not supported, since it's not necessary for client sockets. - * + * * @see AFServerSocket */ @Override @@ -302,10 +305,10 @@ final String toStringSuffix() { /** * Returns true iff {@link AFSocket}s are supported by the current Java VM. - * + * * To support {@link AFSocket}s, a custom JNI library must be loaded that is supplied with * junixsocket. - * + * * @return {@code true} iff supported. */ public static boolean isSupported() { @@ -314,9 +317,9 @@ public static boolean isSupported() { /** * Checks if {@link AFSocket}s are supported by the current Java VM. - * + * * If not, an {@link UnsupportedOperationException} is thrown. - * + * * @throws UnsupportedOperationException if not supported. */ public static void ensureSupported() throws UnsupportedOperationException { @@ -325,13 +328,18 @@ public static void ensureSupported() throws UnsupportedOperationException { /** * Returns the version of the junixsocket library, as a string, for debugging purposes. - * + * * NOTE: Do not rely on the format of the version identifier, use socket capabilities instead. - * + * * @return String The version identifier, or {@code null} if it could not be determined. * @see #supports(AFSocketCapability) */ public static final String getVersion() { + String v = BuildProperties.getBuildProperties().get("git.build.version"); + if (v != null && !v.startsWith("$")) { + return v; + } + try { return NativeLibraryLoader.getJunixsocketVersion(); } catch (IOException e) { @@ -342,9 +350,9 @@ public static final String getVersion() { /** * Returns an identifier of the loaded native library, or {@code null} if the library hasn't been * loaded yet. - * + * * The identifier is useful mainly for debugging purposes. - * + * * @return The identifier of the loaded junixsocket-native library, or {@code null}. */ public static final String getLoadedLibrary() { @@ -371,6 +379,11 @@ public final void ensureAncillaryReceiveBufferSize(int minSize) { impl.ensureAncillaryReceiveBufferSize(minSize); } + private static boolean isCapDisabled(AFSocketCapability cap) { + return Boolean.parseBoolean(System.getProperty(PROP_LIBRARY_DISABLE_CAPABILITY_PREFIX + cap + .name(), "false")); + } + private static int initCapabilities() { if (!isSupported()) { return 0; @@ -392,41 +405,58 @@ private static int initCapabilities() { } } - private static boolean isCapDisabled(AFSocketCapability cap) { - return Boolean.valueOf(System.getProperty(PROP_LIBRARY_DISABLE_CAPABILITY_PREFIX + cap.name(), - "false")); + private static synchronized int capabilities() { + if (capabilitiesValue == null) { + capabilitiesValue = initCapabilities(); + } + return capabilitiesValue; } /** * Checks if the current environment (system platform, native library, etc.) supports a given * junixsocket capability. - * + * * Deprecated. Please use {@link #supports(AFSocketCapability)} instead. - * + * * NOTE: The result may or may not be cached from a previous call or from a check upon * initialization. - * + * * @param capability The capability. * @return true if supported. * @see #supports(AFSocketCapability) */ @Deprecated public static final boolean supports(AFUNIXSocketCapability capability) { - return (CAPABILITIES & capability.getBitmask()) != 0; + return (capabilities() & capability.getBitmask()) != 0; } /** * Checks if the current environment (system platform, native library, etc.) supports a given * junixsocket capability. - * + * * NOTE: The result may or may not be cached from a previous call or from a check upon * initialization. - * + * * @param capability The capability. * @return true if supported. */ public static final boolean supports(AFSocketCapability capability) { - return (CAPABILITIES & capability.getBitmask()) != 0; + return (capabilities() & capability.getBitmask()) != 0; + } + + /** + * Checks if the current environment (system platform, native library, etc.) supports "unsafe" + * operations (as controlled via the {@link AFSocketCapability#CAPABILITY_UNSAFE} capability). + * + * If supported, the method returns normally. If not supported, an {@link IOException} is thrown. + * + * @throws IOException if "unsafe" operations are not supported. + * @see Unsafe + */ + public static final void ensureUnsafeSupported() throws IOException { + if (!AFSocket.supports(AFSocketCapability.CAPABILITY_UNSAFE)) { + throw new IOException("Unsafe operations are not supported in this environment"); + } } @Override @@ -442,7 +472,7 @@ public final synchronized void close() throws IOException { /** * Registers a {@link Closeable} that should be closed when this socket is closed. - * + * * @param closeable The closeable. */ public final void addCloseable(Closeable closeable) { @@ -451,7 +481,7 @@ public final void addCloseable(Closeable closeable) { /** * Unregisters a previously registered {@link Closeable}. - * + * * @param closeable The closeable. */ public final void removeCloseable(Closeable closeable) { @@ -514,7 +544,7 @@ public final AFOutputStream getOutputStream() throws IOException { /** * Returns the internal helper instance for address-specific extensions. - * + * * @return The helper instance. * @throws UnsupportedOperationException if such extensions are not supported for this address * type. @@ -526,7 +556,7 @@ protected final AFSocketImplExtensions getImplExtensions() { /** * Forces the address to be used for any subsequent call to {@link #connect(SocketAddress)} to be * the given one, regardless of what'll be passed there. - * + * * @param endpoint The forced endpoint address. * @return This instance. */ @@ -539,9 +569,9 @@ public final AFSocket forceConnectAddress(SocketAddress endpoint) { /** * Sets the hook for any subsequent call to {@link #connect(SocketAddress)} or * {@link #connect(SocketAddress, int)} to be the given function. - * + * * The function can monitor events or even alter the target address. - * + * * @param hook The function that gets called for each connect call. * @return This instance. */ @@ -552,10 +582,10 @@ public final AFSocket connectHook(SocketAddressFilter hook) { /** * Probes the status of the socket connection. - * + * * This usually involves checking for {@link #isConnected()}, and if assumed connected, also * sending a zero-length message to the remote. - * + * * @return {@code true} if the connection is known to be closed, {@code false} if the connection * is open/not closed or the condition is unknown. * @throws IOException on an unexpected error. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddress.java index 13ff3a541..686160ec2 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,13 @@ */ package org.newsclub.net.unix; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -29,21 +32,28 @@ import java.net.SocketException; import java.net.URI; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import com.google.errorprone.annotations.Immutable; +import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; + /** * Some {@link SocketAddress} that is supported by junixsocket, such as {@link AFUNIXSocketAddress}. - * + * * @author Christian Kohlschütter */ +@Immutable +@SuppressWarnings({"PMD.CouplingBetweenObjects", "PMD.CyclomaticComplexity"}) public abstract class AFSocketAddress extends InetSocketAddress { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // do not change! /** * Just a marker for "don't actually bind" (checked with "=="). Used in combination with a @@ -73,30 +83,42 @@ protected ByteBuffer initialValue() { } }; + private static final boolean USE_DESERIALIZATION_FOR_INIT; + + static { + String v = System.getProperty("org.newsclub.net.unix.AFSocketAddress.deserialize", ""); + USE_DESERIALIZATION_FOR_INIT = v.isEmpty() ? NativeLibraryLoader.isAndroid() : Boolean.valueOf( + v); + } + /** * Some byte-level representation of this address, which can only be converted to a native * representation in combination with the domain ID. */ - private final byte[] bytes; + @SuppressFBWarnings("JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS") + private byte[] bytes; /** * An {@link InetAddress}-wrapped representation of this address. Only created upon demand. */ - private InetAddress inetAddress = null; + @SuppressFBWarnings("JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS") // only modified during + // construction/deserialization + private InetAddress inetAddress = null; // derived from bytes /** * The system-native representation of this address, or {@code null}. */ - private final ByteBuffer nativeAddress; + @SuppressWarnings("PMD.ImmutableField") + private transient ByteBuffer nativeAddress; /** * The address family. */ - private final AFAddressFamily addressFamily; + private transient AFAddressFamily addressFamily; /** * Creates a new socket address. - * + * * @param port The port. * @param socketAddress The socket address in junixsocket-specific byte-array representation. * @param nativeAddress The socket address in system-native representation. @@ -108,59 +130,200 @@ protected AFSocketAddress(int port, final byte[] socketAddress, ByteBuffer nativ /* * Initializing the superclass with an unresolved hostname helps us pass the #equals and * #hashCode checks, which unfortunately are declared final in InetSocketAddress. - * + * * Using a resolved address (with the address bit initialized) would be ideal, but resolved * addresses can only be IPv4 or IPv6 (at least as of Java 16 and earlier). */ - super(AFInetAddress.createUnresolvedHostname(socketAddress, af), 0); - this.nativeAddress = nativeAddress == null ? null : (ByteBuffer) nativeAddress.duplicate() - .rewind(); + super(AFInetAddress.createUnresolvedHostname(socketAddress, af), port >= 0 && port <= 0xffff + ? port : 0); + initAFSocketAddress(this, port, socketAddress, nativeAddress, af); + } + + /** + * Only for {@link SentinelSocketAddress}. + * + * @param clazz The {@link SentinelSocketAddress} class. + * @param port A sentinel port number. + */ + @SuppressWarnings("PMD.UnusedFormalParameter") + AFSocketAddress(Class clazz, int port) { + super(InetAddress.getLoopbackAddress(), port); + this.nativeAddress = null; + this.bytes = new byte[0]; + this.addressFamily = null; + } + + @SuppressWarnings({"cast", "this-escape"}) + private static void initAFSocketAddress(AFSocketAddress addr, int port, + final byte[] socketAddress, ByteBuffer nativeAddress, AFAddressFamily af) + throws SocketException { + if (socketAddress.length == 0) { + throw new SocketException("Illegal address length: " + socketAddress.length); + } + + addr.nativeAddress = nativeAddress == null ? null : (ByteBuffer) (Object) nativeAddress + .duplicate().rewind(); if (port < -1) { throw new IllegalArgumentException("port out of range"); - } - if (port > 0) { + } else if (port > 0xffff) { if (!NativeUnixSocket.isLoaded()) { throw (SocketException) new SocketException( "Cannot set SocketAddress port - junixsocket JNI library is not available").initCause( NativeUnixSocket.unsupportedException()); } - NativeUnixSocket.setPort1(this, port); + NativeUnixSocket.setPort1(addr, port); } - if (socketAddress.length == 0) { - throw new SocketException("Illegal address length: " + socketAddress.length); + + addr.bytes = socketAddress.clone(); + addr.addressFamily = af; + } + + /** + * Returns a new {@link AFSocketAddress} instance via deserialization. This is a trick to + * workaround certain environments that do not allow the construction of {@link InetSocketAddress} + * instances without trying DNS resolution. + * + * @param The subclass (must be a direct subclass of {@link AFSocketAddress}). + * @param port The port to use. + * @param socketAddress The junixsocket representation of the socket address. + * @param nativeAddress The system-native representation of the socket address, or {@code null}. + * @param af The address family, corresponding to the subclass + * @param constructor The constructor to use as fallback + * @return The new instance. + * @throws SocketException on error. + */ + protected static A newDeserializedAFSocketAddress(int port, + final byte[] socketAddress, ByteBuffer nativeAddress, AFAddressFamily af, + AFSocketAddressConstructor constructor) throws SocketException { + String hostname = AFInetAddress.createUnresolvedHostname(socketAddress, af); + if (hostname == null || hostname.isEmpty()) { + return constructor.newAFSocketAddress(port, socketAddress, nativeAddress); + } + try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(AFSocketAddress + .craftSerializedObject(af.getSocketAddressClass(), hostname, (port >= 0 && port <= 0xffff + ? port : 0))))) { + @SuppressWarnings("unchecked") + A addr = (A) oin.readObject(); + initAFSocketAddress(addr, port, socketAddress, nativeAddress, af); + return addr; + } catch (SocketException e) { + throw e; + } catch (ClassNotFoundException | IOException e) { + throw (SocketException) new SocketException("Unexpected deserialization problem").initCause( + e); } + } + + /** + * Creates a byte-representation of a serialized {@link AFSocketAddress} instance, overriding + * hostname and port, which allows bypassing DNS resolution. + * + * @param className The actual subclass. + * @param hostname The hostname to use (must not be empty or null). + * @param port The port to use. + * @return The byte representation. + */ + private static byte[] craftSerializedObject(Class className, + String hostname, int port) { + ByteBuffer bb = ByteBuffer.allocate(768); + bb.putShort((short) 0xaced); // STREAM_MAGIC + bb.putShort((short) 5); // STREAM_VERSION + bb.put((byte) 0x73); // TC_OBJECT + bb.put((byte) 0x72); // TC_CLASSDESC + + putShortLengthUtf8(bb, className.getName()); + bb.putLong(1); // serialVersionUID of subclass (expected to be 1) + bb.putInt(0x02000078); + bb.put((byte) 0x72); + + putShortLengthUtf8(bb, AFSocketAddress.class.getName()); + bb.putLong(serialVersionUID); // serialVersionUID of AFSocketAddress + bb.putInt(0x0300025B); + putShortLengthUtf8(bb, "bytes"); + + bb.putInt(0x7400025B); + bb.putShort((short) 0x424C); + + putShortLengthUtf8(bb, "inetAddress"); + bb.put((byte) 0x74); + + putShortLengthEncodedClassName(bb, InetAddress.class); + + bb.putShort((short) 0x7872); + putShortLengthUtf8(bb, InetSocketAddress.class.getName()); + bb.putLong(5076001401234631237L); // NOPMD InetSocketAddress serialVersionUID + + bb.putInt(0x03000349); + putShortLengthUtf8(bb, "port"); - this.bytes = socketAddress.clone(); - this.addressFamily = af; + bb.put((byte) 0x4C); + putShortLengthUtf8(bb, "addr"); + + bb.putInt(0x71007E00); + bb.putShort((short) 0x034C); + putShortLengthUtf8(bb, "hostname"); + bb.put((byte) 0x74); + + putShortLengthEncodedClassName(bb, String.class); + + bb.putShort((short) 0x7872); + putShortLengthUtf8(bb, SocketAddress.class.getName()); + bb.putLong(5215720748342549866L); // NOPMD SocketAddress serialVersionUID + + bb.putInt(0x02000078); + bb.put((byte) 0x70); + bb.putInt(port); + + bb.putShort((short) 0x7074); + putShortLengthUtf8(bb, hostname); + + bb.putInt(0x78707077); + bb.put((byte) 0x0B); + + putShortLengthUtf8(bb, "undefined"); + + bb.put((byte) 0x78); // TC_ENDBLOCKDATA + bb.flip(); + + byte[] buf = new byte[bb.remaining()]; + bb.get(buf); + return buf; + } + + private static void putShortLengthEncodedClassName(ByteBuffer bb, Class klazz) { + putShortLengthUtf8(bb, "L" + klazz.getName().replace('.', '/') + ";"); + } + + private static void putShortLengthUtf8(ByteBuffer bb, String s) { + byte[] utf8 = s.getBytes(StandardCharsets.UTF_8); + bb.putShort((short) utf8.length); + bb.put(utf8); } /** - * Only for {@link SentinelSocketAddress}. - * - * @param clazz The {@link SentinelSocketAddress} class. - * @param port A sentinel port number. + * Checks if {@link AFSocketAddress} instantiation should be performed via deserialization. + * + * @return {@code true} if so. + * @see #newDeserializedAFSocketAddress(int, byte[], ByteBuffer, AFAddressFamily, + * AFSocketAddressConstructor) */ - @SuppressWarnings("PMD.UnusedFormalParameter") - protected AFSocketAddress(Class clazz, int port) { - super(InetAddress.getLoopbackAddress(), port); - this.nativeAddress = null; - this.bytes = new byte[0]; - this.addressFamily = null; + protected static boolean isUseDeserializationForInit() { + return USE_DESERIALIZATION_FOR_INIT; } /** * Checks if the address can be resolved to a {@link File}. - * + * * @return {@code true} if the address has a filename. */ public abstract boolean hasFilename(); /** * Returns the {@link File} corresponding with this address, if possible. - * + * * A {@link FileNotFoundException} is thrown if there is no filename associated with the address, * which applies to addresses in the abstract namespace, for example. - * + * * @return The filename. * @throws FileNotFoundException if the address is not associated with a filename. */ @@ -168,7 +331,7 @@ protected AFSocketAddress(Class clazz, int port) { /** * Returns the corresponding {@link AFAddressFamily}. - * + * * @return The address family instance. */ public final AFAddressFamily getAddressFamily() { @@ -177,7 +340,7 @@ public final AFAddressFamily getAddressFamily() { /** * Wraps the socket name/peer name of a file descriptor as an {@link InetAddress}. - * + * * @param fdesc The file descriptor. * @param peerName If {@code true}, the remote peer name (instead of the local name) is retrieved. * @param af The address family. @@ -188,7 +351,7 @@ protected static final InetAddress getInetAddress(FileDescriptor fdesc, boolean if (!fdesc.valid()) { return null; } - byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, peerName); // FIXME + byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, peerName); if (addr == null) { return null; } @@ -197,7 +360,7 @@ protected static final InetAddress getInetAddress(FileDescriptor fdesc, boolean /** * Gets the socket name/peer name of a file descriptor as an {@link AFSocketAddress}. - * + * * @param The corresponding address type. * @param fdesc The file descriptor. * @param peerName If {@code true}, the remote peer name (instead of the local name) is retrieved. @@ -210,7 +373,7 @@ protected static final InetAddress getInetAddress(FileDescriptor fdesc, boolean if (!fdesc.valid()) { return null; } - byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, peerName); // FIXME + byte[] addr = NativeUnixSocket.sockname(af.getDomain(), fdesc, peerName); if (addr == null) { return null; } @@ -259,7 +422,7 @@ static final AFSocketAddress preprocessSocketAddress( /** * Returns the (non-native) byte-level representation of this address. - * + * * @return The byte array. */ protected final byte[] getBytes() { @@ -269,12 +432,12 @@ protected final byte[] getBytes() { /** * Returns a "special" {@link InetAddress} that contains information about this * {@link AFSocketAddress}. - * + * * IMPORTANT: This {@link InetAddress} does not properly compare (using * {@link InetAddress#equals(Object)} and {@link InetAddress#hashCode()}). It should be used * exclusively to circumvent existing APIs like {@link DatagramSocket} that only accept/return * {@link InetAddress} and not arbitrary {@link SocketAddress} types. - * + * * @return The "special" {@link InetAddress}. */ public final InetAddress wrapAddress() { @@ -283,7 +446,7 @@ public final InetAddress wrapAddress() { /** * A reference to the constructor of an AFSocketAddress subclass. - * + * * @param The actual subclass. * @author Christian Kohlschütter */ @@ -291,7 +454,7 @@ public final InetAddress wrapAddress() { protected interface AFSocketAddressConstructor { /** * Constructs a new AFSocketAddress instance. - * + * * @param port The port. * @param socketAddress The socket address in junixsocket-specific byte-array representation. * @param nativeAddress The socket address in system-native representation. @@ -306,7 +469,7 @@ T newAFSocketAddress(int port, byte[] socketAddress, ByteBuffer nativeAddress) /** * Resolves a junixsocket-specific byte-array representation of an {@link AFSocketAddress} to an * actual {@link AFSocketAddress} instance, possibly reusing a cached instance. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @param socketAddress The socket address in junixsocket-specific byte-array representation. * @param port The port. @@ -317,8 +480,6 @@ T newAFSocketAddress(int port, byte[] socketAddress, ByteBuffer nativeAddress) @SuppressWarnings({"unchecked", "null"}) protected static final A resolveAddress(final byte[] socketAddress, int port, AFAddressFamily af) throws SocketException { - A instance; - if (socketAddress.length == 0) { throw new SocketException("Address cannot be empty"); } @@ -339,6 +500,7 @@ protected static final A resolveAddress(final byte[] direct.rewind(); direct.limit(limit); + A instance; synchronized (AFSocketAddress.class) { Map map; Map> mapPorts = ADDRESS_CACHE.get(af); @@ -408,7 +570,7 @@ static final A ofInternal(ByteBuffer socketAddressBu /** * Wraps an address as an {@link InetAddress}. - * + * * @param af The address family. * @return The {@link InetAddress}. */ @@ -421,7 +583,7 @@ protected final synchronized InetAddress getInetAddress(AFAddressFamily af) { /** * Wraps this address as an {@link InetAddress}. - * + * * @return The {@link InetAddress}. */ protected final InetAddress getInetAddress() { @@ -439,7 +601,7 @@ static final ByteBuffer newSockAddrKeyBuffer(int length) { /** * Returns an {@link AFSocketAddress} given a special {@link InetAddress} that encodes the byte * sequence of an AF_UNIX etc. socket address, like those returned by {@link #wrapAddress()}. - * + * * @param The corresponding address type. * @param address The "special" {@link InetAddress}. * @param port The port (use 0 for "none"). @@ -460,7 +622,7 @@ protected static final A unwrap(InetAddress address, * Returns an {@link AFSocketAddress} given a special {@link InetAddress} hostname that encodes * the byte sequence of an AF_UNIX etc. socket address, like those returned by * {@link #wrapAddress()}. - * + * * @param The corresponding address type. * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}. * @param port The port (use 0 for "none"). @@ -479,16 +641,26 @@ protected static final A unwrap(String hostname, int static final int unwrapAddressDirectBufferInternal(ByteBuffer socketAddressBuffer, SocketAddress address) throws SocketException { + if (!NativeUnixSocket.isLoaded()) { + throw new SocketException("Unsupported operation; junixsocket native library is not loaded"); + } Objects.requireNonNull(address); - // FIXME: add support for UnixDomainSocketAddress + if (!(address instanceof AFSocketAddress)) { - throw new SocketException("Unsupported address"); + Supplier supp = AFUNIXSocketAddress.supportedAddressSupplier( + address); + address = supp == null ? null : supp.get(); + if (address == null) { + throw new SocketException("Unsupported address"); + } } - byte[] addr = ((AFSocketAddress) address).getBytes(); - int domain = ((AFSocketAddress) address).getAddressFamily().getDomain(); - int len = NativeUnixSocket.isLoaded() ? NativeUnixSocket.bytesToSockAddr(domain, - socketAddressBuffer, addr) : -1; + AFSocketAddress socketAddress = (AFSocketAddress) address; + + byte[] addr = socketAddress.getBytes(); + int domain = socketAddress.getAddressFamily().getDomain(); + + int len = NativeUnixSocket.bytesToSockAddr(domain, socketAddressBuffer, addr); if (len == -1) { throw new SocketException("Unsupported domain"); } @@ -498,13 +670,19 @@ static final int unwrapAddressDirectBufferInternal(ByteBuffer socketAddressBuffe /** * Returns a thread-local direct ByteBuffer containing the native socket address representation of * this {@link AFSocketAddress}. - * + * * @return The direct {@link ByteBuffer}. */ - final ByteBuffer getNativeAddressDirectBuffer() { - ByteBuffer direct = getNativeAddressDirectBuffer(nativeAddress.limit()); - nativeAddress.position(0); - direct.put(nativeAddress); + final ByteBuffer getNativeAddressDirectBuffer() throws SocketException { + ByteBuffer address = nativeAddress; + if (address == null) { + throw (SocketException) new SocketException("Cannot access native address").initCause( + NativeUnixSocket.unsupportedException()); + } + + ByteBuffer direct = getNativeAddressDirectBuffer(address.limit()); + address.position(0); + direct.put(address); return direct; } @@ -518,7 +696,7 @@ static final ByteBuffer getNativeAddressDirectBuffer(int limit) { /** * Checks if the given address is supported by this address family. - * + * * @param addr The address. * @param af The address family. * @return {@code true} if supported. @@ -529,22 +707,23 @@ protected static final boolean isSupportedAddress(InetAddress addr, AFAddressFam /** * Writes the native (system-level) representation of this address to the given buffer. - * + * * The position of the target buffer will be at the end (i.e., after) the written data. - * + * * @param buf The target buffer. * @throws IOException on error. */ public final void writeNativeAddressTo(ByteBuffer buf) throws IOException { if (nativeAddress == null) { - throw new IOException("Cannot access native address"); + throw (SocketException) new SocketException("Cannot access native address").initCause( + NativeUnixSocket.unsupportedException()); } buf.put(nativeAddress); } /** * Creates a new socket connected to this address. - * + * * @return The socket instance. * @throws IOException on error. */ @@ -556,7 +735,7 @@ public AFSocket newConnectedSocket() throws IOException { /** * Creates a new server socket bound to this address. - * + * * @return The server socket instance. * @throws IOException on error. */ @@ -570,7 +749,7 @@ public AFServerSocket newBoundServerSocket() throws IOException { * Creates a new server socket force-bound to this address (i.e., any additional call to * {@link ServerSocket#bind(SocketAddress)} will ignore the passed address and use this one * instead. - * + * * @return The server socket instance. * @throws IOException on error. */ @@ -582,10 +761,10 @@ public AFServerSocket newForceBoundServerSocket() throws IOException { /** * Tries to parse the given URI and return a corresponding {@link AFSocketAddress} for it. - * + * * NOTE: Only certain URI schemes are supported, such as {@code unix://} (for * {@link AFUNIXSocketAddress}) and {@code tipc://} for {@link AFTIPCSocketAddress}. - * + * * @param u The URI. * @return The address. * @throws SocketException on error. @@ -598,10 +777,10 @@ public static AFSocketAddress of(URI u) throws SocketException { /** * Tries to parse the given URI and return a corresponding {@link AFSocketAddress} for it. - * + * * NOTE: Only certain URI schemes are supported, such as {@code unix://} (for * {@link AFUNIXSocketAddress}) and {@code tipc://} for {@link AFTIPCSocketAddress}. - * + * * @param u The URI. * @param overridePort The port to forcibly use, or {@code -1} for "don't override". * @return The address. @@ -619,7 +798,7 @@ public static AFSocketAddress of(URI u, int overridePort) throws SocketException /** * Tries to create a URI based on this {@link AFSocketAddress}. - * + * * @param scheme The target scheme. * @param template An optional template to reuse certain parameters (e.g., the "path" component * for an {@code http} request), or {@code null}. @@ -634,14 +813,15 @@ public URI toURI(String scheme, URI template) throws IOException { * Returns a address string that can be used with {@code socat}'s {@code SOCKET-CONNECT}, * {@code SOCKET-LISTEN}, {@code SOCKET-DATAGRAM}, etc., address types, or {@code null} if the * address type is not natively supported by this platform. - * + * * This call is mostly suited for debugging purposes. The resulting string is specific to the - * platform the code is executed on, and thus may be different among platforms (or {@code null}). - * + * platform the code is executed on, and thus may be different among platforms. + * * @param socketType The socket type, or {@code null} to omit from string. * @param socketProtocol The socket protocol, or {@code null} to omit from string. - * @return The string (such as 1:0:x2f746d702f796f), or {@code null} if unable to retrieve. - * @throws IOException on error. + * @return The string (such as 1:0:x2f746d702f796f). + * @throws IOException on error (a {@link SocketException} is thrown if the native address cannot + * be accessed). */ public @Nullable @SuppressWarnings("PMD.NPathComplexity") String toSocatAddressString( AFSocketType socketType, AFSocketProtocol socketProtocol) throws IOException { @@ -650,10 +830,11 @@ public URI toURI(String scheme, URI template) throws IOException { return null; } if (nativeAddress == null) { - return null; + throw (SocketException) new SocketException("Cannot access native address").initCause( + NativeUnixSocket.unsupportedException()); } if (socketProtocol != null && socketProtocol.getId() != 0) { - throw new IOException("Protocol not (yet) supported"); // FIXME + throw new IOException("Protocol not (yet) supported"); // FIXME support additional protocols } int family = (nativeAddress.get(SOCKADDR_NATIVE_FAMILY_OFFSET) & 0xFF); @@ -682,17 +863,47 @@ public URI toURI(String scheme, URI template) throws IOException { /** * Checks if the given address could cover another address. - * + * * By default, this is only true if both addresses are regarded equal using * {@link #equals(Object)}. - * + * * However, implementations may support "wildcard" addresses, and this method would compare a * wildcard address against some non-wildcard address, for example. - * + * * @param other The other address that could be covered by this address. * @return {@code true} if the other address could be covered. */ public boolean covers(AFSocketAddress other) { return this.equals(other); } + + /** + * Custom serialization: Reference {@link AFAddressFamily} instance by identifier string. + * + * @param in The {@link ObjectInputStream}. + * @throws ClassNotFoundException on error. + * @throws IOException on error. + */ + private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { + in.defaultReadObject(); + + String af = in.readUTF(); + if ("undefined".equals(af)) { + this.addressFamily = null; + } else { + this.addressFamily = Objects.requireNonNull(AFAddressFamily.getAddressFamily(af), + "address family"); + } + } + + /** + * Custom serialization: Reference {@link AFAddressFamily} instance by identifier string. + * + * @param out The {@link ObjectOutputStream}. + * @throws IOException on error. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeUTF(addressFamily == null ? "undefined" : addressFamily.getJuxString()); + } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressConfig.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressConfig.java index f4b6bc194..f37f1da4a 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressConfig.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressConfig.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * The implementation-specifics for a given {@link AFSocketAddress} subclass implementation. - * + * * @param The supported address type. * @author Christian Kohlschütter * @see AFAddressFamilyConfig @@ -40,7 +40,7 @@ protected AFSocketAddressConfig() { /** * Tries to parse the given address-specific URI. - * + * * @param u The URI. * @param port The port to use, or {@code -1} for "unspecified". * @return The address. @@ -50,23 +50,23 @@ protected AFSocketAddressConfig() { /** * Returns the implementation's address constructor. - * + * * @return The implementation's address constructor. */ protected abstract AFSocketAddressConstructor addressConstructor(); /** * Returns the name of the implementation's selector provider class. - * + * * @return The name of the implementation's selector provider class. */ protected abstract String selectorProviderClassname(); /** * Returns the set of supported URI schemes that can be parsed via {@link #parseURI(URI,int)}. - * + * * These schemes must be unique to this {@link AFSocketAddress} type. - * + * * @return The set of supported URI schemes. */ protected abstract Set uriSchemes(); diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressFromHostname.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressFromHostname.java index c7d67a8af..7c006b22e 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressFromHostname.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketAddressFromHostname.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,14 @@ /** * Helps converting an internet "hostname" to an {@link AFSocketAddress}. - * + * * @author Christian Kohlschütter + * @param The supported address type. */ public interface AFSocketAddressFromHostname { /** * Translates a "host" string (and port) to an {@link AFSocketAddress}. - * + * * @param host The hostname * @param port The port, or 0. * @return The {@link AFSocketAddress} @@ -40,7 +41,7 @@ public interface AFSocketAddressFromHostname { /** * Checks whether the given hostname is supported by this socket factory. If not, calls to * createSocket will cause a {@link SocketException}. - * + * * @param host The host to check. * @return {@code true} if supported. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCapability.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCapability.java index 0fb7019db..456995d15 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCapability.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCapability.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,9 +22,16 @@ /** * Describes junixsocket capabilities the current environment (system platform, native library, * etc.) may or may not support. - * + * * You can check whether your environment supports a given capability by calling * {@link AFSocket#supports(AFSocketCapability)}. + * + * You can also manually disable a given capability by specifying a System property of the form + * org.newsclub.net.unix.library.disable.CAPABILITY_SOMETHING_SOMETHING=true + * when invoking the JVM (make sure this property is set before junixsocket is accessed). + * + * A simple way to check which capabilities are supported in an environment is to run the + * `junixsocket-selftest` jar. */ public enum AFSocketCapability { // see org_newsclub_net_unix_NativeUnixSocket.c in junixsocket-native @@ -41,16 +48,16 @@ public enum AFSocketCapability { /** Socket addressing supports the abstract namespace (Linux). */ CAPABILITY_ABSTRACT_NAMESPACE(3), - /** Support for AF_UNIX datagrams (not on Windows yet). */ + /** Support for AF_UNIX datagrams. */ CAPABILITY_UNIX_DATAGRAMS(4), /** * A pair of interconnected sockets can be created natively as AF_UNIX sockets. - * + * * This currently not possible on Windows, but instead emulated via anonymous AF_INET ports when * you use {@link AFSocketPair}. Other systems may provide partial implementations of pipe-based * (i.e., non-socket) pairs. - * + * * This capability is specific to AF_UNIX sockets. Other sockets, such as AF_VSOCK, may not * implement socketpair natively even if this capability is set, but would work-around that * limitation in a similar fashion but maybe without resorting to AF_INET. @@ -59,14 +66,14 @@ public enum AFSocketCapability { /** * A file descriptor can be converted to {@link Redirect}. - * + * * This feature currently uses Java SDK internals that may change/disappear. */ CAPABILITY_FD_AS_REDIRECT(6), /** * Support for AF_TIPC. - * + * * Availability of this feature is checked upon launch and therefore loading the "tipc" kernel * module at a later point may not be properly reflected. */ @@ -74,10 +81,10 @@ public enum AFSocketCapability { /** * Support for AF_UNIX. - * + * * Availability of this feature is checked upon launch and therefore, on systems adding support at * a later point, may not be properly reflected when checking at a later point. - * + * * NOTE: While this capability is typically supported on most systems that can actually load a * junixsocket JNI library, it is unavailable for older Windows versions (such as 8.1, 10 before * AFUNIX.SYS was included, etc.) and on systems where support for UNIX domain sockets is actively @@ -90,7 +97,7 @@ public enum AFSocketCapability { * * Availability of this feature is checked upon launch and therefore enabling vsock at a later * point may not be properly reflected. - * + * * @see #CAPABILITY_VSOCK_DGRAM */ CAPABILITY_VSOCK(9), @@ -106,16 +113,39 @@ public enum AFSocketCapability { /** * Support for zero-length send(2). - * + * * This can be used to perform a connection check, but not all operating systems support this or * behave correctly (particularly, IBM AIX, IBM i, and IBM z/OS) at the moment. - * + * * If not supported, junixsocket will simply ignore writes of zero-length, and connection checking * with {@link AFSocket#checkConnectionClosed()} may return {@code false} regardless of the actual * condition. */ CAPABILITY_ZERO_LENGTH_SEND(11), + /** + * Support for "unsafe" operations. + * + * Trading-in safety for speed or simplicity may be justified sometimes. + * + * @see Unsafe + * @see AFSocket#ensureUnsafeSupported() + */ + CAPABILITY_UNSAFE(12), + + /** + * Support for port numbers larger than 65535 (0xffff). + * + * Not all systems allow setting port numbers beyond the default TCP range (we use JNI tricks for + * that). This capability is required for RMI support. + */ + CAPABILITY_LARGE_PORTS(13), + + /** + * Support for certain Darwin (macOS Kernel)-specific features, such as the AF_SYSTEM domain. + */ + CAPABILITY_DARWIN(14), + ; // end of list private final int bitmask; diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketChannel.java index 5c05353df..e825e11ff 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ /** * A selectable channel for stream-oriented connecting sockets. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @author Christian Kohlschütter */ @@ -45,11 +45,11 @@ public abstract class AFSocketChannel extends SocketC /** * Creates a new socket channel for the given socket, using the given {@link SelectorProvider}. - * + * * @param socket The socket. * @param sp The {@link SelectorProvider}. */ - @SuppressWarnings("null") + @SuppressWarnings("all") protected AFSocketChannel(AFSocket socket, AFSelectorProvider sp) { super(sp); this.afSocket = Objects.requireNonNull(socket); @@ -57,7 +57,7 @@ protected AFSocketChannel(AFSocket socket, AFSelectorProvider sp) { /** * Returns the corresponding {@link AFSocket}. - * + * * @return The corresponding socket. */ protected final AFSocket getAFSocket() { @@ -66,14 +66,14 @@ protected final AFSocket getAFSocket() { /** * A reference to a method that provides an {@link AFSocket} instance. - * + * * @param The concrete {@link AFSocketAddress} that is supported by this type. */ @FunctionalInterface protected interface AFSocketSupplier { /** * Returns a new {@link AFSocket} instance. - * + * * @return The instance. * @throws IOException on error. */ @@ -85,7 +85,7 @@ protected interface AFSocketSupplier { * * @param The concrete {@link AFSocketAddress} that is supported by this type. * @param supplier The AFSocketChannel constructor. - * + * * @return The new channel * @throws IOException on error. */ @@ -131,7 +131,7 @@ public final T getOption(SocketOption name) throws IOException { if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - return (T) afSocket.getAFImpl().getOption(optionId.intValue()); + return (T) afSocket.getAFImpl().getOption(optionId); } } @@ -145,7 +145,7 @@ public final AFSocketChannel setOption(SocketOption name, T value) thr if (optionId == null) { throw new UnsupportedOperationException("unsupported option"); } else { - afSocket.getAFImpl().setOption(optionId.intValue(), value); + afSocket.getAFImpl().setOption(optionId, value); } return this; } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketConnector.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketConnector.java new file mode 100644 index 000000000..86d2725f3 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketConnector.java @@ -0,0 +1,40 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.IOException; + +/** + * Some connector that is able to connect to a given {@link AFSocketAddress}. + * + * @param The address type to connect to. + * @param The address type for the returned socket (which should either be identical to + * {@code A} or {@link AFSocketAddress} to indicate that this could be any socket). + * @author Christian Kohlschütter + * @see AFServerSocketConnector + */ +public interface AFSocketConnector { + /** + * Connect to the socket at the given address. + * + * @param addr The address to connect to. + * @return The connected socket. + * @throws IOException on error. + */ + AFSocket connect(A addr) throws IOException; +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCore.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCore.java index 9fa3276ff..567b95f25 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCore.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketCore.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A shared core that is common for all AF* sockets (datagrams, streams). - * + * * @author Christian Kohlschütter */ class AFSocketCore extends AFCore { @@ -51,6 +51,7 @@ protected AFAddressFamily addressFamily() { } @Override + @SuppressWarnings("UnsafeFinalization" /* errorprone */) protected void doClose() throws IOException { NativeUnixSocket.shutdown(fd, SHUT_RD_WR); unblockAccepts(); @@ -91,13 +92,14 @@ boolean isConnected(boolean boundOk) { return false; } - @SuppressWarnings({"unchecked", "null"}) + @SuppressWarnings({"unchecked"}) T getOption(AFSocketOption name) throws IOException { Class type = name.type(); if (Boolean.class.isAssignableFrom(type)) { return (T) (Object) (NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), - Integer.class).intValue() != 0); + Integer.class) != 0); } else if (NamedInteger.HasOfValue.class.isAssignableFrom(type)) { + @SuppressWarnings("all") // "null" creates another warning int v = NativeUnixSocket.getSocketOption(fd, name.level(), name.optionName(), Integer.class); try { return (T) type.getMethod("ofValue", int.class).invoke(null, v); @@ -113,7 +115,7 @@ T getOption(AFSocketOption name) throws IOException { void setOption(AFSocketOption name, T value) throws IOException { final Object val; if (value instanceof Boolean) { - val = (((Boolean) value).booleanValue() ? 1 : 0); + val = (((Boolean) value) ? 1 : 0); } else if (value instanceof NamedInteger) { val = ((NamedInteger) value).value(); } else { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketExtensions.java index 5ea9f9dd1..e68a1f633 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketExtensions.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketExtensions.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,35 +20,35 @@ /** * Defines certain methods that all junixsocket socket implementations share and extend beyond the * standard socket API. - * + * * The set of features include methods to support working with ancillary messages (such as file * descriptors) as well as socket credentials. - * + * * Keep in mind that the platform this code runs on may not support these features, and exceptions * may be thrown when not checking for the corresponding {@link AFSocketCapability} first. - * + * * @author Christian Kohlschütter */ public interface AFSocketExtensions { /** * Returns the size of the receive buffer for ancillary messages (in bytes). - * + * * @return The size. */ int getAncillaryReceiveBufferSize(); /** * Sets the size of the receive buffer for ancillary messages (in bytes). - * + * * To disable handling ancillary messages, set it to 0 (default). - * + * * @param size The size. */ void setAncillaryReceiveBufferSize(int size); /** * Ensures a minimum ancillary receive buffer size. - * + * * @param minSize The minimum size (in bytes). */ void ensureAncillaryReceiveBufferSize(int minSize); diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketFactory.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketFactory.java index e4aba2fdc..b7228ea6c 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketFactory.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,11 +28,12 @@ /** * The base for a SocketFactory that connects to UNIX sockets. - * + * * Typically, the "hostname" is used as a reference to a socketFile on the file system. The actual * mapping is left to the implementor. - * + * * @see AFUNIXSocketFactory + * @param The supported address type. */ public abstract class AFSocketFactory extends SocketFactory implements AFSocketAddressFromHostname { @@ -47,10 +48,10 @@ protected AFSocketFactory() { /** * Checks whether the given {@link InetAddress} is supported by this socket factory. If not, calls * to createSocket will cause a {@link SocketException}. - * + * * By default, this only checks the hostname part of the address via * {@link #isHostnameSupported(String)}. - * + * * @param address The address to check. * @return {@code true} if supported. */ @@ -63,7 +64,7 @@ protected final boolean isInetAddressSupported(InetAddress address) { /** * Creates a new {@link AFSocket}, connected to the given address. - * + * * @param addr The address to connect to. * @return The socket instance. * @throws IOException on error. @@ -140,7 +141,7 @@ public static final class FixedAddressSocketFactory extends AFSocketFactory The supported address type. */ -@SuppressWarnings({"PMD.CyclomaticComplexity"}) +@SuppressWarnings({ + "PMD.CyclomaticComplexity", "PMD.CouplingBetweenObjects", + "UnsafeFinalization" /* errorprone */}) public abstract class AFSocketImpl extends SocketImplShim { private static final int SHUT_RD = 0; private static final int SHUT_WR = 1; @@ -96,7 +99,7 @@ private void decPendingAccepts() throws SocketException { pendingAccepts.decrementAndGet(); } - protected void createSocket(FileDescriptor fdTarget, AFSocketType type) throws IOException { + void createSocket(FileDescriptor fdTarget, AFSocketType type) throws IOException { NativeUnixSocket.createSocket(fdTarget, addressFamily().getDomain(), type.getId()); } @@ -133,7 +136,7 @@ protected void unblockAccepts() { } catch (Exception e) { // ignore } - } catch (Exception e) { + } catch (RuntimeException e) { // ignore } @@ -149,13 +152,11 @@ protected void unblockAccepts() { /** * Creates a new {@link AFSocketImpl} instance. - * + * * @param addressFamily The address family. * @param fdObj The socket's {@link FileDescriptor}. - * @throws SocketException on error. */ - protected AFSocketImpl(AFAddressFamily<@NonNull A> addressFamily, FileDescriptor fdObj) - throws SocketException { + protected AFSocketImpl(AFAddressFamily<@NonNull A> addressFamily, FileDescriptor fdObj) { super(); this.addressFamily = addressFamily; this.address = InetAddress.getLoopbackAddress(); @@ -167,7 +168,7 @@ protected AFSocketImpl(AFAddressFamily<@NonNull A> addressFamily, FileDescriptor /** * Creates a new {@link InputStream} for this socket. - * + * * @return The new stream. */ protected final AFInputStream newInputStream() { @@ -176,7 +177,7 @@ protected final AFInputStream newInputStream() { /** * Creates a new {@link OutputStream} for this socket. - * + * * @return The new stream. */ protected final AFOutputStream newOutputStream() { @@ -230,6 +231,7 @@ protected final void accept(SocketImpl socket) throws IOException { accept0(socket); } + @SuppressWarnings("Finally" /* errorprone */) final boolean accept0(SocketImpl socket) throws IOException { FileDescriptor fdesc = core.validFdOrException(); if (isClosed()) { @@ -261,7 +263,7 @@ final boolean accept0(SocketImpl socket) throws IOException { .get())) { return false; } - } catch (SocketException e) { + } catch (SocketException e) { // NOPMD.ExceptionAsFlowControl caught = e; } finally { // NOPMD.DoNotThrowExceptionInFinally if (!isBound() || isClosed()) { @@ -375,7 +377,7 @@ final boolean connect0(SocketAddress addr, int connectTimeout) throws IOExceptio if (addr == AFSocketAddress.INTERNAL_DUMMY_CONNECT) { // NOPMD this.connected.set(true); return true; - } else if (addr == AFSocketAddress.INTERNAL_DUMMY_CONNECT) { // NOPMD) + } else if (addr == AFSocketAddress.INTERNAL_DUMMY_DONT_CONNECT) { // NOPMD) return false; } @@ -397,7 +399,7 @@ final boolean connect0(SocketAddress addr, int connectTimeout) throws IOExceptio if (ignoreSpuriousTimeout) { Object o = getOption(SocketOptions.SO_TIMEOUT); if (o instanceof Integer) { - if (((Integer) o).intValue() == 0) { + if (((Integer) o) == 0) { ignoreSpuriousTimeout = false; continue; } @@ -424,7 +426,7 @@ protected final void create(boolean stream) throws IOException { } if (fd.valid()) { if (createType != null) { - if (createType.booleanValue() != stream) { + if (createType.booleanValue() != stream) { // NOPMD.UnnecessaryBoxing throw new IllegalStateException("Already created with different mode"); } } else { @@ -663,7 +665,7 @@ private static int expectBoolean(Object value) throws SocketException { new NullPointerException()); } try { - return ((Boolean) value).booleanValue() ? 1 : 0; + return ((Boolean) value) ? 1 : 0; } catch (final ClassCastException e) { throw (SocketException) new SocketException("Unsupported value: " + value).initCause(e); } @@ -747,7 +749,7 @@ private void setOption0(int optID, Object value) throws SocketException { /** * Like {@link #getOption(int)}, but ignores exceptions for certain option IDs. - * + * * @param optID The option ID. * @return The value. * @throws SocketException on error. @@ -768,7 +770,7 @@ protected final Object getOptionLenient(int optID) throws SocketException { /** * Like {@link #setOption(int, Object)}, but ignores exceptions for certain option IDs. - * + * * @param optID The option ID. * @param value The value. * @throws SocketException on error. @@ -844,7 +846,7 @@ static final void setOptionDefault(FileDescriptor fdesc, int optID, Object value /** * Shuts down both input and output at once. Equivalent to calling {@link #shutdownInput()} and * {@link #shutdownOutput()}. - * + * * @throws IOException on error. */ protected final void shutdown() throws IOException { @@ -963,7 +965,7 @@ final AFAddressFamily getAddressFamily() { @Override protected void setOption(SocketOption name, T value) throws IOException { if (name instanceof AFSocketOption) { - ((AFSocketImpl) this).getCore().setOption((AFSocketOption) name, value); + getCore().setOption((AFSocketOption) name, value); return; } Integer optionId = SocketOptionsMapper.resolve(name); @@ -978,7 +980,7 @@ protected void setOption(SocketOption name, T value) throws IOException { @Override protected T getOption(SocketOption name) throws IOException { if (name instanceof AFSocketOption) { - return ((AFSocketImpl) this).getCore().getOption((AFSocketOption) name); + return getCore().getOption((AFSocketOption) name); } Integer optionId = SocketOptionsMapper.resolve(name); if (optionId == null) { @@ -995,7 +997,7 @@ protected Set> supportedOptions() { /** * Returns the internal helper instance for address-specific extensions. - * + * * @return The helper instance. * @throws UnsupportedOperationException if such extensions are not supported for this address * type. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketImplExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketImplExtensions.java index 8e9ae8837..c855baa41 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketImplExtensions.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketImplExtensions.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ /** * Placeholder for protocol-specific code that resides in the native library. - * + * * @param The corresponding address type. * @author Christian Kohlschütter * @see AFTIPCSocketImplExtensions diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketOption.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketOption.java index d69ef554b..dda7004f5 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketOption.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketOption.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * A special socket option supported by some junixsocket-based implementation. - * + * * @param The option's value type. * @author Christian Kohlschütter */ @@ -33,7 +33,7 @@ public final class AFSocketOption implements SocketOption { /** * Creates a new socket option. This should only be called by {@link AFSocket} implementations. - * + * * @param name The name of the option. * @param type The value type. * @param level The socket level (as defined in junixsocket-native). diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketPair.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketPair.java index 051ee7f63..b5eb3a6fd 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketPair.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketPair.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,14 +23,14 @@ /** * A pair of sockets. - * + * * @param The socket type. * @author Christian Kohlschütter */ public abstract class AFSocketPair extends CloseablePair { /** * Creates a socket pair. - * + * * @param a The first socket. * @param b The second socket. */ @@ -40,7 +40,7 @@ protected AFSocketPair(T a, T b) { /** * Creates a socket pair. - * + * * @param a The first socket. * @param b The second socket. * @param alsoClose Some closeable that is also closed upon {@link #close()}, or {@code null}. @@ -51,7 +51,7 @@ protected AFSocketPair(T a, T b, Closeable alsoClose) { /** * Returns the first socket of the pair. - * + * * @return The first socket. */ public final @NonNull T getSocket1() { @@ -60,7 +60,7 @@ protected AFSocketPair(T a, T b, Closeable alsoClose) { /** * Returns the second socket of the pair. - * + * * @return The second socket. */ public final @NonNull T getSocket2() { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketProtocol.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketProtocol.java index f64fce676..e816bc1eb 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketProtocol.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketProtocol.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ /** * Describes the "protocol" part of a socket. - * + * * @author Christian Kohlschütter */ public enum AFSocketProtocol { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketType.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketType.java index c0f65dec8..b47dfe290 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketType.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSocketType.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ /** * Describes the "type" of a socket. - * + * * @author Christian Kohlschütter */ public enum AFSocketType { @@ -34,18 +34,22 @@ public enum AFSocketType { SOCK_DGRAM(NativeUnixSocket.SOCK_DGRAM), // /** - * Sequential packet socket. + * Raw mode. */ - SOCK_SEQPACKET(NativeUnixSocket.SOCK_SEQPACKET), // + SOCK_RAW(NativeUnixSocket.SOCK_RAW), // /** * Reliably-delivered datagram messages. - * + * * Used by {@code AFTIPCDatagramSocket} to differentiate between datagram connects that may or may * not permit package loss. */ SOCK_RDM(NativeUnixSocket.SOCK_RDM), // + /** + * Sequential packet socket. + */ + SOCK_SEQPACKET(NativeUnixSocket.SOCK_SEQPACKET), // ; private final int id; diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSomeSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSomeSocket.java index 7315c12de..1cfc23eb6 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSomeSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFSomeSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ /** * Marker interface that combines junixsocket-based {@link SocketChannel}s, {@link Socket}s, * {@link DatagramChannel}s and {@link DatagramSocket}s. - * + * * @author Christian Kohlschütter * @see AFSocketPair * @see AFSocket @@ -41,7 +41,7 @@ public interface AFSomeSocket extends Closeable, FileDescriptorAccess { /** * Returns the socket's local socket address, or {@code null} if unavailable or if there was a * problem retrieving it. - * + * * @return The local socket address, or {@code null}. */ @Nullable @@ -50,7 +50,7 @@ public interface AFSomeSocket extends Closeable, FileDescriptorAccess { /** * Returns the socket's remote socket address, or {@code null} if unavailable/not connected, or if * there was a problem retrieving it. - * + * * @return The remote socket address, or {@code null}. */ @Nullable diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketAddress.java index 5efcb28ef..2a5ec519f 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ /** * An {@link AFSocketAddress} for TIPC sockets. - * + * * The TIPC socket API provides three different address types: *
    *
  • Service Address. @@ -106,11 +106,11 @@ * indicated node. If this value is zero, all matching sockets in the whole cluster, as visible from * the source node, are eligible. *

    - * + * * @author Christian Kohlschütter (documentation credits to Jon Maloy and the TIPC team). */ public final class AFTIPCSocketAddress extends AFSocketAddress { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // do not change! private static final Pattern PAT_TIPC_URI_HOST_AND_PORT = Pattern.compile( "^((?:(?:(?cluster|node|default|[0-9a-fx]+)\\-)?(?service|service-range|socket)\\.)|" @@ -131,7 +131,7 @@ public final class AFTIPCSocketAddress extends AFSocketAddress { /** * The TIPC address type. - * + * * @author Christian Kohlschütter */ @NonNullByDefault @@ -191,7 +191,7 @@ interface DebugStringProvider extends Serializable { /** * Formats an integer as an unsigned, zero-padded 32-bit hexadecimal number. - * + * * @param i The number. * @return The string. */ @@ -211,7 +211,7 @@ private String toDebugString(Scope scope, int a, int b, int c) { /** * The TIPC visibility scope. - * + * * @author Christian Kohlschütter */ @NonNullByDefault @@ -249,7 +249,7 @@ private Scope(String name, int id) { /** * Returns a {@link Scope} instance given an integer value. - * + * * @param v The scope value. * @return The {@link Scope} instance. */ @@ -263,6 +263,12 @@ private AFTIPCSocketAddress(int port, final byte[] socketAddress, ByteBuffer nat super(port, socketAddress, nativeAddress, addressFamily()); } + private static AFTIPCSocketAddress newAFSocketAddress(int port, final byte[] socketAddress, + ByteBuffer nativeAddress) throws SocketException { + return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, addressFamily(), + AFTIPCSocketAddress::new); + } + /** * Returns an {@link AFTIPCSocketAddress} that refers to a given service type and instance, using * the given scope. @@ -294,7 +300,7 @@ public static AFTIPCSocketAddress ofService(int type, int instance) throws Socke /** * Returns an {@link AFTIPCSocketAddress} that refers to a given service type and instance, using * the given scope and the given lookup domain. - * + * * @param scope The address scope. * @param type The service type (0-63 are reserved). * @param instance The service instance ID. @@ -434,7 +440,7 @@ private static int parseUnsignedInt(String v) { /** * Returns an {@link AFTIPCSocketAddress} given a special {@link InetAddress} that encodes the * byte sequence of an AF_TIPC socket address, like those returned by {@link #wrapAddress()}. - * + * * @param address The "special" {@link InetAddress}. * @param port The port (use 0 for "none"). * @return The {@link AFTIPCSocketAddress} instance. @@ -449,7 +455,7 @@ public static AFTIPCSocketAddress unwrap(InetAddress address, int port) throws S * Returns an {@link AFTIPCSocketAddress} given a special {@link InetAddress} hostname that * encodes the byte sequence of an AF_TIPC socket address, like those returned by * {@link #wrapAddress()}. - * + * * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}. * @param port The port (use 0 for "none"). * @return The {@link AFTIPCSocketAddress} instance. @@ -462,7 +468,7 @@ public static AFTIPCSocketAddress unwrap(String hostname, int port) throws Socke /** * Returns an {@link AFTIPCSocketAddress} given a generic {@link SocketAddress}. - * + * * @param address The address to unwrap. * @return The {@link AFTIPCSocketAddress} instance. * @throws SocketException if the operation fails, for example when an unsupported address is @@ -478,7 +484,7 @@ public static AFTIPCSocketAddress unwrap(SocketAddress address) throws SocketExc /** * Returns the scope of this address. - * + * * @return The scope. */ public Scope getScope() { @@ -491,7 +497,7 @@ public Scope getScope() { /** * Returns the TIPC type part of this address. - * + * * @return The type identifier */ public int getTIPCType() { @@ -502,7 +508,7 @@ public int getTIPCType() { /** * Returns the TIPC instance part of this address. - * + * * @return The instance identifier. */ public int getTIPCInstance() { @@ -513,7 +519,7 @@ public int getTIPCInstance() { /** * Returns the TIPC domain part of this address. - * + * * @return The domain identifier. */ public int getTIPCDomain() { @@ -524,7 +530,7 @@ public int getTIPCDomain() { /** * Returns the TIPC lower instance of this address. - * + * * @return The lower instance identifier. */ public int getTIPCLower() { @@ -535,7 +541,7 @@ public int getTIPCLower() { /** * Returns the TIPC upper instance of this address. - * + * * @return The lower instance identifier. */ public int getTIPCUpper() { @@ -546,7 +552,7 @@ public int getTIPCUpper() { /** * Returns the TIPC ref of this address. - * + * * @return The ref identifier. */ public int getTIPCRef() { @@ -557,7 +563,7 @@ public int getTIPCRef() { /** * Returns the TIPC node hash of this address. - * + * * @return The node hash. */ public int getTIPCNodeHash() { @@ -602,7 +608,7 @@ public File getFile() throws FileNotFoundException { /** * Checks if an {@link InetAddress} can be unwrapped to an {@link AFTIPCSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #wrapAddress() @@ -614,7 +620,7 @@ public static boolean isSupportedAddress(InetAddress addr) { /** * Checks if a {@link SocketAddress} can be unwrapped to an {@link AFTIPCSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #unwrap(InetAddress, int) @@ -636,7 +642,7 @@ private static byte[] toBytes(AddressType addrType, Scope scope, int a, int b, i /** * Returns the corresponding {@link AFAddressFamily}. - * + * * @return The address family instance. */ @SuppressWarnings("null") @@ -645,6 +651,10 @@ public static synchronized AFAddressFamily addressFamily() afTipc = AFAddressFamily.registerAddressFamily("tipc", // AFTIPCSocketAddress.class, new AFSocketAddressConfig() { + private final AFSocketAddressConstructor addrConstr = + isUseDeserializationForInit() ? AFTIPCSocketAddress::newAFSocketAddress + : AFTIPCSocketAddress::new; + @Override protected AFTIPCSocketAddress parseURI(URI u, int port) throws SocketException { return AFTIPCSocketAddress.of(u, port); @@ -652,7 +662,7 @@ protected AFTIPCSocketAddress parseURI(URI u, int port) throws SocketException { @Override protected AFSocketAddressConstructor addressConstructor() { - return AFTIPCSocketAddress::new; + return addrConstr; } @Override @@ -684,7 +694,7 @@ private String toTipcInt(int v) { /** * Returns an {@link AFTIPCSocketAddress} for the given URI, if possible. - * + * * @param uri The URI. * @return The address. * @throws SocketException if the operation fails. @@ -696,7 +706,7 @@ public static AFTIPCSocketAddress of(URI uri) throws SocketException { /** * Returns an {@link AFTIPCSocketAddress} for the given URI, if possible. - * + * * @param uri The URI. * @param overridePort The port to forcibly use, or {@code -1} for "don't override". * @return The address. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketImplExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketImplExtensions.java index 4a4268d55..98e4eab92 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketImplExtensions.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFTIPCSocketImplExtensions.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * TIPC-specific code that resides in the native library. To be used by {@code AFTIPCSocket} and * {@code AFTIPCDatagramSocket} only. - * + * * @author Christian Kohlschütter */ public final class AFTIPCSocketImplExtensions implements @@ -36,9 +36,9 @@ public final class AFTIPCSocketImplExtensions implements /** * Returns the TIPC "ErrInfo" data from the ancillary receive buffer. - * + * * Invalid for any other use. - * + * * @return The errinfo. */ public int[] getTIPCErrInfo() { @@ -47,9 +47,9 @@ public int[] getTIPCErrInfo() { /** * Returns the TIPC "DestName" data from the ancillary receive buffer. - * + * * Invalid for any other use. - * + * * @return The DestName. */ public int[] getTIPCDestName() { @@ -58,7 +58,7 @@ public int[] getTIPCDestName() { /** * Retrieves the 16-byte TIPC node identity given a node hash. - * + * * @param peer The node hash. * @return The node identity, or {@code null} if unsupported. * @throws IOException on error. @@ -69,7 +69,7 @@ public byte[] getTIPCNodeId(int peer) throws IOException { /** * Retrieves the TIPC link name given a node hash and bearer Id. - * + * * @param peer The node hash. * @param bearerId The bearer Id. * @return The link name, or {@code null} if unsupported. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramChannel.java index 79e2369d4..e9bece9db 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A {@link DatagramChannel} implementation that works with AF_UNIX Unix domain sockets. - * + * * @author Christian Kohlschütter */ public final class AFUNIXDatagramChannel extends AFDatagramChannel implements diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocket.java index ab3329d5e..3f332bc9e 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * A {@link DatagramSocket} implementation that works with AF_UNIX Unix domain sockets. - * + * * @author Christian Kohlschütter */ public final class AFUNIXDatagramSocket extends AFDatagramSocket implements @@ -34,6 +34,11 @@ public final class AFUNIXDatagramSocket extends AFDatagramSocket { + return new AFUNIXDatagramSocket(fd, socketType); + }); + } + static AFUNIXDatagramSocket newInstance(FileDescriptor fdObj, int localPort, int remotePort) throws IOException { return (AFUNIXDatagramSocket) newInstance(AFUNIXDatagramSocket::new, fdObj, localPort, diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocketImpl.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocketImpl.java index 4afca7d5c..669542c03 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocketImpl.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXDatagramSocketImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,11 @@ final class AFUNIXDatagramSocketImpl extends AFDatagramSocketImpl { AFUNIXDatagramSocketImpl(FileDescriptor fd) throws IOException { - super(AFUNIXSocketAddress.AF_UNIX, fd, AFSocketType.SOCK_DGRAM); + this(fd, AFSocketType.SOCK_DGRAM); + } + + AFUNIXDatagramSocketImpl(FileDescriptor fd, AFSocketType socketType) throws IOException { + super(AFUNIXSocketAddress.AF_UNIX, fd, socketType); } AFUNIXSocketCredentials getPeerCredentials() throws IOException { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXProtocolFamily.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXProtocolFamily.java index 5e8411bb2..9b6074a82 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXProtocolFamily.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXProtocolFamily.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * Describes the protocol family supported by {@link AFUNIXSocketAddress} etc. - * + * * @author Christian Kohlschütter */ public enum AFUNIXProtocolFamily implements ProtocolFamily { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSelectorProvider.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSelectorProvider.java index 9dbf8344c..48c247153 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSelectorProvider.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSelectorProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,7 @@ private AFUNIXSelectorProvider() { /** * Returns the singleton instance. - * + * * @return The instance. */ @SuppressFBWarnings("MS_EXPOSE_REP") @@ -98,7 +98,7 @@ public static AFUNIXSelectorProvider getInstance() { /** * Returns the singleton instance. - * + * * @return The instance. */ public static AFUNIXSelectorProvider provider() { @@ -107,7 +107,7 @@ public static AFUNIXSelectorProvider provider() { @Override protected

    AFSocketPair

    newSocketPair(P s1, P s2) { - return new AFUNIXSocketPair

    (s1, s2); + return new AFUNIXSocketPair<>(s1, s2); } @SuppressWarnings("unchecked") @@ -122,6 +122,13 @@ public AFUNIXSocketPair openDatagramChannelPair() throws return (AFUNIXSocketPair) super.openDatagramChannelPair(); } + @SuppressWarnings("unchecked") + @Override + public AFUNIXSocketPair openDatagramChannelPair(AFSocketType type) + throws IOException { + return (AFUNIXSocketPair) super.openDatagramChannelPair(type); + } + @Override protected AFUNIXSocket newSocket() throws IOException { return AFUNIXSocket.newInstance(); @@ -132,6 +139,11 @@ public AFUNIXDatagramChannel openDatagramChannel() throws IOException { return AFUNIXDatagramSocket.newInstance().getChannel(); } + @Override + public AFUNIXDatagramChannel openDatagramChannel(AFSocketType type) throws IOException { + return AFUNIXDatagramSocket.newInstance(type).getChannel(); + } + @Override public AFUNIXDatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { return (AFUNIXDatagramChannel) super.openDatagramChannel(family); diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocket.java index d2d75fe1b..b30abaa81 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,13 +26,13 @@ /** * The server part of an AF_UNIX domain socket. - * + * * @author Christian Kohlschütter */ public class AFUNIXServerSocket extends AFServerSocket { /** * Constructs a new, unconnected instance. - * + * * @throws IOException if the operation fails. */ protected AFUNIXServerSocket() throws IOException { @@ -41,7 +41,7 @@ protected AFUNIXServerSocket() throws IOException { /** * Constructs a new instance, optionally associated with the given file descriptor. - * + * * @param fdObj The file descriptor, or {@code null}. * @throws IOException if the operation fails. */ @@ -61,7 +61,7 @@ public AFUNIXServerSocketChannel getChannel() { /** * Returns a new, unbound AF_UNIX {@link ServerSocket}. - * + * * @return The new, unbound {@link AFServerSocket}. * @throws IOException if the operation fails. */ @@ -78,7 +78,7 @@ static AFUNIXServerSocket newInstance(FileDescriptor fdObj, int localPort, int r /** * Returns a new AF_UNIX {@link ServerSocket} that is bound to the given * {@link AFUNIXSocketAddress}. - * + * * @param addr The socket file to bind to. * @return The new, bound {@link AFServerSocket}. * @throws IOException if the operation fails. @@ -89,7 +89,7 @@ public static AFUNIXServerSocket bindOn(final AFUNIXSocketAddress addr) throws I /** * Returns a new AF_UNIX {@link ServerSocket} that is bound to the given {@link AFSocketAddress}. - * + * * @param addr The socket file to bind to. * @param deleteOnClose If {@code true}, the socket file (if the address points to a file) will be * deleted upon {@link #close}. @@ -103,7 +103,7 @@ public static AFUNIXServerSocket bindOn(final AFUNIXSocketAddress addr, boolean /** * Returns a new AF_UNIX {@link ServerSocket} that is bound to the given path. - * + * * @param path The path to bind to. * @param deleteOnClose If {@code true}, the socket file will be deleted upon {@link #close}. * @return The new, bound {@link AFServerSocket}. @@ -116,7 +116,7 @@ public static AFUNIXServerSocket bindOn(final File path, boolean deleteOnClose) /** * Returns a new AF_UNIX {@link ServerSocket} that is bound to the given path. - * + * * @param path The path to bind to. * @param deleteOnClose If {@code true}, the socket file will be deleted upon {@link #close}. * @return The new, bound {@link AFServerSocket}. @@ -131,7 +131,7 @@ public static AFUNIXServerSocket bindOn(final Path path, boolean deleteOnClose) /** * Returns a new, unbound AF_UNIX {@link ServerSocket} that will always bind to the given * address, regardless of any socket address used in a call to bind. - * + * * @param forceAddr The address to use. * @return The new, yet unbound {@link AFServerSocket}. * @throws IOException if an exception occurs. @@ -142,13 +142,13 @@ public static AFUNIXServerSocket forceBindOn(final AFUNIXSocketAddress forceAddr } @Override - protected AFUNIXSocketImpl newImpl(FileDescriptor fdObj) throws SocketException { + protected AFSocketImpl newImpl(FileDescriptor fdObj) throws SocketException { return new AFUNIXSocketImpl(fdObj); } /** * Returns a new {@link AFSocket} instance. - * + * * @return The new instance. * @throws IOException on error. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocketChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocketChannel.java index be58f5b93..77d8a7ab1 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocketChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXServerSocketChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * A selectable channel for stream-oriented listening sockets. - * + * * @author Christian Kohlschütter */ public final class AFUNIXServerSocketChannel extends AFServerSocketChannel { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocket.java index a4313402e..6c87662f8 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * Implementation of an AF_UNIX domain socket. - * + * * @author Christian Kohlschütter */ public final class AFUNIXSocket extends AFSocket implements @@ -58,12 +58,12 @@ protected AFUNIXSocketChannel newChannel() { /** * Creates a new, unbound {@link AFSocket}. - * + * * This "default" implementation is a bit "lenient" with respect to the specification. - * + * * In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and * {@link Socket#setTcpNoDelay(boolean)}. - * + * * @return A new, unbound socket. * @throws IOException if the operation fails. */ @@ -87,10 +87,10 @@ static AFUNIXSocket newInstance(AFUNIXSocketFactory factory) throws SocketExcept /** * Creates a new, unbound, "strict" {@link AFSocket}. - * + * * This call uses an implementation that tries to be closer to the specification than * {@link #newInstance()}, at least for some cases. - * + * * @return A new, unbound socket. * @throws IOException if the operation fails. */ @@ -100,7 +100,7 @@ public static AFUNIXSocket newStrictInstance() throws IOException { /** * Creates a new {@link AFSocket} and connects it to the given {@link AFUNIXSocketAddress}. - * + * * @param addr The address to connect to. * @return A new, connected socket. * @throws IOException if the operation fails. @@ -147,14 +147,14 @@ public boolean hasOutboundFileDescriptors() { /** * Returns true iff {@link AFUNIXSocket}s are supported by the current Java VM. - * + * * To support {@link AFSocket}s, a custom JNI library must be loaded that is supplied with * junixsocket, and the system must support AF_UNIX sockets. - * + * * This call is equivalent to checking {@link AFSocket#isSupported()} and * {@link AFSocket#supports(AFSocketCapability)} with * {@link AFSocketCapability#CAPABILITY_UNIX_DOMAIN}. - * + * * @return {@code true} iff supported. */ public static boolean isSupported() { @@ -163,9 +163,9 @@ public static boolean isSupported() { /** * Very basic self-test function. - * + * * Prints "supported" and "capabilities" status to System.out. - * + * * @param args ignored. */ public static void main(String[] args) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketAddress.java index 4325b2a56..72b35db5c 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,20 +35,21 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import org.eclipse.jdt.annotation.NonNull; /** * Describes an {@link InetSocketAddress} that actually uses AF_UNIX sockets instead of AF_INET. - * + * * The ability to specify a port number is not specified by AF_UNIX sockets, but we need it * sometimes, for example for RMI-over-AF_UNIX. - * + * * @author Christian Kohlschütter */ @SuppressWarnings("PMD.ShortMethodName") public final class AFUNIXSocketAddress extends AFSocketAddress { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // do not change! private static final Charset ADDRESS_CHARSET = Charset.defaultCharset(); @@ -57,6 +58,10 @@ public final class AFUNIXSocketAddress extends AFSocketAddress { .registerAddressFamily("un", // AFUNIXSocketAddress.class, new AFSocketAddressConfig() { + private final AFSocketAddressConstructor addrConstr = + isUseDeserializationForInit() ? AFUNIXSocketAddress::newAFSocketAddress + : AFUNIXSocketAddress::new; + @Override public AFUNIXSocketAddress parseURI(URI u, int port) throws SocketException { return AFUNIXSocketAddress.of(u, port); @@ -64,7 +69,7 @@ public AFUNIXSocketAddress parseURI(URI u, int port) throws SocketException { @Override protected AFSocketAddressConstructor addressConstructor() { - return AFUNIXSocketAddress::new; + return addrConstr; } @Override @@ -86,12 +91,13 @@ private AFUNIXSocketAddress(int port, final byte[] socketAddress, ByteBuffer nat /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * file and port. Legacy constructor, do not use! - * + * * @param socketFile The socket to connect to. * @throws SocketException if the operation fails. * @deprecated Use {@link #of(File)} instead. * @see #of(File) */ + @Deprecated public AFUNIXSocketAddress(File socketFile) throws SocketException { this(socketFile, 0); } @@ -99,22 +105,29 @@ public AFUNIXSocketAddress(File socketFile) throws SocketException { /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * file. Legacy constructor, do not use! - * + * * @param socketFile The socket to connect to. * @param port The port associated with this socket, or {@code 0} when no port should be assigned. * @throws SocketException if the operation fails. * @deprecated Use {@link #of(File, int)} instead. * @see #of(File, int) */ + @Deprecated public AFUNIXSocketAddress(File socketFile, int port) throws SocketException { this(port, of(socketFile, port).getPathAsBytes(), of(socketFile, port) .getNativeAddressDirectBuffer()); } + private static AFUNIXSocketAddress newAFSocketAddress(int port, final byte[] socketAddress, + ByteBuffer nativeAddress) throws SocketException { + return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, AF_UNIX, + AFUNIXSocketAddress::new); + } + /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * file. - * + * * @param socketFile The socket to connect to. * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws SocketException if the operation fails. @@ -126,7 +139,7 @@ public static AFUNIXSocketAddress of(final File socketFile) throws SocketExcepti /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * file, assigning the given port to it. - * + * * @param socketFile The socket to connect to. * @param port The port associated with this socket, or {@code 0} when no port should be assigned. * @return A corresponding {@link AFUNIXSocketAddress} instance. @@ -139,10 +152,10 @@ public static AFUNIXSocketAddress of(final File socketFile, int port) throws Soc /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * byte sequence. - * + * * NOTE: By specifying a byte array that starts with a zero byte, you indicate that the abstract * namespace is to be used. This feature is not available on all target platforms. - * + * * @param socketAddress The socket address (as bytes). * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws SocketException if the operation fails. @@ -155,7 +168,7 @@ public static AFUNIXSocketAddress of(final byte[] socketAddress) throws SocketEx /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * byte sequence, assigning the given port to it. - * + * * NOTE: By specifying a byte array that starts with a zero byte, you indicate that the abstract * namespace is to be used. This feature is not available on all target platforms. * @@ -173,31 +186,35 @@ public static AFUNIXSocketAddress of(final byte[] socketAddress, int port) /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * path. - * + * * @param socketPath The socket to connect to. * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws SocketException if the operation fails. */ - public static AFUNIXSocketAddress of(final Path socketPath) throws SocketException { + public static AFUNIXSocketAddress of(Path socketPath) throws SocketException { return of(socketPath, 0); } /** * Returns an {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the given * path, assigning the given port to it. - * + * * @param socketPath The socket to connect to. * @param port The port associated with this socket, or {@code 0} when no port should be assigned. * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws SocketException if the operation fails. */ - public static AFUNIXSocketAddress of(final Path socketPath, int port) throws SocketException { + public static AFUNIXSocketAddress of(Path socketPath, int port) throws SocketException { + if (!PathUtil.isPathInDefaultFileSystem(socketPath)) { + throw new SocketException("Path is not in the default file system"); + } + return of(socketPath.toString().getBytes(ADDRESS_CHARSET), port); } /** * Returns an {@link AFUNIXSocketAddress} for the given URI, if possible. - * + * * @param u The URI. * @return The address. * @throws SocketException if the operation fails. @@ -208,7 +225,7 @@ public static AFUNIXSocketAddress of(URI u) throws SocketException { /** * Returns an {@link AFUNIXSocketAddress} for the given URI, if possible. - * + * * @param u The URI. * @param overridePort The port to forcibly use, or {@code -1} for "don't override". * @return The address. @@ -240,7 +257,7 @@ public static AFUNIXSocketAddress of(URI u, int overridePort) throws SocketExcep /** * Returns an {@link AFUNIXSocketAddress} that points to a temporary, non-existent but accessible * path in the file system. - * + * * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws IOException if the operation fails. */ @@ -251,7 +268,7 @@ public static AFUNIXSocketAddress ofNewTempFile() throws IOException { /** * Returns an {@link AFUNIXSocketAddress} that points to a temporary, non-existent but accessible * path in the file system, assigning the given port to it. - * + * * @param port The port associated with this socket, or {@code 0} when no port should be assigned. * @return A corresponding {@link AFUNIXSocketAddress} instance. * @throws IOException if the operation fails. @@ -260,6 +277,24 @@ public static AFUNIXSocketAddress ofNewTempPath(int port) throws IOException { return of(newTempPath(true), port); } + /** + * Returns an {@link AFUNIXSocketAddress} based on the given {@link SocketAddress}. + * + * This either simply casts an existing {@link AFUNIXSocketAddress}, or converts a + * {@code UnixDomainSocketAddress} to it. + * + * @param address The address to convert. + * @return A corresponding {@link AFUNIXSocketAddress} instance. + * @throws SocketException if the operation fails. + */ + public static AFUNIXSocketAddress of(SocketAddress address) throws IOException { + AFUNIXSocketAddress addr = unwrap(Objects.requireNonNull(address)); + if (addr == null) { + throw new SocketException("Could not convert SocketAddress to AFUNIXSocketAddress"); + } + return addr; + } + static File newTempPath(boolean deleteOnExit) throws IOException { File f = File.createTempFile("jux", ".sock"); if (deleteOnExit) { @@ -274,7 +309,7 @@ static File newTempPath(boolean deleteOnExit) throws IOException { /** * Returns an {@link AFUNIXSocketAddress} given a special {@link InetAddress} that encodes the * byte sequence of an AF_UNIX socket address, like those returned by {@link #wrapAddress()}. - * + * * @param address The "special" {@link InetAddress}. * @param port The port (use 0 for "none"). * @return The {@link AFUNIXSocketAddress} instance. @@ -287,7 +322,7 @@ public static AFUNIXSocketAddress unwrap(InetAddress address, int port) throws S /** * Returns an {@link AFUNIXSocketAddress} given a generic {@link SocketAddress}. - * + * * @param address The address to unwrap. * @return The {@link AFUNIXSocketAddress} instance. * @throws SocketException if the operation fails, for example when an unsupported address is @@ -296,17 +331,18 @@ public static AFUNIXSocketAddress unwrap(InetAddress address, int port) throws S public static AFUNIXSocketAddress unwrap(SocketAddress address) throws SocketException { // FIXME: add support for UnixDomainSocketAddress Objects.requireNonNull(address); - if (!isSupportedAddress(address)) { + Supplier supplier = supportedAddressSupplier(address); + if (supplier == null) { throw new SocketException("Unsupported address"); } - return (AFUNIXSocketAddress) address; + return supplier.get(); } /** * Returns an {@link AFUNIXSocketAddress} given a special {@link InetAddress} hostname that * encodes the byte sequence of an AF_UNIX socket address, like those returned by * {@link #wrapAddress()}. - * + * * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}. * @param port The port (use 0 for "none"). * @return The {@link AFUNIXSocketAddress} instance. @@ -319,11 +355,11 @@ public static AFUNIXSocketAddress unwrap(String hostname, int port) throws Socke /** * Convenience method to create an {@link AFUNIXSocketAddress} in the abstract namespace. - * + * * The returned socket address will use the byte representation of this identifier (using the * system's default character encoding), prefixed with a null byte (to indicate the abstract * namespace is used). - * + * * @param name The identifier in the abstract namespace, without trailing zero or @. * @return The address. * @throws SocketException if the operation fails. @@ -334,11 +370,11 @@ public static AFUNIXSocketAddress inAbstractNamespace(String name) throws Socket /** * Convenience method to create an {@link AFUNIXSocketAddress} in the abstract namespace. - * + * * The returned socket address will use the byte representation of this identifier (using the * system's default character encoding), prefixed with a null byte (to indicate the abstract * namespace is used). - * + * * @param name The identifier in the abstract namespace, without trailing zero or @. * @param port The port associated with this socket, or {@code 0} when no port should be assigned. * @return The address. @@ -380,10 +416,10 @@ public String toString() { /** * Returns the path to the UNIX domain socket, as a human-readable string using the default * encoding. - * + * * For addresses in the abstract namespace, the US_ASCII encoding is used; zero-bytes are * converted to '@', other non-printable bytes are converted to '.' - * + * * @return The path. * @see #getPathAsBytes() */ @@ -411,10 +447,10 @@ public String getPath() { /** * Returns the {@link Charset} used to encode/decode {@link AFUNIXSocketAddress}es. - * + * * This is usually the system default charset, unless that is {@link StandardCharsets#US_ASCII} * (7-bit), in which case {@link StandardCharsets#ISO_8859_1} is used instead. - * + * * @return The charset. */ public static Charset addressCharset() { @@ -423,7 +459,7 @@ public static Charset addressCharset() { /** * Returns the path to the UNIX domain socket, as bytes. - * + * * @return The path. * @see #getPath() */ @@ -432,8 +468,9 @@ public byte[] getPathAsBytes() { } /** - * Checks if the address is in the abstract namespace. - * + * Checks if the address is in the abstract namespace (or, for Haiku OS, in the internal + * namespace). + * * @return {@code true} if the address is in the abstract namespace. */ public boolean isInAbstractNamespace() { @@ -462,7 +499,7 @@ public File getFile() throws FileNotFoundException { /** * Checks if an {@link InetAddress} can be unwrapped to an {@link AFUNIXSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #wrapAddress() @@ -474,18 +511,35 @@ public static boolean isSupportedAddress(InetAddress addr) { /** * Checks if a {@link SocketAddress} can be unwrapped to an {@link AFUNIXSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #unwrap(InetAddress, int) */ public static boolean isSupportedAddress(SocketAddress addr) { - return (addr instanceof AFUNIXSocketAddress); + return supportedAddressSupplier(addr) != null; + } + + /** + * Checks if the given address can be unwrapped to an {@link AFUNIXSocketAddress}, and if so, + * returns a supplier function; if not, {@code null} is returned. + * + * @param addr The address. + * @return The supplier, or {@code null}. + */ + static Supplier supportedAddressSupplier(SocketAddress addr) { + if (addr == null) { + return null; + } else if (addr instanceof AFUNIXSocketAddress) { + return () -> ((AFUNIXSocketAddress) addr); + } else { + return SocketAddressUtil.supplyAFUNIXSocketAddress(addr); + } } /** * Returns the corresponding {@link AFAddressFamily}. - * + * * @return The address family instance. */ @SuppressWarnings("null") diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCapability.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCapability.java index 8e56621a8..4cbf992e6 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCapability.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCapability.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ /** * Describes junixsocket capabilities the current environment (system platform, native library, * etc.) may or may not support. - * + * * You can check whether your environment supports a given capability by calling * {@link AFUNIXSocket#supports(AFUNIXSocketCapability)}. - * + * * This enum is deprecated. Use {@link AFSocketCapability} instead. - * + * * @see AFSocketCapability */ @Deprecated @@ -49,7 +49,7 @@ public enum AFUNIXSocketCapability { /** * A pair of interconnected sockets can be created natively. - * + * * This currently not possible on Windows, but instead emulated via anonymous AF_INET ports when * you use {@link AFUNIXSocketPair}. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketChannel.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketChannel.java index a5f27583f..8a14096c5 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketChannel.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketChannel.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * A selectable channel for stream-oriented connecting sockets. - * + * * @author Christian Kohlschütter */ public final class AFUNIXSocketChannel extends AFSocketChannel implements diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCredentials.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCredentials.java index 28b7f7c93..575b57f69 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCredentials.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketCredentials.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ public final class AFUNIXSocketCredentials implements Serializable { /** * Returns the "pid" (process ID), or {@code -1} if it could not be retrieved. - * + * * @return The pid, or -1. */ public long getPid() { @@ -72,7 +72,7 @@ public long getPid() { /** * Returns the "uid" (user ID), or {@code -1} if it could not be retrieved. - * + * * @return The uid, or -1. */ public long getUid() { @@ -81,7 +81,7 @@ public long getUid() { /** * Returns the primary "gid" (group ID), or {@code -1} if it could not be retrieved. - * + * * @return The gid, or -1. */ public long getGid() { @@ -90,10 +90,10 @@ public long getGid() { /** * Returns all "gid" values (group IDs), or {@code null} if they could not be retrieved. - * + * * Note that this list may be incomplete (only the primary gid may be returned), but it is * guaranteed that the first one in the list is the primary gid as returned by {@link #getGid()}. - * + * * @return The gids, or null. */ public long[] getGids() { @@ -103,7 +103,7 @@ public long[] getGids() { /** * Returns the process' unique identifier, or {@code null} if no such identifier could be * retrieved. Note that all processes run by the same Java runtime may share the same UUID. - * + * * @return The UUID, or null. */ public UUID getUUID() { @@ -120,7 +120,7 @@ void setGids(long[] gids) { /** * Checks if neither of the possible peer credentials are set. - * + * * @return {@code true} if no credentials set. */ public boolean isEmpty() { @@ -201,10 +201,10 @@ public boolean equals(Object obj) { /** * Returns the {@link AFUNIXSocketCredentials} for the currently active remote session, or * {@code null} if it was not possible to retrieve these credentials. - * + * * NOTE: For now, only RMI remote sessions are supported (RemoteServer sessions during a remote * method invocation). - * + * * If you want to retrieve the peer credentials for an RMI server, see junixsocket-rmi's * RemotePeerInfo. * diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketExtensions.java index a59ea780d..0045f7297 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketExtensions.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketExtensions.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,23 +24,23 @@ /** * Defines certain methods that all junixsocket AF_UNIX socket implementations share and extend * beyond the standard socket API. - * + * * The set of features include methods to support working with ancillary messages (such as file * descriptors) as well as socket credentials. - * + * * Keep in mind that the platform this code runs on may not support these features, and exceptions * may be thrown when not checking for the corresponding {@link AFUNIXSocketCapability} first. - * + * * @author Christian Kohlschütter */ public interface AFUNIXSocketExtensions extends AFSocketExtensions { /** * Retrieves an array of incoming {@link FileDescriptor}s that were sent as ancillary messages, * along with a call to {@link InputStream#read()}, etc. - * + * * NOTE: Another call to this method will not return the same file descriptors again (most likely, * an empty array will be returned). - * + * * @return The file descriptors, or an empty array if none were available. * @throws IOException if the operation fails. */ @@ -54,15 +54,15 @@ public interface AFUNIXSocketExtensions extends AFSocketExtensions { /** * Sets a list of {@link FileDescriptor}s that should be sent as an ancillary message along with * the next write. - * + * * Important: There can only be one set of file descriptors active until the write completes. The * socket also needs to be connected for this operation to succeed. - * + * * It is also important to know that there may be an upper limit imposed by the operation system * as to how many file descriptors can be sent at once. Linux, for example, may support up to 253. * If the number of file descriptors exceeds the limit, an exception may be thrown when sending * data along with the ancillary message containing the file descriptors. - * + * * @param fdescs The file descriptors, or {@code null} if none. * @throws IOException if the operation fails. */ @@ -71,7 +71,7 @@ public interface AFUNIXSocketExtensions extends AFSocketExtensions { /** * Returns {@code true} if there are pending file descriptors to be sent as part of an ancillary * message. - * + * * @return {@code true} if there are file descriptors pending. */ boolean hasOutboundFileDescriptors(); @@ -80,7 +80,7 @@ public interface AFUNIXSocketExtensions extends AFSocketExtensions { * Retrieves the "peer credentials" for this connection. * * These credentials may be useful to authenticate the other end of the socket (client or server). - * + * * Depending on the socket/connection/environment, you may not receive any or all credentials. For * example, on Linux, {@link AFUNIXDatagramSocket} and {@link AFUNIXDatagramChannel} may not be * able to retrieve credentials at all. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketFactory.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketFactory.java index 3ac279104..46b3e18bd 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketFactory.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,10 +28,10 @@ /** * The base for a SocketFactory that connects to UNIX sockets. - * + * * Typically, the "hostname" is used as a reference to a socketFile on the file system. The actual * mapping is left to the implementor. - * + * * @see AFUNIXSocketFactory.FactoryArg * @see AFUNIXSocketFactory.SystemProperty * @see AFUNIXSocketFactory.URIScheme @@ -83,9 +83,9 @@ private static String getDefaultSocketHostname() { * A socket factory that handles a custom hostname ("localhost", by default, and configured by the * system property "org.newsclub.net.unix.socket.hostname"), forwarding all other * requests to the fallback {@link SocketFactory}. - * + * * The socket path is configured through an argument passed by to the constructor. - * + * * This is particularly useful for JDBC drivers that take a "socketFactory" and a * "socketFactoryArg". The latter will be passed as a constructor argument. */ @@ -94,7 +94,7 @@ public static final class FactoryArg extends DefaultSocketHostnameSocketFactory /** * Constructs a new {@link FactoryArg} factory using the given socket path. - * + * * @param socketPath The path to the socket. */ public FactoryArg(String socketPath) { @@ -106,7 +106,7 @@ public FactoryArg(String socketPath) { /** * Constructs a new {@link FactoryArg} factory using the given socket path. - * + * * @param file The path to the socket. */ public FactoryArg(File file) { @@ -126,10 +126,10 @@ public AFUNIXSocketAddress addressFromHost(String host, int port) throws SocketE * A socket factory that handles a custom hostname ("junixsocket.localhost", by default, and * configured by the system property "org.newsclub.net.unix.socket.hostname"), * forwarding all other requests to the fallback {@link SocketFactory}. - * + * * The socket path is configured through a system property, * "org.newsclub.net.unix.socket.default". - * + * * NOTE: While it is technically possible, it is highly discouraged to programmatically change the * value of the property as it can lead to concurrency issues and undefined behavior. */ @@ -157,12 +157,12 @@ public AFUNIXSocketAddress addressFromHost(String host, int port) throws SocketE /** * A socket factory that handles special host names formatted as file:// URIs. - * + * * The file:// URI may also be specified in URL-encoded format, i.e., file:%3A%2F%2F etc. - * + * * You may also surround the URL with square brackets ("[" and "]"), whereas the closing bracket * may be omitted. - * + * * NOTE: In some circumstances it is recommended to use "[file:%3A%2F%2F(...)", i.e. * encoded and without the closing bracket. Since this is an invalid hostname, it will not trigger * a DNS lookup, but can still be used within a JDBC Connection URL. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java index 35934871b..b18342770 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,18 +23,18 @@ import java.net.SocketException; class AFUNIXSocketImpl extends AFSocketImpl { - protected AFUNIXSocketImpl(FileDescriptor fdObj) throws SocketException { + protected AFUNIXSocketImpl(FileDescriptor fdObj) { super(AFUNIXSocketAddress.AF_UNIX, fdObj); } /** * Changes the behavior to be somewhat lenient with respect to the specification. - * + * * In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and * {@link Socket#setTcpNoDelay(boolean)}. */ static final class Lenient extends AFUNIXSocketImpl { - protected Lenient(FileDescriptor fdObj) throws SocketException { + Lenient(FileDescriptor fdObj) throws SocketException { super(fdObj); } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketPair.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketPair.java index 08e22cbee..6d43c03c9 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketPair.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFUNIXSocketPair.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,11 @@ package org.newsclub.net.unix; import java.io.IOException; +import java.nio.channels.DatagramChannel; /** * A pair of sockets. - * + * * @param The socket type. * @author Christian Kohlschütter */ @@ -32,7 +33,7 @@ public final class AFUNIXSocketPair extends AFSocketPair /** * Opens a socket pair of interconnected channels. - * + * * @return The new channel pair. * @throws IOException on error. */ @@ -42,11 +43,23 @@ public static AFUNIXSocketPair open() throws IOException { /** * Opens a socket pair of interconnected datagram channels. - * + * * @return The new channel pair. * @throws IOException on error. */ public static AFUNIXSocketPair openDatagram() throws IOException { return AFUNIXSelectorProvider.provider().openDatagramChannelPair(); } + + /** + * Opens a socket pair of interconnected {@link DatagramChannel}s, using the given socket type. + * + * @param type The socket type. + * @return The new channel pair. + * @throws IOException on error. + */ + public static AFUNIXSocketPair openDatagram(AFSocketType type) + throws IOException { + return AFUNIXSelectorProvider.provider().openDatagramChannelPair(type); + } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketAddress.java index a9c8012e0..d9cc795e8 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,11 +35,11 @@ /** * An {@link AFSocketAddress} for VSOCK sockets. - * + * * @author Christian Kohlschütter */ public final class AFVSOCKSocketAddress extends AFSocketAddress { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // do not change! private static final Pattern PAT_VSOCK_URI_HOST_AND_PORT = Pattern.compile( "^(?any|[0-9a-fx\\-]+)(\\.(?any|hypervisor|local|host|[0-9a-fx\\-]+))?(?:\\:(?[0-9]+))?$"); @@ -76,6 +76,12 @@ private AFVSOCKSocketAddress(int port, final byte[] socketAddress, ByteBuffer na super(port, socketAddress, nativeAddress, addressFamily()); } + private static AFVSOCKSocketAddress newAFSocketAddress(int port, final byte[] socketAddress, + ByteBuffer nativeAddress) throws SocketException { + return newDeserializedAFSocketAddress(port, socketAddress, nativeAddress, addressFamily(), + AFVSOCKSocketAddress::new); + } + /** * Returns an {@link AFVSOCKSocketAddress} that refers to a given VSOCK port and CID; the "java * port" is set to -1. @@ -104,7 +110,7 @@ public static AFVSOCKSocketAddress ofHypervisorPort(int port) throws SocketExcep /** * Returns an {@link AFVSOCKSocketAddress}, especially useful for binding, that refers to "any" * port on the hypervisor; the "java port" is set to -1. - * + * * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. */ @@ -115,7 +121,7 @@ public static AFVSOCKSocketAddress ofAnyHypervisorPort() throws SocketException /** * Returns an {@link AFVSOCKSocketAddress} that refers to the given port with the local/loopback * CID; the "java port" is set to -1. - * + * * @param port The VSOCK port. * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. @@ -127,7 +133,7 @@ public static AFVSOCKSocketAddress ofLocalPort(int port) throws SocketException /** * Returns an {@link AFVSOCKSocketAddress}, especially useful for binding, that refers to "any" * port with the local/loopback CID; the "java port" is set to -1. - * + * * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. */ @@ -150,7 +156,7 @@ public static AFVSOCKSocketAddress ofHostPort(int port) throws SocketException { /** * Returns an {@link AFVSOCKSocketAddress}, especially useful for binding, that refers to "any" * port on the host; the "java port" is set to -1. - * + * * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. */ @@ -161,7 +167,7 @@ public static AFVSOCKSocketAddress ofAnyHostPort() throws SocketException { /** * Returns an {@link AFVSOCKSocketAddress}, especially useful for binding, that refers to "any" * port and CID; the "java port" is set to -1. - * + * * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. */ @@ -172,7 +178,7 @@ public static AFVSOCKSocketAddress ofAnyPort() throws SocketException { /** * Returns an {@link AFVSOCKSocketAddress}, especially useful for binding, that refers to the * given port with "any CID"; the "java port" is set to -1. - * + * * @param port The VSOCK port. * @return A corresponding {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails. @@ -198,7 +204,7 @@ public static AFVSOCKSocketAddress ofPortAndCID(int javaPort, int vsockPort, int /** * Returns an {@link AFVSOCKSocketAddress} given a special {@link InetAddress} that encodes the * byte sequence of an AF_VSOCK socket address, like those returned by {@link #wrapAddress()}. - * + * * @param address The "special" {@link InetAddress}. * @param port The port (use 0 for "none"). * @return The {@link AFVSOCKSocketAddress} instance. @@ -213,7 +219,7 @@ public static AFVSOCKSocketAddress unwrap(InetAddress address, int port) throws * Returns an {@link AFVSOCKSocketAddress} given a special {@link InetAddress} hostname that * encodes the byte sequence of an AF_VSOCK socket address, like those returned by * {@link #wrapAddress()}. - * + * * @param hostname The "special" hostname, as provided by {@link InetAddress#getHostName()}. * @param port The port (use 0 for "none"). * @return The {@link AFVSOCKSocketAddress} instance. @@ -226,7 +232,7 @@ public static AFVSOCKSocketAddress unwrap(String hostname, int port) throws Sock /** * Returns an {@link AFVSOCKSocketAddress} given a generic {@link SocketAddress}. - * + * * @param address The address to unwrap. * @return The {@link AFVSOCKSocketAddress} instance. * @throws SocketException if the operation fails, for example when an unsupported address is @@ -242,7 +248,7 @@ public static AFVSOCKSocketAddress unwrap(SocketAddress address) throws SocketEx /** * Returns the "VSOCK port" part of this address. - * + * * @return The VSOCK port identifier * @see #getPort() */ @@ -254,7 +260,7 @@ public int getVSOCKPort() { /** * Returns the "VSOCK CID" part of this address. - * + * * @return The VSOCK CID identifier. */ public int getVSOCKCID() { @@ -265,7 +271,7 @@ public int getVSOCKCID() { /** * Returns the "VSOCK reserved1" part of this address. - * + * * @return The "reserved1" identifier, which should be 0. */ public int getVSOCKReserved1() { @@ -313,7 +319,7 @@ public File getFile() throws FileNotFoundException { /** * Checks if an {@link InetAddress} can be unwrapped to an {@link AFVSOCKSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #wrapAddress() @@ -325,7 +331,7 @@ public static boolean isSupportedAddress(InetAddress addr) { /** * Checks if a {@link SocketAddress} can be unwrapped to an {@link AFVSOCKSocketAddress}. - * + * * @param addr The instance to check. * @return {@code true} if so. * @see #unwrap(InetAddress, int) @@ -345,7 +351,7 @@ private static byte[] toBytes(int port, int cid) { /** * Returns the corresponding {@link AFAddressFamily}. - * + * * @return The address family instance. */ @SuppressWarnings("null") @@ -354,6 +360,10 @@ public static synchronized AFAddressFamily addressFamily() afVsock = AFAddressFamily.registerAddressFamily("vsock", // AFVSOCKSocketAddress.class, new AFSocketAddressConfig() { + private final AFSocketAddressConstructor addrConstr = + isUseDeserializationForInit() ? AFVSOCKSocketAddress::newAFSocketAddress + : AFVSOCKSocketAddress::new; + @Override protected AFVSOCKSocketAddress parseURI(URI u, int port) throws SocketException { return AFVSOCKSocketAddress.of(u, port); @@ -361,7 +371,7 @@ protected AFVSOCKSocketAddress parseURI(URI u, int port) throws SocketException @Override protected AFSocketAddressConstructor addressConstructor() { - return AFVSOCKSocketAddress::new; + return addrConstr; } @Override @@ -385,7 +395,7 @@ protected Set uriSchemes() { /** * Returns an {@link AFVSOCKSocketAddress} for the given URI, if possible. - * + * * @param uri The URI. * @return The address. * @throws SocketException if the operation fails. @@ -397,7 +407,7 @@ public static AFVSOCKSocketAddress of(URI uri) throws SocketException { /** * Returns an {@link AFVSOCKSocketAddress} for the given URI, if possible. - * + * * @param uri The URI. * @param overridePort The port to forcibly use, or {@code -1} for "don't override". * @return The address. @@ -551,13 +561,13 @@ private static int parseInt(String v) { /** * Checks if the given address could cover another address. - * + * * By default, this is only true if both addresses are regarded equal using * {@link #equals(Object)}. - * + * * However, implementations may support "wildcard" addresses, and this method would compare a * wildcard address against some non-wildcard address, for example. - * + * * @param covered The other address that could be covered by this address. * @return {@code true} if the other address could be covered. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketImplExtensions.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketImplExtensions.java index fc7b65e66..2e985958e 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketImplExtensions.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AFVSOCKSocketImplExtensions.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * VSOCK-specific code that resides in the native library. To be used by {@code AFVSOCKSocket} and * {@code AFVSOCKDatagramSocket} only. - * + * * @author Christian Kohlschütter */ public final class AFVSOCKSocketImplExtensions implements @@ -36,12 +36,12 @@ public final class AFVSOCKSocketImplExtensions implements /** * Returns the local CID. - * + * * If the system does not support vsock, or status about support cannot be retrieved, -1 * ({@link AFVSOCKSocketAddress#VMADDR_CID_ANY}) is returned. - * + * * The value may be cached upon initialization of the library. - * + * * @return The CID, or -1. * @throws IOException on error. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AddressUnavailableSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AddressUnavailableSocketException.java index c892bcd13..06db22615 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AddressUnavailableSocketException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AddressUnavailableSocketException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * A {@link SocketException} that may be thrown upon some "address unavailable" condition from * native code (e.g., EADDRNOTAVAIL is returned). - * + * * @author Christian Kohlschütter */ public class AddressUnavailableSocketException extends InvalidSocketException { @@ -37,7 +37,7 @@ public AddressUnavailableSocketException() { /** * Constructs a new {@link AddressUnavailableSocketException}. - * + * * @param msg The error message. */ public AddressUnavailableSocketException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/AncillaryDataSupport.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/AncillaryDataSupport.java index e6c63764d..0bb95ddaf 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/AncillaryDataSupport.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/AncillaryDataSupport.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,11 @@ import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; +import java.net.SocketException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -38,10 +38,10 @@ final class AncillaryDataSupport implements Closeable { .ancillaryBufMinLen() : 0; private final Map openReceivedFileDescriptors = Collections - .synchronizedMap(new HashMap()); + .synchronizedMap(new HashMap<>()); private final List receivedFileDescriptors = Collections.synchronizedList( - new LinkedList()); + new ArrayList<>()); // referenced from native code private ByteBuffer ancillaryReceiveBuffer = EMPTY_BUFFER; @@ -131,7 +131,12 @@ public void close() throws IOException { openReceivedFileDescriptors.remove(fdesc); } }; - NativeUnixSocket.attachCloseable(fdesc, cleanup); + + try { + NativeUnixSocket.attachCloseable(fdesc, cleanup); + } catch (SocketException e) { + // ignore (cannot attach) + } } this.receivedFileDescriptors.add(descriptors); @@ -193,10 +198,12 @@ void setOutboundFileDescriptors(FileDescriptor... fdescs) throws IOException { public void close() { synchronized (openReceivedFileDescriptors) { for (FileDescriptor desc : openReceivedFileDescriptors.keySet()) { - try { - NativeUnixSocket.close(desc); - } catch (Exception e) { - // ignore + if (desc.valid()) { + try { + NativeUnixSocket.close(desc); + } catch (Exception e) { + // ignore + } } } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/BrokenPipeSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/BrokenPipeSocketException.java index 55973cb62..1d9e423a1 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/BrokenPipeSocketException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/BrokenPipeSocketException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * A {@link SocketException} indicating that a socket connection was broken ("broken pipe"). - * + * * @author Christian Kohlschütter */ public final class BrokenPipeSocketException extends SocketException { @@ -36,7 +36,7 @@ public BrokenPipeSocketException() { /** * Constructs a new {@link BrokenPipeSocketException}. - * + * * @param msg The error message. */ public BrokenPipeSocketException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/CleanableState.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/CleanableState.java index e77bfe952..b3db5d71f 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/CleanableState.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/CleanableState.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * This wrapper (along with the Java 8-specific counterpart in src/main/java8) allows us to * implement cleanup logic for objects that are garbage-collectable/no longer reachable. - * + * *

    * Usage: *

      @@ -46,7 +46,7 @@ *
    1. In Java 8 or earlier, {@link #finalize()} calls {@link #doClean()} directly.
    2. *
*

- * + * * @author Christian Kohlschütter */ abstract class CleanableState implements Closeable { @@ -56,7 +56,7 @@ abstract class CleanableState implements Closeable { /** * Creates a state object to be used as an implementation detail of the specified observed * instance. - * + * * @param observed The observed instance (the outer class referencing this * {@link CleanableState}). */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/CloseablePair.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/CloseablePair.java index 557c1eb97..ab86a5dbb 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/CloseablePair.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/CloseablePair.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A pair of two closeable items. - * + * * @param The type of the items. * @author Christian Kohlschütter */ @@ -36,7 +36,7 @@ public class CloseablePair implements Closeable { /** * Creates a pair of two items. - * + * * @param a The first item. * @param b The second item. */ @@ -46,7 +46,7 @@ public CloseablePair(T a, T b) { /** * Creates a pair of two items. - * + * * @param a The first item. * @param b The second item. * @param alsoClose Some closeable that is also closed upon {@link #close()}, or {@code null}. @@ -70,7 +70,7 @@ public final void close() throws IOException { /** * Returns the pair's first item. - * + * * @return The first item. */ public final @NonNull T getFirst() { @@ -79,7 +79,7 @@ public final void close() throws IOException { /** * Returns the pair's second item. - * + * * @return The second item. */ public final @NonNull T getSecond() { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/Closeables.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/Closeables.java index c2c3be5a9..57ec5792d 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/Closeables.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/Closeables.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,10 +26,11 @@ /** * A set of {@link Closeables} that can be closed at once. - * + * * @author Christian Kohlschütter */ public final class Closeables implements Closeable { + private boolean closed = false; private List> list; /** @@ -41,12 +42,13 @@ public Closeables() { /** * Creates a new {@link Closeables} instance, populating it with the given {@link Closeable} * objects. - * + * * @param closeable The {@link Closeable}s to add. */ public Closeables(Closeable... closeable) { + this.list = new ArrayList<>(); for (Closeable cl : closeable) { - this.list.add(new HardReference(cl)); + this.list.add(new HardReference<>(cl)); } } @@ -55,9 +57,18 @@ public void close() throws IOException { close(null); } + /** + * Checks if this instance has been closed already. + * + * @return {@code true} if closed. + */ + public synchronized boolean isClosed() { + return closed; + } + /** * Closes all registered closeables. - * + * * @param superException If set, any exceptions thrown in here will be chained to the given * exception via addSuppressed, and then thrown. * @throws IOException if an exception occurs. @@ -65,21 +76,32 @@ public void close() throws IOException { public void close(IOException superException) throws IOException { IOException exc = superException; - if (list != null) { - for (WeakReference ref : list) { - @SuppressWarnings("resource") - Closeable cl = ref.get(); - if (cl == null) { - continue; - } - try { - cl.close(); - } catch (IOException e) { - if (exc == null) { - exc = e; - } else { - exc.addSuppressed(e); - } + List> l; + synchronized (this) { + closed = true; + + l = this.list; + if (l == null) { + return; + } + + l = new ArrayList<>(l); + this.list = null; + } + + for (WeakReference ref : l) { + @SuppressWarnings("resource") + Closeable cl = ref.get(); + if (cl == null) { + continue; + } + try { + cl.close(); + } catch (IOException e) { + if (exc == null) { + exc = e; + } else { + exc.addSuppressed(e); } } } @@ -106,12 +128,15 @@ public V get() { /** * Adds the given closeable, but only using a weak reference. - * + * * @param closeable The closeable. - * @return {@code true} iff the closeable was added, {@code false} if it was {@code null} or - * already added before. + * @return {@code true} iff the closeable was added, {@code false} if it was {@code null}, already + * added before, or if the {@link Closeables} instance has been closed already. */ public synchronized boolean add(WeakReference closeable) { + if (closed) { + return false; + } Closeable cl = closeable.get(); if (cl == null) { // ignore @@ -133,7 +158,7 @@ public synchronized boolean add(WeakReference closeable) { /** * Adds the given closeable. - * + * * @param closeable The closeable. * @return {@code true} iff the closeable was added, {@code false} if it was {@code null} or * already added before. @@ -144,13 +169,13 @@ public synchronized boolean add(Closeable closeable) { /** * Removes the given closeable. - * + * * @param closeable The closeable. * @return {@code true} iff the closeable was removed, {@code fale} if it was {@code null} or not * previously added. */ public synchronized boolean remove(Closeable closeable) { - if (list == null || closeable == null) { + if (list == null || closeable == null || closed) { return false; } for (Iterator> it = list.iterator(); it.hasNext();) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketImplShim.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketImplShim.java index 6ee295499..1ea0a9f69 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketImplShim.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketImplShim.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * A shim that is filled with Java version-specific overrides. This variant is for Java 9 and above. - * + * * @author Christian Kohlschütter */ abstract class DatagramSocketImplShim extends DatagramSocketImpl { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketShim.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketShim.java new file mode 100644 index 000000000..f9619357d --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/DatagramSocketShim.java @@ -0,0 +1,70 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.DatagramSocketImpl; +import java.net.SocketOption; + +abstract class DatagramSocketShim extends DatagramSocket { + + protected DatagramSocketShim(DatagramSocketImpl impl) { + super(impl); + } + + @Override + public T getOption(SocketOption name) throws IOException { + if (name instanceof AFSocketOption) { + return getOption((AFSocketOption) name); + } else { + return super.getOption(name); + } + } + + @Override + public DatagramSocket setOption(SocketOption name, T value) throws IOException { + if (name instanceof AFSocketOption) { + return setOption((AFSocketOption) name, value); + } else { + return super.setOption(name, value); + } + } + + /** + * Returns the value of a junixsocket socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @return The value of the socket option. + * @throws IOException on error. + */ + public abstract T getOption(AFSocketOption name) throws IOException; + + /** + * Sets the value of a socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @param value The value of the socket option. + * @return this DatagramSocket. + * @throws IOException on error. + */ + @SuppressWarnings("PMD.LinguisticNaming") + public abstract DatagramSocket setOption(AFSocketOption name, T value) throws IOException; +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorAccess.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorAccess.java index 15f5da800..dbad4ae08 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorAccess.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorAccess.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,13 @@ /** * Something that has a {@link FileDescriptor}. - * + * * @author Christian Kohlschütter */ public interface FileDescriptorAccess { /** * Returns the corresponding {@link FileDescriptor}. - * + * * @return The corresponding {@link FileDescriptor}. * @throws IOException on error. */ diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorCast.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorCast.java index e77caa507..6bcb283d1 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorCast.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/FileDescriptorCast.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,16 +46,16 @@ *

*

  * FileDescriptor fd;
- * 
+ *
  * // succeeds if fd refers to an AF_UNIX stream socket
- * AFUNIXSocket socket = FileDescriptorCast.using(fd).as(AFUNIXSocket.class); 
- * 
+ * AFUNIXSocket socket = FileDescriptorCast.using(fd).as(AFUNIXSocket.class);
+ *
  * // succeeds if fd refers to an AF_UNIX datagram socket
- * AFUNIXDatagramChannel channel = FileDescriptorCast.using(fd).as(AFUNIXDatagramChannel.class); 
- * 
+ * AFUNIXDatagramChannel channel = FileDescriptorCast.using(fd).as(AFUNIXDatagramChannel.class);
+ *
  * // always succeeds
- * InputStream in = FileDescriptorCast.using(fd).as(InputStream.class); 
- * OutputStream in = FileDescriptorCast.using(fd).as(OutputStream.class); 
+ * InputStream in = FileDescriptorCast.using(fd).as(InputStream.class);
+ * OutputStream in = FileDescriptorCast.using(fd).as(OutputStream.class);
  * 
*

* IMPORTANT: On some platforms (e.g., Solaris, Illumos) you may need to re-apply a read timeout @@ -66,18 +66,13 @@ * that do not encode this information directly (such as {@link AFUNIXSocketAddress} and * {@link AFTIPCSocketAddress}). *

- * + * * @author Christian Kohlschütter */ public final class FileDescriptorCast implements FileDescriptorAccess { private static final Map, CastingProviderMap> PRIMARY_TYPE_PROVIDERS_MAP = Collections .synchronizedMap(new HashMap<>()); - private final FileDescriptor fdObj; - - private int localPort = 0; - private int remotePort = 0; - private static final Function FD_IS_PROVIDER = System .getProperty("osv.version") != null ? LenientFileInputStream::new : FileInputStream::new; @@ -108,7 +103,6 @@ public WritableByteChannel provideAs(FileDescriptorCast fdc, } }); addProvider(ReadableByteChannel.class, new CastingProvider() { - @SuppressWarnings("resource") @Override public ReadableByteChannel provideAs(FileDescriptorCast fdc, Class desiredType) throws IOException { @@ -138,6 +132,25 @@ public FileInputStream provideAs(FileDescriptorCast fdc, return FD_IS_PROVIDER.apply(fdc.getFileDescriptor()); } }); + addProvider(FileDescriptor.class, new CastingProvider() { + @Override + public FileDescriptor provideAs(FileDescriptorCast fdc, + Class desiredType) throws IOException { + return fdc.getFileDescriptor(); + } + }); + addProvider(Integer.class, new CastingProvider() { + @Override + public Integer provideAs(FileDescriptorCast fdc, Class desiredType) + throws IOException { + FileDescriptor fd = fdc.getFileDescriptor(); + int val = fd.valid() ? NativeUnixSocket.getFD(fd) : -1; + if (val == -1) { + throw new IOException("Not a valid file descriptor"); + } + return val; + } + }); if (AFSocket.supports(AFSocketCapability.CAPABILITY_FD_AS_REDIRECT)) { addProvider(Redirect.class, new CastingProvider() { @@ -156,6 +169,15 @@ public Redirect provideAs(FileDescriptorCast fdc, Class desire } }; + private static final int FD_IN = getFdIfPossible(FileDescriptor.in); + private static final int FD_OUT = getFdIfPossible(FileDescriptor.out); + private static final int FD_ERR = getFdIfPossible(FileDescriptor.err); + + private final FileDescriptor fdObj; + + private int localPort = 0; + private int remotePort = 0; + private final CastingProviderMap cpm; private FileDescriptorCast(FileDescriptor fdObj, CastingProviderMap cpm) { @@ -163,6 +185,20 @@ private FileDescriptorCast(FileDescriptor fdObj, CastingProviderMap cpm) { this.cpm = Objects.requireNonNull(cpm); } + private static int getFdIfPossible(FileDescriptor fd) { + if (!NativeUnixSocket.isLoaded()) { + return -1; + } + try { + if (!fd.valid()) { + return -1; + } + return NativeUnixSocket.getFD(fd); + } catch (IOException e) { + return -1; + } + } + private static void registerCastingProviders(Class primaryType, CastingProviderMap cpm) { Objects.requireNonNull(primaryType); CastingProviderMap prev; @@ -224,6 +260,7 @@ private abstract static class CastingProviderMap { private final Map, CastingProvider> providers = new HashMap<>(); private final Set> classes = Collections.unmodifiableSet(providers.keySet()); + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") protected CastingProviderMap() { addProviders(); @@ -270,7 +307,7 @@ private interface CastingProvider { /** * Creates a {@link FileDescriptorCast} using the given file descriptor. - * + * * @param fdObj The file descriptor. * @return The {@link FileDescriptorCast} instance. * @throws IOException on error, especially if the given file descriptor is invalid or @@ -291,17 +328,72 @@ public static FileDescriptorCast using(FileDescriptor fdObj) throws IOException return new FileDescriptorCast(fdObj, map == null ? GLOBAL_PROVIDERS : map); } + /** + * Creates a {@link FileDescriptorCast} using the given native file descriptor value. + *

+ * This method is inherently unsafe as it may + *

    + *
  1. make assumptions on the internal system representation of a file descriptor (which differs + * between Windows and Unix, for example).
  2. + *
  3. provide access to resources that are otherwise not accessible
  4. + *
+ *

+ * Note that attempts are made to reuse {@link FileDescriptor#in}, {@link FileDescriptor#out}, and + * {@link FileDescriptor#err}, respectively. + * + * @param fd The system-native file descriptor value. + * @return The {@link FileDescriptorCast} instance. + * @throws IOException on error, especially if the given file descriptor is invalid or + * unsupported, or when "unsafe" operations are unavailable or manually disabled for the + * current environment. + */ + @Unsafe + public static FileDescriptorCast unsafeUsing(int fd) throws IOException { + AFSocket.ensureUnsafeSupported(); + + FileDescriptor fdObj; + if (fd == -1) { + throw new IOException("Not a valid file descriptor"); + } else if (fd == FD_IN) { + fdObj = FileDescriptor.in; + } else if (fd == FD_OUT) { + fdObj = FileDescriptor.out; + } else if (fd == FD_ERR) { + fdObj = FileDescriptor.err; + } else { + fdObj = null; + } + + if (fdObj != null) { + int check = getFdIfPossible(fdObj); + if (fd == check) { + return using(fdObj); + } + } + + fdObj = new FileDescriptor(); + NativeUnixSocket.initFD(fdObj, fd); + + return using(fdObj); + } + private static void triggerInit() { - AFUNIXSocketAddress.addressFamily().getClass(); // trigger registration - AFTIPCSocketAddress.addressFamily().getClass(); // trigger registration + for (AFAddressFamily family : new AFAddressFamily[] { + AFUNIXSocketAddress.addressFamily(), // + AFTIPCSocketAddress.addressFamily(), // + AFVSOCKSocketAddress.addressFamily(), // + AFSYSTEMSocketAddress.addressFamily(), // + }) { + Objects.requireNonNull(family.getClass()); // trigger init + } } /** * Registers the given port number as the "local port" for this file descriptor. - * + * * Important: This only changes the state of this instance. The actual file descriptor is not * affected. - * + * * @param port The port to assign to (must be >= 0). * @return This instance. */ @@ -315,10 +407,10 @@ public FileDescriptorCast withLocalPort(int port) { /** * Registers the given port number as the "remote port" for this file descriptor. - * + * * Important: This only changes the state of this instance. The actual file descriptor is not * affected. - * + * * @param port The port to assign to (must be >= 0). * @return This instance. */ @@ -332,7 +424,7 @@ public FileDescriptorCast withRemotePort(int port) { /** * Casts this instance to the desired type. - * + * * @param The desired type. * @param desiredType The class of the desired type. * @return s An instance of the desired type. @@ -357,7 +449,7 @@ public FileDescriptorCast withRemotePort(int port) { /** * Checks if the instance can be cast as the given desired type (using {@link #as(Class)}). - * + * * @param desiredType The class of the desired type. * @return {@code true} if the cast can be made. * @throws IOException on error. @@ -370,7 +462,7 @@ public boolean isAvailable(Class desiredType) throws IOException { /** * Returns a collection of available types this instance can be cast to (using * {@link #as(Class)}). - * + * * @return The collection of available types. */ public Set> availableTypes() { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/HostAndPort.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/HostAndPort.java index ba1782759..01e21991d 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/HostAndPort.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/HostAndPort.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * Hostname and port. - * + * * @author Christian Kohlschütter */ public final class HostAndPort { @@ -39,7 +39,7 @@ public final class HostAndPort { /** * Creates a new hostname and port combination. - * + * * @param hostname The hostname. * @param port The port, or {@code -1} for "no port". */ @@ -88,7 +88,7 @@ public String toString() { /** * Tries to extract hostname and port information from the given URI. - * + * * @param u The URI to extract from. * @return The parsed {@link HostAndPort} instance. * @throws SocketException on error. @@ -130,7 +130,7 @@ private static String urlEncode(String s) { /** * Returns the hostname. - * + * * @return The hostname. */ public String getHostname() { @@ -139,7 +139,7 @@ public String getHostname() { /** * Returns the port, or {@code -1} for "no port specified". - * + * * @return The port. */ public int getPort() { @@ -148,7 +148,7 @@ public int getPort() { /** * Returns a URI with this hostname and port. - * + * * @param scheme The scheme to use. * @return The URI. */ @@ -159,7 +159,7 @@ public URI toURI(String scheme) { /** * Returns a URI with this hostname and port, potentially reusing other URI parameters from the * given template URI (authority, path, query, fragment). - * + * * @param scheme The scheme to use. * @param template The template. or {@code null}. * @return The URI. @@ -192,7 +192,7 @@ public URI toURI(String scheme, URI template) { /** * Returns a URI with this hostname and port, potentially using other URI parameters from the * given set of parameters. - * + * * @param scheme The scheme to use. * @param rawAuthority The raw authority field, or {@code null}. * @param rawPath The raw path field, or {@code null}. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidArgumentSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidArgumentSocketException.java index b644cfc37..6123bb27d 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidArgumentSocketException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidArgumentSocketException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * A {@link SocketException} that may be thrown upon some "invalid argument" being passed into * native code (i.e., EINVAL is returned). - * + * * @author Christian Kohlschütter */ public class InvalidArgumentSocketException extends InvalidSocketException { @@ -37,7 +37,7 @@ public InvalidArgumentSocketException() { /** * Constructs a new {@link InvalidArgumentSocketException}. - * + * * @param msg The error message. */ public InvalidArgumentSocketException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidSocketException.java index c1aaa8ab8..675c9d39d 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidSocketException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/InvalidSocketException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * A {@link SocketException} that may be thrown upon some "invalid" state, mostly detected in native * code. - * + * * @author Christian Kohlschütter * @see InvalidArgumentSocketException * @see AddressUnavailableSocketException @@ -40,7 +40,7 @@ public InvalidSocketException() { /** * Constructs a new {@link InvalidSocketException}. - * + * * @param msg The error message. */ public InvalidSocketException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedInteger.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedInteger.java index 647a32914..29d994398 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedInteger.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedInteger.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,9 +27,9 @@ /** * A "named integer", usually used for constants. - * + * * See the concrete implementations for usage. - * + * * @author Christian Kohlschütter */ @NonNullByDefault @@ -57,7 +57,7 @@ public interface HasOfValue { /** * Creates a new {@link NamedInteger} instance, without actually naming it. A name of "UNDEFINED" * is used. - * + * * @param id The value. */ protected NamedInteger(int id) { @@ -66,7 +66,7 @@ protected NamedInteger(int id) { /** * Creates a new {@link NamedInteger} instance. - * + * * @param name The name. * @param id The value. */ @@ -77,7 +77,7 @@ protected NamedInteger(String name, int id) { /** * Returns the name. - * + * * @return The name. */ public final String name() { @@ -86,7 +86,7 @@ public final String name() { /** * Returns the value. - * + * * @return The value. */ public final int value() { @@ -118,7 +118,7 @@ public final boolean equals(@Nullable Object obj) { /** * Ensures that the {@code VALUES} array is configured correctly. - * + * * @param The instance type. * @param values The {@code VALUES} array. * @return The verified {@code VALUES} array. @@ -135,14 +135,14 @@ protected static final T[] init(T[] values) { /** * Constructor for "undefined" values. - * + * * @param The instance type. */ @FunctionalInterface protected interface UndefinedValueConstructor { /** * Creates a new "undefined" value instance. - * + * * @param id The value. * @return The instance. */ @@ -151,7 +151,7 @@ protected interface UndefinedValueConstructor { /** * Returns an instance given an integer value. - * + * * @param The instance type. * @param values The {@code VALUES} array. * @param constr The constructor for undefined values. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedIntegerBitmask.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedIntegerBitmask.java index af525ded1..e436bbea5 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedIntegerBitmask.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NamedIntegerBitmask.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * Describes a 32-bit bitmask that supports named flags. - * + * * @param The subclass's type itself. * @author Christian Kohlschütter */ @@ -49,7 +49,7 @@ public abstract class NamedIntegerBitmask> impl /** * Creates a new named flag. - * + * * @param name The name of the flag / flag set. * @param flags The flag value. */ @@ -60,7 +60,7 @@ protected NamedIntegerBitmask(@Nullable String name, int flags) { /** * Returns the name of the flag / flag set. - * + * * @return The name. */ public final String name() { @@ -69,7 +69,7 @@ public final String name() { /** * Returns the value of the flag / flag set. - * + * * @return The value. */ public final int value() { @@ -78,7 +78,7 @@ public final int value() { /** * Checks if the given flag is set. - * + * * @param flag The flag to check. * @return {@code true} iff set. */ @@ -95,7 +95,7 @@ public final String toString() { /** * Combines two flags / flag sets (use this to implement * {@link #combineWith(NamedIntegerBitmask)}). - * + * * @param allFlags The array of all defined flags, expected "none". * @param flagsNone The "none" flag set. * @param constr The constructor. @@ -109,7 +109,7 @@ protected final T combineWith(T[] allFlags, T flagsNone, Constructor constr, /** * Combines two flags / flag sets. - * + * * @param other The other flag / flag set. * @return An instance combining both. */ @@ -118,14 +118,14 @@ protected final T combineWith(T[] allFlags, T flagsNone, Constructor constr, /** * Creates a new instance. - * + * * @param This type. */ @FunctionalInterface protected interface Constructor> { /** * Creates a new instance. - * + * * @param name The name. * @param flags The flag value. * @return The instance. @@ -135,7 +135,7 @@ protected interface Constructor> { /** * Returns a {@link NamedIntegerBitmask} instance given a flag value. - * + * * @param The subclass's type itself. * @param allFlags The array of all defined flags, expected "none". * @param flagsNone The "none" flag set. @@ -167,7 +167,7 @@ protected static final > T resolve(T[] allFlags /** * Returns a {@link NamedIntegerBitmask} instance given a series of flags. - * + * * @param The subclass's type itself. * @param allFlags The array of all defined flags, expected "none". * @param flagsNone The "none" flag set. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeLibraryLoader.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeLibraryLoader.java index eb225b1f6..c7d502bf9 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeLibraryLoader.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeLibraryLoader.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; @@ -44,7 +47,8 @@ final class NativeLibraryLoader implements Closeable { private static final List ARCHITECTURE_AND_OS = architectureAndOS(); private static final String LIBRARY_NAME = "junixsocket-native"; - private static boolean loaded = false; + private static final AtomicBoolean LOADED = new AtomicBoolean(false); + private static final boolean IS_ANDROID = checkAndroid(); static { String dir = System.getProperty(PROP_LIBRARY_TMPDIR, null); @@ -56,7 +60,7 @@ final class NativeLibraryLoader implements Closeable { /** * Returns the temporary directory where the native library is extracted to; debugging only. - * + * * @return The temporary directory. */ static File tempDir() { @@ -75,6 +79,12 @@ private List tryProviderClass(String providerClassname, String public static String getJunixsocketVersion() throws IOException { // NOTE: This can't easily be tested from within the junixsocket-common Maven build + + String v = BuildProperties.getBuildProperties().get("git.build.version"); + if (v != null && !v.startsWith("$")) { + return v; + } + return getArtifactVersion(AFSocket.class, "junixsocket-common"); } @@ -120,7 +130,7 @@ public String toString() { private static final class StandardLibraryCandidate extends LibraryCandidate { StandardLibraryCandidate(String version) { - super(version == null ? null : LIBRARY_NAME + "-" + version); + super(version == null ? LIBRARY_NAME : LIBRARY_NAME + "-" + version); } @Override @@ -141,20 +151,19 @@ public void close() { public String toString() { return super.toString() + "(standard library path)"; } - } private static final class ClasspathLibraryCandidate extends LibraryCandidate { private final String artifactName; - private final InputStream libraryIn; + private final URL library; private final String path; ClasspathLibraryCandidate(String artifactName, String libraryNameAndVersion, String path, - InputStream libraryIn) { + URL library) { super(libraryNameAndVersion); this.artifactName = artifactName; this.path = path; - this.libraryIn = libraryIn; + this.library = library; } @Override @@ -162,35 +171,63 @@ synchronized String load() throws IOException, LinkageError { if (libraryNameAndVersion == null) { return null; } - File libFile; - try { - libFile = createTempFile("libtmp", System.mapLibraryName(libraryNameAndVersion)); - try (OutputStream out = new FileOutputStream(libFile)) { // NOPMD UseTryWithResources - byte[] buf = new byte[4096]; - int read; - while ((read = libraryIn.read(buf)) >= 0) { - out.write(buf, 0, read); + + File libDir = TEMP_DIR; + + for (int attempt = 0; attempt < 3; attempt++) { + File libFile; + try { + libFile = File.createTempFile("libtmp", System.mapLibraryName(libraryNameAndVersion), + libDir); + try (InputStream libraryIn = library.openStream(); + OutputStream out = new FileOutputStream(libFile)) { // NOPMD UseTryWithResources + byte[] buf = new byte[4096]; + int read; + while ((read = libraryIn.read(buf)) >= 0) { + out.write(buf, 0, read); + } + } + } catch (IOException e) { + throw e; + } + + try { + System.load(libFile.getAbsolutePath()); + } catch (UnsatisfiedLinkError e) { + String message = e.getMessage().toLowerCase(Locale.getDefault()); + if (!message.contains("perm")) { + throw e; + } + + // Operation not permitted; permission denied; EPERM... + // -> tmp directory may be mounted with "noexec", try loading from user.home, user.dir + + switch (attempt) { + case 0: + libDir = new File(System.getProperty("user.home", ".")); + break; + case 1: + libDir = new File(System.getProperty("user.dir", ".")); + break; + default: + throw e; } + + continue; } finally { - libraryIn.close(); + if (!libFile.delete()) { + libFile.deleteOnExit(); + } } - } catch (IOException e) { - throw e; - } - System.load(libFile.getAbsolutePath()); - if (!libFile.delete()) { - libFile.deleteOnExit(); + + // If we reach this, then we were able to load the library + break; // NOPMD.AvoidBranchingStatementAsLastInLoop } return artifactName + "/" + libraryNameAndVersion; } @Override public void close() { - try { - libraryIn.close(); - } catch (IOException e) { - // ignore - } } @Override @@ -205,11 +242,10 @@ private synchronized void setLoaded(String library) { @SuppressFBWarnings("THROWS_METHOD_THROWS_RUNTIMEEXCEPTION") private static synchronized void setLoaded0(String library) { - if (!loaded) { - loaded = true; + if (LOADED.compareAndSet(false, true)) { + NativeUnixSocket.setLoaded(true); AFSocket.loadedLibrary = library; try { - NativeUnixSocket.initPre(); NativeUnixSocket.init(); } catch (RuntimeException e) { throw e; @@ -234,7 +270,7 @@ private Throwable loadLibraryOverride() { setLoaded(libraryOverride); return null; } catch (Exception | LinkageError e) { - if (Boolean.valueOf(libraryOverrideForce)) { + if (Boolean.parseBoolean(libraryOverrideForce)) { throw e; } return e; @@ -254,15 +290,16 @@ private static Object loadLibrarySyncMonitor() { } } - // NOPMD @SuppressWarnings("null") public synchronized void loadLibrary() { synchronized (loadLibrarySyncMonitor()) { // NOPMD We want to lock this class' classloader. - if (loaded) { + if (LOADED.get()) { // Already loaded return; } + NativeUnixSocket.initPre(); + // set -Dorg.newsclub.net.unix.library.override.force=provided to assume that // we already have loaded the library via System.load, etc. if ("provided".equals(System.getProperty(PROP_LIBRARY_OVERRIDE_FORCE, ""))) { @@ -270,7 +307,19 @@ public synchronized void loadLibrary() { return; } - if (Boolean.valueOf(System.getProperty(PROP_LIBRARY_DISABLE, "false"))) { + boolean provided = false; + try { + NativeUnixSocket.noop(); + provided = true; + } catch (UnsatisfiedLinkError | Exception e) { + // expected unless we manually loaded the library + } + if (provided) { + setLoaded("provided"); + return; + } + + if (Boolean.parseBoolean(System.getProperty(PROP_LIBRARY_DISABLE, "false"))) { throw initCantLoadLibraryError(Collections.singletonList(new UnsupportedOperationException( "junixsocket disabled by System.property " + PROP_LIBRARY_DISABLE))); } @@ -330,11 +379,14 @@ private UnsatisfiedLinkError initCantLoadLibraryError(List suppressed private List initLibraryCandidates(List suppressedThrowables) { List candidates = new ArrayList<>(); try { - candidates.add(new StandardLibraryCandidate(getArtifactVersion(getClass(), - "junixsocket-common", "junixsocket-core"))); + String version = getArtifactVersion(getClass(), "junixsocket-common", "junixsocket-core"); + if (version != null) { + candidates.add(new StandardLibraryCandidate(version)); + } } catch (Exception e) { suppressedThrowables.add(e); } + try { candidates.addAll(tryProviderClass("org.newsclub.lib.junixsocket.custom.NarMetadata", "junixsocket-native-custom")); @@ -348,14 +400,25 @@ private List initLibraryCandidates(List suppressedT suppressedThrowables.add(e); } + candidates.add(new StandardLibraryCandidate(null)); + return candidates; } + private static String lookupArchProperty(String key, String defaultVal) { + return System.getProperty(key, defaultVal).replaceAll("[ /\\\\'\";:\\$]", ""); + } + private static List architectureAndOS() { - String arch = System.getProperty("os.arch", "UnknownArch").replaceAll("[ /\\\\'\";:\\$]", ""); - String osName = System.getProperty("os.name", "UnknownOS").replaceAll("[ /\\\\'\";:\\$]", ""); + String arch = lookupArchProperty("os.arch", "UnknownArch"); + String osName = lookupArchProperty("os.name", "UnknownOS"); List list = new ArrayList<>(); + if (IS_ANDROID) { + // Android identifies itself as os.name="Linux" + // let's probe for an Android-specific library first + list.add(arch + "-Android"); + } list.add(arch + "-" + osName); if (osName.startsWith("Windows") && !"Windows10".equals(osName)) { list.add(arch + "-" + "Windows10"); @@ -364,6 +427,33 @@ private static List architectureAndOS() { return list; } + private static boolean checkAndroid() { + String vmName = lookupArchProperty("java.vm.name", "UnknownVM"); + String vmSpecVendor = lookupArchProperty("java.vm.specification.vendor", + "UnknownSpecificationVendor"); + + return ("Dalvik".equals(vmName) || vmSpecVendor.contains("Android")); + } + + static boolean isAndroid() { + return IS_ANDROID; + } + + static List getArchitectureAndOS() { + return ARCHITECTURE_AND_OS; + } + + private static URL validateResourceURL(URL url) { + if (url == null) { + return null; + } + try (InputStream in = url.openStream()) { + return url; + } catch (IOException e) { + return null; + } + } + private List findLibraryCandidates(String artifactName, String libraryNameAndVersion, Class providerClass) { String mappedName = System.mapLibraryName(libraryNameAndVersion); @@ -380,11 +470,11 @@ private List findLibraryCandidates(String artifactName, for (String prefix : prefixes) { String path = "/lib/" + archOs + "-" + compiler + "/jni/" + prefix + mappedName; - InputStream in; + URL url; - in = providerClass.getResourceAsStream(path); - if (in != null) { - list.add(new ClasspathLibraryCandidate(artifactName, libraryNameAndVersion, path, in)); + url = validateResourceURL(providerClass.getResource(path)); + if (url != null) { + list.add(new ClasspathLibraryCandidate(artifactName, libraryNameAndVersion, path, url)); } // NOTE: we have to try .nodeps version _after_ trying the properly linked one. @@ -392,10 +482,10 @@ private List findLibraryCandidates(String artifactName, // with a "symbol lookup error" String nodepsPath = nodepsPath(path); if (nodepsPath != null) { - in = providerClass.getResourceAsStream(nodepsPath); - if (in != null) { + url = validateResourceURL(providerClass.getResource(nodepsPath)); + if (url != null) { list.add(new ClasspathLibraryCandidate(artifactName, libraryNameAndVersion, - nodepsPath, in)); + nodepsPath, url)); } } } @@ -413,10 +503,6 @@ private String nodepsPath(String path) { } } - private static File createTempFile(String prefix, String suffix) throws IOException { - return File.createTempFile(prefix, suffix, TEMP_DIR); - } - @Override public void close() { } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java index 18ef5de11..be828626f 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; @@ -28,6 +29,8 @@ import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.spi.AbstractSelectableChannel; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import org.newsclub.net.unix.AFSelector.PollFd; @@ -36,18 +39,20 @@ /** * JNI connector to native JNI C code. - * + * * @author Christian Kohlschütter */ final class NativeUnixSocket { - private static boolean loaded; + private static final CompletableFuture LOADED = new CompletableFuture<>(); static final int DOMAIN_UNIX = 1; static final int DOMAIN_TIPC = 30; static final int DOMAIN_VSOCK = 40; + static final int DOMAIN_SYSTEM = 32; static final int SOCK_STREAM = 1; static final int SOCK_DGRAM = 2; + static final int SOCK_RAW = 3; static final int SOCK_RDM = 4; static final int SOCK_SEQPACKET = 5; @@ -69,20 +74,24 @@ final class NativeUnixSocket { static final int SOCKETSTATUS_BOUND = 1; static final int SOCKETSTATUS_CONNECTED = 2; + @SuppressWarnings("StaticAssignmentOfThrowable" /* errorprone */) private static Throwable initError = null; - @ExcludeFromCodeCoverageGeneratedReport + @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable") private NativeUnixSocket() { throw new UnsupportedOperationException("No instances"); } static { + boolean loadSuccessful = false; try (NativeLibraryLoader nll = new NativeLibraryLoader()) { nll.loadLibrary(); - loaded = true; + loadSuccessful = true; } catch (RuntimeException | Error e) { initError = e; - e.printStackTrace(); // keep + StackTraceUtil.printStackTraceSevere(e); + } finally { + setLoaded(loadSuccessful); } AFAddressFamily.registerAddressFamily("un", NativeUnixSocket.DOMAIN_UNIX, @@ -91,10 +100,18 @@ private NativeUnixSocket() { "org.newsclub.net.unix.AFTIPCSocketAddress"); AFAddressFamily.registerAddressFamily("vsock", NativeUnixSocket.DOMAIN_VSOCK, "org.newsclub.net.unix.AFVSOCKSocketAddress"); + AFAddressFamily.registerAddressFamily("system", NativeUnixSocket.DOMAIN_SYSTEM, + "org.newsclub.net.unix.AFSYSTEMSocketAddress"); } static boolean isLoaded() { - return loaded; + boolean loadSuccessful; + try { + loadSuccessful = LOADED.get(); + } catch (InterruptedException | ExecutionException e) { + loadSuccessful = false; + } + return loadSuccessful; } static void ensureSupported() throws UnsupportedOperationException { @@ -106,7 +123,7 @@ static void ensureSupported() throws UnsupportedOperationException { static UnsupportedOperationException unsupportedException() { if (!isLoaded()) { return (UnsupportedOperationException) new UnsupportedOperationException( - "junixsocket is not supported on this platform").initCause(initError); + "junixsocket may not be fully supported on this platform").initCause(initError); } else { return null; } @@ -120,6 +137,11 @@ static void initPre() { // in some environments, JNI FindClass won't find these classes unless we resolve them first tryResolveClass(AbstractSelectableChannel.class.getName()); tryResolveClass("java.lang.ProcessBuilder$RedirectPipeImpl"); + tryResolveClass(InetSocketAddress.class.getName()); + tryResolveClass(OperationNotSupportedSocketException.class.getName()); + tryResolveClass(InvalidArgumentSocketException.class.getName()); + tryResolveClass(AddressUnavailableSocketException.class.getName()); + tryResolveClass(NoSuchDeviceSocketException.class.getName()); } private static void tryResolveClass(String className) { @@ -136,6 +158,13 @@ private static void tryResolveClass(String className) { @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION") static native void destroy() throws Exception; + /** + * Can be used to check (without side-effects) if the library has been loaded. + * + * Terminates normally if so; throws {@link UnsatisfiedLinkError} if not. + */ + static native void noop(); + static native int capabilities(); static native byte[] sockname(int domain, FileDescriptor fd, boolean peer); @@ -161,7 +190,7 @@ static native boolean connect(ByteBuffer sockaddr, int sockaddrLen, FileDescript /** * Reads data from an {@link AFSocketImpl}. - * + * * @param fd The corresponding file descriptor. * @param buf The buffer to read into, or {@code null} if a single byte should be read. * @param off The buffer offset. @@ -177,7 +206,7 @@ static native int read(FileDescriptor fd, byte[] buf, int off, int len, int opti /** * Writes data to an {@link AFSocketImpl}. - * + * * @param fd The corresponding file descriptor. * @param buf The buffer to write from, or {@code null} if a single byte should be written. * @param off The buffer offset, or the byte to write if {@code buf} is {@code null}. @@ -232,7 +261,8 @@ static native void initServerImpl(ServerSocket serverSocket, AFSocketImpl imp static native void copyFileDescriptor(FileDescriptor source, FileDescriptor target) throws IOException; - static native void attachCloseable(FileDescriptor fdsec, Closeable closeable); + static native void attachCloseable(FileDescriptor fdsec, Closeable closeable) + throws SocketException; static native int maxAddressLength(); @@ -285,4 +315,10 @@ static native boolean initPipe(FileDescriptor source, FileDescriptor sink, boole static native int sockTypeToNative(int type) throws IOException; static native int vsockGetLocalCID() throws IOException; + + static native int systemResolveCtlId(FileDescriptor fd, String ctlName) throws IOException; + + static void setLoaded(boolean successful) { + LOADED.complete(successful); + } } diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NoSuchDeviceSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NoSuchDeviceSocketException.java new file mode 100644 index 000000000..2bfe0743c --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NoSuchDeviceSocketException.java @@ -0,0 +1,46 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.net.SocketException; + +/** + * A {@link SocketException} that may be thrown upon some "no such device" condition from native + * code (e.g., ENODEV is returned). + * + * @author Christian Kohlschütter + */ +public class NoSuchDeviceSocketException extends InvalidSocketException { + private static final long serialVersionUID = 1L; + + /** + * Constructs a new {@link NoSuchDeviceSocketException}. + */ + public NoSuchDeviceSocketException() { + super(); + } + + /** + * Constructs a new {@link NoSuchDeviceSocketException}. + * + * @param msg The error message. + */ + public NoSuchDeviceSocketException(String msg) { + super(msg); + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/OperationNotSupportedSocketException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/OperationNotSupportedSocketException.java index f97dce16c..1e8ed2e73 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/OperationNotSupportedSocketException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/OperationNotSupportedSocketException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,9 @@ /** * A {@link SocketException} that may be thrown upon some "unsupported operation" condition from - * native code (e.g., EOPNOTSUPP is returned). - * + * native code (e.g., ENOTSUP, EOPNOTSUPP, EPROTONOSUPPORT, ESOCKTNOSUPPORT, EPFNOSUPPORT, + * EAFNOSUPPORT is returned). + * * @author Christian Kohlschütter */ public class OperationNotSupportedSocketException extends InvalidSocketException { @@ -37,7 +38,7 @@ public OperationNotSupportedSocketException() { /** * Constructs a new {@link OperationNotSupportedSocketException}. - * + * * @param msg The error message. */ public OperationNotSupportedSocketException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/PathUtil.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/PathUtil.java new file mode 100644 index 000000000..6ff8144d3 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/PathUtil.java @@ -0,0 +1,48 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; + +/** + * Some {@link Path}-related helper methods. + * + * @author Christian Kohlschütter + */ +final class PathUtil { + private PathUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Checks if the given path is in the default file system. + * + * @param p The path. + * @return {@code true} if so. + */ + @SuppressWarnings("PMD.CompareObjectsWithEquals") + static boolean isPathInDefaultFileSystem(Path p) { + FileSystem fs = p.getFileSystem(); + if (fs != FileSystems.getDefault() || fs.getClass().getModule() != Object.class.getModule()) { + return false; + } + return true; + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/RAFChannelProvider.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/RAFChannelProvider.java index 21b9b26ef..09a80affc 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/RAFChannelProvider.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/RAFChannelProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ /** * Hack to get a readable AND writable {@link FileChannel} for a {@link FileDescriptor}. - * + * * @author Christian Kohlschütter */ final class RAFChannelProvider extends RandomAccessFile implements FileDescriptorAccess { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SentinelSocketAddress.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SentinelSocketAddress.java index 7d0d6e91a..859643eff 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/SentinelSocketAddress.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SentinelSocketAddress.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressFilter.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressFilter.java index 5c82039fa..b166cd8b7 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressFilter.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressFilter.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * A filter that takes a {@link SocketAddress}, and potentially changes it, or throws an exception * if certain criteria are met. - * + * * @author Christian Kohlschütter */ @FunctionalInterface @@ -31,7 +31,7 @@ public interface SocketAddressFilter { /** * Applies the filter on the given address. - * + * * @param address The address. * @return The address itself or a changed address. * @throws IOException on error or if a certain error condition is desired. diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressUtil.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressUtil.java new file mode 100644 index 000000000..58b208213 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketAddressUtil.java @@ -0,0 +1,56 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnixDomainSocketAddress; +import java.util.function.Supplier; + +/** + * {@link SocketAddress}-related helper methods. + * + * @author Christian Kohlschütter + */ +final class SocketAddressUtil { + private SocketAddressUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Try to convert a {@link SocketAddress} that is not a {@link AFSocketAddress} to one that is. + * + * @param address The address. + * @return A supplier for the given address, or {@code null}. + */ + static Supplier supplyAFUNIXSocketAddress(SocketAddress address) { + if (address instanceof UnixDomainSocketAddress) { + UnixDomainSocketAddress udsa = (UnixDomainSocketAddress) address; + + return () -> { + try { + return AFUNIXSocketAddress.of(udsa.getPath()); + } catch (SocketException e) { + return null; + } + }; + } else { + return null; + } + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketClosedException.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketClosedException.java index afce7ad58..e882a5d5a 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketClosedException.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketClosedException.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * A {@link SocketException} indicating that a socket was closed or is not open for other reasons. - * + * * @author Christian Kohlschütter */ public final class SocketClosedException extends SocketException { @@ -36,7 +36,7 @@ public SocketClosedException() { /** * Constructs a new {@link SocketClosedException}. - * + * * @param msg The error message. */ public SocketClosedException(String msg) { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketImplShim.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketImplShim.java index c5829ad7b..709827206 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketImplShim.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketImplShim.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * A shim that is filled with Java version-specific overrides. This variant is for Java 9 and above. - * + * * @author Christian Kohlschütter */ abstract class SocketImplShim extends SocketImpl { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketOptionsMapper.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketOptionsMapper.java index ebc58ac13..99a5033e1 100644 --- a/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketOptionsMapper.java +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/SocketOptionsMapper.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * Maps new SocketOption classes to the old integer-based scheme. - * + * * @author Christian Kohlschütter */ final class SocketOptionsMapper { diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/StackTraceUtil.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/StackTraceUtil.java new file mode 100644 index 000000000..53172f2c9 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/StackTraceUtil.java @@ -0,0 +1,49 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +/** + * Internal helper class to dump stack traces when deemed appropriate. + * + * NOTE: For junit testing classes, use {@code kohlschutter-test-util}'s {@code TestStackTraceUtil}. + * + * @author Christian Kohlschütter + */ +public final class StackTraceUtil { + private StackTraceUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Print/Log the stack trace of the given Throwable. + * + * @param t The throwable to log. + */ + public static void printStackTrace(Throwable t) { + t.printStackTrace(); // NOPMD + } + + /** + * Print/Log the stack trace of the given Throwable, marking this entry as a "severe condition". + * + * @param t The throwable to log. + */ + public static void printStackTraceSevere(Throwable t) { + t.printStackTrace(); // NOPMD + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/UngrowableSet.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/UngrowableSet.java new file mode 100644 index 000000000..50855d087 --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/UngrowableSet.java @@ -0,0 +1,101 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + * A {@link Set} that won't allow adding elements. + * + * @param The element type. + * @author Christian Kohlschütter + */ +final class UngrowableSet implements Set { + private final Set set; + + UngrowableSet(Set set) { + this.set = set; + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(o); + } + + @Override + public Iterator iterator() { + return set.iterator(); + } + + @Override + public Object[] toArray() { + return set.toArray(); + } + + @Override + public E[] toArray(E[] a) { + return set.toArray(a); + } + + @Override + public boolean add(T e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + return set.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return set.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + return set.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return set.removeAll(c); + } + + @Override + public void clear() { + set.clear(); + } +} diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/Unsafe.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/Unsafe.java new file mode 100644 index 000000000..0859e2dcb --- /dev/null +++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/Unsafe.java @@ -0,0 +1,41 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Declares some operation "unsafe", which is guarded via + * {@link AFSocketCapability#CAPABILITY_UNSAFE}. + *

+ * Methods annotated with this type should also call {@link AFSocket#ensureUnsafeSupported()} to + * check availability at runtime. + *

+ * NOTE: This annotation has nothing to do with {@code sun.misc.Unsafe}. + * + * @author Christian Kohlschütter + * @see AFSocket#ensureUnsafeSupported() + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Unsafe { + +} diff --git a/junixsocket-common/src/main/java15/org/newsclub/net/unix/SocketAddressUtil.java b/junixsocket-common/src/main/java15/org/newsclub/net/unix/SocketAddressUtil.java new file mode 100644 index 000000000..2badb8610 --- /dev/null +++ b/junixsocket-common/src/main/java15/org/newsclub/net/unix/SocketAddressUtil.java @@ -0,0 +1,43 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.net.SocketAddress; +import java.net.SocketException; +import java.util.function.Supplier; + +/** + * {@link SocketAddress}-related helper methods. + * + * @author Christian Kohlschütter + */ +final class SocketAddressUtil { + private SocketAddressUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Try to convert a {@link SocketAddress} that is not a {@link AFSocketAddress} to one that is. + * + * @param address The address. + * @return A supplier for the given address, or {@code null}. + */ + static Supplier supplyAFUNIXSocketAddress(SocketAddress address) { + return null; + } +} diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFInputStream.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFInputStream.java index bd11450b5..45fa0a98b 100644 --- a/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFInputStream.java +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFInputStream.java @@ -1,3 +1,20 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.io.IOException; diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFOutputStream.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFOutputStream.java index 4523aa88f..289452a1b 100644 --- a/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFOutputStream.java +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/AFOutputStream.java @@ -1,3 +1,20 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.io.IOException; @@ -16,11 +33,11 @@ public abstract class AFOutputStream extends OutputStream implements FileDescrip * Reads all bytes from the given input stream and writes the bytes to this output stream in the * order that they are read. On return, this input stream will be at end of stream. This method * does not close either stream. - * + * * This method effectively is the reverse notation of * {@link InputStream#transferTo(OutputStream)}, which may or may not be optimized for * {@link AFSocket}s. - * + * * @param in The {@link InputStream} to transfer from. * @return The number of bytes transferred. * @throws IOException on error. diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/CleanableState.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/CleanableState.java index d78704771..fae457470 100644 --- a/junixsocket-common/src/main/java8/org/newsclub/net/unix/CleanableState.java +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/CleanableState.java @@ -1,3 +1,20 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.io.Closeable; @@ -17,8 +34,9 @@ public final void runCleaner() { } } - @SuppressWarnings("all") + @SuppressWarnings({"all", "Finalize" /* errorprone */}) @Override + @Deprecated protected final void finalize() { try { runCleaner(); diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketImplShim.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketImplShim.java index 3bdb3ac43..c76531ac0 100644 --- a/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketImplShim.java +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketImplShim.java @@ -1,10 +1,27 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.net.DatagramSocketImpl; /** * A shim that is filled with Java version-specific overrides. This variant is for Java 7 and 8. - * + * * @author Christian Kohlschütter */ abstract class DatagramSocketImplShim extends DatagramSocketImpl { @@ -14,6 +31,7 @@ protected DatagramSocketImplShim() { @SuppressWarnings("all") @Override + @Deprecated protected final void finalize() { try { close(); diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketShim.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketShim.java new file mode 100644 index 000000000..4b6632946 --- /dev/null +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/DatagramSocketShim.java @@ -0,0 +1,51 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.DatagramSocketImpl; + +abstract class DatagramSocketShim extends DatagramSocket { + + protected DatagramSocketShim(DatagramSocketImpl impl) { + super(impl); + } + + /** + * Returns the value of a junixsocket socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @return The value of the socket option. + * @throws IOException on error. + */ + public abstract T getOption(AFSocketOption name) throws IOException; + + /** + * Sets the value of a socket option. + * + * @param The type of the socket option value. + * @param name The socket option. + * @param value The value of the socket option. + * @return this DatagramSocket. + * @throws IOException on error. + */ + @SuppressWarnings("PMD.LinguisticNaming") + public abstract DatagramSocket setOption(AFSocketOption name, T value) throws IOException; +} diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/PathUtil.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/PathUtil.java new file mode 100644 index 000000000..eb1088963 --- /dev/null +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/PathUtil.java @@ -0,0 +1,48 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; + +/** + * Some {@link Path}-related helper methods. + * + * @author Christian Kohlschütter + */ +final class PathUtil { + private PathUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Checks if the given path is in the default file system. + * + * @param p The path. + * @return {@code true} if so. + */ + @SuppressWarnings("PMD.CompareObjectsWithEquals") + static boolean isPathInDefaultFileSystem(Path p) { + FileSystem fs = p.getFileSystem(); + if (fs != FileSystems.getDefault()) { + return false; + } + return true; + } +} diff --git a/junixsocket-common/src/main/java8/org/newsclub/net/unix/SocketImplShim.java b/junixsocket-common/src/main/java8/org/newsclub/net/unix/SocketImplShim.java index d5e3d8963..aa5df18b9 100644 --- a/junixsocket-common/src/main/java8/org/newsclub/net/unix/SocketImplShim.java +++ b/junixsocket-common/src/main/java8/org/newsclub/net/unix/SocketImplShim.java @@ -1,3 +1,20 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.io.IOException; @@ -8,7 +25,7 @@ /** * A shim that is filled with Java version-specific overrides. This variant is for Java 7 and 8. - * + * * @author Christian Kohlschütter */ abstract class SocketImplShim extends SocketImpl { @@ -16,8 +33,9 @@ protected SocketImplShim() { super(); } - @SuppressWarnings("all") + @SuppressWarnings({"all", "Finalize" /* errorprone */}) @Override + @Deprecated protected final void finalize() { try { close(); diff --git a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/jni-config.json b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/jni-config.json index ca3ffe2b7..fdfa881b2 100644 --- a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/jni-config.json +++ b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/jni-config.json @@ -8,6 +8,10 @@ "name":"java.lang.Boolean", "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Class", + "methods":[{"name":"getName","parameterTypes":[] }] +}, { "name":"java.lang.IllegalStateException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -27,6 +31,10 @@ "name":"java.lang.NullPointerException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Object", + "methods":[{"name":"getClass","parameterTypes":[] }] +}, { "name":"java.lang.ProcessBuilder$RedirectPipeImpl", "fields":[{"name":"fd"}], @@ -51,7 +59,8 @@ }, { "name":"java.net.InetSocketAddress", - "fields":[{"name":"holder"}] + "fields":[{"name":"holder"}], + "methods":[{"name":"","parameterTypes":["int"] }] }, { "name":"java.net.InetSocketAddress$InetSocketAddressHolder", @@ -141,9 +150,11 @@ "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFTIPCDatagramSocket" }, "name":"org.newsclub.net.unix.tipc.AFTIPCDatagramSocket" }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFTIPCGroupRequest" }, "name":"org.newsclub.net.unix.tipc.AFTIPCGroupRequest", "methods":[ {"name":"fromNative","parameterTypes":["int","int","int","int"] }, @@ -154,15 +165,46 @@ ] }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFTIPCSocket" }, "name":"org.newsclub.net.unix.tipc.AFTIPCSocket" }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFVSOCKDatagramSocket" }, "name":"org.newsclub.net.unix.vsock.AFVSOCKDatagramSocket" }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFVSOCKSocket" }, "name":"org.newsclub.net.unix.vsock.AFVSOCKSocket" }, { + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFVSOCKServerSocket" }, "name":"org.newsclub.net.unix.vsock.AFVSOCKServerSocket" +}, +{ + "name":"java.net.SocketImpl" +}, +{ + "name":"java.net.DatagramSocketImpl" +}, +{ + "name":"org.newsclub.net.unix.AFSocketImpl" +}, +{ + "name":"org.newsclub.net.unix.SocketImplShim" +}, +{ + "name":"org.newsclub.net.unix.AFUNIXSocketImpl" +}, +{ + "name":"org.newsclub.net.unix.AFUNIXDatagramSocketImpl" +}, +{ + "name":"org.newsclub.net.unix.AFDatagramSocketImpl" +}, +{ + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMDatagramSocket" +}, +{ + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMSocket" } -] \ No newline at end of file +] diff --git a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/reflect-config.json b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/reflect-config.json index 7c4e0ed58..1109044dd 100644 --- a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/reflect-config.json +++ b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/reflect-config.json @@ -3,14 +3,13 @@ "name":"java.nio.channels.spi.AbstractSelectableChannel" }, { + "condition" : { "typeReachable" : "org.newsclub.lib.junixsocket.common.NarMetadata" }, "name":"org.newsclub.lib.junixsocket.common.NarMetadata" }, { + "condition" : { "typeReachable" : "org.newsclub.lib.junixsocket.custom.NarMetadata" }, "name":"org.newsclub.lib.junixsocket.custom.NarMetadata" }, -{ - "name":"org.newsclub.net.unix.AFSocketCapabilityRequirement" -}, { "name":"org.newsclub.net.unix.AFTIPCSocketAddress", "methods":[{"name":"addressFamily","parameterTypes":[] }] @@ -24,9 +23,51 @@ "methods":[{"name":"addressFamily","parameterTypes":[] }] }, { - "name":"org.newsclub.net.unix.tipc.AFTIPCSelectorProvider" + "name":"org.newsclub.net.unix.AFUNIXSelectorProvider", + "allPublicMethods":true +}, +{ + "condition" : { "typeReachable" : "org.newsclub.net.unix.tipc.AFTIPCSelectorProvider" }, + "name":"org.newsclub.net.unix.tipc.AFTIPCSelectorProvider", + "allPublicMethods":true +}, +{ + "condition" : { "typeReachable" : "org.newsclub.net.unix.vsock.AFVSOCKSelectorProvider" }, + "name":"org.newsclub.net.unix.vsock.AFVSOCKSelectorProvider", + "allPublicMethods":true +}, +{ + "condition" : { "typeReachable" : "org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider" }, + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider", + "allPublicMethods":true +}, +{ + "name":"com.kohlschutter.testutil.AvailabilityExecutionCondition", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.kohlschutter.testutil.AvailabilityRequirement", + "queryAllPublicMethods":true +}, +{ + "name":"com.kohlschutter.testutil.ExecutionEnvironmentExecutionCondition", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.kohlschutter.testutil.ExecutionEnvironmentRequirement", + "queryAllPublicMethods":true +}, +{ + "name":"java.net.InetSocketAddress" +}, +{ + "name":"java.net.UnixDomainSocketAddress" +}, +{ + "name":"org.newsclub.net.unix.AFSYSTEMSocketAddress", + "methods":[{"name":"addressFamily","parameterTypes":[] }] }, { - "name":"org.newsclub.net.unix.vsock.AFVSOCKSelectorProvider" + "name":"org.newsclub.net.unix.OperationNotSupportedSocketException" } ] diff --git a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/resource-config.json b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/resource-config.json index d75817c78..081835ed6 100644 --- a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/resource-config.json +++ b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/resource-config.json @@ -5,9 +5,11 @@ "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-common/pom.properties\\E" }, { + "condition" : { "typeReachable" : "org.newsclub.lib.junixsocket.common.NarMetadata" }, "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-common/pom.properties\\E" }, { + "condition" : { "typeReachable" : "org.newsclub.lib.junixsocket.custom.NarMetadata" }, "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-custom/pom.properties\\E" } ]}, diff --git a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/serialization-config.json b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/serialization-config.json index c6a8c2121..3ec8b3ef9 100644 --- a/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/serialization-config.json +++ b/junixsocket-common/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-common/serialization-config.json @@ -1,9 +1,35 @@ { "types":[ + { + "name":"byte[]" + }, + { + "name":"java.lang.String" + }, { "name":"java.net.InetAddress" + }, + { + "name":"java.net.InetSocketAddress" + }, + { + "name":"java.net.SocketAddress" + }, + { + "name":"org.newsclub.net.unix.AFSocketAddress" + }, + { + "name":"org.newsclub.net.unix.AFUNIXSocketAddress" + }, + { + "name":"org.newsclub.net.unix.AFTIPCSocketAddress" + }, + { + "name":"org.newsclub.net.unix.AFVSOCKSocketAddress" } ], "lambdaCapturingTypes":[ + ], + "proxies":[ ] } \ No newline at end of file diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFDatagramUtil.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFDatagramUtil.java index db1cf6755..7bf837b2d 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFDatagramUtil.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFDatagramUtil.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,11 @@ /** * Helper methods for datagrams, but mostly for testing. - * + * * @author Christian Kohlschütter */ public final class AFDatagramUtil { - @ExcludeFromCodeCoverageGeneratedReport + @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable") private AFDatagramUtil() { throw new IllegalStateException("No instances"); } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSYSTEMSocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSYSTEMSocketAddressTest.java new file mode 100644 index 000000000..e6bb23009 --- /dev/null +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSYSTEMSocketAddressTest.java @@ -0,0 +1,82 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.SocketException; +import java.net.URI; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFSYSTEMSocketAddress.SysAddr; + +import com.kohlschutter.testutil.AssertUtil; + +public class AFSYSTEMSocketAddressTest { + + @Test + public void testSchemesAvailable() throws Exception { + AssertUtil.assertSetContains(AFAddressFamily.uriSchemes(), // + Arrays.asList("afsystem")); + } + + @Test + public void testParseFail() throws Exception { + assertThrows(SocketException.class, () -> AFSYSTEMSocketAddress.of(URI.create( + "afsystem://invalid"))); + assertThrows(SocketException.class, () -> AFSYSTEMSocketAddress.of(URI.create( + "afsystem://socket.23/"))); + } + + private AFSYSTEMSocketAddress validateAddress(String uri) throws IOException { + URI u = URI.create(uri); + AFSYSTEMSocketAddress address = AFSYSTEMSocketAddress.of(u); + URI u2 = address.toURI(u.getScheme(), null); + assertEquals(address, AFSYSTEMSocketAddress.of(u2)); + return address; + } + + @Test + public void testURI() throws Exception { + assertEquals(AFSYSTEMSocketAddress.ofSysAddrIdUnit(SysAddr.AF_SYS_CONTROL, 3, 4), + validateAddress("afsystem://2.3.4")); + } + + @Test + public void testSocatString() throws Exception { + String socatString; + try { + socatString = AFSYSTEMSocketAddress.ofSysAddrIdUnit(SysAddr.AF_SYS_CONTROL, 3, 4) + .toSocatAddressString(AFSocketType.SOCK_STREAM, AFSocketProtocol.DEFAULT); + assertNotNull(socatString); + } catch (SocketException e) { + if (AFSocket.supports(AFSocketCapability.CAPABILITY_DARWIN)) { + throw e; + } else { + // expected + return; + } + } + assertTrue(socatString.contains(":")); + } +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityCondition.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityCondition.java index 987f587c5..ec70b49c2 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityCondition.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityCondition.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,12 @@ import org.junit.jupiter.api.extension.ExtensionContext; public final class AFSocketCapabilityCondition implements ExecutionCondition { + /** + * Constructs a new {@link AFSocketCapabilityCondition}. + */ + public AFSocketCapabilityCondition() { + } + @SuppressWarnings("exports") @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityRequirement.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityRequirement.java index c6c37d7d7..d456dbd7d 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityRequirement.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFSocketCapabilityRequirement.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFTIPCSocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFTIPCSocketAddressTest.java index ec22687b4..4eb10364a 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFTIPCSocketAddressTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFTIPCSocketAddressTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package org.newsclub.net.unix; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -125,12 +125,19 @@ public void testGeneric() throws Exception { @Test public void testSocatString() throws Exception { - String socatString = AFTIPCSocketAddress.ofService(123, 456).toSocatAddressString( - AFSocketType.SOCK_STREAM, AFSocketProtocol.DEFAULT); - if (socatString == null) { - assertFalse(AFSocket.supports(AFSocketCapability.CAPABILITY_TIPC)); - } else { - assertTrue(socatString.contains(":")); + String socatString; + try { + socatString = AFTIPCSocketAddress.ofService(123, 456).toSocatAddressString( + AFSocketType.SOCK_STREAM, AFSocketProtocol.DEFAULT); + assertNotNull(socatString); + } catch (SocketException e) { + if (AFSocket.supports(AFSocketCapability.CAPABILITY_TIPC)) { + throw e; + } else { + // expected + return; + } } + assertTrue(socatString.contains(":")); } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFUNIXSocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFUNIXSocketAddressTest.java index 828125a7f..e19eca244 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFUNIXSocketAddressTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFUNIXSocketAddressTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.URI; import java.util.Arrays; @@ -133,4 +137,22 @@ public void testAbstractNamespace() throws Exception { assertNotNull(AFUNIXSocketAddress.inAbstractNamespace("test")); assertNotNull(AFUNIXSocketAddress.inAbstractNamespace("test", 1234)); } + + @Test + public void testSerialize() throws Exception { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos)) { + + AFUNIXSocketAddress addr = AFUNIXSocketAddress.of(URI.create("file:/tmp/yo")); + oos.writeObject(addr); + oos.flush(); + + try (ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bos + .toByteArray()))) { + AFUNIXSocketAddress addr2 = (AFUNIXSocketAddress) oin.readObject(); + assertEquals(addr, addr2); + assertEquals(addr.getAddressFamily(), addr2.getAddressFamily()); + } + } + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFVSOCKSocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFVSOCKSocketAddressTest.java index 8a13033dd..fec01767b 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AFVSOCKSocketAddressTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AFVSOCKSocketAddressTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package org.newsclub.net.unix; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -96,12 +96,19 @@ public void testNamedCIDs() throws Exception { @Test public void testSocatString() throws Exception { - String socatString = AFVSOCKSocketAddress.ofPortAndCID(123, 4).toSocatAddressString( - AFSocketType.SOCK_STREAM, AFSocketProtocol.DEFAULT); - if (socatString == null) { - assertFalse(AFSocket.supports(AFSocketCapability.CAPABILITY_VSOCK)); - } else { - assertTrue(socatString.contains(":")); + String socatString; + try { + socatString = AFVSOCKSocketAddress.ofPortAndCID(123, 4).toSocatAddressString( + AFSocketType.SOCK_STREAM, AFSocketProtocol.DEFAULT); + assertNotNull(socatString); + } catch (SocketException e) { + if (AFSocket.supports(AFSocketCapability.CAPABILITY_VSOCK)) { + throw e; + } else { + // expected + return; + } } + assertTrue(socatString.contains(":")); } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AcceptTimeoutTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AcceptTimeoutTest.java index 58f14c1a0..5854c0351 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AcceptTimeoutTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AcceptTimeoutTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ /** * Verifies that accept properly times out when an soTimeout was specified. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ @@ -144,7 +144,6 @@ public void run() { } System.out.println("SocketTimeout, trying connect again (" + i + ")"); - e.printStackTrace(); continue; } catch (TestAbortedWithImportantMessageException e) { runtimeExceptionCF.complete(e); @@ -164,6 +163,14 @@ public void run() { try (Socket socket = serverSock.accept();) { assertNotNull(socket); accepted.set(true); + } catch (SocketTimeoutException e) { + String msg = checkKnownBugAcceptTimeout(e); + if (msg == null) { + throw e; + } else { + throw new TestAbortedWithImportantMessageException( + MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, msg, summaryImportantMessage(msg), e); + } } time = System.currentTimeMillis() - time; @@ -183,18 +190,29 @@ public void run() { if (msg == null) { throw e; } else { - throw new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_WITH_ISSUES, - msg, e); + throw new TestAbortedWithImportantMessageException( + MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, msg, summaryImportantMessage(msg), e); } } finally { keepRunning.set(false); } } + /** + * Subclasses may override this to tell that there is a known issue with "Accept timeout after + * delay" when a SocketTimeoutException was thrown. + * + * @param e The exception + * @return An explanation iff this should not cause a test failure but trigger "With issues". + */ + protected String checkKnownBugAcceptTimeout(SocketTimeoutException e) { + return null; + } + /** * Subclasses may override this to tell that there is a known issue with "Accept timeout after * delay". - * + * * @param serverAddr The server address. * @return An explanation iff this should not cause a test failure but trigger "With issues". */ @@ -205,6 +223,10 @@ protected String checkKnownBugAcceptTimeout(SocketAddress serverAddr) { @Test public void testAcceptWithoutBindToService() throws Exception { ServerSocket ss = newServerSocket(); - assertThrows(SocketException.class, () -> ss.accept()); + assertThrows(SocketException.class, () -> { + try (Socket s = ss.accept()) { + fail("Should not be reached"); + } + }); } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AddressSpecifics.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AddressSpecifics.java index 3e5d3a770..21b990f07 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AddressSpecifics.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AddressSpecifics.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,19 +31,21 @@ /** * Test-related methods to work with a particular {@link AFSocket} implementation. - * + * * It is essential to use these methods in tests instead of directly calling the {@link AFSocket} * etc. methods: Some socket implementations (and sometimes only in certain kernel/environment * configurations) may expose unexpected behavior that is otherwise hard to catch. - * + * * This is especially relevant when connecting/binding sockets. - * + * * @param The socket address. * @author Christian Kohlschütter * @see SocketTestBase */ public interface AddressSpecifics { + String addressFamilyString(); + Socket newStrictSocket() throws IOException; Socket newSocket() throws IOException; @@ -104,8 +106,10 @@ default CloseablePair newInterconnectedSockets() throws IOExce ServerSocket server = newServerSocketBindOn(address); Socket client = connectTo(server.getLocalSocketAddress()); final Socket socket = server.accept(); - return new CloseablePair>((AFSocket) client, (AFSocket) socket, server); + return new CloseablePair<>((AFSocket) client, (AFSocket) socket, server); } DatagramChannel newDatagramChannel() throws IOException; -} \ No newline at end of file + + String summaryImportantMessage(String message); +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AncillaryMessageTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AncillaryMessageTest.java index 619066b12..f964ea1df 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AncillaryMessageTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AncillaryMessageTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/AvailableTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/AvailableTest.java index f954cc65f..ea568e16b 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/AvailableTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/AvailableTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/BufferOverflowTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/BufferOverflowTest.java index 467c63a13..3b2303274 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/BufferOverflowTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/BufferOverflowTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/BuildPropertiesTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/BuildPropertiesTest.java new file mode 100644 index 000000000..d74582bf5 --- /dev/null +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/BuildPropertiesTest.java @@ -0,0 +1,56 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.kohlschutter.util.ExecutionEnvironmentUtil; + +public class BuildPropertiesTest { + @Test + public void testNotEmpty() throws Exception { + Map properties = BuildProperties.getBuildProperties(); + assertNotEquals(0, properties.size()); + } + + @Test + public void testHasProperties() throws Exception { + Map properties = BuildProperties.getBuildProperties(); + assertTrue(properties.containsKey("project.version")); + assertTrue(properties.containsKey("git.commit.id.full")); + } + + @Test + public void testResolved() throws Exception { + Map properties = BuildProperties.getBuildProperties(); + + String projectVersion = properties.get("project.version"); + if (ExecutionEnvironmentUtil.isInEclipse()) { + assertEquals("${project.version}", projectVersion); + } else { + assertNotEquals("", projectVersion); + assertNotEquals("${project.version}", projectVersion); + } + } +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/CancelAcceptTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/CancelAcceptTest.java index d40fdc774..ed705d4bd 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/CancelAcceptTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/CancelAcceptTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; @@ -33,10 +34,11 @@ import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; import com.kohlschutter.testutil.TestAbortedWithImportantMessageException; import com.kohlschutter.testutil.TestAbortedWithImportantMessageException.MessageType; +import com.kohlschutter.testutil.TestStackTraceUtil; /** * Tests breaking out of accept. - * + * * @see Issue 6 */ @SuppressFBWarnings({ @@ -80,9 +82,11 @@ protected ExceptionHandlingDecision handleException(Exception e) { try (Socket sock = connectTo(serverThread.getServerAddress())) { // open and close + Objects.requireNonNull(sock); // silence Xlint warning } try (Socket sock = connectTo(serverThread.getServerAddress())) { // open and close + Objects.requireNonNull(sock); // silence Xlint warning } @SuppressWarnings("resource") @@ -99,7 +103,7 @@ protected ExceptionHandlingDecision handleException(Exception e) { serverSocket.close(); try { - try (Socket sock = connectTo(serverAddress)) { + try (Socket unused = connectTo(serverAddress)) { // open and close } @@ -119,6 +123,7 @@ protected ExceptionHandlingDecision handleException(Exception e) { try { try (Socket sock = connectTo(serverAddress)) { + Objects.requireNonNull(sock); // silence Xlint warning fail("ServerSocket should have been closed already"); } String noticeNoSocketException = checkKnownConditionDidNotThrowSocketException(); @@ -132,14 +137,14 @@ protected ExceptionHandlingDecision handleException(Exception e) { // as expected } } catch (SocketException e) { - e.printStackTrace(); + TestStackTraceUtil.printStackTrace(e); } } /** * Subclasses may override this to tell that there is a known condition where an otherwise * expected SocketException is not thrown. - * + * * @return An explanation iff this should not cause a test failure but just add a notice. */ protected String checkKnownConditionDidNotThrowSocketException() { diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/DatagramSocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/DatagramSocketTest.java index 5980c1bad..3bd92e34c 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/DatagramSocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/DatagramSocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import java.time.Duration; import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; @@ -108,7 +109,7 @@ private static void assertDatagramPacketAddress(DatagramPacket dp, SocketAddress } @Test - public void testBindConnect() throws SocketException, IOException, InterruptedException { + public void testBindConnect() throws Exception { AFSocketAddress ds1Addr = (AFSocketAddress) newTempAddressForDatagram(); AFSocketAddress ds2Addr = (AFSocketAddress) newTempAddressForDatagram(); @@ -130,7 +131,15 @@ public void testBindConnect() throws SocketException, IOException, InterruptedEx ds1.connect(ds2Addr); - assertConnectedDatagramSocket(ds1, ds1Addr, ds2Addr); + try { + assertConnectedDatagramSocket(ds1, ds1Addr, ds2Addr); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } byte[] data = "Hello world!".getBytes(StandardCharsets.UTF_8); byte[] buf = new byte[512]; diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/EndOfFileTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/EndOfFileTest.java index 37408db53..7286e77d9 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/EndOfFileTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/EndOfFileTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ /** * See http://code.google.com/p/junixsocket/issues/detail?id=9 - * + * * @author Derrick Rice (April, 2010) */ @SuppressFBWarnings({ @@ -234,11 +234,11 @@ public void serverWriteToSocketClosedByClient() throws Exception { try { /* * http://www.unixguide.net/network/socketfaq/2.1.shtml http://www.faqs.org/rfcs/rfc793.html - * + * * The TCP RFC allows the open side to continue sending data. In most (all?) * implementations, the closed side will respond with a RST. For this reason, it takes two * writes to cause an IOException with TCP sockets. (or more, if there is latency) - * + * * However, it is expected that the write give an IOException as soon as possible - which * means it is OK for our socket implementation to give an IOException on the first write. */ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/FileDescriptorCastTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/FileDescriptorCastTest.java index 6738a5c6e..c5907cf1d 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/FileDescriptorCastTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/FileDescriptorCastTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -95,6 +97,26 @@ public void testStdout() throws IOException { } } + @Test + public void testCastAsInteger() throws Exception { + assertNotEquals(-1, FileDescriptorCast.using(FileDescriptor.in).as(Integer.class)); + assertNotEquals(-1, FileDescriptorCast.using(FileDescriptor.out).as(Integer.class)); + assertNotEquals(-1, FileDescriptorCast.using(FileDescriptor.err).as(Integer.class)); + + assertThrows(IOException.class, () -> FileDescriptorCast.using(new FileDescriptor()).as( + Integer.class)); + } + + @Test + @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNSAFE) + public void testUnsafeCast() throws Exception { + assertEquals(1, FileDescriptorCast.unsafeUsing(1).as(Integer.class)); + assertEquals(-2, FileDescriptorCast.unsafeUsing(-2).as(Integer.class)); + assertSame(FileDescriptor.out, FileDescriptorCast.unsafeUsing(FileDescriptorCast.using( + FileDescriptor.out).as(Integer.class)).as(FileDescriptor.class)); + assertThrows(IOException.class, () -> FileDescriptorCast.unsafeUsing(-1).as(Integer.class)); + } + @Test public void testRandomAccessFile() throws Exception { File tempFile = AFUNIXSocketAddress.newTempPath(false); diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTest.java index 03eab2f38..31ab22227 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,14 +44,13 @@ /** * This tests the issue reported in * issue 29. - * + * * We need to ensure that the native file descriptor is closed whenever our socket implementation is * garbage collected, even when {@link AFSocket#close()} is not called. - * + * * @author Christian Kohlschütter */ @CommandAvailabilityRequirement(commands = {"lsof"}) -@ForkedVMRequirement(forkSupported = true) @SuppressFBWarnings({ "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"}) public abstract class FinalizeTest extends SocketTestBase { @@ -61,6 +60,7 @@ protected FinalizeTest(AddressSpecifics asp) { super(asp); } + @ForkedVMRequirement(forkSupported = true) @Test public void testLeak() throws Exception { assertTimeoutPreemptively(Duration.ofSeconds(10), () -> { @@ -78,10 +78,12 @@ protected void handleConnection(final Socket socket) throws IOException { try { assumeTrue(process.pid() > 0); Object preRunCheck = null; - try (OutputStream out = socket.getOutputStream(); - InputStream in = socket.getInputStream()) { - preRunCheck = preRunCheck(process); - out.write('@'); + try { + try (OutputStream out = socket.getOutputStream(); + InputStream unused = socket.getInputStream()) { + preRunCheck = preRunCheck(process); + out.write('@'); + } } finally { future.complete(preRunCheck); } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTestClient.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTestClient.java index 19673a531..cf113ecd6 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTestClient.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/FinalizeTestClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,25 @@ import java.net.Socket; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; /** * A potentially file descriptor-leaking client. - * + * * See issue 29 for details. - * + * * @see FinalizeTest * @author Christian Kohlschütter */ @SuppressFBWarnings({ "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"}) public class FinalizeTestClient { - @SuppressFBWarnings({"RV_RETURN_VALUE_IGNORED"}) + @SuppressWarnings({"ModifiedButNotUsed" /* errorprone */}) + @SuppressFBWarnings({"RV_RETURN_VALUE_IGNORED", "UC_USELESS_OBJECT"}) public static void main(String[] args) throws Exception { String socketType = System.getProperty("test.junixsocket.socket.type", ""); String socketName = System.getProperty("test.junixsocket.socket", ""); @@ -56,9 +59,11 @@ public static void main(String[] args) throws Exception { } socket = AFSocket.connectTo(Objects.requireNonNull(addr)); socket.getInputStream().read(); + + // create some pressure on GC + List list = new ArrayList<>(); while (true) { - // create some pressure on GC - new String("junixsocket".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + list.add(new String("junixsocket".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)); } } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/ImplUtil.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/ImplUtil.java index ce2a174d2..78fbb449f 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/ImplUtil.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/ImplUtil.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/InetAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/InetAddressTest.java index b753fbb5c..29d60f9fe 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/InetAddressTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/InetAddressTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/PipeTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/PipeTest.java index 8a7b6e580..9bfe058c8 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/PipeTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/PipeTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ /** * Tests the behavior of {@link AFPipe}. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION") @@ -47,7 +47,7 @@ public final class PipeTest { /** * Tests sequential writing/reading. - * + * * @throws IOException on error. */ @Test @@ -78,7 +78,7 @@ public void testPipe() throws IOException { /** * Tests concurrent writing/reading from the pipe. - * + * * @throws IOException on error. * @throws InterruptedException on error. * @throws ExecutionException on error. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/ReadWriteTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/ReadWriteTest.java index f18a685aa..58b71c740 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/ReadWriteTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/ReadWriteTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ /** * Reads and writes data either using byte arrays or byte-for-byte (which may be implemented * differently). - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ @@ -90,7 +90,7 @@ public void testReceiveDataByteForByteSendByteForByte() { } private final class ByteArrayWritingServerThread extends ServerThread { - public ByteArrayWritingServerThread() throws IOException { + public ByteArrayWritingServerThread() throws IOException, InterruptedException { super(); } @@ -110,7 +110,7 @@ protected void handleConnection(final Socket sock) throws IOException { } private final class ByteForByteWritingServerThread extends ServerThread { - public ByteForByteWritingServerThread() throws IOException { + public ByteForByteWritingServerThread() throws IOException, InterruptedException { super(); } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelectorTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelectorTest.java index 9e67e4af0..61903ed82 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelectorTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelectorTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -175,11 +175,11 @@ private Future newHelloClient(SocketAddress serverAddr, Semaphore sema) /** * Tests a non-blocking server setup where a client connects, sends "Hello" to the server and then * disconnects after the server has received the message. - * + * * In the selection loop, after the client has disconnected, the server should see that the * corresponding {@link SelectionKey} is "readable" (even if that means the connection was * closed). - * + * * @throws Exception on error. */ @Test @@ -193,7 +193,7 @@ public void testConnectionCloseEventualClientDisconnect() throws Exception { * * Unlike {@link #testConnectionCloseEventualClientDisconnect()}, this may uncover additional * faults, such as an unexpected exception when trying to read the socket name. - * + * * @throws Exception on error. */ @Test @@ -204,11 +204,11 @@ public void testConnectionCloseImmediateClientDisconnect() throws Exception { /** * Tests a non-blocking server setup where a client connects, sends "Hello" to the server and then * disconnects after the server has received the message. - * + * * In the selection loop, after the client has disconnected, the server should see that the * corresponding {@link SelectionKey} is "readable" (even if that means the connection was * closed). - * + * * @throws Exception on error. */ @Test @@ -222,7 +222,7 @@ public void testConnectionCloseEventualClientDisconnectKeepLooping() throws Exce * * Unlike {@link #testConnectionCloseEventualClientDisconnect()}, this may uncover additional * faults, such as an unexpected exception when trying to read the socket name. - * + * * @throws Exception on error. */ @Test @@ -253,10 +253,12 @@ private void testConnectionClose(boolean clientCloseImmediately, boolean checkIn int numReadable = 0; int numClosedChannelException = 0; - int timeout = 5000; + long timeout = 5000; selectLoop : while (timeout > 0) { long time = System.currentTimeMillis(); - while (selector.select(timeout) != 0) { + int n; + while ((n = selector.select(timeout)) != 0) { + assertTrue(n > 0); for (SelectionKey key : selector.selectedKeys()) { if (numAcceptable > 3 || numReadable > 2) { break selectLoop; @@ -324,7 +326,7 @@ private void testConnectionClose(boolean clientCloseImmediately, boolean checkIn } catch (ExecutionException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { - throw (RuntimeException) t; + throw (RuntimeException) t; // NOPMD.PreserveStackTrace } } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestDiagnosticsHelper.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestDiagnosticsHelper.java index bc0544efa..af926dff6 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestDiagnosticsHelper.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestDiagnosticsHelper.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,11 @@ package org.newsclub.net.unix; import java.io.File; +import java.util.Map; /** * Some bridging code that allows junixsocket-selftest to do some in-depth diagnostics. - * + * * @author Christian Kohlschütter */ public final class SelftestDiagnosticsHelper { @@ -30,7 +31,7 @@ private SelftestDiagnosticsHelper() { /** * Returns the error that prevented the native library from loading, or {@code null}. - * + * * @return The error, or {@code null}. */ public static Throwable initError() { @@ -39,10 +40,21 @@ public static Throwable initError() { /** * Returns the temporary directory used for storing the native library, or {@code null}. - * + * * @return The directory, or {@code null}. */ public static File tempDir() { return NativeLibraryLoader.tempDir(); } + + /** + * Returns properties determined upon Maven build time. + * + * For performance reasons, these will not be correctly resolves when developing in Eclipse. + * + * @return The properties. + */ + public static Map buildProperties() { + return BuildProperties.getBuildProperties(); + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestProvider.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestProvider.java index 0c8412221..04cf5dac7 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestProvider.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SelftestProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,10 @@ */ package org.newsclub.net.unix; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.net.InetSocketAddress; +import java.nio.charset.Charset; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -27,20 +30,23 @@ /** * Provides references to all "junixsocket-common" tests that should be included in * junixsocket-selftest. - * + * * @author Christian Kohlschütter */ @SuppressWarnings("PMD.CouplingBetweenObjects") public class SelftestProvider { - final Map>> testMap = new LinkedHashMap<>(); + private static final String COMMON = "junixsocket-common"; + private static final String COMMON_JAVA_INET = "junixsocket-common.JavaInet"; + + final Map>> testMap = new LinkedHashMap<>(); // NOPMD.LooseCoupling // CPD-OFF @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.UnnecessaryFullyQualifiedName"}) public SelftestProvider() { - registerTest("junixsocket-common", org.newsclub.net.unix.AFTIPCSocketAddressTest.class); + registerTest(COMMON, org.newsclub.net.unix.AFTIPCSocketAddressTest.class); - registerTest("junixsocket-common", org.newsclub.net.unix.AFUNIXSocketAddressTest.class); + registerTest(COMMON, org.newsclub.net.unix.AFUNIXSocketAddressTest.class); registerTest(org.newsclub.net.unix.domain.AbstractNamespaceTest.class); @@ -50,6 +56,8 @@ public SelftestProvider() { registerTest(org.newsclub.net.unix.domain.AvailableTest.class); + registerTest(COMMON, BuildPropertiesTest.class); + registerTest(org.newsclub.net.unix.domain.BufferOverflowTest.class); registerTest(org.newsclub.net.unix.domain.CancelAcceptTest.class); @@ -58,8 +66,8 @@ public SelftestProvider() { registerTest(org.newsclub.net.unix.domain.EndOfFileTest.class); - registerTest("junixsocket-common", org.newsclub.net.unix.FileDescriptorCastTest.class); - registerTest("junixsocket-common", org.newsclub.net.unix.domain.FileDescriptorCastTest.class); + registerTest(COMMON, org.newsclub.net.unix.FileDescriptorCastTest.class); + registerTest(COMMON, org.newsclub.net.unix.domain.FileDescriptorCastTest.class); // file-descriptor passing is AF_UNIX-specific registerTest(org.newsclub.net.unix.domain.FileDescriptorsTest.class); @@ -71,7 +79,7 @@ public SelftestProvider() { // peer credential passing is AF_UNIX specific registerTest(org.newsclub.net.unix.domain.PeerCredentialsTest.class); - registerTest("junixsocket-common", PipeTest.class); + registerTest(COMMON, PipeTest.class); registerTest(org.newsclub.net.unix.domain.ReadWriteTest.class); @@ -101,20 +109,22 @@ public SelftestProvider() { registerTest(org.newsclub.net.unix.domain.ThroughputTest.class); registerTestJavaInet(org.newsclub.net.unix.java.ThroughputTest.class); + + registerTest(org.newsclub.net.unix.domain.UnixDomainSocketAddressTest.class); } public Set modulesDisabledByDefault() { - return Collections.singleton("junixsocket-common.JavaInet"); + return Collections.singleton(COMMON_JAVA_INET); } private void registerTest( // Class> testUnixDomain) { - registerTest("junixsocket-common", testUnixDomain); + registerTest(COMMON, testUnixDomain); } private void registerTestJavaInet( // Class> testJava) { - registerTest("junixsocket-common.JavaInet", testJava); + registerTest(COMMON_JAVA_INET, testJava); } private void registerTest(String group, Class test) { @@ -126,9 +136,18 @@ private void registerTest(String group, Class test) { public Map[]> tests() { Map[]> tests = new LinkedHashMap<>(); testMap.forEach((key, set) -> { - tests.put(key, set.toArray(new Class[0])); + tests.put(key, set.toArray(new Class[0])); }); return tests; } + + public void printAdditionalProperties(PrintWriter out) { + out.println("Native architecture: " + NativeLibraryLoader.getArchitectureAndOS()); + } + + public static void main(String[] args) { + new SelftestProvider().printAdditionalProperties(new PrintWriter(new OutputStreamWriter( + System.out, Charset.defaultCharset()), true)); + } } \ No newline at end of file diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketCloseTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketCloseTest.java index d5b48bf7d..7e5d3314e 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketCloseTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketCloseTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,8 +68,10 @@ private void testUnblockAccepts(int timeout) throws Exception { final CountDownLatch cdl = new CountDownLatch(numThreads); + @SuppressWarnings("all") // "resource"; only since Java 19 ThreadPoolExecutor is + // AutoCloseable ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, - TimeUnit.SECONDS, new SynchronousQueue()); + TimeUnit.SECONDS, new SynchronousQueue<>()); for (int i = 0; i < numThreads; i++) { threadPool.submit(new Runnable() { diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketTest.java index 66ec18760..468c9b506 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/ServerSocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ /** * Tests some otherwise uncovered methods of {@link AFSocket}. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SoTimeoutTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SoTimeoutTest.java index 9f8b0b1b4..97b4579f4 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SoTimeoutTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SoTimeoutTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,14 +32,15 @@ import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; -import org.opentest4j.TestAbortedException; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; import com.kohlschutter.testutil.AssertUtil; +import com.kohlschutter.testutil.TestAbortedWithImportantMessageException; +import com.kohlschutter.testutil.TestAbortedWithImportantMessageException.MessageType; /** * Tests {@link Socket#setSoTimeout(int)} behavior. - * + * * @see Issue 14 */ @SuppressFBWarnings({ @@ -52,7 +53,7 @@ protected SoTimeoutTest(AddressSpecifics asp) { /** * Triggers a case where {@link Socket#setSoTimeout(int)} fails on some platforms: when the socket * is closed. - * + * * @throws IOException on error. */ @Test @@ -82,7 +83,7 @@ protected void handleConnection(final Socket socket) throws IOException { /** * Triggers a regular case where {@link Socket#setSoTimeout(int)} should work. - * + * * @throws IOException on error. */ @Test @@ -135,7 +136,20 @@ public void testSocketTimeoutExceptionWrite() throws Exception { Socket socket = pair.getSecond(); socket.setSoTimeout(500); if (socket.getSoTimeout() == 0) { - throw new TestAbortedException("Could not set socket timeout"); + boolean isZOS = "z/OS".equals(System.getProperty("os.name", "")); + + String afs = addressFamilyString(); + + if (isZOS && "AF_UNIX".equals(afs)) { + // https://www.ibm.com/docs/en/zos/2.5.0?topic=csd-getsockopt-setsockopt-bpx1opt-bpx4opt-get-set-options-associated-socket + throw new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_WITH_ISSUES, + "Could not set socket timeout via Socket.setSoTimeout (" + addressFamilyString() + + "); this is a known limitation of z/OS"); + } else { + throw new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_WITH_ISSUES, + "Could not set socket timeout via Socket.setSoTimeout (" + addressFamilyString() + + ")"); + } } byte[] buf = new byte[socket.getSendBufferSize()]; OutputStream out = socket.getOutputStream(); diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketChannelTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketChannelTest.java index fb4dea1d4..67c897607 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketChannelTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketChannelTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ */ package org.newsclub.net.unix; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -24,11 +25,13 @@ import java.io.IOException; import java.net.SocketAddress; import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Objects; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,6 +40,7 @@ import com.kohlschutter.testutil.TestAbortedWithImportantMessageException; import com.kohlschutter.testutil.TestAbortedWithImportantMessageException.MessageType; +import com.kohlschutter.testutil.TestAsyncUtil; public abstract class SocketChannelTest extends SocketTestBase { protected SocketChannelTest(AddressSpecifics asp) { @@ -55,7 +59,7 @@ public void testNonBlockingConnect() throws IOException { { SocketChannel sc = selectorProvider().openSocketChannel(); sc.configureBlocking(false); - if (!sc.connect(sa)) { + if (!handleConnect(sc, sa)) { // connect pending assertTrue(sc.isConnected() || sc.isConnectionPending()); long now = System.currentTimeMillis(); @@ -90,13 +94,15 @@ public void testDoubleBindAddressReusable() throws Exception { testDoubleBind(true); } - @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.CognitiveComplexity", "PMD.NPathComplexity"}) + @SuppressWarnings({ + "PMD.ExcessiveMethodLength", "PMD.CognitiveComplexity", "PMD.NPathComplexity", + "PMD.CyclomaticComplexity"}) private void testDoubleBind(boolean reuseAddress) throws Exception { SocketAddress sa0 = newTempAddress(); - final CompletableFuture acceptCall; - final CompletableFuture acceptCall2; - CompletableFuture connectCall = null; + final Future acceptCall; + final Future acceptCall2; + Future connectCall = null; AtomicBoolean socketDomainWillAcceptCallOnFirstBind = new AtomicBoolean(true); @@ -106,7 +112,7 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { AtomicBoolean connectMustSucceed = new AtomicBoolean(false); - acceptCall = CompletableFuture.supplyAsync(() -> { + acceptCall = TestAsyncUtil.supplyAsync(() -> { try { SocketChannel sc = ssc1.accept(); socketDomainWillAcceptCallOnFirstBind.set(false); @@ -116,11 +122,23 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { } return sc; } catch (SocketException e) { + String msg = checkKnownBugAcceptFailure(e); + if (msg != null) { + throw new TestAbortedWithImportantMessageException( + MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, msg, summaryImportantMessage(msg), e); + } if (reuseAddress) { // expected (Software caused connection abort) } else { fail(e); } + } catch (SocketTimeoutException e) { + String msg = checkKnownBugAcceptFailure(e); + if (msg != null) { + throw new TestAbortedWithImportantMessageException( + MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, msg, summaryImportantMessage(msg), e); + } + fail(e); } catch (IOException e) { fail(e); } @@ -146,7 +164,7 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { } if (reuseAddress) { - acceptCall2 = CompletableFuture.supplyAsync(() -> { + acceptCall2 = TestAsyncUtil.supplyAsync(() -> { try { SocketChannel sc = ssc2.accept(); socketDomainWillAcceptCallOnFirstBind.set(false); @@ -171,7 +189,7 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { // unblock accept of any successful bind if (!acceptCall.isDone() && socketDomainWillAcceptCallOnFirstBind.get()) { - connectCall = CompletableFuture.runAsync(() -> { + connectCall = TestAsyncUtil.supplyAsync(() -> { try { newSocket().connect(sa); } catch (SocketException e) { @@ -205,12 +223,20 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { acceptCall.get(5, TimeUnit.SECONDS); } catch (ExecutionException e) { // ignore socket closed etc. + if (e.getCause() instanceof TestAbortedWithImportantMessageException) { + throw (TestAbortedWithImportantMessageException) e.getCause(); + } } catch (TimeoutException e) { triggerWithIssues = checkKnownBugFirstAcceptCallNotTerminated(); if (triggerWithIssues == null) { fail("First accept call did not terminate"); } } + if (triggerWithIssues != null) { + throw new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, + triggerWithIssues, summaryImportantMessage(triggerWithIssues)); + } + if (connectCall != null) { try { connectCall.get(5, TimeUnit.SECONDS); @@ -220,17 +246,126 @@ private void testDoubleBind(boolean reuseAddress) throws Exception { fail("Connect call did not terminate"); } } + } - if (triggerWithIssues != null) { - throw new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_WITH_ISSUES, - triggerWithIssues); + /** + * Subclasses may override this to tell that there is a known issue with "accept". + * + * @param e The exception + * @return An explanation iff this should not cause a test failure but trigger "With issues". + */ + protected String checkKnownBugAcceptFailure(SocketException e) { + return null; + } + + /** + * Subclasses may override this to tell that there is a known issue with "accept". + * + * @param e The exception + * @return An explanation iff this should not cause a test failure but trigger "With issues". + */ + protected String checkKnownBugAcceptFailure(SocketTimeoutException e) { + return null; + } + + /** + * Bind the given address to the given {@link ServerSocketChannel}. + * + * By default, this just calls `ssc.bind(sa)`, but you may handle some exceptions by overriding + * this method in a subclass. + * + * @param ssc The server socket channel. + * @param sa The socket address to bind to. + * @throws IOException on error. + */ + protected void handleBind(ServerSocketChannel ssc, SocketAddress sa) throws IOException { + ssc.bind(sa); + } + + /** + * Connect the given {@link SocketChannel} with the given address. + * + * By default, this just calls `sc.connect(sa)`, but you may handle some exceptions by overriding + * this method in a subclass. + * + * @param sc The socket channel. + * @param sa The socket address to connect to. + * @throws IOException on error. + */ + protected boolean handleConnect(SocketChannel sc, SocketAddress sa) throws IOException { + return sc.connect(sa); + } + + @Test + public void testByteBufferWithPositionOffset() throws Exception { + SocketAddress sa = newTempAddress(); + + final int bb1Offset = 32; + final int bb2Offset = 1; + + byte[] data = new byte[96]; + getRandom().nextBytes(data); + + try (ServerSocketChannel ssc = selectorProvider().openServerSocketChannel()) { + handleBind(ssc, sa); + + ByteBuffer bb1 = ByteBuffer.allocate(data.length + bb1Offset); + bb1.position(bb1Offset); + + bb1.put(data); + bb1.flip(); + + bb1.position(bb1Offset); + assertEquals(bb1Offset, bb1.position()); + + TestAsyncUtil.runAsync(() -> { + try (SocketChannel sc = ssc.accept()) { + int written = 0; + while (bb1.hasRemaining()) { + written += sc.write(bb1); + } + assertEquals(data.length, written); + } catch (IOException e) { + fail(e); + } + }); + + SocketChannel sc = selectorProvider().openSocketChannel(); + + ByteBuffer bb2 = ByteBuffer.allocate(data.length + bb2Offset); + + assertTrue(handleConnect(sc, ssc.getLocalAddress())); + + bb2.position(bb2Offset); + + int read = 0; + int r; + do { + r = sc.read(bb2); + if (r == -1) { + break; + } + read += r; + } while (bb2.hasRemaining()); + assertEquals(data.length, read); + + assertEquals(bb1.capacity(), bb1.position()); + assertEquals(bb1.capacity(), bb1.limit()); + assertEquals(data.length + bb2Offset, bb2.position()); + assertEquals(bb2.capacity(), bb2.limit()); + + bb1.position(bb1Offset); + + for (int i = 0, n = data.length; i < n; i++) { + assertEquals(bb1.get(bb1Offset + i), bb2.get(bb2Offset + i), "at pos " + i); + } } } /** * Subclasses may override this to tell that there is a known issue with "First accept call did * not terminate". - * + * * @return An explanation iff this should not cause a test failure but trigger "With issues". */ protected String checkKnownBugFirstAcceptCallNotTerminated() { @@ -239,9 +374,9 @@ protected String checkKnownBugFirstAcceptCallNotTerminated() { /** * Returns the temporary address usable to binding on for a second bind. - * + * * Depending on the socket domain, a wildcard address may be permittable or not for a second bind. - * + * * @param originalAddress The original temporary address (e.g., a wildcard address). * @param ssc The socket that was bound to that address. * @return The local bound address, or the {@code originalAddress}. @@ -256,7 +391,7 @@ protected SocketAddress resolveAddressForSecondBind(SocketAddress originalAddres /** * Override to declare that a certain socket domain permits double-binding an address, * particularly when the address is comparable to a wildcard address. - * + * * @return {@code true} iff double-binding the same address is allowed. * @see #testDoubleBindAddressReusable() */ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketOptionsTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketOptionsTest.java index 43432f20f..5f878bb15 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketOptionsTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketOptionsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketPairTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketPairTest.java index d798c6006..e3ebdd092 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketPairTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketPairTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -30,6 +29,7 @@ import java.util.Objects; import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; @@ -87,14 +87,25 @@ public void testSocketPair() throws Exception { bb2.flip(); assertEquals(0x04030201, bb2.getInt()); + // Haiku OS uses "0x00 AA BB CC DD EE" as socket addresses for internal socket IDs + if (getServerBindAddress() instanceof AFUNIXSocketAddress) { - assertNull(pair.getFirst().getLocalAddress()); - assertNull(pair.getSecond().getLocalAddress()); - assertNull(pair.getFirst().getRemoteAddress()); - assertNull(pair.getSecond().getRemoteAddress()); - } else { + if (pair.getFirst().getLocalAddress() == null && pair.getSecond().getLocalAddress() == null + && pair.getFirst().getRemoteAddress() == null && pair.getSecond() + .getRemoteAddress() == null) { + // on many systems, socketpair with AF_UNIX returns all-null addresses + return; + } + } + try { assertCovered(pair.getFirst().getLocalAddress(), pair.getSecond().getRemoteAddress()); assertCovered(pair.getSecond().getLocalAddress(), pair.getFirst().getRemoteAddress()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } } } @@ -130,14 +141,25 @@ public void testDatagramPair() throws Exception { bb2.flip(); assertEquals(0x04030201, bb2.getInt()); + // NOTE: Haiku OS uses "0x00 AA BB CC DD EE" as socket addresses for internal socket IDs + if (getServerBindAddress() instanceof AFUNIXSocketAddress) { - assertNull(pair.getFirst().getLocalAddress()); - assertNull(pair.getSecond().getLocalAddress()); - assertNull(pair.getFirst().getRemoteAddress()); - assertNull(pair.getSecond().getRemoteAddress()); - } else { + if (pair.getFirst().getLocalAddress() == null && pair.getSecond().getLocalAddress() == null + && pair.getFirst().getRemoteAddress() == null && pair.getSecond() + .getRemoteAddress() == null) { + // on many systems, socketpair with AF_UNIX returns all-null addresses + return; + } + } + try { assertCovered(pair.getFirst().getLocalAddress(), pair.getSecond().getRemoteAddress()); assertCovered(pair.getSecond().getLocalAddress(), pair.getFirst().getRemoteAddress()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } } } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTest.java index 2044a0d42..80fd0099f 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ /** * Tests some otherwise uncovered methods of {@link AFSocket}. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTestBase.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTestBase.java index 3fbf715fe..6b020ce91 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTestBase.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/SocketTestBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,20 +48,20 @@ /** * Some base functionality for socket tests. - * + * * This class provides access to the {@link AddressSpecifics} methods for the socket implementation * under test. It is essential to use these wrapper methods in tests instead of directly calling the * {@link AFSocket} etc. methods: Some socket implementations (and sometimes only in certain * kernel/environment configurations) may expose unexpected behavior that is otherwise hard to * catch. - * + * * This is especially relevant when connecting/binding sockets (see * {@link #connectSocket(Socket, SocketAddress)}, #bindServerSocket(ServerSocket, SocketAddress)}, * etc.) - * + * * @author Christian Kohlschuetter */ -@SuppressWarnings("PMD.AbstractClassWithoutAbstractMethod") +@SuppressWarnings({"PMD.AbstractClassWithoutAbstractMethod", "PMD.CouplingBetweenObjects"}) @SuppressFBWarnings({ "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"}) public abstract class SocketTestBase { // NOTE: needs to be public for @@ -145,9 +145,9 @@ protected ServerSocket startServer() throws IOException { /** * Checks if an optional connection check via {@link AFSocket#checkConnectionClosed()}, is to be * run upon {@link AFServerSocket#accept()}. - * + * * Override to enable. - * + * * @return {@code true} if enabled; default is {@code false} = disabled. */ protected boolean shouldDoConnectionCheckUponAccept() { @@ -164,14 +164,21 @@ protected abstract class ServerThread extends Thread implements AutoCloseable { private volatile Error error = null; private final AtomicBoolean loop = new AtomicBoolean(true); private final Semaphore sema = new Semaphore(1); + private final Semaphore readySema = new Semaphore(0); @SuppressFBWarnings("SC_START_IN_CTOR") - protected ServerThread() throws IOException { + protected ServerThread() throws IOException, InterruptedException { super(); serverSocket = startServer(); // NOPMD setDaemon(true); start(); + readySema.acquire(); + } + + @Override + public final void start() { + super.start(); } protected ServerSocket startServer() throws IOException { @@ -186,7 +193,7 @@ public void close() throws Exception { /** * Stops the server. - * + * * @throws IOException on error. */ public void shutdown() throws IOException { @@ -199,11 +206,11 @@ public void shutdown() throws IOException { /** * Callback used to handle a connection call. - * + * * After returning from this call, the socket is closed. - * + * * Use {@link #stopAcceptingConnections()} to stop accepting new calls. - * + * * @param sock The socket to handle. * @throws IOException upon error. */ @@ -212,9 +219,9 @@ public void shutdown() throws IOException { /** * Called from within {@link #handleConnection(Socket)} to tell the server to no longer accept * new calls and to terminate the server thread. - * + * * Note that this will lead to existing client connections to be closed. - * + * * If you want to deny new connections but finish your work on the client side (in another * thread), then please use semaphores etc. to ensure reaching a safe state before calling this * method. @@ -229,7 +236,7 @@ protected void onServerSocketClose() { /** * Returns the server socket. - * + * * @return the server socket. */ @SuppressFBWarnings("EI_EXPOSE_REP") @@ -239,7 +246,7 @@ public ServerSocket getServerSocket() { /** * Returns the server's address to connect to. - * + * * @return the address. */ public SocketAddress getServerAddress() { @@ -248,7 +255,7 @@ public SocketAddress getServerAddress() { /** * Called upon receiving an exception that may be handled specifically. - * + * * @param e The exception * @return {@link ExceptionHandlingDecision#RAISE} if we should handle the exception somehow, * {@link ExceptionHandlingDecision#IGNORE} if we should pretend the exception never @@ -279,7 +286,7 @@ protected void acceptAndHandleConnection() throws IOException { if (acceptSuccess) { handleConnection(sock); } - } catch (IOException e) { + } catch (IOException e) { // NOPMD.ExceptionAsFlowControl if (!acceptSuccess) { // ignore: connection closed before accept could complete if (serverSocket.isClosed()) { @@ -299,6 +306,7 @@ protected void acceptAndHandleConnection() throws IOException { public final void run() { try { loop.set(true); + readySema.release(); onServerReady(); while (loop.get()) { acceptAndHandleConnection(); @@ -323,9 +331,9 @@ protected void onServerReady() { /** * Checks if there were any exceptions thrown during the lifetime of this ServerThread. - * + * * NOTE: This call blocks until the Thread actually terminates. - * + * * @throws Exception upon error. */ public void checkException() throws Exception { @@ -343,7 +351,7 @@ public void checkException() throws Exception { } protected abstract class AFUNIXServerThread extends ServerThread { - protected AFUNIXServerThread() throws IOException { + protected AFUNIXServerThread() throws IOException, InterruptedException { super(); } @@ -357,7 +365,7 @@ protected final void handleConnection(Socket sock) throws IOException { /** * Sleeps for the given amount of milliseconds. - * + * * @param ms The duration in milliseconds. * @throws InterruptedIOException when interrupted. */ @@ -455,6 +463,10 @@ protected CloseablePair newInterconnectedSockets() throws IOEx return asp.newInterconnectedSockets(); } + protected String addressFamilyString() { + return asp.addressFamilyString(); + } + protected Random getRandom() { return RANDOM; } @@ -463,7 +475,7 @@ protected Random getRandom() { * Returns the Linux kernel's major and minor version as an integer array (i.e., {@code 5.10.2 -> * int[]{5,10}}), or {@code null} if the running system isn't Linux or the version could not be * determined. - * + * * @return The running Linux kernels' major and minor version as an integer array, or * {@code null}. */ @@ -478,4 +490,15 @@ protected int[] getLinuxMajorMinorVersion() { return null; // NOPMD.PMD.ReturnEmptyCollectionRatherThanNull } } + + /** + * Provide a potentially different string for the "important message" section in selftest, given + * an existing error message. + * + * @param message The original message. + * @return A potentially different string. + */ + protected String summaryImportantMessage(String message) { + return asp.summaryImportantMessage(message); + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/StandardSocketOptionsTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/StandardSocketOptionsTest.java index 4ed2fc770..e64cdf42a 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/StandardSocketOptionsTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/StandardSocketOptionsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,11 +47,12 @@ /** * Tests the {@code Socket#getOption(SocketOption)} API available since Java 9. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"}) +@SuppressWarnings("PMD.CouplingBetweenObjects") public abstract class StandardSocketOptionsTest extends SocketTestBase { private static final Set> IGNORABLE_OPTIONS = Set.of( // StandardSocketOptions.IP_TOS, // diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/StdinSocketApp.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/StdinSocketApp.java index 060e258ab..03227a6c1 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/StdinSocketApp.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/StdinSocketApp.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * This test app responds with "Hello world" over a {@link Socket} that was passed from another * process via standard input. - * + * * @author Christian Kohlschütter * @see org.newsclub.net.unix.domain.FileDescriptorCastTest#testForkedVMRedirectStdin() */ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/TcpNoDelayTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/TcpNoDelayTest.java index d11dcfc5e..62fccfd8c 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/TcpNoDelayTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/TcpNoDelayTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/TestUtil.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/TestUtil.java new file mode 100644 index 000000000..c6f93d954 --- /dev/null +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/TestUtil.java @@ -0,0 +1,100 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix; + +import com.kohlschutter.testutil.TestAbortedWithImportantMessageException; +import com.kohlschutter.testutil.TestAbortedWithImportantMessageException.MessageType; + +/** + * Some test-related checks. + * + * @author Christian Kohlschütter + */ +public final class TestUtil { + private static final boolean IS_HAIKU_OS = "Haiku".equals(System.getProperty("os.name")); + + private TestUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Checks if the system under test is Haiku Os. + * + * @return {@code true} if so. + */ + public static boolean isHaikuOS() { + // Checks if the system + // under test is Haiku Os. + // Return true if so. + return IS_HAIKU_OS; + } + + private static void handleBug(String id, Throwable e) throws Exception { + String key = "selftest." + id; + switch (System.getProperty(key, "")) { + case "dump": + e.printStackTrace(); + break; + case "fail": + if (e instanceof Exception) { + throw (Exception) e; + } else if (e instanceof Error) { + throw (Error) e; + } else { + throw new IllegalStateException(e); + } + case "": + // nothing + return; + default: + System.err.println("Invalid value for System property " + key); + } + } + + /** + * In certain Haiku environments, socketpair/connect/accept for AF_UNIX is buggy; it may not set + * the "connected state" (fixed in hrev57189). + * + * See Haiku Bug 18534 for details. + * + * @param e The caught throwable. + * @return A TestAbortedWithImportantMessageException wrapping the caught throwable. + */ + public static Exception haikuBug18534(Throwable e) throws Exception { + handleBug("haikuBug18534", e); + return new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, + "AF_UNIX support is buggy in this Haiku release; see https://dev.haiku-os.org/ticket/18534", + e); + } + + /** + * In certain Haiku environments, working with datagram sockets may result in a kernel hang + * (spinlock upon send). + * + * See Haiku Bug 18535 for details. + * + * @param e The caught throwable. + * @return A TestAbortedWithImportantMessageException wrapping the caught throwable. + */ + public static Exception haikuBug18535(Throwable e) throws Exception, Error { + handleBug("haikuBug18535", e); + return new TestAbortedWithImportantMessageException(MessageType.TEST_ABORTED_SHORT_WITH_ISSUES, + "AF_UNIX datagram support is buggy in this Haiku release or environment; see https://dev.haiku-os.org/ticket/18535", + e); + } +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/ThroughputTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/ThroughputTest.java index 386068f96..a9bbf98ba 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/ThroughputTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/ThroughputTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,6 @@ import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,20 +54,27 @@ import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.platform.commons.JUnitException; +import org.opentest4j.AssertionFailedError; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; +import com.kohlschutter.testutil.TestAsyncUtil; +import com.kohlschutter.testutil.TestStackTraceUtil; import com.kohlschutter.util.SystemPropertyUtil; /** * This test measures throughput for sending and receiving messages over AF_UNIX, comparing * implementations of junixsocket and JEP 380 (Java 16). * - * The test is enabled by default, and also included with the self-test. + * The test is enabled by default (only runs for a very short time), and is also included within the + * self-test. * * The tests can be configured as follows (all system properties): *

    *
  • org.newsclub.net.unix.throughput-test.enabled (0/1, default: 1)
  • *
  • org.newsclub.net.unix.throughput-test.payload-size (bytes, e.g., 8192)
  • + *
  • org.newsclub.net.unix.throughput-test.payload-size.datagram (bytes, e.g., 2048; + * defaults to value specified with "payload-size" above)
  • *
  • org.newsclub.net.unix.throughput-test.seconds (default: 0)
  • *
* @@ -82,8 +88,12 @@ public abstract class ThroughputTest extends SocketTest "org.newsclub.net.unix.throughput-test.enabled", 1); protected static final int PAYLOAD_SIZE = SystemPropertyUtil.getIntSystemProperty( "org.newsclub.net.unix.throughput-test.payload-size", 2048); // 8192 is much faster + protected static final int PAYLOAD_SIZE_DATAGRAM = SystemPropertyUtil.getIntSystemProperty( + "org.newsclub.net.unix.throughput-test.payload-size.datagram", PAYLOAD_SIZE); protected static final int NUM_SECONDS = SystemPropertyUtil.getIntSystemProperty( "org.newsclub.net.unix.throughput-test.seconds", 0); + protected static final int GRACE_TIME_NUM_SECONDS = SystemPropertyUtil.getIntSystemProperty( + "org.newsclub.net.unix.throughput-test.gracetime.seconds", 5); protected static final int NUM_MILLISECONDS = Math.max(50, NUM_SECONDS * 1000); protected ThroughputTest(AddressSpecifics asp) { @@ -111,7 +121,7 @@ public void testSocket() throws Exception { assumeTrue(ENABLED > 0, "Throughput tests are disabled"); assumeTrue(PAYLOAD_SIZE > 0, "Payload must be positive"); - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { try (ServerThread serverThread = new ServerThread() { @Override protected void handleConnection(final Socket sock) throws IOException { @@ -128,9 +138,10 @@ protected void handleConnection(final Socket sock) throws IOException { }) { AtomicBoolean keepRunning = new AtomicBoolean(true); - Executors.newSingleThreadScheduledExecutor().schedule(() -> { + + TestAsyncUtil.runAsyncDelayed(NUM_MILLISECONDS, TimeUnit.MILLISECONDS, () -> { keepRunning.set(false); - }, NUM_MILLISECONDS, TimeUnit.MILLISECONDS); + }); try (Socket sock = connectTo(serverThread.getServerAddress())) { byte[] buf = createTestData(PAYLOAD_SIZE); @@ -177,7 +188,7 @@ protected void handleConnection(final Socket sock) throws IOException { public void testSocketChannel() throws Exception { assumeTrue(ENABLED > 0, "Throughput tests are disabled"); assumeTrue(PAYLOAD_SIZE > 0, "Payload must be positive"); - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { runtestSocketChannel(false); }); } @@ -186,7 +197,7 @@ public void testSocketChannel() throws Exception { public void testSocketChannelDirectBuffer() throws Exception { assumeTrue(ENABLED > 0, "Throughput tests are disabled"); assumeTrue(PAYLOAD_SIZE > 0, "Payload must be positive"); - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { runtestSocketChannel(true); }); } @@ -216,7 +227,7 @@ protected void runTestSocketChannel(String implId, SocketAddress sba, ServerSock SupplierWithException sscSupp, boolean direct) throws Exception { final AtomicBoolean keepRunning = new AtomicBoolean(true); - try (ServerThread serverThread = new ServerThread() { + try (ServerThread unused = new ServerThread() { @Override protected ServerSocket startServer() throws IOException { @@ -264,9 +275,9 @@ protected void handleConnection(Socket sock) throws IOException { } }) { - Executors.newSingleThreadScheduledExecutor().schedule(() -> { + TestAsyncUtil.runAsyncDelayed(NUM_MILLISECONDS, TimeUnit.MILLISECONDS, () -> { keepRunning.set(false); - }, NUM_MILLISECONDS, TimeUnit.MILLISECONDS); + }); try (SocketChannel sc = sscSupp.get()) { ByteBuffer bb = direct ? ByteBuffer.allocateDirect(PAYLOAD_SIZE) : ByteBuffer.allocate( @@ -278,7 +289,7 @@ protected void handleConnection(Socket sock) throws IOException { bb.clear(); bb.put(createTestData(PAYLOAD_SIZE)); bb.flip(); - int remaining = sc.write(bb); + long remaining = sc.write(bb); bb.clear(); long read; // limited by net.local.stream.recvspace / sendspace etc. @@ -301,123 +312,161 @@ protected void handleConnection(Socket sock) throws IOException { @Test @SuppressWarnings("PMD.CognitiveComplexity") public void testDatagramPacket() throws Exception { - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { - SocketAddress dsAddr = newTempAddressForDatagram(); - SocketAddress dcAddr = newTempAddressForDatagram(); + try { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { + SocketAddress dsAddr = newTempAddressForDatagram(); + SocketAddress dcAddr = newTempAddressForDatagram(); - try (DatagramSocket ds = newDatagramSocket(); DatagramSocket dc = newDatagramSocket()) { - if (!ds.isBound()) { - ds.bind(dsAddr); - } - if (!dc.isBound()) { - dc.bind(dcAddr); - } + try (DatagramSocket ds = newDatagramSocket(); DatagramSocket dc = newDatagramSocket()) { + if (!ds.isBound()) { + ds.bind(dsAddr); + } + if (!dc.isBound()) { + dc.bind(dcAddr); + } - dsAddr = ds.getLocalSocketAddress(); - dcAddr = dc.getLocalSocketAddress(); + dsAddr = ds.getLocalSocketAddress(); + dcAddr = dc.getLocalSocketAddress(); - assertNotEquals(dsAddr, dcAddr); + assertNotEquals(dsAddr, dcAddr); - dc.connect(dsAddr); + dc.connect(dsAddr); - AtomicBoolean keepRunning = new AtomicBoolean(true); - Executors.newSingleThreadScheduledExecutor().schedule(() -> { - keepRunning.set(false); - }, NUM_MILLISECONDS, TimeUnit.MILLISECONDS); + AtomicBoolean keepRunning = new AtomicBoolean(true); + TestAsyncUtil.runAsyncDelayed(NUM_MILLISECONDS, TimeUnit.MILLISECONDS, () -> { + keepRunning.set(false); + }); - AtomicLong readTotal = new AtomicLong(); - long sentTotal = 0; + AtomicLong readTotal = new AtomicLong(); + long sentTotal = 0; - new Thread() { - final DatagramPacket dp = new DatagramPacket(new byte[PAYLOAD_SIZE], PAYLOAD_SIZE); + new Thread() { + final DatagramPacket dp = new DatagramPacket(new byte[PAYLOAD_SIZE_DATAGRAM], + PAYLOAD_SIZE_DATAGRAM); - @Override - public void run() { - try { - while (!Thread.interrupted() && !ds.isClosed()) { - try { - ds.receive(dp); - } catch (SocketTimeoutException e) { - continue; + @Override + public void run() { + try { + while (!Thread.interrupted() && !ds.isClosed()) { + try { + ds.receive(dp); + } catch (SocketTimeoutException e) { + continue; + } + int read = dp.getLength(); + if (read != PAYLOAD_SIZE_DATAGRAM && read != 0) { + throw new IOException("Unexpected response length: " + read); + } + readTotal.addAndGet(dp.getLength()); } - int read = dp.getLength(); - if (read != PAYLOAD_SIZE && read != 0) { - throw new IOException("Unexpected response length: " + read); + } catch (SocketException e) { + if (keepRunning.get()) { + TestStackTraceUtil.printStackTrace(e); } - readTotal.addAndGet(dp.getLength()); - } - } catch (SocketException e) { - if (keepRunning.get()) { - e.printStackTrace(); + } catch (IOException e) { // NOPMD.ExceptionAsFlowControl + TestStackTraceUtil.printStackTrace(e); } - } catch (IOException e) { - e.printStackTrace(); } - } - }.start(); + }.start(); - long time = System.currentTimeMillis(); + long time = System.currentTimeMillis(); - DatagramPacket dp = new DatagramPacket(new byte[PAYLOAD_SIZE], PAYLOAD_SIZE); - byte[] data = dp.getData(); - for (int i = 0; i < data.length; i++) { - data[i] = (byte) i; - } - - while (keepRunning.get()) { - try { - dc.send(dp); - } catch (PortUnreachableException e) { - e.addSuppressed(new Exception(dp.getSocketAddress().toString())); - throw e; + DatagramPacket dp = new DatagramPacket(new byte[PAYLOAD_SIZE_DATAGRAM], + PAYLOAD_SIZE_DATAGRAM); + byte[] data = dp.getData(); + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; } - sentTotal += PAYLOAD_SIZE; - } - time = System.currentTimeMillis() - time; - keepRunning.set(false); - ds.close(); // terminate server - long readTotal0 = readTotal.get(); + while (keepRunning.get()) { + try { + dc.send(dp); + } catch (PortUnreachableException e) { + e.addSuppressed(new Exception(dp.getSocketAddress().toString())); + throw e; + } + sentTotal += PAYLOAD_SIZE_DATAGRAM; + } + time = System.currentTimeMillis() - time; + keepRunning.set(false); + ds.close(); // terminate server - reportResults(stbTestType() + " DatagramPacket", ((1000f * readTotal0 / time) / 1000f - / 1000f) + " MB/s for payload size " + PAYLOAD_SIZE + "; " + String.format( - Locale.ENGLISH, "%.1f%% packet loss", 100 * (1 - (readTotal0 - / (float) sentTotal)))); - } - }); + long readTotal0 = readTotal.get(); + reportResults(stbTestType() + " DatagramPacket", ((1000f * readTotal0 / time) / 1000f + / 1000f) + " MB/s for datagram payload size " + PAYLOAD_SIZE_DATAGRAM + "; " + String + .format(Locale.ENGLISH, "%.1f%% packet loss", 100 * (1 - (readTotal0 + / (float) sentTotal)))); + } + }); + } catch (JUnitException e) { + // Ignore timeout failure (this is a throughput test only) + TestStackTraceUtil.printStackTrace(e); + } } @Test @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DATAGRAMS) public void testDatagramChannel() throws Exception { - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { - testDatagramChannel(false, true); - }); + try { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { + testDatagramChannel(false, true); + }); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18535(e); + } else { + throw e; + } + } catch (JUnitException e) { + // Ignore timeout failure (this is a throughput test only) + TestStackTraceUtil.printStackTrace(e); + } } @Test @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DATAGRAMS) public void testDatagramChannelDirect() throws Exception { - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { - testDatagramChannel(true, true); - }); + try { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { + testDatagramChannel(true, true); + }); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18535(e); + } else { + throw e; + } + } catch (JUnitException e) { + // Ignore timeout failure (this is a throughput test only) + TestStackTraceUtil.printStackTrace(e); + } } @Test @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DATAGRAMS) public void testDatagramChannelNonBlocking() throws Exception { - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { - testDatagramChannel(false, false); - }); + try { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { + testDatagramChannel(false, false); + }); + } catch (JUnitException e) { + // Ignore timeout failure (this is a throughput test only) + TestStackTraceUtil.printStackTrace(e); + } } @Test @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DATAGRAMS) public void testDatagramChannelNonBlockingDirect() throws Exception { - assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + 5), () -> { - testDatagramChannel(true, false); - }); + try { + assertTimeoutPreemptively(Duration.ofSeconds(NUM_SECONDS + GRACE_TIME_NUM_SECONDS), () -> { + testDatagramChannel(true, false); + }); + } catch (JUnitException e) { + // Ignore timeout failure (this is a throughput test only) + TestStackTraceUtil.printStackTrace(e); + } } private void testDatagramChannel(boolean direct, boolean blocking) throws Exception { @@ -458,14 +507,14 @@ protected void testSocketDatagramChannel(String id, DatagramChannel ds, Datagram // ds.setOption(StandardSocketOptions.SO_RCVBUF, (PAYLOAD_SIZE + 82)); AtomicBoolean keepRunning = new AtomicBoolean(true); - Executors.newSingleThreadScheduledExecutor().schedule(() -> { + TestAsyncUtil.runAsyncDelayed(NUM_MILLISECONDS, TimeUnit.MILLISECONDS, () -> { keepRunning.set(false); try { ds.close(); } catch (IOException e) { // ignore } - }, NUM_MILLISECONDS, TimeUnit.MILLISECONDS); + }); AtomicLong readTotal = new AtomicLong(); long sentTotal = 0; @@ -477,8 +526,8 @@ protected void testSocketDatagramChannel(String id, DatagramChannel ds, Datagram new Thread() { @Override public void run() { - final ByteBuffer receiveBuffer = direct ? ByteBuffer.allocateDirect(PAYLOAD_SIZE) - : ByteBuffer.allocate(PAYLOAD_SIZE); + final ByteBuffer receiveBuffer = direct ? ByteBuffer.allocateDirect(PAYLOAD_SIZE_DATAGRAM) + : ByteBuffer.allocate(PAYLOAD_SIZE_DATAGRAM); try { SelectionKey key; if (readSelector != null) { @@ -503,7 +552,7 @@ public void run() { } read = ds.read(receiveBuffer); receiveBuffer.rewind(); - if (read != PAYLOAD_SIZE && read != 0 && read != -1) { + if (read != PAYLOAD_SIZE_DATAGRAM && read != 0 && read != -1) { throw new IOException("Unexpected response length: " + read); } readTotal.addAndGet(read); @@ -516,7 +565,7 @@ public void run() { } else { bytesRead.complete(readTotal.get()); } - } catch (Exception e) { + } catch (Exception e) { // NOPMD.ExceptionAsFlowControl keepRunning.set(false); bytesRead.completeExceptionally(e); } @@ -525,8 +574,8 @@ public void run() { time = System.currentTimeMillis(); - final ByteBuffer sendBuffer = direct ? ByteBuffer.allocateDirect(PAYLOAD_SIZE) : ByteBuffer - .allocate(PAYLOAD_SIZE); + final ByteBuffer sendBuffer = direct ? ByteBuffer.allocateDirect(PAYLOAD_SIZE_DATAGRAM) + : ByteBuffer.allocate(PAYLOAD_SIZE_DATAGRAM); try (AbstractSelector writeSelector = sp == null ? null : sp.openSelector()) { // NOPMD if (sp != null) { @@ -551,11 +600,11 @@ public void run() { } } - if (written != PAYLOAD_SIZE && written != 0) { + if (written != PAYLOAD_SIZE_DATAGRAM && written != 0) { throw new IOException("Unexpected written length: " + written); } - sentTotal += PAYLOAD_SIZE; + sentTotal += PAYLOAD_SIZE_DATAGRAM; sendBuffer.rewind(); } } finally { @@ -566,16 +615,21 @@ public void run() { } try { - bytesRead.get(2, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - e.printStackTrace(); + bytesRead.get(NUM_MILLISECONDS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + if (NUM_SECONDS != 0) { + TestStackTraceUtil.printStackTrace(e); + } + } catch (InterruptedException | ExecutionException e) { + TestStackTraceUtil.printStackTrace(e); } long readTotal0 = readTotal.get(); reportResults(id + " direct=" + direct + ";blocking=" + blocking, ((1000f * readTotal0 / time) - / 1000f / 1000f) + " MB/s for payload size " + PAYLOAD_SIZE + "; " + String.format( - Locale.ENGLISH, "%.1f%% packet loss", 100 * (1 - (readTotal0 / (float) sentTotal)))); + / 1000f / 1000f) + " MB/s for datagram payload size " + PAYLOAD_SIZE_DATAGRAM + "; " + + String.format(Locale.ENGLISH, "%.1f%% packet loss", 100 * (1 - (readTotal0 + / (float) sentTotal)))); } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AFUNIXAddressSpecifics.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AFUNIXAddressSpecifics.java index 29b1e9993..9b1d2940f 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AFUNIXAddressSpecifics.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AFUNIXAddressSpecifics.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,4 +116,14 @@ public AFServerSocket newServerSocketBindOn(SocketAddress addr, boolean delet public Socket connectTo(SocketAddress socket) throws IOException { return AFUNIXSocket.connectTo((AFUNIXSocketAddress) socket); } + + @Override + public String addressFamilyString() { + return "AF_UNIX"; + } + + @Override + public String summaryImportantMessage(String message) { + return message; + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java index 151ba5215..63840c6eb 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AbstractNamespaceTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AcceptTimeoutTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AcceptTimeoutTest.java index 4fdae4da7..c0a3167f6 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AcceptTimeoutTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AcceptTimeoutTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AvailableTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AvailableTest.java index ca0212895..660edb6f6 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AvailableTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/AvailableTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/BufferOverflowTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/BufferOverflowTest.java index 2b57aa390..97b915c2c 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/BufferOverflowTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/BufferOverflowTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/CancelAcceptTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/CancelAcceptTest.java index 9d2dad683..503cb3bcb 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/CancelAcceptTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/CancelAcceptTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/DatagramSocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/DatagramSocketTest.java index 7d7a83002..355f17333 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/DatagramSocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/DatagramSocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,22 @@ */ package org.newsclub.net.unix.domain; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; import org.newsclub.net.unix.AFSocketCapability; import org.newsclub.net.unix.AFSocketCapabilityRequirement; +import org.newsclub.net.unix.AFSocketType; +import org.newsclub.net.unix.AFUNIXDatagramChannel; import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.AFUNIXSocketPair; +import org.newsclub.net.unix.OperationNotSupportedSocketException; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; +import com.kohlschutter.testutil.TestAbortedNotAnIssueException; @AFSocketCapabilityRequirement({ AFSocketCapability.CAPABILITY_UNIX_DOMAIN, AFSocketCapability.CAPABILITY_UNIX_DATAGRAMS}) @@ -32,4 +43,26 @@ public final class DatagramSocketTest extends public DatagramSocketTest() { super(AFUNIXAddressSpecifics.INSTANCE); } + + @Test + public void testSeqPacket() throws Exception { + AFUNIXSocketPair pair; + try { + pair = AFUNIXSocketPair.openDatagram(AFSocketType.SOCK_SEQPACKET); + } catch (OperationNotSupportedSocketException e) { + throw new TestAbortedNotAnIssueException("SEQPACKET not supported", e); + } + + String msg = "Hello World"; + + ByteBuffer buf = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)); + ByteBuffer dst = ByteBuffer.allocate(64); + pair.getSocket1().send(buf, null); + int r = pair.getSocket2().read(dst); + assertEquals(buf.limit(), r); + dst.flip(); + assertEquals(buf.limit(), dst.limit()); + + assertEquals(msg, StandardCharsets.UTF_8.decode(dst).toString()); + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/EndOfFileTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/EndOfFileTest.java index cc98adb62..d6f9191e9 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/EndOfFileTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/EndOfFileTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorCastTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorCastTest.java index 0647d9577..29d13826c 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorCastTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorCastTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; +import java.util.Objects; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; @@ -52,6 +53,8 @@ import org.newsclub.net.unix.AFUNIXSocketPair; import org.newsclub.net.unix.FileDescriptorCast; import org.newsclub.net.unix.StdinSocketApp; +import org.newsclub.net.unix.TestUtil; +import org.opentest4j.AssertionFailedError; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; import com.kohlschutter.testutil.ForkedVM; @@ -83,8 +86,17 @@ public void testSocketPair() throws Exception { } else { Socket sock2 = FileDescriptorCast.using(sock2fd).as(Socket.class); assertEquals(AFUNIXSocket.class, sock2.getClass()); - assertTrue(sock2.isConnected()); + assertEquals(sock2fd, ((AFUNIXSocket) sock2).getFileDescriptor()); + try { + assertTrue(sock2.isConnected()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } } } @@ -99,7 +111,16 @@ public void testSocketPairNative() throws Exception { AFUNIXSocketChannel sock2chan = FileDescriptorCast.using(sock2fd).as(AFUNIXSocketChannel.class); assertEquals(sock2fd, sock2chan.getFileDescriptor()); - assertTrue(sock2chan.isConnected()); + + try { + assertTrue(sock2chan.isConnected()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } ByteBuffer bb = ByteBuffer.allocate(32); bb.putInt(0x12345678); @@ -167,9 +188,17 @@ public void testServer() throws Exception { AFUNIXSocket sock = AFUNIXSocket.connectTo(serverAddress)) { AFUNIXSocket sock1 = FileDescriptorCast.using(sock.getFileDescriptor()).as( AFUNIXSocket.class); - assertEquals(sock.isBound(), sock1.isBound()); - assertEquals(sock.isConnected(), sock1.isConnected()); - assertTrue(sock1.isConnected()); + try { + assertEquals(sock.isBound(), sock1.isBound()); + assertEquals(sock.isConnected(), sock1.isConnected()); + assertTrue(sock1.isConnected()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } assertEquals(sock.getRemoteSocketAddress(), sock1.getRemoteSocketAddress()); assertEquals(ass.getLocalSocketAddress(), sock.getRemoteSocketAddress()); @@ -220,33 +249,55 @@ public void testDatagramSocket() throws Exception { dc1c.connect(addr2); dc2c.connect(addr1); assertTrue(dc1c.isConnected()); - assertTrue(dc1.isConnected()); - - assertEquals(dc1.getLocalAddress(), dc2c.getRemoteAddress()); - assertEquals(dc1.getRemoteAddress(), dc2c.getLocalAddress()); - assertEquals(dc1c.getLocalAddress(), dc2.getRemoteAddress()); - assertEquals(dc1c.getRemoteAddress(), dc2.getLocalAddress()); assertEquals(4, dc1c.write(bb)); bb.clear(); assertEquals(4, dc2c.read(bb)); bb.flip(); assertEquals(1234, bb.getInt()); + + try { + assertTrue(dc1.isConnected()); + assertEquals(dc1.getLocalAddress(), dc2c.getRemoteAddress()); + assertEquals(dc1.getRemoteAddress(), dc2c.getLocalAddress()); + assertEquals(dc1c.getLocalAddress(), dc2.getRemoteAddress()); + assertEquals(dc1c.getRemoteAddress(), dc2.getLocalAddress()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } } } @Test + @SuppressFBWarnings("DCN_NULLPOINTER_EXCEPTION") public void testSocketPorts() throws Exception { AFUNIXSocketAddress addr = AFUNIXSocketAddress.ofNewTempPath(123); try (AFUNIXServerSocket ass = AFUNIXServerSocket.bindOn(addr)) { assertEquals(123, ass.getLocalPort()); + @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance") AFUNIXServerSocket ass1 = FileDescriptorCast.using(ass.getFileDescriptor()).withLocalPort(123) .as(AFUNIXServerSocket.class); assertEquals(123, ass.getLocalPort()); AFUNIXSocket socket = AFUNIXSocket.connectTo(addr); - assertEquals(123, socket.getRemoteSocketAddress().getPort()); + AFUNIXSocketAddress rsa = socket.getRemoteSocketAddress(); + + try { + Objects.requireNonNull(rsa); + } catch (NullPointerException e) { + if (TestUtil.isHaikuOS()) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } + + assertEquals(123, rsa.getPort()); AFUNIXSocket socket1 = FileDescriptorCast.using(socket.getFileDescriptor()).withRemotePort( 123).as(AFUNIXSocket.class); @@ -285,7 +336,15 @@ public void testDatagramPorts() throws Exception { dc1.bind(addr); dc2.connect(addr); - assertEquals(dc1.getLocalAddress(), dc2.getRemoteAddress()); + try { + assertEquals(dc1.getLocalAddress(), dc2.getRemoteAddress()); + } catch (AssertionFailedError e) { + if (TestUtil.isHaikuOS() && dc2.getRemoteAddress() == null) { + throw TestUtil.haikuBug18534(e); + } else { + throw e; + } + } assertEquals(123, getPort(dc1.getLocalAddress())); @@ -308,7 +367,13 @@ public void testDatagramPorts() throws Exception { .withRemotePort(123).as(AFUNIXDatagramChannel.class); assertEquals(123, getPort(dc1c.getLocalAddress())); - assertEquals(123, getPort(dc2c.getRemoteAddress())); + int remotePort = getPort(dc2c.getRemoteAddress()); + if (remotePort == -1) { + // that's acceptable, too (seen on z/OS) + assertNull(dc2c.getRemoteAddress()); + } else { + assertEquals(123, remotePort); + } } } } @@ -346,7 +411,7 @@ public void testDatagramFileChannel() throws Exception { /** * Passes a socket to a newly created Java process as "standard input". The new process sends back * "Hello world" over that socket. - * + * * @throws Exception on failure. * @see StdinSocketApp */ diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorsTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorsTest.java index bc548aa60..67795711f 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorsTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FileDescriptorsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ /** * Tests sending and receiving file descriptors. - * + * * @author Christian Kohlschütter */ @AFSocketCapabilityRequirement({ @@ -414,7 +414,7 @@ public void testDatagramSocket() throws Exception { assertEquals(1, fds.length); try (FileOutputStream fos2 = new FileOutputStream(fds[0])) { - fos.write("Hello".getBytes(StandardCharsets.UTF_8)); + fos2.write("Hello".getBytes(StandardCharsets.UTF_8)); // closing the received file descriptor will not close the original one ... } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FinalizeTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FinalizeTest.java index 9c910db6f..8731a3051 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FinalizeTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/FinalizeTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ */ package org.newsclub.net.unix.domain; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -25,6 +24,10 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import org.newsclub.net.unix.AFSocketCapability; import org.newsclub.net.unix.AFSocketCapabilityRequirement; @@ -47,67 +50,79 @@ protected String socketType() { } @SuppressFBWarnings({"RV_DONT_JUST_NULL_CHECK_READLINE"}) - private static int lsofUnixSockets(long pid) throws IOException, TestAbortedException, + private static List lsofUnixSockets(long pid) throws IOException, TestAbortedException, InterruptedException { assertTrue(pid > 0); + List lines = new ArrayList<>(); + Process p; try { p = Runtime.getRuntime().exec(new String[] {"lsof", "-U", "-a", "-p", String.valueOf(pid)}); } catch (Exception e) { assumeTrue(false, e.getMessage()); - return -1; + return Collections.emptyList(); } - int lines = 0; try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset .defaultCharset()))) { String l; while ((l = in.readLine()) != null) { - lines++; + lines.add(l); if (l.contains("busybox")) { assumeTrue(false, "incompatible lsof binary"); } } + p.waitFor(); + } finally { + p.destroy(); + assumeTrue(p.exitValue() == 0, "lsof should terminate with RC=0"); } - assumeTrue(p.waitFor() == 0, "lsof should terminate with RC=0"); return lines; } @Override protected Object preRunCheck(Process process) throws TestAbortedException, IOException, InterruptedException { - int linesBefore = lsofUnixSockets(process.pid()); + List linesBefore = lsofUnixSockets(process.pid()); // If that's not true, we need to skip the test - assumeTrue(linesBefore > 0); + assumeTrue(!linesBefore.isEmpty()); return linesBefore; } @Override protected void postRunCheck(Process process, Object linesBeforeObj) throws TestAbortedException, IOException, InterruptedException { - assertNotNull(linesBeforeObj); - int linesBefore = (int) linesBeforeObj; + assumeTrue(linesBeforeObj != null, "Environment does not support lsof check"); + + @SuppressWarnings("unchecked") + List linesBefore = (List) linesBeforeObj; try { - int linesAfter = 0; - for (int i = 0; i < 10; i++) { + List linesAfter = null; + for (int i = 0; i < 50; i++) { Thread.sleep(100); - linesAfter = lsofUnixSockets(process.pid()); - if (linesAfter != linesBefore) { + if (!process.isAlive()) { break; } - if (!process.isAlive()) { + linesAfter = lsofUnixSockets(process.pid()); + if (linesBefore == null || linesAfter.size() < linesBefore.size()) { break; } } - assumeTrue(linesAfter > 0, "lsof may fail to return anything"); + assumeTrue(Objects.requireNonNull(linesAfter).size() > 0, "lsof may fail to return anything"); - assertTrue(linesAfter < linesBefore, - "Our unix socket file handle should have been cleared out"); + if (linesAfter != null && linesBefore != null) { + if (linesAfter.size() >= linesBefore.size()) { + System.err.println("lsof: Unexpected output"); + System.err.println("lsof: Output before: " + linesBefore); + System.err.println("lsof: Output after: " + linesAfter); + } + assertTrue(linesAfter.size() < linesBefore.size(), + "Our unix socket file handle should have been cleared out"); + } } finally { process.destroy(); process.waitFor(); } } - } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/PeerCredentialsTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/PeerCredentialsTest.java index 8f7dc4dec..500538495 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/PeerCredentialsTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/PeerCredentialsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ /** * Verifies that peer credentials are properly set. - * + * * @author Christian Kohlschütter */ @AFSocketCapabilityRequirement({ @@ -80,7 +80,7 @@ protected void handleConnection(final AFUNIXSocket socket) throws IOException { } } }; AFUNIXSocket socket = (AFUNIXSocket) connectTo(serverThread.getServerAddress())) { - try (InputStream in = socket.getInputStream()) { + try (InputStream unused = socket.getInputStream()) { AFUNIXSocketCredentials serverCreds = socket.getPeerCredentials(); AFUNIXSocketCredentials clientCreds = clientCredsFuture.get(); diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ReadWriteTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ReadWriteTest.java index a39bebb67..ba8566118 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ReadWriteTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ReadWriteTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SelectorTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SelectorTest.java index 3c7352bd2..092fb0486 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SelectorTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SelectorTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketCloseTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketCloseTest.java index bbd62c133..e3e77e8f1 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketCloseTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketCloseTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketTest.java index b9c191fa2..7b7df5e34 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ServerSocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SoTimeoutTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SoTimeoutTest.java index 35b663d37..6f1c186b5 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SoTimeoutTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SoTimeoutTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketAddressTest.java index 62af20e6b..77320740d 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketAddressTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketAddressTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,8 @@ import java.nio.charset.Charset; import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFSocket; +import org.newsclub.net.unix.AFSocketCapability; import org.newsclub.net.unix.AFUNIXSocketAddress; import org.newsclub.net.unix.SocketTestBase; @@ -50,18 +52,33 @@ public SocketAddressTest() { public void testPort() throws IOException { assertEquals(0, AFUNIXSocketAddress.of(new File("/tmp/whatever")).getPort()); assertEquals(123, AFUNIXSocketAddress.of(new File("/tmp/whatever"), 123).getPort()); - assertEquals(44444, AFUNIXSocketAddress.of(new File("/tmp/whatever"), 44444).getPort()); try { AFUNIXSocketAddress.of(new File("/tmp/whatever"), -2); fail("Expected IllegalArgumentException for illegal port"); - } catch (final IllegalArgumentException e) { + } catch (IllegalArgumentException e) { // expected } + + assertEquals(44444, AFUNIXSocketAddress.of(new File("/tmp/whatever"), 44444).getPort()); + + AFUNIXSocketAddress.of(new File("/tmp/whatever"), 65535); + } + + @Test + public void testLargePort() throws Exception { + boolean supportsLargePorts = AFSocket.supports(AFSocketCapability.CAPABILITY_LARGE_PORTS); + + try { + assertEquals(544444, AFUNIXSocketAddress.of(new File("/tmp/whatever"), 544444).getPort()); + } catch (SocketException e) { + assertFalse(supportsLargePorts); + } + try { AFUNIXSocketAddress.of(new File("/tmp/whatever"), 65536); - } catch (final IllegalArgumentException e) { - fail("AFUNIXSocketAddress supports ports larger than 65535"); + } catch (SocketException e) { + assertFalse(supportsLargePorts); } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketChannelTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketChannelTest.java index a5aa7152c..6c08842d6 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketChannelTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketChannelTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketFactoryTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketFactoryTest.java index 6e349c8b6..4b2d50b4a 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketFactoryTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketFactoryTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.newsclub.net.unix.domain; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.InetAddress; @@ -83,7 +84,7 @@ public void testURISchemeCeateSocketWithIllegalArguments() throws Exception { assertThrows(IllegalArgumentException.class, () -> { // Illegal local port try (Socket sock = factory.createSocket("file:///", 0, null, -1)) { - // not reached + fail("Should not be reached: " + sock); } }); } @@ -95,61 +96,61 @@ public void testURISchemeCeateSocketWithInvalidHostname() throws Exception { assertThrows(SocketException.class, () -> { // We don't support empty hosts try (Socket sock = factory.createSocket("", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // We don't support empty hosts try (Socket sock = factory.createSocket("", 0, null, 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // We don't support IP addresses try (Socket sock = factory.createSocket(InetAddress.getByName(""), 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // We don't support IP addresses try (Socket sock = factory.createSocket(InetAddress.getLoopbackAddress(), 0, null, 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file:// has an empty path component try (Socket sock = factory.createSocket("file:", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file:// has an empty path component try (Socket sock = factory.createSocket("file:/", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file:// has an empty path component try (Socket sock = factory.createSocket("file://", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file://not-absolute is not an absolute path (three slashes needed) try (Socket sock = factory.createSocket("file://not-absolute", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // incomplete try (Socket sock = factory.createSocket("[", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // encoded; incomplete trailing escape try (Socket sock = factory.createSocket("file%3A%2F%2F%", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); } @@ -161,37 +162,37 @@ public void testURISchemeCeateSocketWithHostnameValidCases() throws Exception { assertThrows(SocketException.class, () -> { // file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("file:///", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("file://localhost/", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("[file:///]", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("[file:///", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // encoded; file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("file%3A%2F%2F%2F", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); assertThrows(SocketException.class, () -> { // encoded; file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket("file%3A%2F%2Flocalhost%2F", 0)) { - // not reached + fail("Should not be reached: " + sock); } }); } @@ -209,7 +210,7 @@ public void testSystemProperty() throws Exception { assertThrows(IllegalStateException.class, () -> { // system property "org.newsclub.net.unix.socket.default" not configured try (Socket sock = factory.createSocket(hostnameToConnectTo, 0)) { - // not reached + fail("Should not be reached: " + sock); } }); @@ -217,7 +218,7 @@ public void testSystemProperty() throws Exception { assertThrows(IllegalStateException.class, () -> { // system property "org.newsclub.net.unix.socket.default" not configured try (Socket sock = factory.createSocket(hostnameToConnectTo, 0)) { - // not reached + fail("Should not be reached: " + sock); } }); @@ -225,7 +226,7 @@ public void testSystemProperty() throws Exception { assertThrows(SocketException.class, () -> { // file exists (root directory), but is definitely not a unix socket try (Socket sock = factory.createSocket(hostnameToConnectTo, 0)) { - // not reached + fail("Should not be reached: " + sock); } }); } finally { @@ -244,7 +245,7 @@ public void testFactoryArg() throws Exception { assertThrows(SocketException.class, () -> { AFUNIXSocketFactory.FactoryArg factory = new AFUNIXSocketFactory.FactoryArg("/"); // file exists (root directory), but is definitely not a unix socket - try (Socket sock = factory.createSocket(hostnameToConnectTo, 0)) { + try (Socket unused = factory.createSocket(hostnameToConnectTo, 0)) { // not reached } }); @@ -252,7 +253,7 @@ public void testFactoryArg() throws Exception { assertThrows(SocketException.class, () -> { AFUNIXSocketFactory.FactoryArg factory = new AFUNIXSocketFactory.FactoryArg(new File("/")); // file exists (root directory), but is definitely not a unix socket - try (Socket sock = factory.createSocket(hostnameToConnectTo, 0)) { + try (Socket unused = factory.createSocket(hostnameToConnectTo, 0)) { // not reached } }); diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketPairTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketPairTest.java index 06d5be29c..c2b8bc234 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketPairTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketPairTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketTest.java index 4405a8f6c..e656117cf 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/SocketTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ /** * Tests some otherwise uncovered methods of {@link AFUNIXSocket}. - * + * * @author Christian Kohlschütter */ @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DOMAIN) diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/StandardSocketOptionsTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/StandardSocketOptionsTest.java index c50e667c3..aa95e933e 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/StandardSocketOptionsTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/StandardSocketOptionsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/TcpNoDelayTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/TcpNoDelayTest.java index 1fef1a952..c46d7b408 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/TcpNoDelayTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/TcpNoDelayTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTest.java index 30fdf9027..aeb1df241 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,12 @@ import org.newsclub.net.unix.AFSocketCapability; import org.newsclub.net.unix.AFSocketCapabilityRequirement; -import org.newsclub.net.unix.AFUNIXSocketAddress; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_UNIX_DOMAIN) @SuppressFBWarnings("NM_SAME_SIMPLE_NAME_AS_SUPERCLASS") -public final class ThroughputTest extends - org.newsclub.net.unix.ThroughputTest { +public final class ThroughputTest extends ThroughputTestShim { public ThroughputTest() { super(AFUNIXAddressSpecifics.INSTANCE); diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTestShim.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTestShim.java new file mode 100644 index 000000000..171401490 --- /dev/null +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/ThroughputTestShim.java @@ -0,0 +1,76 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.domain; + +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.AddressSpecifics; + +import com.kohlschutter.testutil.AvailabilityRequirement; + +/** + * Shim class to allow for some tests that test code that is only available in newer Java versions, + * particularly JEP 380-related code (UnixDomainSocketAddress). + * + * @author Christian Kohlschütter + */ +abstract class ThroughputTestShim extends + org.newsclub.net.unix.ThroughputTest { + + protected ThroughputTestShim(AddressSpecifics asp) { + super(asp); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testJEP380() throws Exception { + testJEP380(false); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testJEP380direct() throws Exception { + testJEP380(true); + } + + private void testJEP380(boolean direct) throws Exception { + Path p = newTempFile().toPath(); + try { + UnixDomainSocketAddress usa = UnixDomainSocketAddress.of(p); + + ServerSocketChannel ssc = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + + runTestSocketChannel("JEP380 SocketChannel", usa, ssc, () -> { + SocketChannel sc = SocketChannel.open(StandardProtocolFamily.UNIX); + connectSocket(sc, ssc.getLocalAddress()); + return sc; + }, direct); + } finally { + Files.delete(p); + } + } +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java new file mode 100644 index 000000000..f1727ae75 --- /dev/null +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java @@ -0,0 +1,46 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.File; +import java.net.UnixDomainSocketAddress; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.SocketTestBase; + +import com.kohlschutter.testutil.AvailabilityRequirement; + +public class UnixDomainSocketAddressTest extends SocketTestBase { + public UnixDomainSocketAddressTest() { + super(AFUNIXAddressSpecifics.INSTANCE); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testConvertUnixDomainSocketAddress() throws Exception { + File f = new File("/tmp/hello"); + UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(f.getPath()); + AFUNIXSocketAddress usa = AFUNIXSocketAddress.of(addr); + + assertEquals(f, usa.getFile()); + } +} diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/AcceptTimeoutTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/AcceptTimeoutTest.java index 3b84ffc43..70bdedd61 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/AcceptTimeoutTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/AcceptTimeoutTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/EndOfFileJavaTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/EndOfFileJavaTest.java index 470a462e3..47b11a9f4 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/EndOfFileJavaTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/EndOfFileJavaTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaAddressSpecifics.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaAddressSpecifics.java index 5fb8def66..c3c0efa9d 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaAddressSpecifics.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaAddressSpecifics.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,7 +107,7 @@ public CloseablePair newDatagramSocketPair() throws I DatagramSocket ds2 = new DatagramSocket(0); ds1.connect(ds2.getLocalSocketAddress()); ds2.connect(ds1.getLocalSocketAddress()); - return new CloseablePair(ds1.getChannel(), ds2.getChannel(), () -> { + return new CloseablePair<>(ds1.getChannel(), ds2.getChannel(), () -> { ds1.close(); ds2.close(); }); @@ -137,4 +137,14 @@ public ServerSocket newServerSocketBindOn(SocketAddress addr, boolean deleteOnCl } return new ServerSocket(inetAddr.getPort()); } + + @Override + public String addressFamilyString() { + return "Java standard socket"; + } + + @Override + public String summaryImportantMessage(String message) { + return message; + } } diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackCondition.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackCondition.java index 892b6ecde..3db4d8005 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackCondition.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackCondition.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackRequirement.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackRequirement.java index d02485871..3d79ee5aa 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackRequirement.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/JavaInetStackRequirement.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,10 +26,10 @@ /** * Flags a test that requires the Java Internet stack to function as expected. - * + * * This currently checks if we can bind to an unspecified (0) port on the loopback device, which * unfortunately may not always succeed. In the failure case, the annotated tests will be skipped. - * + * * @author Christian Kohlschütter */ @Target({ElementType.TYPE, ElementType.METHOD}) diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/SelectorTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/SelectorTest.java index 2186a9653..4301e74ef 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/SelectorTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/SelectorTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/ThroughputTest.java b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/ThroughputTest.java index cd35763dc..1e03223a8 100644 --- a/junixsocket-common/src/test/java/org/newsclub/net/unix/java/ThroughputTest.java +++ b/junixsocket-common/src/test/java/org/newsclub/net/unix/java/ThroughputTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.ProtocolFamily; import java.net.SocketAddress; -import java.net.StandardProtocolFamily; import java.nio.channels.DatagramChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; @@ -34,7 +31,6 @@ import org.junit.jupiter.api.Test; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; -import com.kohlschutter.testutil.AvailabilityRequirement; import com.kohlschutter.testutil.SystemPropertyRequirement; @SuppressFBWarnings({ @@ -47,52 +43,6 @@ public ThroughputTest() { super(JavaAddressSpecifics.INSTANCE); } - @Test - @AvailabilityRequirement(classes = {"java.net.UnixDomainSocketAddress"}, // - message = "This test requires Java 16 or later") - public void testJEP380() throws Exception { - assumeTrue(ENABLED > 0, "Throughput tests are disabled"); - assumeTrue(PAYLOAD_SIZE > 0, "Payload must be positive"); - runTestJEP380(false); - } - - @Test - @AvailabilityRequirement(classes = {"java.net.UnixDomainSocketAddress"}, // - message = "This test requires Java 16 or later") - public void testJEP380directBuffer() throws Exception { - assumeTrue(ENABLED > 0, "Throughput tests are disabled"); - assumeTrue(PAYLOAD_SIZE > 0, "Payload must be positive"); - runTestJEP380(true); - } - - private static SocketAddress jep380SocketAddress(String path) throws IllegalAccessException, - IllegalArgumentException, InvocationTargetException, SecurityException { - try { - // We use reflection so we can compile on older Java versions - Class klazz = Class.forName("java.net.UnixDomainSocketAddress"); - return (SocketAddress) klazz.getMethod("of", String.class).invoke(null, path); - } catch (NoSuchMethodException | ClassNotFoundException e) { - assumeTrue(false, "java.net.UnixDomainSocketAddress (JEP 380) not supported by JVM"); - return null; - } - } - - private void runTestJEP380(boolean direct) throws Exception { - SocketAddress sa = jep380SocketAddress(socketFile().getPath()); - - ServerSocketChannel ssc; - // We use reflection so we can compile on older Java versions - try { - ssc = (ServerSocketChannel) ServerSocketChannel.class.getMethod("open", ProtocolFamily.class) - .invoke(null, StandardProtocolFamily.valueOf("UNIX")); - } catch (Exception e) { - throw new IllegalStateException(e); - } - - runTestSocketChannel("JEP380 SocketChannel", sa, ssc, () -> SocketChannel.open(ssc - .getLocalAddress()), direct); - } - @Test @SystemPropertyRequirement(property = "org.newsclub.net.unix.throughput-test.ip.enabled", // value = "1", message = "Loopback TCP/IP testing is disabled") diff --git a/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/ThroughputTestShim.java b/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/ThroughputTestShim.java new file mode 100644 index 000000000..49442777f --- /dev/null +++ b/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/ThroughputTestShim.java @@ -0,0 +1,56 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.domain; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.AddressSpecifics; + +import com.kohlschutter.testutil.AvailabilityRequirement; + +/** + * Shim class to allow for some tests that test code that is only available in newer Java versions, + * particularly JEP 380-related code (UnixDomainSocketAddress). + * + * @author Christian Kohlschütter + */ +abstract class ThroughputTestShim extends + org.newsclub.net.unix.ThroughputTest { + + protected ThroughputTestShim(AddressSpecifics asp) { + super(asp); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testJEP380() throws Exception { + fail("Should not be reached"); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testJEP380direct() throws Exception { + fail("Should not be reached"); + } +} diff --git a/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java b/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java new file mode 100644 index 000000000..6a6783d67 --- /dev/null +++ b/junixsocket-common/src/test/java15/org/newsclub/net/unix/domain/UnixDomainSocketAddressTest.java @@ -0,0 +1,41 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.domain; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.SocketTestBase; + +import com.kohlschutter.testutil.AvailabilityRequirement; + +public class UnixDomainSocketAddressTest extends SocketTestBase { + public UnixDomainSocketAddressTest() { + super(AFUNIXAddressSpecifics.INSTANCE); + } + + @Test + @AvailabilityRequirement(classes = "java.net.UnixDomainSocketAddress", // + message = "This test requires Java 16 or later") + public void testConvertUnixDomainSocketAddress() throws Exception { + fail("Should not be reached"); + } +} diff --git a/junixsocket-common/src/test/java8/org/newsclub/net/unix/FinalizeTest.java b/junixsocket-common/src/test/java8/org/newsclub/net/unix/FinalizeTest.java index 7fdfabd30..b735cc757 100644 --- a/junixsocket-common/src/test/java8/org/newsclub/net/unix/FinalizeTest.java +++ b/junixsocket-common/src/test/java8/org/newsclub/net/unix/FinalizeTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2021 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * Not supported on Java 8 yet, but you can manually verify that it works by running the following * commands from three different shells: - * + * *
    *
  1. (shell 1) {@code nc -l -U /tmp/testsocket}
  2. *
  3. (shell 2) {@code java -Dtest.junixsocket.socket=/tmp/testsocket @@ -37,10 +37,10 @@ *
  4. (shell 1) type some text, followed by enter
  5. *
  6. (shell 3) {@code lsof -U -a -p $(jps | grep FinalizeTestClient | cut -f 1 -d' ') | wc -l} *
- * + * * The number printed in shell 3 after the second {@code lsof} commands should be one less than the * one of the first {@code lsof} command. - * + * * See
issue 29 for details. */ public abstract class FinalizeTest extends SocketTestBase { diff --git a/junixsocket-common/src/test/java8/org/newsclub/net/unix/StandardSocketOptionsTest.java b/junixsocket-common/src/test/java8/org/newsclub/net/unix/StandardSocketOptionsTest.java index d0e9448ef..785e59fab 100644 --- a/junixsocket-common/src/test/java8/org/newsclub/net/unix/StandardSocketOptionsTest.java +++ b/junixsocket-common/src/test/java8/org/newsclub/net/unix/StandardSocketOptionsTest.java @@ -1,3 +1,20 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ package org.newsclub.net.unix; import java.net.SocketAddress; @@ -9,10 +26,10 @@ /** * Tests the {@code Socket#getOption(SocketOption)} API available since Java 9. - * + * * This class (in src/test/java8) is a stub that overrides this type so we can compile for Java 8 * and, at the same time, acknowledge the absence of the test programmatically in jUnit. - * + * * @author Christian Kohlschütter */ public abstract class StandardSocketOptionsTest extends SocketTestBase { diff --git a/junixsocket-common/src/test/java8/org/newsclub/net/unix/domain/FinalizeTest.java b/junixsocket-common/src/test/java8/org/newsclub/net/unix/domain/FinalizeTest.java index a070d3d8f..471222884 100644 --- a/junixsocket-common/src/test/java8/org/newsclub/net/unix/domain/FinalizeTest.java +++ b/junixsocket-common/src/test/java8/org/newsclub/net/unix/domain/FinalizeTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-core/pom.xml b/junixsocket-core/pom.xml index e588428e0..8f25ebdee 100644 --- a/junixsocket-core/pom.xml +++ b/junixsocket-core/pom.xml @@ -7,7 +7,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-core diff --git a/junixsocket-darwin/README.md b/junixsocket-darwin/README.md new file mode 100644 index 000000000..e6d265c1a --- /dev/null +++ b/junixsocket-darwin/README.md @@ -0,0 +1,51 @@ +# junixsocket-darwin + +Darwin (macOS)-specific sockets from Java! + +[junixsocket](https://kohlschutter.github.io/junixsocket/) is an Apache 2.0-licensed Java/JNI library that allows the use of +[Unix Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket) (AF_UNIX sockets), and +other address/protocol families (such as [TIPC](http://tipc.io/)), from Java. + +## AF_SYSTEM + +`AF_SYSTEM` is one way to communicate with the macOS (Darwin) Kernel from userspace. + +Most often, you still have to be root to connect, however you may of course send a file descriptor +to a lesser privileged process and continue the work there. + +One feature junixsocket exposes is `UTUN_CONTROL`, which lets you set up PtP tunnel interfaces (`utun`) +from userspace. This can be used to implement VPNs, for example. AF_SYSTEM with UTUN_CONTROL is +the macOS pendant to Linux's `tun` functionality. + +### Examples + +* [UtunTest — Run a VPN tunnel from Java](https://kohlschutter.github.io/junixsocket/junixsocket-darwin/xref-test/org/newsclub/net/unix/darwin/system/UtunTest.html) +* Also see [junixsocket-darwin's unit tests](https://kohlschutter.github.io/junixsocket/junixsocket-darwin/xref-test/index.html) + +## AF_NDRV + +This is pretty much a "Fake Ethernet" driver; it's the macOS pendant to Linux's `tap` functionality. + +A future version of junixsocket may add support for this domain. + +## AF_MULTIPATH + +This is an Apple-private domain to implement Multipath-TCP/IP. + +A future version of junixsocket may add support for this domain. + +## Maven dependencies + +Add the following dependency to your project (replace X.Y.Z. with the +[latest junixsocket version](https://kohlschutter.github.io/junixsocket/changelog.html)). + + + com.kohlschutter.junixsocket + junixsocket-darwin + X.Y.Z + + +## References + +* [New OSX Book Volume 1 Chapter 16 — Нет-Work: Darwin Networking](http://newosxbook.com/bonus/vol1ch16.html) +* [XNU kernel source](https://github.com/apple-oss-distributions/xnu) diff --git a/junixsocket-darwin/pom.xml b/junixsocket-darwin/pom.xml new file mode 100644 index 000000000..d10757b0b --- /dev/null +++ b/junixsocket-darwin/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + junixsocket-darwin + jar + + com.kohlschutter.junixsocket + junixsocket + 2.7.2 + ../pom.xml + + junixsocket-darwin + + ${project.parent.basedir} + + 8 + + org.newsclub.net.unix=${project.basedir}/../junixsocket-common/${kohlschutter.target.name}/test-classes:${settings.localRepository}/com/kohlschutter/junixsocket/junixsocket-common/${project.version}/junixsocket-common-${project.version}-tests.jar + + + + --patch-module ${patchArgs.org.newsclub.net.unix} + + + The junixsocket implementation for AF_SYSTEM sockets + + + + com.kohlschutter.junixsocket + junixsocket-common + ${project.version} + test-jar + test + tests + + + + com.kohlschutter.junixsocket + junixsocket-common + ${project.version} + + + com.kohlschutter.junixsocket + junixsocket-core + ${project.version} + pom + + + + com.kohlschutter.junixsocket + junixsocket-native-common + ${project.version} + test + + + + + + maven-compiler-plugin + + + default-testCompile + test-compile + + + --patch-module + ${patchArgs.org.newsclub.net.unix} + + ${xlint.compiler.arg} + + + + + + + + + + + with-native-custom + + + !junixsocket.native-custom.skip + + + + + com.kohlschutter.junixsocket + junixsocket-native-custom + ${project.version} + test + default + true + + + + + diff --git a/junixsocket-darwin/src/main/java/module-info.java b/junixsocket-darwin/src/main/java/module-info.java new file mode 100644 index 000000000..5a163a1c6 --- /dev/null +++ b/junixsocket-darwin/src/main/java/module-info.java @@ -0,0 +1,11 @@ +/** + * The junixsocket implementation for things specific to Darwin (macOS kernel), such as AF_SYSTEM + * sockets. + */ +module org.newsclub.net.unix.darwin { + exports org.newsclub.net.unix.darwin.system; + + requires transitive org.newsclub.net.unix; + requires static com.kohlschutter.annotations.compiletime; + requires static org.eclipse.jdt.annotation; +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/package-info.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/package-info.java new file mode 100644 index 000000000..59282e23b --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/package-info.java @@ -0,0 +1,4 @@ +/** + * The junixsocket implementation for Darwin (macOS Kernel)-specific sockets. + */ +package org.newsclub.net.unix.darwin; diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramChannel.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramChannel.java new file mode 100644 index 000000000..5c0169549 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramChannel.java @@ -0,0 +1,60 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.IOException; +import java.net.ProtocolFamily; +import java.nio.channels.DatagramChannel; + +import org.newsclub.net.unix.AFDatagramChannel; +import org.newsclub.net.unix.AFSYSTEMSocketAddress; + +/** + * A {@link DatagramChannel} implementation that works with {@code AF_SYSTEM} sockets. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMDatagramChannel extends AFDatagramChannel + implements AFSYSTEMSocketExtensions { + AFSYSTEMDatagramChannel(AFSYSTEMDatagramSocket socket) { + super(AFSYSTEMSelectorProvider.getInstance(), socket); + } + + /** + * Opens a datagram channel. + * + * @return The new channel + * @throws IOException if an I/O error occurs + */ + public static AFSYSTEMDatagramChannel open() throws IOException { + return AFSYSTEMSelectorProvider.provider().openDatagramChannel(); + } + + /** + * Opens a datagram channel. + * + * @param family The protocol family + * @return A new datagram channel + * + * @throws UnsupportedOperationException if the specified protocol family is not supported + * @throws IOException if an I/O error occurs + */ + public static AFSYSTEMDatagramChannel open(ProtocolFamily family) throws IOException { + return AFSYSTEMSelectorProvider.provider().openDatagramChannel(family); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocket.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocket.java new file mode 100644 index 000000000..f1e30a3f6 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocket.java @@ -0,0 +1,111 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramSocket; + +import org.newsclub.net.unix.AFDatagramSocket; +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSYSTEMSocketImplExtensions; +import org.newsclub.net.unix.AFSocketType; + +/** + * A {@link DatagramSocket} implementation that works with {@code AF_SYSTEM} sockets. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMDatagramSocket extends AFDatagramSocket implements + AFSYSTEMSocketExtensions { + + AFSYSTEMDatagramSocket(FileDescriptor fd) throws IOException { + this(fd, AFSocketType.SOCK_DGRAM); + } + + AFSYSTEMDatagramSocket(FileDescriptor fd, AFSocketType socketType) throws IOException { + super(new AFSYSTEMDatagramSocketImpl(fd, socketType)); + } + + @Override + protected AFSYSTEMDatagramChannel newChannel() { + return new AFSYSTEMDatagramChannel(this); + } + + /** + * Returns a new {@link AFSYSTEMDatagramSocket} instance, using the default + * {@link AFSocketType#SOCK_DGRAM} socket type. + * + * @return The new instance. + * @throws IOException on error. + */ + public static AFSYSTEMDatagramSocket newInstance() throws IOException { + return (AFSYSTEMDatagramSocket) newInstance(AFSYSTEMDatagramSocket::new); + } + + /** + * Returns a new {@link AFSYSTEMDatagramSocket} instance for the given socket type. + * + * @param socketType The socket type. + * @return The new instance. + * @throws IOException on error. + */ + public static AFSYSTEMDatagramSocket newInstance(AFSocketType socketType) throws IOException { + return (AFSYSTEMDatagramSocket) newInstance((fd) -> { + return new AFSYSTEMDatagramSocket(fd, socketType); + }); + } + + @Override + public AFSYSTEMDatagramChannel getChannel() { + return (AFSYSTEMDatagramChannel) super.getChannel(); + } + + @Override + protected AFSYSTEMSocketImplExtensions getImplExtensions() { + return (AFSYSTEMSocketImplExtensions) super.getImplExtensions(); + } + + /** + * Retrieves the kernel control ID given a kernel control name. + * + * An {@link IOException} is thrown if the ID is invalid or could not be accessed due to access + * restrictions. + * + * @param name The control name + * @return The control Id. + * @throws IOException on error. + */ + public int getNodeIdentity(String name) throws IOException { + return getImplExtensions().getKernelControlId(getFileDescriptor(), name); + } + + /** + * Retrieves the kernel control ID given a kernel control name. + * + * An {@link IOException} is thrown if the ID is invalid or could not be accessed due to access + * restrictions. + * + * @param name The control name + * @return The control Id. + * @throws IOException on error. + */ + public int getNodeIdentity(WellKnownKernelControlNames name) throws IOException { + return getNodeIdentity(name.getControlName()); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocketImpl.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocketImpl.java new file mode 100644 index 000000000..0db949848 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocketImpl.java @@ -0,0 +1,31 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.FileDescriptor; +import java.io.IOException; + +import org.newsclub.net.unix.AFDatagramSocketImpl; +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSocketType; + +final class AFSYSTEMDatagramSocketImpl extends AFDatagramSocketImpl { + AFSYSTEMDatagramSocketImpl(FileDescriptor fd, AFSocketType socketType) throws IOException { + super(AFSYSTEMSelectorProvider.AF_SYSTEM, fd, socketType); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMProtocolFamily.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMProtocolFamily.java new file mode 100644 index 000000000..8896bf930 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMProtocolFamily.java @@ -0,0 +1,32 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.net.ProtocolFamily; + +/** + * Describes the protocol families supported by junixsocket-afsystem. + * + * @author Christian Kohlschütter + */ +public enum AFSYSTEMProtocolFamily implements ProtocolFamily { + /** + * SYSTEM. + */ + SYSTEM; +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSelectorProvider.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSelectorProvider.java new file mode 100644 index 000000000..e6cbb748e --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSelectorProvider.java @@ -0,0 +1,201 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.IOException; +import java.net.ProtocolFamily; +import java.net.SocketAddress; + +import org.eclipse.jdt.annotation.NonNull; +import org.newsclub.net.unix.AFAddressFamily; +import org.newsclub.net.unix.AFAddressFamilyConfig; +import org.newsclub.net.unix.AFDatagramChannel; +import org.newsclub.net.unix.AFDatagramSocket; +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSelectorProvider; +import org.newsclub.net.unix.AFServerSocket; +import org.newsclub.net.unix.AFServerSocketChannel; +import org.newsclub.net.unix.AFSocket; +import org.newsclub.net.unix.AFSocketChannel; +import org.newsclub.net.unix.AFSocketPair; +import org.newsclub.net.unix.AFSocketType; +import org.newsclub.net.unix.AFSomeSocket; + +import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; + +/** + * Service-provider class for junixsocket selectors and selectable channels. + */ +public final class AFSYSTEMSelectorProvider extends AFSelectorProvider { + private static final AFSYSTEMSelectorProvider INSTANCE = new AFSYSTEMSelectorProvider(); + + @SuppressWarnings("null") + static final AFAddressFamily<@NonNull AFSYSTEMSocketAddress> AF_SYSTEM = AFAddressFamily + .registerAddressFamilyImpl("system", AFSYSTEMSocketAddress.addressFamily(), + new AFAddressFamilyConfig() { + + @Override + protected Class> socketClass() { + return AFSYSTEMSocket.class; + } + + @Override + protected AFSocket.Constructor socketConstructor() { + return AFSYSTEMSocket::new; + } + + @Override + protected Class> serverSocketClass() { + return AFSYSTEMServerSocket.class; + } + + @Override + protected AFServerSocket.Constructor serverSocketConstructor() { + return AFSYSTEMServerSocket::new; + } + + @Override + protected Class> socketChannelClass() { + return AFSYSTEMSocketChannel.class; + } + + @Override + protected Class> serverSocketChannelClass() { + return AFSYSTEMServerSocketChannel.class; + } + + @Override + protected Class> datagramSocketClass() { + return AFSYSTEMDatagramSocket.class; + } + + @Override + protected AFDatagramSocket.Constructor datagramSocketConstructor() { + return AFSYSTEMDatagramSocket::new; + } + + @Override + protected Class> datagramChannelClass() { + return AFSYSTEMDatagramChannel.class; + } + }); + + private AFSYSTEMSelectorProvider() { + super(); + } + + /** + * Returns the singleton instance. + * + * @return The instance. + */ + @SuppressFBWarnings("MS_EXPOSE_REP") + public static AFSYSTEMSelectorProvider getInstance() { + return INSTANCE; + } + + /** + * Returns the singleton instance. + * + * @return The instance. + */ + public static AFSYSTEMSelectorProvider provider() { + return getInstance(); + } + + /** + * Constructs a new socket pair from two sockets. + * + * @param s1 Some socket, the first one. + * @param s2 Some socket, the second one. + * @return The pair. + */ + @Override + protected

AFSocketPair

newSocketPair(P s1, P s2) { + return new AFSYSTEMSocketPair<>(s1, s2); + } + + @SuppressWarnings("unchecked") + @Override + public AFSYSTEMSocketPair openSocketChannelPair() throws IOException { + return (AFSYSTEMSocketPair) super.openSocketChannelPair(); + } + + @SuppressWarnings("unchecked") + @Override + public AFSYSTEMSocketPair openDatagramChannelPair() throws IOException { + return (AFSYSTEMSocketPair) super.openDatagramChannelPair(); + } + + @SuppressWarnings("unchecked") + @Override + public AFSYSTEMSocketPair openDatagramChannelPair(AFSocketType type) + throws IOException { + return (AFSYSTEMSocketPair) super.openDatagramChannelPair(type); + } + + @Override + protected AFSYSTEMSocket newSocket() throws IOException { + return AFSYSTEMSocket.newInstance(); + } + + @Override + public AFSYSTEMDatagramChannel openDatagramChannel() throws IOException { + return AFSYSTEMDatagramSocket.newInstance().getChannel(); + } + + @Override + public AFSYSTEMDatagramChannel openDatagramChannel(AFSocketType type) throws IOException { + return AFSYSTEMDatagramSocket.newInstance(type).getChannel(); + } + + @Override + public AFSYSTEMDatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { + return (AFSYSTEMDatagramChannel) super.openDatagramChannel(family); + } + + @Override + public AFSYSTEMServerSocketChannel openServerSocketChannel() throws IOException { + return AFSYSTEMServerSocket.newInstance().getChannel(); + } + + @Override + public AFSYSTEMServerSocketChannel openServerSocketChannel(SocketAddress sa) throws IOException { + return AFSYSTEMServerSocket.bindOn(AFSYSTEMSocketAddress.unwrap(sa)).getChannel(); + } + + @Override + public AFSYSTEMSocketChannel openSocketChannel() throws IOException { + return (AFSYSTEMSocketChannel) super.openSocketChannel(); + } + + @Override + public AFSYSTEMSocketChannel openSocketChannel(SocketAddress sa) throws IOException { + return AFSYSTEMSocket.connectTo(AFSYSTEMSocketAddress.unwrap(sa)).getChannel(); + } + + @Override + protected ProtocolFamily protocolFamily() { + return AFSYSTEMProtocolFamily.SYSTEM; + } + + @Override + protected AFAddressFamily<@NonNull AFSYSTEMSocketAddress> addressFamily() { + return AF_SYSTEM; + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocket.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocket.java new file mode 100644 index 000000000..3e122b955 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocket.java @@ -0,0 +1,124 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.SocketException; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFServerSocket; +import org.newsclub.net.unix.AFServerSocketChannel; +import org.newsclub.net.unix.AFSocket; +import org.newsclub.net.unix.AFSocketAddress; +import org.newsclub.net.unix.AFSocketImpl; + +/** + * The server part of an {@code AF_SYSTEM} socket. + * + * @author Christian Kohlschütter + */ +public class AFSYSTEMServerSocket extends AFServerSocket { + AFSYSTEMServerSocket(FileDescriptor fdObj) throws IOException { + super(fdObj); + } + + @Override + protected AFServerSocketChannel newChannel() { + return new AFSYSTEMServerSocketChannel(this); + } + + @Override + public AFSYSTEMServerSocketChannel getChannel() { + return (AFSYSTEMServerSocketChannel) super.getChannel(); + } + + /** + * Returns a new, unbound AF_SYSTEM {@link ServerSocket}. + * + * @return The new, unbound {@link AFServerSocket}. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMServerSocket newInstance() throws IOException { + return (AFSYSTEMServerSocket) AFServerSocket.newInstance(AFSYSTEMServerSocket::new); + } + + static AFSYSTEMServerSocket newInstance(FileDescriptor fdObj, int localPort, int remotePort) + throws IOException { + return (AFSYSTEMServerSocket) AFServerSocket.newInstance(AFSYSTEMServerSocket::new, fdObj, + localPort, remotePort); + } + + /** + * Returns a new AF_SYSTEM {@link ServerSocket} that is bound to the given + * {@link AFSYSTEMSocketAddress}. + * + * @param addr The socket file to bind to. + * @return The new, bound {@link AFServerSocket}. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMServerSocket bindOn(final AFSYSTEMSocketAddress addr) throws IOException { + return (AFSYSTEMServerSocket) AFServerSocket.bindOn(AFSYSTEMServerSocket::new, addr); + } + + /** + * Returns a new AF_SYSTEM {@link ServerSocket} that is bound to the given + * {@link AFSocketAddress}. + * + * @param addr The socket file to bind to. + * @param deleteOnClose If {@code true}, the socket file (if the address points to a file) will be + * deleted upon {@link #close}. + * @return The new, bound {@link AFServerSocket}. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMServerSocket bindOn(final AFSYSTEMSocketAddress addr, boolean deleteOnClose) + throws IOException { + return (AFSYSTEMServerSocket) AFServerSocket.bindOn(AFSYSTEMServerSocket::new, addr, + deleteOnClose); + } + + /** + * Returns a new, unbound AF_SYSTEM {@link ServerSocket} that will always bind to the + * given address, regardless of any socket address used in a call to bind. + * + * @param forceAddr The address to use. + * @return The new, yet unbound {@link AFServerSocket}. + * @throws IOException if an exception occurs. + */ + public static AFSYSTEMServerSocket forceBindOn(final AFSYSTEMSocketAddress forceAddr) + throws IOException { + return (AFSYSTEMServerSocket) AFServerSocket.forceBindOn(AFSYSTEMServerSocket::new, forceAddr); + } + + @Override + protected AFSocketImpl newImpl(FileDescriptor fdObj) + throws SocketException { + return new AFSYSTEMSocketImpl(fdObj); + } + + @Override + protected AFSocket newSocketInstance() throws IOException { + return AFSYSTEMSocket.newInstance(); + } + + @Override + public AFSYSTEMSocket accept() throws IOException { + return (AFSYSTEMSocket) super.accept(); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocketChannel.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocketChannel.java new file mode 100644 index 000000000..ddb25739d --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMServerSocketChannel.java @@ -0,0 +1,45 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.IOException; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFServerSocketChannel; + +/** + * A selectable channel for stream-oriented listening sockets. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMServerSocketChannel extends + AFServerSocketChannel { + AFSYSTEMServerSocketChannel(AFSYSTEMServerSocket socket) { + super(socket, AFSYSTEMSelectorProvider.getInstance()); + } + + /** + * Opens a server-socket channel. + * + * @return The new channel + * @throws IOException on error. + */ + public static AFSYSTEMServerSocketChannel open() throws IOException { + return AFSYSTEMServerSocket.newInstance().getChannel(); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocket.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocket.java new file mode 100644 index 000000000..de5969b19 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocket.java @@ -0,0 +1,138 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSYSTEMSocketImplExtensions; +import org.newsclub.net.unix.AFSocket; +import org.newsclub.net.unix.AFSocketCapability; +import org.newsclub.net.unix.AFSocketFactory; + +/** + * Implementation of an {@code AF_SYSTEM} socket. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMSocket extends AFSocket implements + AFSYSTEMSocketExtensions { + private static AFSYSTEMSocketImplExtensions staticExtensions = null; + + AFSYSTEMSocket(FileDescriptor fdObj, AFSocketFactory factory) + throws SocketException { + super(new AFSYSTEMSocketImpl(fdObj), factory); + } + + @SuppressWarnings("unused") + private static synchronized AFSYSTEMSocketImplExtensions getStaticImplExtensions() + throws IOException { + if (staticExtensions == null) { + try (AFSYSTEMSocket socket = new AFSYSTEMSocket(null, null)) { + staticExtensions = (AFSYSTEMSocketImplExtensions) socket.getImplExtensions(); + } + } + return staticExtensions; + } + + /** + * Returns true iff {@link AFSYSTEMSocket}s (sockets of type "AF_SYSTEM") are + * supported by the current Java VM and the kernel. + * + * To support {@link AFSYSTEMSocket}s, a custom JNI library must be loaded that is supplied with + * junixsocket, and the system must support AF_SYSTEM sockets. + * + * This call is equivalent to checking {@link AFSocket#isSupported()} and + * {@link AFSocket#supports(AFSocketCapability)} with + * {@link AFSocketCapability#CAPABILITY_DARWIN}. + * + * @return {@code true} iff supported. + */ + public static boolean isSupported() { + return AFSocket.isSupported() && AFSocket.supports(AFSocketCapability.CAPABILITY_DARWIN); + } + + @Override + protected AFSYSTEMSocketChannel newChannel() { + return new AFSYSTEMSocketChannel(this); + } + + /** + * Creates a new, unbound {@link AFSocket}. + * + * This "default" implementation is a bit "lenient" with respect to the specification. + * + * In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and + * {@link Socket#setTcpNoDelay(boolean)}. + * + * @return A new, unbound socket. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMSocket newInstance() throws IOException { + return (AFSYSTEMSocket) AFSocket.newInstance(AFSYSTEMSocket::new, (AFSYSTEMSocketFactory) null); + } + + static AFSYSTEMSocket newInstance(AFSYSTEMSocketFactory factory) throws SocketException { + return (AFSYSTEMSocket) AFSocket.newInstance(AFSYSTEMSocket::new, factory); + } + + /** + * Creates a new, unbound, "strict" {@link AFSocket}. + * + * This call uses an implementation that tries to be closer to the specification than + * {@link #newInstance()}, at least for some cases. + * + * @return A new, unbound socket. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMSocket newStrictInstance() throws IOException { + return (AFSYSTEMSocket) AFSocket.newInstance(AFSYSTEMSocket::new, (AFSYSTEMSocketFactory) null); + } + + /** + * Creates a new {@link AFSocket} and connects it to the given {@link AFSYSTEMSocketAddress}. + * + * @param addr The address to connect to. + * @return A new, connected socket. + * @throws IOException if the operation fails. + */ + public static AFSYSTEMSocket connectTo(AFSYSTEMSocketAddress addr) throws IOException { + return (AFSYSTEMSocket) AFSocket.connectTo(AFSYSTEMSocket::new, addr); + } + + @Override + public AFSYSTEMSocketChannel getChannel() { + return (AFSYSTEMSocketChannel) super.getChannel(); + } + + /** + * Very basic self-test function. + * + * Prints "supported" and "capabilities" status to System.out. + * + * @param args ignored. + */ + public static void main(String[] args) { + System.out.print(AFSYSTEMSocket.class.getName() + ".isSupported(): "); + System.out.flush(); + System.out.println(AFSYSTEMSocket.isSupported()); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketChannel.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketChannel.java new file mode 100644 index 000000000..95566ded1 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketChannel.java @@ -0,0 +1,33 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSocketChannel; + +/** + * A selectable channel for stream-oriented connecting sockets. + * + * @author Christian Kohlschütter + */ +public final class AFSYSTEMSocketChannel extends AFSocketChannel implements + AFSYSTEMSocketExtensions { + AFSYSTEMSocketChannel(AFSYSTEMSocket socket) { + super(socket, AFSYSTEMSelectorProvider.getInstance()); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketExtensions.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketExtensions.java new file mode 100644 index 000000000..3d1b1a7c6 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketExtensions.java @@ -0,0 +1,29 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import org.newsclub.net.unix.AFSocketExtensions; + +/** + * Defines certain methods that all junixsocket AF_SYSTEM socket implementations share and extend + * beyond the standard socket API. + * + * @author Christian Kohlschütter + */ +public interface AFSYSTEMSocketExtensions extends AFSocketExtensions { +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketFactory.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketFactory.java new file mode 100644 index 000000000..0e62599a8 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketFactory.java @@ -0,0 +1,58 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSocketFactory; + +/** + * The base for a SocketFactory that connects to AF_SYSTEM sockets. + */ +public abstract class AFSYSTEMSocketFactory extends AFSocketFactory { + /** + * Creates a {@link AFSYSTEMSocketFactory}. + */ + protected AFSYSTEMSocketFactory() { + super(); + } + + @Override + public final Socket createSocket() throws SocketException { + return configure(AFSYSTEMSocket.newInstance(this)); + } + + @Override + protected final AFSYSTEMSocket connectTo(AFSYSTEMSocketAddress addr) throws IOException { + return configure(AFSYSTEMSocket.connectTo(addr)); + } + + /** + * Performs some optional configuration on a newly created socket. + * + * @param sock The socket. + * @return The very socket. + * @throws SocketException on error. + */ + protected AFSYSTEMSocket configure(AFSYSTEMSocket sock) throws SocketException { + return sock; + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketImpl.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketImpl.java new file mode 100644 index 000000000..b7190b434 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketImpl.java @@ -0,0 +1,40 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.FileDescriptor; +import java.net.SocketException; + +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSocketImpl; + +final class AFSYSTEMSocketImpl extends AFSocketImpl { + AFSYSTEMSocketImpl(FileDescriptor fdObj) { + super(AFSYSTEMSelectorProvider.AF_SYSTEM, fdObj); + } + + @Override + public Object getOption(int optID) throws SocketException { + return getOptionLenient(optID); + } + + @Override + public void setOption(int optID, Object value) throws SocketException { + setOptionLenient(optID, value); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketOptions.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketOptions.java new file mode 100644 index 000000000..bf230554e --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketOptions.java @@ -0,0 +1,32 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * AF_SYSTEM-specific socket options. + * + * @author Christian Kohlschütter + */ +@NonNullByDefault +public final class AFSYSTEMSocketOptions { + private AFSYSTEMSocketOptions() { + throw new IllegalStateException(); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketPair.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketPair.java new file mode 100644 index 000000000..99b028dbb --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/AFSYSTEMSocketPair.java @@ -0,0 +1,61 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.io.IOException; + +import org.newsclub.net.unix.AFSocketPair; +import org.newsclub.net.unix.AFSomeSocket; + +/** + * A pair of sockets. + * + * @param The socket type. + * @author Christian Kohlschütter + */ +public final class AFSYSTEMSocketPair extends AFSocketPair { + /** + * Creates a new socket pair. + * + * @param socket1 The first socket. + * @param socket2 The second socket. + */ + AFSYSTEMSocketPair(T socket1, T socket2) { + super(socket1, socket2); + } + + /** + * Opens a socket pair of interconnected channels. + * + * @return The new channel pair. + * @throws IOException on error. + */ + public static AFSYSTEMSocketPair open() throws IOException { + return AFSYSTEMSelectorProvider.provider().openSocketChannelPair(); + } + + /** + * Opens a socket pair of interconnected datagram channels. + * + * @return The new channel pair. + * @throws IOException on error. + */ + public static AFSYSTEMSocketPair openDatagram() throws IOException { + return AFSYSTEMSelectorProvider.provider().openDatagramChannelPair(); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/IPUtil.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/IPUtil.java new file mode 100644 index 000000000..d1c8cb44c --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/IPUtil.java @@ -0,0 +1,173 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import java.nio.ByteBuffer; + +/** + * Some IP protocol-related helper methods. + * + * @author Christian Kohlschütter + */ +public final class IPUtil { + /** + * The length (in bytes) of the "domain" header used in loopback packet systems like UTUN_CONTROL. + */ + public static final int DOMAIN_HEADER_LENGTH = 4; // bytes + + /** + * The identifier for AF_INET (at least on Darwin). + */ + public static final int DOMAIN_AF_INET = 2; + + /** + * The length (in bytes) of an IPv4 header without options. + */ + public static final int IPV4_DEFAULT_HEADER_SIZE = 20; // bytes + + /** + * The ICMP protocol. + */ + public static final byte AF_INET_PROTOCOL_ICMP = 1; + + private IPUtil() { + throw new IllegalStateException("No instances"); + } + + /** + * Computes the checksum for an IPv4 header, and overwrites any existing checksum with the correct + * one. + * + * @param bb The buffer containing the IPv4 header + * @param start The beginning position of the header in the buffer. + * @param end The end position (exclusive) of the header in the buffer. + * @return The computed 16-bit checksum + */ + public static int checksumIPv4header(ByteBuffer bb, int start, int end) { + return checksumIPstyle(bb, start, end, 10); + } + + /** + * Computes the checksum for an ICMP header, and overwrites any existing checksum with the correct + * one. + * + * Also see RFC 792. + * + * @param bb The buffer containing the ICMP header + * @param start The beginning position of the header in the buffer. + * @param end The end position (exclusive) of the header in the buffer. + * @return The computed 16-bit checksum + */ + public static int checksumICMPheader(ByteBuffer bb, int start, int end) { + return checksumIPstyle(bb, start, end, 2); + } + + /** + * Computes the 16-bit checksum for some header used in IP networking, and overwrites any existing + * checksum with the correct one. + * + * Also see RFC 1071. + * + * @param bb The buffer containing the ICMP header + * @param start The beginning position of the header in the buffer. + * @param end The end position (exclusive) of the header in the buffer. + * @param checksumOffset The offset from start for an existing 16-bit checksum that is to be + * ignored. + * @return The computed 16-bit checksum + */ + private static int checksumIPstyle(ByteBuffer bb, int start, int end, int checksumOffset) { + final int checksumAt = start + checksumOffset; + int sum = 0; + + if (checksumOffset >= end) { + throw new IllegalArgumentException("checksumOffset"); + } + + // While we could pretend the checksum is 0 (by ignoring the computation at position + // checksumAt), we zero it out here, and later put the correct checksum back in. + // This should not only be faster than two for-loops or checking the position prior to adding, + // it also puts the correct checksum in place, which can come in handy when composing packets. + // It is also the recommended strategy as per RFC 1071. The downside is that we modify the + // contents of the buffer, but that's OK since we control the API. + bb.putShort(checksumAt, (short) 0); + + for (int i = start; i < end; i += 2) { + int v = bb.getShort(i) & 0xFFFF; + + sum += v; + + int overflow = (sum & ~0xFFFF); + if (overflow != 0) { + // overflow -> add carry and trim to 16-bit + sum = (sum + (overflow >>> 16)) & 0xFFFF; + } + } + + int checksum = (~sum) & 0xFFFF; + + // fix checksum + bb.putShort(checksumAt, (short) checksum); + + return checksum; + } + + /** + * Put (write) an IPv4 header to the given byte buffer, using the given parameters. + * + * This should write exactly 20 bytes to the buffer. The buffer position then is at the end of the + * header. + * + * @param bb The target byte buffer. + * @param payloadLength The length of the payload (excluding the IPv4 header). + * @param protocol The protocol identifier. + * @param srcIP The source IPv4 address. + * @param dstIP The destination IPv4 address. + */ + public static void putIPv4Header(ByteBuffer bb, int payloadLength, byte protocol, int srcIP, + int dstIP) { + bb.put((byte) 0x45); // IPv4, 5*4=20 bytes header + bb.put((byte) 0); // TOS/DSCP + bb.putShort((short) (20 + payloadLength)); // total length = header + payload + bb.putShort((short) 0); // identification + bb.putShort((short) 0); // flags and fragment offset + bb.put((byte) 65); // TTL + bb.put(protocol); // protocol (e.g., ICMP) + bb.putShort((short) 0); // header checksum (placeholder) + bb.putInt(srcIP); + bb.putInt(dstIP); + // end of header (20 bytes) + } + + /** + * Put (write) an ICMP echo response header to the given byte buffer, using the given parameters. + * + * @param bb The target byte buffer. + * @param echoIdentifier The identifier, from the ICMP echo request. + * @param sequenceNumber The sequence number, from the ICMP echo request. + * @param payload The payload, from the ICMP echo request. + */ + public static void putICMPEchoResponse(ByteBuffer bb, short echoIdentifier, short sequenceNumber, + ByteBuffer payload) { + bb.put((byte) 0); // Echo response + bb.put((byte) 0); // Echo has no other code + bb.putShort((short) 0); // ICMP checksum (placeholder) + bb.putShort(echoIdentifier); // ICMP echo identifier + bb.putShort(sequenceNumber); // ICMP echo sequence number + bb.put(payload); + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/WellKnownKernelControlNames.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/WellKnownKernelControlNames.java new file mode 100644 index 000000000..c1e7dce71 --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/WellKnownKernelControlNames.java @@ -0,0 +1,186 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +/** + * Well-known AF_SYSTEM control names. + * + * Most of these can only be accessed by the superuser (root). Unless otherwise specified, one + * usually accesses these as datagrams (SOCK_DGRAM). + * + * You can see which control names are registered via {@code netstat -an}; check for the IDs below + * "Registered kernel control modules". + * + * Also see New OSX Book Volume 1 Chapter 16 + * + * @author Christian Kohlschütter + */ +public enum WellKnownKernelControlNames { + + /** + * {@code com.apple.netsrc}. Network/route policies and statistics. + * + * Available to non-root. + * + * See https://github.com/apple-oss-distributions/xnu/blob/main/bsd/net/netsrc.h and + * https://github.com/apple-oss-distributions/xnu/blob/main/bsd/net/netsrc.c, as well as + * https://github.com/appleopen/Libinfo/blob/master/lookup.subproj/si_compare.c + */ + NETSRC("com.apple.netsrc"), // + + /** + * {@code com.apple.network.statistics}. Live socket statistics and notifications. + * + * Available to non-root. + * + * See {@code http://newosxbook.com/src.jl?tree=listings&file=lsock.c} as well as + * https://github.com/packetzero/libntstat + */ + NETWORK_STATISTICS("com.apple.network.statistics"), // + + /** + * {@code com.apple.flow-divert}. MPTCP flow diversions (XNU-2422). + * + * See https://github.com/apple-oss-distributions/xnu/blob/main/bsd/netinet/flow_divert.c and + * https://github.com/apple-oss-distributions/xnu/blob/main/bsd/netinet/flow_divert.h + */ + FLOW_DIVERT("com.apple.flow-divert"), // + + /** + * {@code com.apple.net.netagent}. + */ + NET_NETAGENT("com.apple.net.netagent"), // + + /** + * {@code com.apple.content-filter}. + */ + CONTENT_FILTER("com.apple.content-filter"), // + + /** + * {@code com.apple.net.utun_control}. User-mode tunneling (VPN). + */ + UTUN_CONTROL("com.apple.net.utun_control"), // + + /** + * {@code com.apple.net.ipsec_control}. + */ + IPSEC_CONTROL("com.apple.net.ipsec_control"), // + + /** + * {@code com.apple.network.tcp_ccdebug}. Requires SOCK_STREAM. + */ + NETWORK_TCP_CCDEBUG("com.apple.network.tcp_ccdebug"), // + + /** + * {@code com.apple.network.advisory}. + */ + NETWORK_ADVISORY("com.apple.network.advisory"), // + + /** + * {@code com.apple.net.rvi_control}. + */ + NET_RVI_CONTROL("com.apple.net.rvi_control"), // + + /** + * {@code com.apple.nke.sockwall}. + */ + NKE_SOCKWALL("com.apple.nke.sockwall"), // + + /** + * {@code com.apple.spmi.nfc}. Available to non-root. Requires SOCK_STREAM. + */ + SPMI_NFC("com.apple.spmi.nfc"), // + + /** + * {@code com.apple.packet-mangler}. + */ + PACKET_MANGLER("com.apple.packet-mangler"), // + + /** + * {@code com.apple.net.necp_control}. + */ + NECP_CONTROL("com.apple.net.necp_control"), // + + /** + * {@code com.apple.fileutil.kext.stateful.ctl}. + */ + FILEUTIL_KEXT_STATEFUL_CTL("com.apple.fileutil.kext.stateful.ctl"), // + + /** + * {@code com.apple.fileutil.kext.stateless.ctl}. + */ + FILEUTIL_KEXT_STATELESS_CTL("com.apple.fileutil.kext.stateless.ctl"), // + + /** + * {@code com.apple.mcx.kernctl.alr}. + */ + MCX_KERNCTL_ALR("com.apple.mcx.kernctl.alr"), // + + /** + * {@code com.apple.nke.webcontentfilter}. + */ + NKE_WEBCONTENTFILTER("com.apple.nke.webcontentfilter"), // + + /** + * {@code com.apple.uart.wlan-debug}. + */ + UART_WLAN_DEBUG("com.apple.uart.wlan-debug"), // + + /** + * {@code com.apple.uart.sk.wlan-debug}. + */ + UART_SK_WLAN_DEBUG("com.apple.uart.sk.wlan-debug"), // + + /** + * {@code com.apple.uart.debug-console}. + */ + UART_DEBUG_CONSOLE("com.apple.uart.debug-console"), // + + /** + * {@code com.apple.uart.sk.debug-console}. + */ + UART_SK_DEBUG_CONSOLE("com.apple.uart.sk.debug-console"), // + + /** + * {@code com.apple.userspace_ethernet}. + */ + USERSPACE_ETHERNET("com.apple.userspace_ethernet"), // + + ; + + // non-Apple: + // - com.avira.fac + // - com.vmware.kext.vmci + // - com.vmware.kext.vmnet + // - com.vmware.kext.vmx86 + + private final String name; + + WellKnownKernelControlNames(String name) { + this.name = name; + } + + /** + * Returns the control name identifier. + * + * @return The name. + */ + public String getControlName() { + return name; + } +} diff --git a/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/package-info.java b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/package-info.java new file mode 100644 index 000000000..31e82a8ce --- /dev/null +++ b/junixsocket-darwin/src/main/java/org/newsclub/net/unix/darwin/system/package-info.java @@ -0,0 +1,4 @@ +/** + * The junixsocket implementation for Darwin AF_SYSTEM sockets. + */ +package org.newsclub.net.unix.darwin.system; diff --git a/junixsocket-darwin/src/site/site.xml b/junixsocket-darwin/src/site/site.xml new file mode 100644 index 000000000..873972284 --- /dev/null +++ b/junixsocket-darwin/src/site/site.xml @@ -0,0 +1,33 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + + + + + kohlschutter/junixsocket + right + + + + + + ${project.name} + + + + ${project.scm.url} + + +

+ + + + diff --git a/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/SelftestProvider.java b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/SelftestProvider.java new file mode 100644 index 000000000..06003e99a --- /dev/null +++ b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/SelftestProvider.java @@ -0,0 +1,116 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin; + +import java.io.PrintWriter; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.newsclub.net.unix.darwin.system.KernelControlNamesTest; +import org.newsclub.net.unix.darwin.system.UtunTest; + +/** + * Provides references to all "junixsocket-afystem" tests that should be included in + * junixsocket-selftest. + * + * @author Christian Kohlschütter + */ +@SuppressWarnings("PMD.CouplingBetweenObjects") +public class SelftestProvider { + private static final String MODULE = "junixsocket-darwin"; + final Map>> testMap = new LinkedHashMap<>(); // NOPMD.LooseCoupling + + // CPD-OFF + + @SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.UnnecessaryFullyQualifiedName"}) + public SelftestProvider() { + registerTest(MODULE, KernelControlNamesTest.class); + registerTest(MODULE, UtunTest.class); + + // registerTest(AFSYSTEMExtensionsTest.class); + // + // registerTest(AcceptTimeoutTest.class); + // + // registerTest(AncillaryMessageTest.class); + // + // registerTest(AvailableTest.class); + // + // registerTest(BufferOverflowTest.class); + // + // registerTest(CancelAcceptTest.class); + // + // registerTest(DatagramSocketTest.class); + // + // registerTest(EndOfFileTest.class); + // + // disabled: FinalizeTest + // + // registerTest(ReadWriteTest.class); + // + // registerTest(SelectorTest.class); + // + // registerTest(ServerSocketCloseTest.class); + // + // registerTest(ServerSocketTest.class); + // + // registerTest(SocketChannelTest.class); + // + // registerTest(SocketOptionsTest.class); + // + // registerTest(SocketPairTest.class); + // + // registerTest(SocketTest.class); + // + // registerTest(SoTimeoutTest.class); + // + // registerTest(StandardSocketOptionsTest.class); + // + // registerTest(TcpNoDelayTest.class); + // + // registerTest(ThroughputTest.class); + } + + public Set modulesDisabledByDefault() { + return Collections.emptySet(); + } + + // private void registerTest(Class> test) { + // registerTest(MODULE, test); + // } + + private void registerTest(String group, Class test) { + if (test != null) { + testMap.computeIfAbsent(group, (key) -> new LinkedHashSet<>()).add(test); + } + } + + public Map[]> tests() { + Map[]> tests = new LinkedHashMap<>(); + testMap.forEach((key, set) -> { + tests.put(key, set.toArray(new Class[0])); + }); + + return tests; + } + + public void printAdditionalProperties(PrintWriter out) { + } +} diff --git a/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/IPUtilTest.java b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/IPUtilTest.java new file mode 100644 index 000000000..1a0a00682 --- /dev/null +++ b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/IPUtilTest.java @@ -0,0 +1,45 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.ByteBuffer; + +import org.junit.jupiter.api.Test; + +public class IPUtilTest { + + @Test + public void testIPHeaderChecksum() { + // example from https://www.thegeekstuff.com/2012/05/ip-header-checksum/ + ByteBuffer bb = ByteBuffer.wrap(new byte[] { + 0x45, 0x00, // + 0x00, 0x3c, // + 0x1c, 0x46, // + 0x40, 0x00, // + 0x40, 0x06, // + 0x00, 0x00, // + (byte) 0xac, 0x10, // + 0x0a, 0x63, // + (byte) 0xac, 0x10, // + 0x0a, 0x0c}); + + assertEquals(0xB1E6, IPUtil.checksumIPv4header(bb, 0, bb.remaining())); + } +} diff --git a/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/KernelControlNamesTest.java b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/KernelControlNamesTest.java new file mode 100644 index 000000000..59f9ad959 --- /dev/null +++ b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/KernelControlNamesTest.java @@ -0,0 +1,58 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.SocketException; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +public class KernelControlNamesTest { + @Test + // @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_DARWIN) + public void testStandardKernelControlNames() throws Exception { + try (AFSYSTEMDatagramSocket socket = AFSYSTEMDatagramSocket.newInstance()) { + assertThrows(IOException.class, () -> socket.getNodeIdentity("definitely.missing")); + + int errors = 0; + Set ids = new LinkedHashSet<>(); + for (WellKnownKernelControlNames n : WellKnownKernelControlNames.values()) { + int id; + try { + id = socket.getNodeIdentity(n.getControlName()); + assertTrue(id > 0, "id should be a positive integer"); + ids.add(id); + } catch (SocketException e) { + errors++; + continue; + } + } + + System.out.println("Resolved control names " + ids.size() + "/" + WellKnownKernelControlNames + .values().length); + + assertEquals(WellKnownKernelControlNames.values().length - errors, ids.size()); + } + } +} diff --git a/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/UtunTest.java b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/UtunTest.java new file mode 100644 index 000000000..3e8a15df1 --- /dev/null +++ b/junixsocket-darwin/src/test/java/org/newsclub/net/unix/darwin/system/UtunTest.java @@ -0,0 +1,271 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.darwin.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFSYSTEMSocketAddress; +import org.newsclub.net.unix.AFSYSTEMSocketAddress.SysAddr; +import org.newsclub.net.unix.AFSocketCapability; +import org.newsclub.net.unix.AFSocketCapabilityRequirement; + +import com.kohlschutter.testutil.ExecutionEnvironmentRequirement; +import com.kohlschutter.testutil.ExecutionEnvironmentRequirement.Rule; + +/** + * Demo code to exercise AF_SYSTEM with UTUN_CONTROL. + * + * Creates a PtP VPN tunnel, sends a ping via Java SDK code, parses the ICMP echo request (ping) + * packet, and responds with a hand-crafted ICMP echo reply (pong). + * + * @author Christian Kohlschütter + */ +@SuppressWarnings("PMD.AvoidUsingHardCodedIP") +public class UtunTest { + private static final Inet4Address UTUN_SRC_IP; + private static final Inet4Address UTUN_DST_IP; + + static { + try { + UTUN_SRC_IP = (Inet4Address) InetAddress.getByName("169.254.3.4"); // "this host" + UTUN_DST_IP = (Inet4Address) InetAddress.getByName("169.254.3.5"); // "other end" + } catch (UnknownHostException e) { + throw new IllegalStateException(e); + } + } + + /** + * Dummy method to indicate the given parameter is not checked by our test code. + * + * @param v The parameter. + * @return The parameter. + */ + private static Object unchecked(Object v) { + return v; + } + + /** + * Returns the given IPv4 address as an integer. + * + * @param addr The IPv4 address object. + * @return The integer. + */ + private static int getAddressAsInt(Inet4Address addr) { + // In the JDK implementation of Inet4Address, this happens to be the hash code. + return addr.hashCode(); + } + + @SuppressWarnings({ + "checkstyle:VariableDeclarationUsageDistance", "PMD.JUnitTestContainsTooManyAsserts", + "PMD.AvoidBranchingStatementAsLastInLoop"}) + @Test + @ExecutionEnvironmentRequirement(root = Rule.REQUIRED) + @AFSocketCapabilityRequirement(AFSocketCapability.CAPABILITY_DARWIN) + public void testTunnelPingPong() throws Exception { + try (AFSYSTEMDatagramSocket socket = AFSYSTEMDatagramSocket.newInstance()) { + int id = socket.getNodeIdentity(WellKnownKernelControlNames.UTUN_CONTROL); + + // NOTE: Connecting requires root privileges, but we could do that in a separate process + // and send the socket FD via AF_UNIX to a non-privileged helper process. + try { + socket.connect(AFSYSTEMSocketAddress.ofSysAddrIdUnit(SysAddr.AF_SYS_CONTROL, id, 0)); + } catch (SocketException e) { + assumeTrue(false, "Could not connect to UTUN_CONTROL: " + e); + return; + } + + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { + + AFSYSTEMSocketAddress rsa = socket.getRemoteSocketAddress(); + Objects.requireNonNull(rsa); + + assertEquals(SysAddr.AF_SYS_CONTROL, rsa.getSysAddr()); + assertEquals(id, rsa.getId()); + assertNotEquals(0, rsa.getUnit()); // utunN: N=(unit-1), e.g., unit=9 -> utun8 + + String utun = "utun" + (rsa.getUnit() - 1); + // System.out.println(utun); + + Process p = Runtime.getRuntime().exec(new String[] { + "/sbin/ifconfig", utun, UTUN_SRC_IP.getHostAddress(), UTUN_DST_IP.getHostAddress()}); + int rcIfconfig; + try { + rcIfconfig = p.waitFor(); + } finally { + p.destroyForcibly(); + } + + assertEquals(0, rcIfconfig, "Could not set IP address for " + utun); + + AFSYSTEMDatagramChannel channel = socket.getChannel(); + ByteBuffer bb = ByteBuffer.allocateDirect(1500).order(ByteOrder.BIG_ENDIAN); + + CompletableFuture ping = CompletableFuture.supplyAsync(() -> { + try { + return UTUN_DST_IP.isReachable(1000); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + }); + + while (channel.read(bb) >= 0) { + bb.flip(); + + // Request: Domain (AF_INET) + IPv4 header + ICMP header + ICMP payload + + int totalSize = bb.remaining(); + // assertEquals(76, totalSize); // 4 byte domain header + 72 bytes packet length + + int domain = bb.getInt(); + assertEquals(IPUtil.DOMAIN_AF_INET, domain, "Expect domain 2 (AF_INET)"); + + int ipHeaderStartPos = bb.position(); + + int versionAndIHL = bb.get() & 0xFF; + int version = versionAndIHL >> 4; + assertEquals(4, version, "expect IPv4 packet"); + + // see https://en.wikipedia.org/wiki/Internet_Protocol_version_4#Header + + int ihl = versionAndIHL & 0b1111; + int ihlBytes = ihl * 32 /* bit */ / 8; + assertTrue(ihlBytes >= 20, "expect (at least) 20 bytes header length"); + + int tosDSCP = (bb.get() & 0xFF); + unchecked(tosDSCP); + + int totalLen = (bb.getShort() & 0xFFFF); + assertEquals(totalSize - 4, totalLen); + + int identification = (bb.getShort() & 0xFFFF); + unchecked(identification); + + int flagsAndFragmentOffset = (bb.getShort() & 0xFFFF); + int flags = flagsAndFragmentOffset >> 13; + int fragmentOffset = flagsAndFragmentOffset & 0b1_1111_1111_1111; + assertEquals(0, flags); + assertEquals(0, fragmentOffset); + + int ttl = bb.get() & 0xFF; + assertNotEquals(0, ttl); // e.g., 65 + + int protocol = bb.get() & 0xFF; + assertEquals(IPUtil.AF_INET_PROTOCOL_ICMP, protocol); // 1 == ICMP + + int headerChecksum = bb.getShort() & 0xFFFF; + // see below for verification + + int srcIP = bb.getInt(); + int dstIP = bb.getInt(); + + assertEquals(getAddressAsInt(UTUN_SRC_IP), srcIP); // 10.250.3.4 + assertEquals(getAddressAsInt(UTUN_DST_IP), dstIP); // 10.250.3.5 + + // when ihl=5 -> ihlBytes=ihl*4=20, there are no more options + // but let's check nevertheless... + + int remainingHeaderLength = ihlBytes - 20; + if (remainingHeaderLength > 0) { + System.err.println("Warning: Found unexpected Options section in IPv4 header; len=" + + remainingHeaderLength); + bb.position(bb.position() + remainingHeaderLength); + } + + // we're at the end of the IPv4 header + + int computedHeaderChecksum = IPUtil.checksumIPv4header(bb, ipHeaderStartPos, bb + .position()); + assertEquals(computedHeaderChecksum, headerChecksum); + + int icmpSize = bb.remaining(); + // assertEquals(52, icmpSize); // ICMP header + optional data + + int icmpBeginPosition = bb.position(); + + // begin ICMP header + int icmpType = bb.get() & 0xFF; + assertEquals(8, icmpType); // 8 = Echo Request + + int icmpCode = bb.get() & 0xFF; + assertEquals(0, icmpCode); // Echo Request has no other Code + + int icmpChecksum = bb.getShort() & 0xFFFF; // checked below + + int icmpEchoIdentifier = bb.getShort() & 0xFFFF; + int icmpEchoSequenceNumber = bb.getShort() & 0xFFFF; + + unchecked(icmpEchoIdentifier); + assertEquals(1, icmpEchoSequenceNumber); // first echo packet + + int icmpChecksumComputed = // + IPUtil.checksumICMPheader(bb, icmpBeginPosition, bb.position() + bb.remaining()); + assertEquals(icmpChecksumComputed, icmpChecksum); + + // Now it's time to craft an echo response + // Response: "AF_INET" + IPv4 header + ICMP header + ICMP payload from echo request + + ByteBuffer response = ByteBuffer.allocate(IPUtil.DOMAIN_HEADER_LENGTH + + IPUtil.IPV4_DEFAULT_HEADER_SIZE + icmpSize).order(ByteOrder.BIG_ENDIAN); + response.putInt(IPUtil.DOMAIN_AF_INET); + IPUtil.putIPv4Header(response, icmpSize, IPUtil.AF_INET_PROTOCOL_ICMP, dstIP, srcIP); + + int responsePayloadStart = response.position(); + IPUtil.checksumIPv4header(response, IPUtil.DOMAIN_HEADER_LENGTH, responsePayloadStart); + + IPUtil.putICMPEchoResponse(response, (short) icmpEchoIdentifier, + (short) icmpEchoSequenceNumber, bb); + assertEquals(0, bb.remaining()); // writeEchoResponse consumed the payload + int responsePayloadEnd = response.position(); + + IPUtil.checksumICMPheader(response, responsePayloadStart, responsePayloadEnd); + + response.flip(); + int written = channel.write(response); + bb.clear(); + + assertEquals(response.capacity(), written); + + assertTrue(ping.get(1, TimeUnit.SECONDS)); + + return; + } + + fail("Nothing received"); + }); + } + } +} diff --git a/junixsocket-demo/pom.xml b/junixsocket-demo/pom.xml index 5f2b50659..48d4ca52f 100644 --- a/junixsocket-demo/pom.xml +++ b/junixsocket-demo/pom.xml @@ -1,12 +1,14 @@ - + 4.0.0 junixsocket-demo jar com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-demo @@ -14,11 +16,10 @@ ${project.parent.basedir} 8 - Some example code to demo junixsocket's features - - com.kohlschutter.junixsocket @@ -47,31 +48,18 @@ ${project.version} - mysql - mysql-connector-java - 8.0.30 - - - com.google.protobuf - protobuf-java - - - + com.mysql + mysql-connector-j + 8.1.0 - - com.google.protobuf - protobuf-java - 3.19.6 - - - org.postgresql postgresql - 42.5.0 + 42.6.0 provided @@ -90,10 +78,24 @@ com.squareup.okhttp3 okhttp - 4.10.0 + 4.11.0 + + + com.squareup.okio + okio + 3.5.0 + + + org.apache.mina + mina-core + 2.2.3 + + + io.netty + netty-all + 4.1.97.Final - diff --git a/junixsocket-demo/src/main/java/module-info.java b/junixsocket-demo/src/main/java/module-info.java index 0082407bf..d614bdb85 100644 --- a/junixsocket-demo/src/main/java/module-info.java +++ b/junixsocket-demo/src/main/java/module-info.java @@ -13,8 +13,12 @@ requires nanohttpd; requires okhttp3; requires com.kohlschutter.util; + requires mina.core; requires static com.kohlschutter.annotations.compiletime; requires static org.eclipse.jdt.annotation; + requires io.netty.common; + requires io.netty.buffer; + requires io.netty.transport; exports org.newsclub.net.unix.demo.rmi.services to java.rmi; exports org.newsclub.net.unix.demo.rmi.fd to java.rmi; diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/mysql/demo/AFUNIXDatabaseSocketFactoryDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/mysql/demo/AFUNIXDatabaseSocketFactoryDemo.java index a91d97440..88cb064a7 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/mysql/demo/AFUNIXDatabaseSocketFactoryDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/mysql/demo/AFUNIXDatabaseSocketFactoryDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ /** * Demonstrates how to connect to a local MySQL server. - * + * * @author Christian Kohlschuetter */ public class AFUNIXDatabaseSocketFactoryDemo { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/DemoHelper.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/DemoHelper.java index 50ca192f9..abc9502f9 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/DemoHelper.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/DemoHelper.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ * Just a helper class to simplify controlling the demo from the command line. */ public final class DemoHelper { - @ExcludeFromCodeCoverageGeneratedReport + @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable") private DemoHelper() { throw new IllegalStateException("No instances"); } @@ -45,7 +45,7 @@ private DemoHelper() { /** * Adds a key-value pair to a Properties instance. Takes values from a given system property and * overrides the default value with it. - * + * * @param props The Properties instance to write to. * @param key The name of the property. * @param defaultValue The default value (for demo purposes) diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestClient.java index 694577593..b40c58c29 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,12 +30,12 @@ /** * A simple demo client. - * + * * Reads a server greeting string, then sends a "Hello Server" string as a response. - * + * * Finally, reads integers (via {@link DataInputStream}), then sends twice the sent value each, * unless a "-123" magic number is read to indicate the end of the conversation. - * + * * @author Christian Kohlschütter * @see SimpleTestServer */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestServer.java index 9d80664ce..4f9596afe 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/SimpleTestServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,16 +31,17 @@ /** * A simple demo server. - * + * * Sends a hello message (as a string), then reads back a response string. - * + * * Finally, sends integers (via {@link DataOutputStream}) from 1 to 5, expects an integer response * of twice the sent value each, then sends a "-123" magic number to indicate the end of the * conversation. - * + * * @author Christian Kohlschütter * @see SimpleTestClient */ +@SuppressWarnings({"CatchAndPrintStackTrace" /* errorprone */, "PMD.CognitiveComplexity"}) public final class SimpleTestServer { private static final int MAX_NUMBER = 5; @@ -56,18 +57,18 @@ public static void main(String[] args) throws IOException { try (AFUNIXServerSocket server = AFUNIXServerSocket.newInstance()) { /* * Uncomment the code below to change the bind behavior: - * + * * By default ("reuseAddress" is true), attempting to bind while another server is running on * the same address will cause the first server to terminate, and the new server will take * over the address. Depending on the operating system, this may involve connecting to the * first server in order to "wake up" the accept call. - * + * * In this demo code, we use AFSocket.getConnectionStatus to see if the accepted connection is * alive by sending - * + * * When "reuseAddress" is false, attempting to bind while another server is running won't * disrupt the first connection. The second bind will throw a SocketException instead. - * + * * NOTE: "reuseAddress=true" may not yet be supported on certain operating systems, such as * IBM i and z/OS. On these platforms, the behavior is as if "reuseAddress=false". Please * reach out by filing an issue on https://github.com/kohlschutter/junixsocket/issues if this diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClient.java index 7faf9b515..7b4b6b2b6 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A demo program to configure and run several {@link AFSocket} client demos from the command line. - * + * * @author Christian Kohlschütter */ public final class DemoClient { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClientBase.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClientBase.java index 886406806..07c86f2f4 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClientBase.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/DemoClientBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * An {@link AFUNIXSocket} client that's just good for demo purposes. - * + * * @author Christian Kohlschütter */ abstract class DemoClientBase { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadClient.java index b5886ba52..6b9e6db37 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadFileHandleClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadFileHandleClient.java index a4a113adb..951e93bbb 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadFileHandleClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadFileHandleClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadWriteClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadWriteClient.java index cb71c581f..eb829c382 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadWriteClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/client/ReadWriteClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ /** * A simple bidirectional Unix socket client that reads from/writes to stdin/stdout. */ +@SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */) public final class ReadWriteClient extends DemoClientBase { @Override protected void handleSocket(Socket socket) throws IOException { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/jdbc/PostgresDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/jdbc/PostgresDemo.java index da854ab74..4f7fe3536 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/jdbc/PostgresDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/jdbc/PostgresDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ /** * Demonstrates how to connect to a local PostgreSQL server via unix sockets. - * + * * @author Christian Kohlschuetter * @see AFUNIXSocketFactory */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/MinaTimeServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/MinaTimeServer.java new file mode 100644 index 000000000..abdc69ca8 --- /dev/null +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/MinaTimeServer.java @@ -0,0 +1,60 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.demo.mina; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.mina.core.service.IoAcceptor; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.textline.TextLineCodecFactory; +import org.apache.mina.filter.logging.LoggingFilter; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.newsclub.net.unix.AFUNIXSelectorProvider; +import org.newsclub.net.unix.AFUNIXSocketAddress; + +/** + * Apache Mina-based Time server, modified to use junixsocket. + * + * Based on example code from + * + * Apache Mina user guide, chapter 2.2 — Sample TCP Server + */ +public class MinaTimeServer { + public static void main(String[] args) throws IOException { + int processorCount = Runtime.getRuntime().availableProcessors() + 1; + + // IoAcceptor acceptor = new NioSocketAcceptor(processorCount); // from original example code + IoAcceptor acceptor = new NioSocketAcceptor(processorCount, AFUNIXSelectorProvider.provider()); + // IoAcceptor acceptor = new NioSocketAcceptor(processorCount, + // AFTIPCSelectorProvider.provider()); + + acceptor.getFilterChain().addLast("logger", new LoggingFilter()); + acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory( + StandardCharsets.UTF_8))); + acceptor.setHandler(new TimeServerHandler()); + acceptor.getSessionConfig().setReadBufferSize(2048); + acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); + + // acceptor.bind( new InetSocketAddress(PORT) ); // from original example code + acceptor.bind(AFUNIXSocketAddress.of(new File("/tmp/minatime"))); + // acceptor.bind(AFTIPCSocketAddress.ofService(Scope.SCOPE_CLUSTER, 128, 1)); + } +} \ No newline at end of file diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/TimeServerHandler.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/TimeServerHandler.java new file mode 100644 index 000000000..5161e7249 --- /dev/null +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/mina/TimeServerHandler.java @@ -0,0 +1,55 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.demo.mina; + +import java.util.Date; + +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; + +/** + * Time server handler. + * + * Based on example code from + * + * Apache Mina user guide, chapter 2.2 — Sample TCP Server + */ +class TimeServerHandler extends IoHandlerAdapter { + @Override + public void exceptionCaught(IoSession session, Throwable cause) throws Exception { + cause.printStackTrace(); + } + + @Override + public void messageReceived(IoSession session, Object message) throws Exception { + String str = message.toString(); + if ("quit".equalsIgnoreCase(str.trim())) { + session.closeNow(); + return; + } + Date date = new Date(); + session.write(date.toString()); + System.out.println("Message written..."); + } + + @Override + public void sessionIdle(IoSession session, IdleStatus status) throws Exception { + System.out.println("IDLE " + session.getIdleCount(status)); + } +} diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/nanohttpd/NanoHttpdServerDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/nanohttpd/NanoHttpdServerDemo.java index 133953cdb..fd4bf162b 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/nanohttpd/NanoHttpdServerDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/nanohttpd/NanoHttpdServerDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,9 +32,9 @@ /** * Creates a {@link NanoHTTPD} server, bound to {@code /tmp/junixsocket-http-server.sock}. - * + * * Http requests on that socket should return "Hello world from <hostname>". - * + * * @author Christian Kohlschütter * @see OkHttpClientDemo */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServer.java new file mode 100644 index 000000000..0346b07fa --- /dev/null +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServer.java @@ -0,0 +1,92 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.demo.netty; + +import java.io.File; +import java.nio.channels.spi.SelectorProvider; +import java.util.concurrent.Executor; + +import org.newsclub.net.unix.AFSocketAddress; +import org.newsclub.net.unix.AFUNIXSelectorProvider; +import org.newsclub.net.unix.AFUNIXSocketAddress; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +/** + * Echos any incoming data. + *

+ * Based on example code from Netty user + * guide for 4.x + */ +public class EchoServer { + private final AFSocketAddress addr; + + public EchoServer(AFSocketAddress addr) { + this.addr = addr; + } + + public void run() throws Exception { + SelectorProvider provider = AFUNIXSelectorProvider.provider(); + // SelectorProvider provider = AFTIPCSelectorProvider.provider(); + + // We need to specify our custom selector provider here (1), as well as in (3) + EventLoopGroup bossGroup = new NioEventLoopGroup(0, (Executor) null, provider); // (1) + EventLoopGroup workerGroup = new NioEventLoopGroup(0, (Executor) null, provider); // (1) + try { + ServerBootstrap b = new ServerBootstrap(); // (2) + b.group(bossGroup, workerGroup) // + .channelFactory(() -> new NioServerSocketChannel(provider)) // (3) + .childHandler(new ChannelInitializer() { // (4) + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast(new EchoServerHandler()); + } + }) // + .option(ChannelOption.SO_BACKLOG, 128) // (5) + .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) + + // Bind and start to accept incoming connections. + ChannelFuture f = b.bind(addr).sync(); // (7) + + // Wait until the server socket is closed. + // In this example, this does not happen, but you can do that to gracefully + // shut down your server. + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public static void main(String[] args) throws Exception { + File path = new File("/tmp/nettyecho"); + if (args.length > 0) { + path = new File(args[0]); + } + + new EchoServer(AFUNIXSocketAddress.of(path)).run(); + // new EchoServer(AFTIPCSocketAddress.ofService(Scope.SCOPE_CLUSTER, 128, 3)).run(); + } +} \ No newline at end of file diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServerHandler.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServerHandler.java new file mode 100644 index 000000000..df64ed3ee --- /dev/null +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/netty/EchoServerHandler.java @@ -0,0 +1,43 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.demo.netty; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * Handles a server-side channel. + *

+ * Based on example code from Netty user + * guide for 4.x + */ +class EchoServerHandler extends ChannelInboundHandlerAdapter { // (1) + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + ctx.write(msg); // (1) + ctx.flush(); // (2) + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4) + // Close the connection when an exception is raised. + cause.printStackTrace(); + ctx.close(); + } +} \ No newline at end of file diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientDemo.java index 4d4086b1d..102c17284 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,9 +38,9 @@ /** * Connects to {@code /tmp/junixsocket-http-server.sock} and performs an http request over that * socket, using the OkHttp HTTP client library. - * + * * If that socket is bound by {@link NanoHttpdServerDemo}, the expected output is "Hello world". - * + * * @author Christian Kohlschütter * @see NanoHttpdServerDemo */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientTIPCDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientTIPCDemo.java index 1e1f9d8d6..5fca3b868 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientTIPCDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/okhttp/OkHttpClientTIPCDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,13 +39,14 @@ /** * Connects to TIPC service 8080.1 and performs an HTTP request over that socket, using the * OkHttp HTTP client library. - * + * * If that socket is bound by {@link NanoHttpdServerDemo}, the expected output is "Hello world from * <hostname>" (start {@link NanoHttpdServerDemo} with {@code --url tipc://8080.1}. - * + * * @author Christian Kohlschütter * @see NanoHttpdServerDemo */ +@SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */) public class OkHttpClientTIPCDemo { public static void main(String[] args) throws IOException, InterruptedException { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/HelloWorldImpl.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/HelloWorldImpl.java index 6e3d12d3c..03b7a78b3 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/HelloWorldImpl.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/HelloWorldImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ /** * The implementation of the very simple {@link HelloWorld} service. - * + * * @author Christian Kohlschütter */ public class HelloWorldImpl implements HelloWorld { @@ -37,7 +37,7 @@ public class HelloWorldImpl implements HelloWorld { /** * Creates a new {@link HelloWorld} implementation. - * + * * @param naming The naming instance to use. */ @SuppressFBWarnings("EI_EXPOSE_REP") diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClient.java index aef5a3a6a..abc790c85 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * A simple RMI client. Locates the RMI registry via AF_UNIX sockets and calls * {@link HelloWorld#hello()}. - * + * * @author Christian Kohlschütter */ public final class SimpleRMIClient { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClientActingAsServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClientActingAsServer.java index 082856410..6e1542081 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClientActingAsServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIClientActingAsServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,9 +30,10 @@ /** * A simple RMI client. Locates the RMI registry via AF_UNIX sockets and calls * {@link HelloWorld#hello()}. - * + * * @author Christian Kohlschütter */ +@SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */) public final class SimpleRMIClientActingAsServer { public static void main(String[] args) throws IOException, NotBoundException { AFUNIXNaming naming = AFUNIXNaming.getInstance(); @@ -57,16 +58,16 @@ public static void main(String[] args) throws IOException, NotBoundException { System.out.println("Calling HelloWorld..."); System.out.println(obj.hello() + " " + obj.world() + "!"); - /** + /* * Uncommenting the line below keeps this instance running. - * + * * Try it and run SimpleRMIClient to see the difference. */ naming.unexportAndUnbind("world", world); - /** + /* * Also try to remotely shut down the registry. - * + * * This will not succeed if the server set {@code naming.setRemoteShutdownAllowed(false)}. See * {@link SimpleRMIServer} */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIServer.java index c7e73c41f..fea30e84b 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/SimpleRMIServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * A very simple RMI server. Provides a registry and the implementation of the {@link HelloWorld} * service. - * + * * @author Christian Kohlschütter */ public final class SimpleRMIServer { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/WorldImpl.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/WorldImpl.java index 1c100b131..170356e83 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/WorldImpl.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/WorldImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * The implementation of the very simple {@link World} service. - * + * * @author Christian Kohlschütter */ public class WorldImpl implements World { @@ -31,7 +31,7 @@ public class WorldImpl implements World { /** * Creates a new {@link World} instance. - * + * * @param text The text to return upon calling {@link #world()}. */ public WorldImpl(String text) { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamClient.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamClient.java index 991def136..33c216c18 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamClient.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamClient.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ /** * Demonstrates how to read files via FileDescriptors that are exchanged via RMI. - * + * * @author Christian Kohlschütter * @see StreamServer */ @@ -42,7 +42,7 @@ private StreamClient() { /** * {@link StreamClient} command-line tool. - * + * * @param args Command-line arguments. * @throws IOException on error. * @throws NotBoundException if the server cannot be reached. diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServer.java index b432a7ca9..1e94fe74f 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,14 +24,14 @@ /** * Demonstrates how to read/write files via FileDescriptors that are exchanged via RMI. - * + * * This allows reading/writing from and to files that are otherwise not even accessible by the user. * For example, starting the {@link StreamServer} as root and the {@link StreamClient} as a * non-privileged user will allow the non-privileged user to read files only accessible to root. - * + * * NOTE: For obvious security reasons, running this server without modification is not advised for * anything other than demo purposes. - * + * * @author Christian Kohlschütter * @see StreamClient */ @@ -42,7 +42,7 @@ private StreamServer() { /** * {@link StreamServer} command-line tool. - * + * * @param args Command-line arguments. * @throws IOException on error. * @throws AlreadyBoundException if there was already a server running. diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServiceImpl.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServiceImpl.java index 6ac3eeb55..a00fb9cc2 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServiceImpl.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/fd/StreamServiceImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ /** * An implementation of {@link StreamService}. - * + * * @author Christian Kohlschütter */ public class StreamServiceImpl implements StreamService, Closeable { @@ -43,7 +43,7 @@ public class StreamServiceImpl implements StreamService, Closeable { /** * Creates a new instance. - * + * * @param socketFactory The socket factory to use. * @throws RemoteException on error. */ @@ -82,7 +82,7 @@ public RemoteCloseableImpl openForWriting(File path) throws IO /** * Checks if the given path may be accessed for reading. - * + * * @param path The path to check. * @return {@code true} if permitted. */ @@ -92,7 +92,7 @@ protected boolean mayRead(File path) { /** * Checks if the given path may be accessed for writing. - * + * * @param path The path to check. * @return {@code true} if permitted. */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/HelloWorld.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/HelloWorld.java index bd004d294..1848f77e4 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/HelloWorld.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/HelloWorld.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,13 @@ /** * A very simple "hello world" service. - * + * * @author Christian Kohlschütter */ public interface HelloWorld extends Remote { /** * Returns "Hello". - * + * * @return "Hello" * @throws IOException if the operation fails. */ @@ -36,7 +36,7 @@ public interface HelloWorld extends Remote { /** * Returns "World" (or something else?). - * + * * @return "World" (usually) * @throws IOException if the operation fails. */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/StreamService.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/StreamService.java index 5c56cf14b..90c01ae67 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/StreamService.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/StreamService.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,14 +28,14 @@ /** * The {@link StreamServer}'s RMI service. - * + * * @author Christian Kohlschütter * @see StreamServer */ public interface StreamService extends Remote { /** * Opens the given file for reading. - * + * * @param path The file to open. * @return A remote instance for the file. * @throws IOException on error. @@ -44,7 +44,7 @@ public interface StreamService extends Remote { /** * Opens the given file for writing. - * + * * @param path The file to open. * @return A remote instance for the file. * @throws IOException on error. diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/World.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/World.java index d8f341a0c..2cec6cd21 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/World.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/rmi/services/World.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,14 +22,14 @@ /** * A very simple "world" service. - * + * * @author Christian Kohlschütter * @see HelloWorld */ public interface World extends Remote { /** * Returns "World" (or something else?). - * + * * @return "World" (usually) * @throws RemoteException if the operation fails. */ diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ChargenServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ChargenServer.java index 40338db84..ec8423b84 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ChargenServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ChargenServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ /** * A multi-threaded unix socket server that implements a TCP-style character generator compliant * with RFC864. - * + * * @author Christian Kohlschütter */ public final class ChargenServer extends DemoServerBase { @@ -38,7 +38,7 @@ public final class ChargenServer extends DemoServerBase { /** * Defines a TCP-style character generator compliant with RFC864. - * + * * @see RFC864 */ private interface Chargen { @@ -83,9 +83,9 @@ private synchronized Chargen getChargen(Socket socket) throws SocketException { /** * A simple chargen implementation. - * + * * Even though this looks straightforward, it's not the fastest implementation. - * + * * @see FastChargen */ private static final class SimpleChargen implements Chargen { @@ -113,7 +113,7 @@ public void write(Socket socket) throws IOException { /** * A fast chargen implementation, using a pre-built data buffer that is just large enough to * always send a full array of bytes matching the socket's send buffer capacity. - * + * * @see SimpleChargen */ private static final class FastChargen implements Chargen { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/DemoServerBase.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/DemoServerBase.java index c52f3d14c..da79ef7e9 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/DemoServerBase.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/DemoServerBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,9 +30,10 @@ /** * An {@link SocketServer} that's just good for demo purposes. - * + * * @author Christian Kohlschütter */ +@SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */) abstract class DemoServerBase extends SocketServer { public DemoServerBase(SocketAddress listenAddress) { super(listenAddress); diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/EchoServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/EchoServer.java index aac916a28..28bd499fc 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/EchoServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/EchoServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A multi-threaded unix socket server that simply echoes all input, byte per byte. - * + * * @author Christian Kohlschütter */ public final class EchoServer extends DemoServerBase { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/NullServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/NullServer.java index ac600b8a8..0ce8a9ef3 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/NullServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/NullServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A multi-threaded unix socket server that simply reads all input, byte per byte, not doing * anything else with it. - * + * * @author Christian Kohlschütter */ public final class NullServer extends DemoServerBase { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SendFileHandleServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SendFileHandleServer.java index 281b52578..afca4da81 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SendFileHandleServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SendFileHandleServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,9 +30,10 @@ /** * A multi-threaded unix socket server that simply reads all input, byte per byte, not doing * anything else with it. - * + * * @author Christian Kohlschütter */ +@SuppressWarnings("CatchAndPrintStackTrace" /* errorprone */) public final class SendFileHandleServer extends DemoServerBase { private final File file; @@ -49,7 +50,7 @@ protected void doServeSocket(Socket socket) throws IOException { doServeSocket((AFUNIXSocket) socket); } - protected void doServeSocket(AFUNIXSocket socket) throws IOException { + private void doServeSocket(AFUNIXSocket socket) throws IOException { try (InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); FileInputStream fin = new FileInputStream(file)) { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SocketServerDemo.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SocketServerDemo.java index a23c742e6..fddf11534 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SocketServerDemo.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/SocketServerDemo.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * A demo program to configure and run several {@link SocketServer} demos from the command line. - * + * * @author Christian Kohlschütter */ public final class SocketServerDemo { diff --git a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ZeroServer.java b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ZeroServer.java index 752f4c55f..32c38a861 100644 --- a/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ZeroServer.java +++ b/junixsocket-demo/src/main/java/org/newsclub/net/unix/demo/server/ZeroServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A multi-threaded unix socket server that simply writes null-bytes, and does not attempt to read * anything. - * + * * @author Christian Kohlschütter */ // CPD-OFF diff --git a/junixsocket-dist/pom.xml b/junixsocket-dist/pom.xml index 941f9937a..185ad6371 100644 --- a/junixsocket-dist/pom.xml +++ b/junixsocket-dist/pom.xml @@ -8,7 +8,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-dist @@ -66,7 +66,6 @@ org.apache.maven.plugins maven-assembly-plugin - 3.1.1 build-dist diff --git a/junixsocket-jetty-12/pom.xml b/junixsocket-jetty-12/pom.xml new file mode 100644 index 000000000..1020e2cc1 --- /dev/null +++ b/junixsocket-jetty-12/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + junixsocket-jetty-12 + jar + + com.kohlschutter.junixsocket + junixsocket + 2.7.2 + ../pom.xml + + junixsocket-jetty + + ${project.parent.basedir} + 12.0.1 + + + junixsocket for Jetty 12 (currently test-only; use + junixsocket-jetty as a dependency) + + + + com.kohlschutter.junixsocket + junixsocket-core + ${project.version} + pom + + + + com.kohlschutter.junixsocket + junixsocket-jetty + ${project.version} + test + + + org.eclipse.jetty + jetty-server + + + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + provided + + + + org.slf4j + slf4j-simple + 2.0.9 + test + + + com.squareup.okhttp3 + okhttp + 4.11.0 + test + + + com.kohlschutter.junixsocket + junixsocket-common + ${project.version} + tests + test + + + org.eclipse.jetty + jetty-client + ${jetty.version} + test + + + + org.hamcrest + hamcrest + 2.2 + test + + + diff --git a/junixsocket-jetty-12/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java b/junixsocket-jetty-12/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java new file mode 100644 index 000000000..84804d5c0 --- /dev/null +++ b/junixsocket-jetty-12/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java @@ -0,0 +1,356 @@ +/* + * junixsocket + * + * Copyright 2009-2022 Christian Kohlschütter + * + * Licensed 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. + */ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.unixdomain.server; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1; +import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2; +import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.ConnectionMetaData; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.ProxyConnectionFactory; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.component.LifeCycle; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFSocketAddress; +import org.newsclub.net.unix.AFUNIXSocketAddress; +import org.newsclub.net.unix.jetty.AFSocketClientConnector; +import org.newsclub.net.unix.jetty.AFSocketServerConnector; + +public class UnixDomainTest { + private static final Class unixDomainSocketAddressClass = probe(); + + private static Class probe() { + try { + return ClassLoader.getPlatformClassLoader().loadClass("java.net.UnixDomainSocketAddress"); + } catch (Throwable x) { + return null; + } + } + + private ConnectionFactory[] factories = new ConnectionFactory[] {new HttpConnectionFactory()}; + private Server server; + private Path unixDomainPath; + + @BeforeAll + public static void setUp() { + System.setProperty("org.slf4j.simpleLogger.log.org.eclipse.jetty", "error"); + } + + private void start(Handler handler) throws Exception { + server = new Server(); + // UnixDomainServerConnector connector = new UnixDomainServerConnector(server, factories); + AFSocketServerConnector connector = new AFSocketServerConnector(server, factories); + String dir = System.getProperty("jetty.unixdomain.dir", System.getProperty("java.io.tmpdir")); + assertNotNull(dir); + File dirFile = new File(dir); + dirFile.mkdirs(); + unixDomainPath = Files.createTempFile(dirFile.toPath(), "unix_", ".sock"); + assertTrue(unixDomainPath.toAbsolutePath().toString().length() < 108, + "Unix-Domain path too long"); + Files.delete(unixDomainPath); + // connector.setUnixDomainPath(unixDomainPath); + connector.setListenSocketAddress(AFUNIXSocketAddress.of(unixDomainPath)); + server.addConnector(connector); + server.setHandler(handler); + server.start(); + } + + @AfterEach + public void dispose() { + LifeCycle.stop(server); + } + + @Test + public void testHTTPOverUnixDomain() throws Exception { + String uri = "http://localhost:1234/path"; + start(new Handler.Abstract() { + + @Override + public boolean handle(Request request, Response response, Callback callback) + throws Exception { + // Verify the URI is preserved. + assertEquals(uri, request.getHttpURI().toString()); + + // Verify the SocketAddresses. + ConnectionMetaData connectionMetaData = request.getConnectionMetaData(); + SocketAddress local = connectionMetaData.getLocalSocketAddress(); + SocketAddress remote = connectionMetaData.getRemoteSocketAddress(); + + assertThat(local, Matchers.instanceOf(AFSocketAddress.class)); + if (remote != null) { + // remote should be null if not connected + assertThat(remote, Matchers.instanceOf(AFSocketAddress.class)); + } + + assertDoesNotThrow(connectionMetaData::toString); + + response.write(true, ByteBuffer.allocate(0), new Callback() {}); + + return true; + } + }); + + // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress + .of(unixDomainPath)); + HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); + httpClient.start(); + try { + ContentResponse response = httpClient.newRequest(uri).timeout(5, TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response.getStatus()); + } finally { + httpClient.stop(); + } + } + + @Test + public void testHTTPOverUnixDomainWithHTTPProxy() throws Exception { + int fakeProxyPort = 4567; + int fakeServerPort = 5678; + start(new Handler.Abstract() { + @Override + public boolean handle(Request request, Response response, Callback callback) + throws Exception { + // Proxied requests must have an absolute URI. + HttpURI uri = request.getHttpURI(); + assertNotNull(uri.getScheme()); + assertEquals(fakeServerPort, uri.getPort()); + + response.write(true, ByteBuffer.allocate(0), new Callback() {}); + + return true; + } + }); + + // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress + .of(unixDomainPath)); + + HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); + httpClient.getProxyConfiguration().addProxy(new HttpProxy("localhost", fakeProxyPort)); + httpClient.start(); + try { + ContentResponse response = httpClient.newRequest("localhost", fakeServerPort).timeout(5, + TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response.getStatus()); + } finally { + httpClient.stop(); + } + } + + @Test + public void testHTTPOverUnixDomainWithProxyProtocol() throws Exception { + String srcAddr = "/proxySrcAddr"; + String dstAddr = "/proxyDstAddr"; + factories = new ConnectionFactory[] {new ProxyConnectionFactory(), new HttpConnectionFactory()}; + start(new Handler.Abstract() { + @Override + public boolean handle(Request request, Response response, Callback callback) + throws Exception { + ConnectionMetaData connectionMetaData = request.getConnectionMetaData(); + + String target = request.getHttpURI().getPath(); + + if ("/v1".equals(target)) { + // As PROXYv1 does not support UNIX, the wrapped EndPoint data is used. + Path localPath = toUnixDomainPath(connectionMetaData.getLocalSocketAddress()); + assertThat(localPath, Matchers.equalTo(unixDomainPath)); + } else if ("/v2".equals(target)) { + SocketAddress localSocketAddress = connectionMetaData.getLocalSocketAddress(); + if (localSocketAddress != null) { + assertThat(toUnixDomainPath(localSocketAddress).toString(), Matchers.equalTo(separators( + dstAddr))); + } + SocketAddress remoteSocketAddress = connectionMetaData.getRemoteSocketAddress(); + if (remoteSocketAddress != null) { + assertThat(toUnixDomainPath(remoteSocketAddress).toString(), Matchers.equalTo( + separators(srcAddr))); + } + } else { + Assertions.fail("Invalid PROXY protocol version " + target); + } + + response.write(true, ByteBuffer.allocate(0), new Callback() {}); + + return true; + } + }); + + // Java 11+ portable way to implement SocketChannelWithAddress.Factory. + // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress + .of(unixDomainPath)); + + HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); + httpClient.start(); + try { + // Try PROXYv1 with the PROXY information retrieved from the EndPoint. + // PROXYv1 does not support the UNIX family. + ContentResponse response1 = httpClient.newRequest("localhost", 0).path("/v1").tag( + new V1.Tag()).timeout(5, TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response1.getStatus()); + + // Try PROXYv2 with explicit PROXY information. + V2.Tag tag = new V2.Tag(V2.Tag.Command.PROXY, V2.Tag.Family.UNIX, V2.Tag.Protocol.STREAM, + srcAddr, 0, dstAddr, 0, null); + ContentResponse response2 = httpClient.newRequest("localhost", 0).path("/v2").tag(tag) + .timeout(5, TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response2.getStatus()); + } finally { + httpClient.stop(); + } + } + + @Test + public void testInvalidUnixDomainPath() { + server = new Server(); + // UnixDomainServerConnector connector = new UnixDomainServerConnector(server, factories); + AFSocketServerConnector connector = new AFSocketServerConnector(server, factories); + + // connector.setUnixDomainPath(new File("/does/not/exist").toPath()); + try { + connector.setListenSocketAddress(AFUNIXSocketAddress.of(new File("/does/not/exist") + .toPath())); + } catch (SocketException e) { + throw new IllegalStateException(e); + } + + server.addConnector(connector); + assertThrows(IOException.class, () -> server.start()); + } + + private static Path toUnixDomainPath(SocketAddress address) { + Objects.requireNonNull(address, "address"); + + if (address instanceof AFUNIXSocketAddress) { + return new File(((AFUNIXSocketAddress) address).getPath()).toPath(); + } else if (unixDomainSocketAddressClass != null) { + try { + return (Path) unixDomainSocketAddressClass.getMethod("getPath").invoke(address); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException + | SecurityException e) { + Assertions.fail(e); + throw new AssertionError(e); + } + } else { + throw new IllegalStateException("Unsupported socket address class " + address.getClass() + + ": " + address); + } + } + + public static String separators(String path) { + StringBuilder ret = new StringBuilder(); + for (int i = 0, n = path.length(); i < n; i++) { + char c = path.charAt(i); + if ((c == '/') || (c == '\\')) { + ret.append(File.separatorChar); + } else { + ret.append(c); + } + } + return ret.toString(); + } + + @Test + public void testLargeBody() throws Exception { + String uri = "http://localhost:1234/path"; + + byte[] payload = new byte[512 * 1024]; // 512k + new Random().nextBytes(payload); + + start(new Handler.Abstract() { + @Override + public boolean handle(Request request, Response response, Callback callback) + throws Exception { + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "application/octet-stream"); + + response.write(true, ByteBuffer.wrap(payload), new Callback() {}); + + return true; + } + }); + + // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress + .of(unixDomainPath)); + HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); + httpClient.start(); + try { + ContentResponse response = httpClient.newRequest(uri).timeout(5, TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response.getStatus()); + byte[] data = response.getContent(); + assertArrayEquals(payload, data); + } finally { + httpClient.stop(); + } + } +} \ No newline at end of file diff --git a/junixsocket-jetty-12/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java b/junixsocket-jetty-12/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java new file mode 100644 index 000000000..30cde6070 --- /dev/null +++ b/junixsocket-jetty-12/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java @@ -0,0 +1,122 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ +package org.newsclub.net.unix.jetty; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.time.Duration; + +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFSocketAddress; +import org.newsclub.net.unix.AFSocketFactory; +import org.newsclub.net.unix.domain.AFUNIXAddressSpecifics; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class AFSocketServerConnectorTest { + @BeforeAll + public static void setUp() { + System.setProperty("org.slf4j.simpleLogger.log.org.eclipse.jetty.server", "error"); + } + + @Test + public void testServerAFUNIX() throws Exception { + AFSocketAddress addr = (AFSocketAddress) AFUNIXAddressSpecifics.INSTANCE + .newTempAddressForDatagram(); + Server server1 = newServer(addr); + checkConnection(addr); + assertTrue(server1.isRunning()); + + Server server2 = newServer(addr); + + // isDeleteOnClose is smart enough to not delete the wrong socket + assertTrue(addr.getFile().exists()); + + server1.join(); + assertFalse(server1.isRunning()); + server2.stop(); + server2.join(); + assertFalse(addr.getFile().exists()); // isDeleteOnClose=true by default + } + + private void checkConnection(AFSocketAddress addr) throws Exception { + OkHttpClient.Builder builder = new OkHttpClient.Builder() // + .socketFactory(new AFSocketFactory.FixedAddressSocketFactory(addr)) // + .callTimeout(Duration.ofMinutes(1)); + + OkHttpClient client = builder.build(); + + Request request = new Request.Builder().url("http://localhost/").build(); + try (Response response = client.newCall(request).execute()) { + assertEquals(404, response.code()); + assertNotNull(response.header("Server")); + + ResponseBody body = response.body(); + assertNotNull(body); + + BufferedReader br = new BufferedReader(body.charStream()); + int l = 0; + while (br.readLine() != null) { + l++; + } + assertNotEquals(0, l); + } + } + + private static Server newServer(AFSocketAddress addr) throws Exception { + Server server = new Server(); + + // below code is based upon + // https://www.eclipse.org/jetty/documentation/jetty-10/programming-guide/index.html + + // The number of acceptor threads. + int acceptors = 1; + + // The number of selectors. + int selectors = 1; + + // Create a ServerConnector instance. + AFSocketServerConnector connector = new AFSocketServerConnector(server, acceptors, selectors, + new HttpConnectionFactory()); + + // The AFSocketAddress to listen to. + connector.setListenSocketAddress(addr); + + // The accept queue size. + connector.setAcceptQueueSize(128); + + // Try to automatically stop server if another instance reuses our address + connector.setMayStopServer(true); + + server.addConnector(connector); + server.start(); + + return server; + } +} diff --git a/junixsocket-jetty/pom.xml b/junixsocket-jetty/pom.xml index be05bc148..0253e1b3f 100644 --- a/junixsocket-jetty/pom.xml +++ b/junixsocket-jetty/pom.xml @@ -1,22 +1,39 @@ - + 4.0.0 junixsocket-jetty jar com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-jetty ${project.parent.basedir} - 11.0.12 + 11.0.16 junixsocket for Jetty + + + + + org.codehaus.mojo + versions-maven-plugin + + + false + + + + + + com.kohlschutter.junixsocket @@ -34,13 +51,13 @@ org.slf4j slf4j-simple - 2.0.3 + 2.0.9 test com.squareup.okhttp3 okhttp - 4.10.0 + 4.11.0 test @@ -56,6 +73,14 @@ ${jetty.version} test + org.hamcrest hamcrest @@ -63,4 +88,4 @@ test - \ No newline at end of file + diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java index 5dee06881..6e7b26b73 100644 --- a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,11 +32,11 @@ /** * A {@link Connector} implementation for junixsocket server socket channels (Unix domains etc.) - * + * * Based upon jetty's ClientConnector. - * + * * This implementation should work with jetty version 10.0.8 or newer. - * + * * @author Christian Kohlschütter */ public final class AFSocketClientConnector extends ClientConnector { @@ -50,7 +50,7 @@ private AFSocketClientConnector(AFSocketAddress addr) { /** * Returns a new {@link ClientConnector} configured to use given {@link AFSocketAddress} for * communication with junixsocket sockets. - * + * * @param addr The socket address. * @return The client connector. */ diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketServerConnector.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketServerConnector.java index 7991b8186..ac37bb477 100644 --- a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketServerConnector.java +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketServerConnector.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,11 +50,9 @@ import java.util.EventListener; import java.util.Locale; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -81,12 +79,13 @@ /** * A {@link Connector} implementation for junixsocket server socket channels (Unix domains etc.) - * + * * Based upon jetty's UnixDomainServerConnector. - * + * * This implementation should work with jetty version 9.4.12 or newer. */ @ManagedObject +@SuppressWarnings("PMD.CouplingBetweenObjects") public class AFSocketServerConnector extends AbstractConnector { private static final Logger LOG = LoggerFactory.getLogger(AbstractConnector.class); @@ -100,12 +99,13 @@ public class AFSocketServerConnector extends AbstractConnector { private int acceptedSendBufferSize; private boolean mayStopServer = false; + private boolean mayStopServerForce = false; private final Class selectorManagerListenerClass; private final Server server; /** * Creates a new {@link AFSocketServerConnector}. - * + * * @param server The server this connector will be added to. Must not be null. * @param factories The Connection Factories to use. */ @@ -115,7 +115,7 @@ public AFSocketServerConnector(Server server, ConnectionFactory... factories) { /** * Creates a new {@link AFSocketServerConnector}. - * + * * @param server The server this connector will be added to. Must not be null. * @param acceptors the number of acceptor threads to use, or -1 for a default value. If 0, then * no acceptor threads will be launched and some other mechanism will need to be used to @@ -130,13 +130,13 @@ public AFSocketServerConnector(Server server, int acceptors, int selectors, /** * Creates a new {@link AFSocketServerConnector}. - * + * * @param server The server this connector will be added to. Must not be null. * @param executor An executor for this connector or null to use the servers executor * @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a * server bean or if none set, then a new {@link ScheduledExecutorScheduler} instance. * @param pool A buffer pool for this connector or null to either a {@link ByteBufferPool} set as - * a server bean or none set, the new {@link ArrayByteBufferPool} instance. + * a server bean or none set, the new {code ArrayByteBufferPool} instance. * @param acceptors the number of acceptor threads to use, or -1 for a default value. If 0, then * no acceptor threads will be launched and some other mechanism will need to be used to * accept new connections. @@ -144,6 +144,7 @@ public AFSocketServerConnector(Server server, int acceptors, int selectors, * @param factories The Connection Factories to use. */ @SuppressFBWarnings("EI_EXPOSE_REP2") + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") public AFSocketServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories) { super(server, executor, scheduler, pool, acceptors, factories.length > 0 ? factories @@ -172,14 +173,15 @@ private SelectorManager newSelectorManager(Executor executor, Scheduler schedule /** * Returns the Unix-Domain path this connector listens to. - * + * * Added for compatibility with jetty's {@code UnixDomainServerConnector}. - * + * * @return The Unix-Domain path this connector listens to. * @deprecated Use {@link #getListenSocketAddress()} instead. * @see #getListenSocketAddress() */ @ManagedAttribute("The Unix-Domain path this connector listens to") + @Deprecated public Path getUnixDomainPath() { if (listenSocketAddress instanceof AFUNIXSocketAddress) { AFUNIXSocketAddress addr = (AFUNIXSocketAddress) listenSocketAddress; @@ -196,13 +198,14 @@ public Path getUnixDomainPath() { /** * Sets the Unix-Domain path this connector listens to. - * + * * Added for compatibility with jetty's {@code UnixDomainServerConnector}. - * + * * @param unixDomainPath The path. * @deprecated Use {@link #setListenSocketAddress(AFSocketAddress)} instead. * @see #setListenSocketAddress(AFSocketAddress) */ + @Deprecated public void setUnixDomainPath(Path unixDomainPath) { try { this.listenSocketAddress = AFUNIXSocketAddress.of(unixDomainPath); @@ -213,7 +216,7 @@ public void setUnixDomainPath(Path unixDomainPath) { /** * Returns the socket address this connector listens to. - * + * * @return The socket address, or {@code null} if none set. */ @ManagedAttribute("The socket address this connector listens to") @@ -224,7 +227,7 @@ public AFSocketAddress getListenSocketAddress() { /** * Sets the socket address this connector listens to. - * + * * @param addr The socket address, or {@code null}. */ @SuppressFBWarnings("EI_EXPOSE_REP2") @@ -234,7 +237,7 @@ public void setListenSocketAddress(AFSocketAddress addr) { /** * Checks whether this connector uses a server channel inherited from the JVM. - * + * * @return {@code true} if so. */ @ManagedAttribute("Whether this connector uses a server channel inherited from the JVM") @@ -244,7 +247,7 @@ public boolean isInheritChannel() { /** * Sets whether this connector uses a server channel inherited from the JVM. - * + * * @param inheritChannel {@code true} if so. */ public void setInheritChannel(boolean inheritChannel) { @@ -253,7 +256,7 @@ public void setInheritChannel(boolean inheritChannel) { /** * Returns the accept queue size (backlog) for the server socket. - * + * * @return The backlog. */ @ManagedAttribute("The accept queue size (backlog) for the server socket") @@ -263,7 +266,7 @@ public int getAcceptQueueSize() { /** * Sets the accept queue size (backlog) for the server socket. - * + * * @param acceptQueueSize The backlog. */ public void setAcceptQueueSize(int acceptQueueSize) { @@ -272,7 +275,7 @@ public void setAcceptQueueSize(int acceptQueueSize) { /** * Returns the SO_RCVBUF size for accepted sockets. - * + * * @return The buffer size. */ @ManagedAttribute("The SO_RCVBUF option for accepted sockets") @@ -282,7 +285,7 @@ public int getAcceptedReceiveBufferSize() { /** * Sets the SO_RCVBUF size for accepted sockets. - * + * * @param acceptedReceiveBufferSize The buffer size. */ public void setAcceptedReceiveBufferSize(int acceptedReceiveBufferSize) { @@ -291,7 +294,7 @@ public void setAcceptedReceiveBufferSize(int acceptedReceiveBufferSize) { /** * Returns the SO_SNDBUF size for accepted sockets. - * + * * @return The buffer size. */ @ManagedAttribute("The SO_SNDBUF option for accepted sockets") @@ -301,7 +304,7 @@ public int getAcceptedSendBufferSize() { /** * Sets the SO_SNDBUF size for accepted sockets. - * + * * @param acceptedSendBufferSize The buffer size. */ public void setAcceptedSendBufferSize(int acceptedSendBufferSize) { @@ -342,45 +345,35 @@ protected void accept(int acceptorID) throws IOException { takenOver = !((AFServerSocketChannel) sc).isLocalSocketAddressValid(); } - if (takenOver) { - ExecutorService es = Executors.newSingleThreadExecutor(); - try { - LOG.warn("Another server has taken over our address"); - es.execute(() -> { - Connector[] connectors = server.getConnectors(); - - boolean shutdownServer; - if (connectors == null) { - shutdownServer = true; - } else { - shutdownServer = true; - for (Connector conn : connectors) { - if (conn != AFSocketServerConnector.this && conn.isRunning()) { // NOPMD.CompareObjectsWithEquals - shutdownServer = false; - break; - } - } - } - - if (shutdownServer && mayStopServer) { - LOG.warn("Server has no other connectors; shutting down: " + server); // NOPMD - - try { - server.stop(); - } catch (Exception e1) { - LOG.warn("Exception upon stopping " + server, e1); // NOPMD - } - } - }); - } finally { - es.shutdown(); - } + if (takenOver && isMayStopServer()) { + LOG.warn("Another server has taken over our address"); + ForkJoinPool.commonPool().execute(this::checkServerStop); } throw (ClosedByInterruptException) new ClosedByInterruptException().initCause(e); } } } + private void checkServerStop() { + Connector[] connectors = server.getConnectors(); + + if (connectors != null && !isMayStopServerForce()) { + for (Connector conn : connectors) { + if (conn != AFSocketServerConnector.this && conn.isRunning()) { // NOPMD.CompareObjectsWithEquals + return; // don't stop + } + } + } + + LOG.warn("Server has no other connectors; shutting down: " + server); // NOPMD + + try { + server.stop(); + } catch (Exception e1) { + LOG.warn("Exception upon stopping " + server, e1); // NOPMD + } + } + private void accepted(SocketChannel channel) throws IOException { channel.configureBlocking(false); configure(channel); @@ -390,7 +383,7 @@ private void accepted(SocketChannel channel) throws IOException { /** * Configures an incoming {@link SocketChannel}, setting socket options such as receive and send * buffer sizes. - * + * * @param channel The socket channel to configure. * @throws IOException on error. */ @@ -533,7 +526,7 @@ protected void endPointClosed(EndPoint endpoint) { /** * Checks if this connector may stop the server when it's no longer able to serve and no other * connectors are available. - * + * * @return {@code true} if so. */ @ManagedAttribute("Whether this connector may stop the server when it's no longer able to" @@ -545,10 +538,35 @@ public boolean isMayStopServer() { /** * Sets if this connector may stop the server when it's no longer able to serve and no other * connectors are available. - * + * * @param mayStopServer {@code true} if so. */ public void setMayStopServer(boolean mayStopServer) { this.mayStopServer = mayStopServer; } + + /** + * Checks if this connector may stop the server when it's no longer able to serve, even if other + * connectors are available. + * + * @return {@code true} if so. + */ + @ManagedAttribute("Whether this connector may stop the server when it's no longer able to" + + " serve, even if other connectors are available") + public boolean isMayStopServerForce() { + return mayStopServerForce; + } + + /** + * Sets if this connector may stop the server when it's no longer able to serve and no other + * connectors are available. + * + * @param b {@code true} if so (which then also implies {@code setMayStopServer(true)} + */ + public void setMayStopServerForce(boolean b) { + if (b) { + setMayStopServer(true); + } + this.mayStopServerForce = b; + } } diff --git a/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java b/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java index 78b2027e5..8a5dd61ee 100644 --- a/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java +++ b/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java @@ -31,6 +31,7 @@ package org.eclipse.jetty.unixdomain.server; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -45,6 +46,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; +import java.util.Random; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.HttpClient; @@ -75,6 +77,7 @@ import org.newsclub.net.unix.jetty.AFSocketClientConnector; import org.newsclub.net.unix.jetty.AFSocketServerConnector; +import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -193,7 +196,7 @@ public void handle(String target, Request jettyRequest, HttpServletRequest reque .of(unixDomainPath)); HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); - httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", fakeProxyPort)); + httpClient.getProxyConfiguration().addProxy(new HttpProxy("localhost", fakeProxyPort)); httpClient.start(); try { ContentResponse response = httpClient.newRequest("localhost", fakeServerPort).timeout(5, @@ -308,7 +311,8 @@ private static Path toUnixDomainPath(SocketAddress address) { public static String separators(String path) { StringBuilder ret = new StringBuilder(); - for (char c : path.toCharArray()) { + for (int i = 0, n = path.length(); i < n; i++) { + char c = path.charAt(i); if ((c == '/') || (c == '\\')) { ret.append(File.separatorChar); } else { @@ -317,4 +321,40 @@ public static String separators(String path) { } return ret.toString(); } + + @Test + public void testLargeBody() throws Exception { + String uri = "http://localhost:1234/path"; + + byte[] payload = new byte[512 * 1024]; // 512k + new Random().nextBytes(payload); + + start(new AbstractHandler() { + @Override + public void handle(String target, Request jettyRequest, HttpServletRequest request, + HttpServletResponse response) throws IOException { + jettyRequest.setHandled(true); + + response.setContentType(" application/octet-stream"); + try (ServletOutputStream out = response.getOutputStream()) { + out.write(payload); + } + } + }); + + // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress + .of(unixDomainPath)); + HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); + httpClient.start(); + try { + ContentResponse response = httpClient.newRequest(uri).timeout(5, TimeUnit.SECONDS).send(); + + assertEquals(HttpStatus.OK_200, response.getStatus()); + byte[] data = response.getContent(); + assertArrayEquals(payload, data); + } finally { + httpClient.stop(); + } + } } \ No newline at end of file diff --git a/junixsocket-jetty/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java b/junixsocket-jetty/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java index 7f281526f..30cde6070 100644 --- a/junixsocket-jetty/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java +++ b/junixsocket-jetty/src/test/java/org/newsclub/net/unix/jetty/AFSocketServerConnectorTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-mysql/pom.xml b/junixsocket-mysql/pom.xml index bacd7f4a3..485bb3520 100644 --- a/junixsocket-mysql/pom.xml +++ b/junixsocket-mysql/pom.xml @@ -6,7 +6,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-mysql @@ -23,9 +23,10 @@ ${project.version} - mysql - mysql-connector-java - 8.0.30 + + com.mysql + mysql-connector-j + 8.1.0 provided diff --git a/junixsocket-mysql/src/main/java/module-info.java b/junixsocket-mysql/src/main/java/module-info.java index 300b929d6..ed6b7ba1f 100644 --- a/junixsocket-mysql/src/main/java/module-info.java +++ b/junixsocket-mysql/src/main/java/module-info.java @@ -8,7 +8,8 @@ requires java.sql; requires java.base; - requires mysql.connector.java; + // requires mysql.connector.java; // until 8.0.30 + requires transitive mysql.connector.j; // from 8.0.31 requires static com.kohlschutter.annotations.compiletime; requires static org.eclipse.jdt.annotation; diff --git a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactory.java b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactory.java index 4693d062b..19a725c14 100644 --- a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactory.java +++ b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,13 +31,13 @@ /** * Connect to mysql databases (and compatibles) using UNIX domain sockets. - * + * * NOTE: This SocketFactory currently implements the "old" Connector/J SocketFactory. This may * change in the future. - * + * * For the time being, see AFUNIXDatabaseSocketFactoryCJ to forcibly use the new "CJ"-style * SocketFactory. - * + * * @see AFUNIXDatabaseSocketFactoryCJ */ @SuppressWarnings("deprecation") diff --git a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java index e378ccb41..4749e81ef 100644 --- a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java +++ b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ */ package org.newsclub.net.mysql; -import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.Socket; @@ -38,7 +37,6 @@ */ public class AFUNIXDatabaseSocketFactoryCJ implements SocketFactory { private AFUNIXSocket rawSocket; - private Socket sslSocket; /** * Creates a new instance. @@ -46,11 +44,11 @@ public class AFUNIXDatabaseSocketFactoryCJ implements SocketFactory { public AFUNIXDatabaseSocketFactoryCJ() { } - @SuppressWarnings({"unchecked", "exports"}) + @SuppressWarnings({"unchecked"}) @SuppressFBWarnings("EI_EXPOSE_REP") @Override - public T connect(String hostname, int portNumber, PropertySet props, - int loginTimeout) throws IOException { + public Socket connect(String hostname, int portNumber, PropertySet props, int loginTimeout) + throws IOException { // Adjust the path to your MySQL socket by setting the // "junixsocket.file" property // If no socket path is given, use the default: /tmp/mysql.sock @@ -64,17 +62,15 @@ public T connect(String hostname, int portNumber, Property final File socketFile = new File(sock); this.rawSocket = AFUNIXSocket.connectTo(AFUNIXSocketAddress.of(socketFile)); - this.sslSocket = rawSocket; - return (T) rawSocket; + return rawSocket; } - @SuppressWarnings({"unchecked", "exports"}) + @SuppressWarnings({"unchecked"}) @SuppressFBWarnings("EI_EXPOSE_REP") @Override - public T performTlsHandshake(SocketConnection socketConnection, - ServerSession serverSession) throws IOException { - this.sslSocket = ExportControlled.performTlsHandshake(this.rawSocket, socketConnection, + public Socket performTlsHandshake(SocketConnection socketConnection, ServerSession serverSession) + throws IOException { + return ExportControlled.performTlsHandshake(this.rawSocket, socketConnection, serverSession == null ? null : serverSession.getServerVersion(), null); - return (T) this.sslSocket; } } diff --git a/junixsocket-native-android/META-INF/MANIFEST.MF b/junixsocket-native-android/META-INF/MANIFEST.MF new file mode 100644 index 000000000..4b797f9d8 --- /dev/null +++ b/junixsocket-native-android/META-INF/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Created-By: Maven JAR Plugin 3.3.0 +Build-Jdk-Spec: 20 +Multi-Release: true + diff --git a/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/git.properties b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/git.properties new file mode 100644 index 000000000..c7e13a5b3 --- /dev/null +++ b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/git.properties @@ -0,0 +1,7 @@ +#Generated by Git-Commit-Id-Plugin +git.build.version=2.7.0-SNAPSHOT +git.commit.id.abbrev=edf13c8 +git.commit.id.describe=junixsocket-2.6.2-46-gedf13c8-dirty +git.commit.id.full=edf13c8c86092b6094ad7443c5a8cee2bec9aa96 +git.commit.time=2023-06-17T17\:30\:21+02\:00 +git.dirty=true diff --git a/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.properties b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.properties new file mode 100644 index 000000000..4d210ef64 --- /dev/null +++ b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.properties @@ -0,0 +1,3 @@ +artifactId=junixsocket-native-android +groupId=com.kohlschutter.junixsocket +version=2.7.0-SNAPSHOT diff --git a/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.xml b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.xml new file mode 100644 index 000000000..5bd5a6112 --- /dev/null +++ b/junixsocket-native-android/META-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-android/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + junixsocket-native-android + + jar + + + com.kohlschutter.junixsocket + junixsocket + 2.7.0-SNAPSHOT + ../pom.xml + + junixsocket-native-android + + ${project.parent.basedir} + aar + + + Native junixsocket libraries for Android + + + + com.kohlschutter.junixsocket + junixsocket-common + ${project.version} + + + com.kohlschutter.junixsocket + junixsocket-native + ${project.version} + aarch64-Android-clang-llvm-jni + nar + + + + + + release + + + + org.apache.maven.plugins + maven-jar-plugin + + + build-aar + package + + jar + + + + + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + true + + + + + diff --git a/junixsocket-native-android/pom.xml b/junixsocket-native-android/pom.xml new file mode 100644 index 000000000..06c8321d6 --- /dev/null +++ b/junixsocket-native-android/pom.xml @@ -0,0 +1,164 @@ + + + 4.0.0 + junixsocket-native-android + + pom + + + com.kohlschutter.junixsocket + junixsocket + 2.7.2 + ../pom.xml + + junixsocket-native-android + + ${project.parent.basedir} + ${project.name}-${project.version}.aar + + + Native junixsocket libraries for Android + + + + com.kohlschutter.junixsocket + junixsocket-common + ${project.version} + + + com.kohlschutter.junixsocket + junixsocket-native + ${project.version} + aarch64-Android-clang-llvm-jni + nar + provided + true + + + com.kohlschutter.junixsocket + junixsocket-native + ${project.version} + x86_64-Android-clang-llvm-jni + nar + provided + true + + + com.kohlschutter.junixsocket + junixsocket-native + ${project.version} + i686-Android-clang-llvm-jni + nar + provided + true + + + com.kohlschutter.junixsocket + junixsocket-native + ${project.version} + arm-Android-clang-llvm-jni + nar + provided + true + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + multirelease-jar + none + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-nars + package + + unpack-dependencies + + + **/lib*.so + nar + ${project.build.directory} + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + build-aar + package + + single + + + + src/assembly/aar.xml + + ${aarFinalName} + false + false + + + + + + com.kohlschutter.mavenplugins + copy-rename-maven-plugin + + + rename-zip-to-aar + package + + rename + + + + ${project.build.directory}/${aarFinalName}.zip + + ${project.build.directory}/${aarFinalName} + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-aar + package + + attach-artifact + + + + + + ${project.build.directory}/${aarFinalName} + + aar + + + + + + + + + diff --git a/junixsocket-native-android/src/aar/AndroidManifest.xml b/junixsocket-native-android/src/aar/AndroidManifest.xml new file mode 100644 index 000000000..0d7dd678f --- /dev/null +++ b/junixsocket-native-android/src/aar/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/junixsocket-native-android/src/aar/R.txt b/junixsocket-native-android/src/aar/R.txt new file mode 100644 index 000000000..e69de29bb diff --git a/junixsocket-native-android/src/aar/classes.jar b/junixsocket-native-android/src/aar/classes.jar new file mode 100644 index 000000000..84e5ed105 Binary files /dev/null and b/junixsocket-native-android/src/aar/classes.jar differ diff --git a/junixsocket-native-android/src/assembly/aar.xml b/junixsocket-native-android/src/assembly/aar.xml new file mode 100644 index 000000000..1b9cc6a28 --- /dev/null +++ b/junixsocket-native-android/src/assembly/aar.xml @@ -0,0 +1,51 @@ + + aar + + + zip + + + false + false + + + + ${project.basedir}/src/aar + + + * + + + + res + + **/* + + + + + + + ${project.build.directory}/lib/aarch64-Android-clang/jni/libjunixsocket-native-${project.version}.so + jni/arm64-v8a + libjunixsocket-native.so + + + ${project.build.directory}/lib/x86_64-Android-clang/jni/libjunixsocket-native-${project.version}.so + jni/x86_64 + libjunixsocket-native.so + + + ${project.build.directory}/lib/i686-Android-clang/jni/libjunixsocket-native-${project.version}.so + jni/x86 + libjunixsocket-native.so + + + ${project.build.directory}/lib/arm-Android-clang/jni/libjunixsocket-native-${project.version}.so + jni/armeabi-v7a + libjunixsocket-native.so + + + diff --git a/junixsocket-native-common/pom.xml b/junixsocket-native-common/pom.xml index 49bf4aaaf..cbaefbae5 100644 --- a/junixsocket-native-common/pom.xml +++ b/junixsocket-native-common/pom.xml @@ -7,7 +7,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-native-common @@ -164,6 +164,17 @@ runtime true + - 2.6.1 + 2.7.2 ../pom.xml junixsocket-native-cross @@ -366,6 +366,138 @@ + + crosscompile-aarch64-Android-clang + + run + + + ${junixsocket.cross.disabled} + true + true + + clean + install + + ${project.basedir}/../ + + junixsocket-native + + + llvm + + + aarch64-Android-clang + true + ${project.build.directory}/junixsocket-native-aarch64-Android-clang-llvm + aarch64-linux-android30 + so + aarch64-Android-clang-llvm-jni + ${ignorant} + ${gpgkeyname} + ${gpg.executable} + + + + + crosscompile-x86_64-Android-clang + + run + + + ${junixsocket.cross.disabled} + true + true + + clean + install + + ${project.basedir}/../ + + junixsocket-native + + + llvm + + + x86_64-Android-clang + true + ${project.build.directory}/junixsocket-native-x86_64-Android-clang-llvm + x86_64-linux-android30 + so + x86_64-Android-clang-llvm-jni + ${ignorant} + ${gpgkeyname} + ${gpg.executable} + + + + + crosscompile-i686-Android-clang + + run + + + ${junixsocket.cross.disabled} + true + true + + clean + install + + ${project.basedir}/../ + + junixsocket-native + + + llvm + + + i686-Android-clang + true + ${project.build.directory}/junixsocket-native-i686-Android-clang-llvm + i686-linux-android30 + so + i686-Android-clang-llvm-jni + ${ignorant} + ${gpgkeyname} + ${gpg.executable} + + + + + crosscompile-arm-Android-clang + + run + + + ${junixsocket.cross.disabled} + true + true + + clean + install + + ${project.basedir}/../ + + junixsocket-native + + + llvm + + + arm-Android-clang + true + ${project.build.directory}/junixsocket-native-arm-Android-clang-llvm + arm-linux-androideabi30 + so + arm-Android-clang-llvm-jni + ${ignorant} + ${gpgkeyname} + ${gpg.executable} + + + - 2.6.1 + 2.7.2 ../pom.xml junixsocket-native-custom diff --git a/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/NarMetadata.java b/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/NarMetadata.java index 4613c7e5a..90c999be0 100644 --- a/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/NarMetadata.java +++ b/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/NarMetadata.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,11 @@ /** * Marker class to find native libraries in the classpath. - * + * * @author Christian Kohlschütter */ public final class NarMetadata { - @ExcludeFromCodeCoverageGeneratedReport + @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable") private NarMetadata() { throw new IllegalStateException("No instances"); } diff --git a/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/package-info.java b/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/package-info.java index 068069722..6d0528155 100644 --- a/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/package-info.java +++ b/junixsocket-native-custom/src/main/java/org/newsclub/lib/junixsocket/custom/package-info.java @@ -1,11 +1,11 @@ /** * Helper package to identify the Maven artifact with JNI libraries for specific architectures. - * + * * There are multiple artifacts with the same identifier (junixsocket-native), one per architecture. - * + * * If you want to run junixsocket on your architecture, you need to make sure that the correct one * is in the classpath. - * + * * See "junixsocket-native-common" for an artifact containing the commonly used architectures. */ package org.newsclub.lib.junixsocket.custom; diff --git a/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/reflect-config.json b/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/reflect-config.json new file mode 100644 index 000000000..3ca368701 --- /dev/null +++ b/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/reflect-config.json @@ -0,0 +1,5 @@ +[ +{ + "name":"org.newsclub.lib.junixsocket.custom.NarMetadata" +} +] diff --git a/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/resource-config.json b/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/resource-config.json new file mode 100644 index 000000000..6ea5af423 --- /dev/null +++ b/junixsocket-native-custom/src/main/resources/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-custom/resource-config.json @@ -0,0 +1,9 @@ +{ + "resources":{ + "includes":[ + { + "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-custom/pom.properties\\E" + } + ]}, + "bundles":[] +} \ No newline at end of file diff --git a/junixsocket-native-graalvm/bin/build-selftest b/junixsocket-native-graalvm/bin/build-selftest index 396095776..7e82759aa 100755 --- a/junixsocket-native-graalvm/bin/build-selftest +++ b/junixsocket-native-graalvm/bin/build-selftest @@ -8,6 +8,8 @@ # cd "$(dirname $0)/../" +[[ -n "$GRAALVM_HOME" ]] && export PATH="$GRAALVM_HOME"/bin:$PATH + java -version 2>&1 | grep -q GraalVM if [[ $? -ne 0 ]]; then echo Error: JVM is not a GraalVM. >&2 diff --git a/junixsocket-native-graalvm/bin/graalvm b/junixsocket-native-graalvm/bin/graalvm deleted file mode 100755 index d06e042bd..000000000 --- a/junixsocket-native-graalvm/bin/graalvm +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -java -version 2>&1 | grep -q GraalVM -if [[ $? -eq 0 ]]; then - exec $@ -fi - -# FIXME auto-detect -export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.2.0/Contents/Home -export PATH=$JAVA_HOME/bin:$PATH -exec $@ diff --git a/junixsocket-native-graalvm/bin/with-graalvm b/junixsocket-native-graalvm/bin/with-graalvm new file mode 100755 index 000000000..4f5389a26 --- /dev/null +++ b/junixsocket-native-graalvm/bin/with-graalvm @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# junixsocket +# Copyright 2009-2022 Christian Kohlschütter +# SPDX-License-Identifier: Apache-2.0 +# +# Script to detect and enable GraalVM. +# +# When run without arguments, an eval-able string like "GRAALVM_HOME=/path/to/graalvm" is emitted. +# Otherwise, the arguments are executed with the determined GraalVM in path (GRAALVM_HOME, JAVA_HOME and PATH set accordingly). +# +java -version 2>&1 | grep -q GraalVM +if [[ $? -eq 0 ]]; then + exec $@ +fi + +if [[ -z "$GRAALVM_HOME" ]]; then + for d in $(find /Library/Java/JavaVirtualMachines /usr/lib/jvm -maxdepth 1 -type d -name "*graalvm*" 2>/dev/null | sort -r); do + if [[ -e "$d/bin/java" ]]; then + export GRAALVM_HOME="$d" + break + elif [[ -e "$d/Contents/Home/bin/java" ]]; then + export GRAALVM_HOME="$d/Contents/Home" + break + fi + done +fi + +if [[ -z "$GRAALVM_HOME" ]]; then + echo "Error: Could not determine GRAALVM_HOME -- Please set manually" >&2 + exit 1 +else + if [[ $# -eq 0 ]]; then + echo "GRAALVM_HOME=$GRAALVM_HOME" + fi +fi + +export JAVA_HOME="$GRAALVM_HOME" +export PATH=$JAVA_HOME/bin:$PATH +exec -- $@ diff --git a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/jni-config.json b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/jni-config.json index 50078f6a3..966feab12 100644 --- a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/jni-config.json +++ b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/jni-config.json @@ -8,6 +8,10 @@ "name":"java.lang.Boolean", "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Class", + "methods":[{"name":"getName","parameterTypes":[] }] +}, { "name":"java.lang.IllegalStateException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] @@ -18,15 +22,16 @@ }, { "name":"java.lang.Integer", - "methods":[ - {"name":"","parameterTypes":["int"] }, - {"name":"intValue","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":["int"] }, {"name":"intValue","parameterTypes":[] }] }, { "name":"java.lang.NullPointerException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Object", + "methods":[{"name":"getClass","parameterTypes":[] }] +}, { "name":"java.lang.ProcessBuilder$RedirectPipeImpl", "fields":[{"name":"fd"}], @@ -34,24 +39,19 @@ }, { "name":"java.lang.String", - "methods":[ - {"name":"lastIndexOf","parameterTypes":["int"] }, - {"name":"substring","parameterTypes":["int"] } - ] + "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] }, { "name":"java.lang.System", - "methods":[ - {"name":"getProperty","parameterTypes":["java.lang.String"] }, - {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] } - ] + "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.net.DatagramSocket" }, { "name":"java.net.InetSocketAddress", - "fields":[{"name":"holder"}] + "fields":[{"name":"holder"}], + "methods":[{"name":"","parameterTypes":["int"] }] }, { "name":"java.net.InetSocketAddress$InetSocketAddressHolder", @@ -86,14 +86,7 @@ }, { "name":"org.newsclub.net.unix.AFSelector$PollFd", - "fields":[ - {"name":"fds"}, - {"name":"ops"}, - {"name":"rops"} - ] -}, -{ - "name":"org.newsclub.net.unix.AFTIPCSocketAddress" + "fields":[{"name":"fds"}, {"name":"ops"}, {"name":"rops"}] }, { "name":"org.newsclub.net.unix.AFUNIXDatagramSocket" @@ -109,11 +102,7 @@ }, { "name":"org.newsclub.net.unix.AFUNIXSocketCredentials", - "fields":[ - {"name":"gids"}, - {"name":"pid"}, - {"name":"uid"} - ], + "fields":[{"name":"gids"}, {"name":"pid"}, {"name":"uid"}], "methods":[{"name":"setUUID","parameterTypes":["java.lang.String"] }] }, { @@ -122,15 +111,8 @@ }, { "name":"org.newsclub.net.unix.AncillaryDataSupport", - "fields":[ - {"name":"ancillaryReceiveBuffer"}, - {"name":"pendingFileDescriptors"} - ], - "methods":[ - {"name":"receiveFileDescriptors","parameterTypes":["int[]"] }, - {"name":"setTipcDestName","parameterTypes":["int","int","int"] }, - {"name":"setTipcErrorInfo","parameterTypes":["int","int"] } - ] + "fields":[{"name":"ancillaryReceiveBuffer"}, {"name":"pendingFileDescriptors"}], + "methods":[{"name":"receiveFileDescriptors","parameterTypes":["int[]"] }, {"name":"setTipcDestName","parameterTypes":["int","int","int"] }, {"name":"setTipcErrorInfo","parameterTypes":["int","int"] }] }, { "name":"org.newsclub.net.unix.InvalidArgumentSocketException", @@ -144,19 +126,19 @@ "name":"org.newsclub.net.unix.StdinSocketApp", "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] }, +{ + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMDatagramSocket" +}, +{ + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMSocket" +}, { "name":"org.newsclub.net.unix.selftest.Selftest", "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.newsclub.net.unix.tipc.AFTIPCGroupRequest", - "methods":[ - {"name":"fromNative","parameterTypes":["int","int","int","int"] }, - {"name":"getFlagsValue","parameterTypes":[] }, - {"name":"getInstance","parameterTypes":[] }, - {"name":"getScopeId","parameterTypes":[] }, - {"name":"getType","parameterTypes":[] } - ] + "methods":[{"name":"fromNative","parameterTypes":["int","int","int","int"] }, {"name":"getFlagsValue","parameterTypes":[] }, {"name":"getInstance","parameterTypes":[] }, {"name":"getScopeId","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.vsock.AFVSOCKDatagramSocket" diff --git a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/reflect-config.json b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/reflect-config.json index a0f66c8c9..9b9c4944b 100644 --- a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/reflect-config.json +++ b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/reflect-config.json @@ -1,4 +1,20 @@ [ +{ + "name":"com.kohlschutter.testutil.AvailabilityExecutionCondition", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.kohlschutter.testutil.AvailabilityRequirement", + "queryAllPublicMethods":true +}, +{ + "name":"com.kohlschutter.testutil.ExecutionEnvironmentExecutionCondition", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.kohlschutter.testutil.ExecutionEnvironmentRequirement", + "queryAllPublicMethods":true +}, { "name":"com.kohlschutter.testutil.ProcessUtilCondition", "methods":[{"name":"","parameterTypes":[] }] @@ -10,12 +26,41 @@ { "name":"java.lang.ProcessBuilder$RedirectPipeImpl" }, +{ + "name":"java.lang.Thread", + "fields":[{"name":"threadLocalRandomProbe"}] +}, +{ + "name":"java.net.InetSocketAddress" +}, +{ + "name":"java.net.UnixDomainSocketAddress" +}, { "name":"java.nio.channels.spi.AbstractSelectableChannel" }, { "name":"java.security.SecureRandomParameters" }, +{ + "name":"java.util.concurrent.ForkJoinTask", + "fields":[{"name":"aux"}, {"name":"status"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicBoolean", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, +{ + "name":"java.util.concurrent.atomic.Striped64", + "fields":[{"name":"base"}, {"name":"cellsBusy"}] +}, +{ + "name":"jdk.internal.misc.Unsafe" +}, { "name":"org.apiguardian.api.API", "queryAllPublicMethods":true @@ -54,6 +99,10 @@ { "name":"org.newsclub.lib.junixsocket.custom.NarMetadata" }, +{ + "name":"org.newsclub.net.unix.AFSYSTEMSocketAddress", + "methods":[{"name":"addressFamily","parameterTypes":[] }] +}, { "name":"org.newsclub.net.unix.AFSocketCapabilityCondition", "methods":[{"name":"","parameterTypes":[] }] @@ -73,16 +122,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testGeneric","parameterTypes":[] }, - {"name":"testParseFail","parameterTypes":[] }, - {"name":"testSchemesAvailable","parameterTypes":[] }, - {"name":"testServiceRangeURI","parameterTypes":[] }, - {"name":"testServiceURI","parameterTypes":[] }, - {"name":"testSocatString","parameterTypes":[] }, - {"name":"testSocketURI","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testGeneric","parameterTypes":[] }, {"name":"testParseFail","parameterTypes":[] }, {"name":"testSchemesAvailable","parameterTypes":[] }, {"name":"testServiceRangeURI","parameterTypes":[] }, {"name":"testServiceURI","parameterTypes":[] }, {"name":"testSocatString","parameterTypes":[] }, {"name":"testSocketURI","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.AFUNIXSocketAddress", @@ -95,18 +135,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testAbstractNamespace","parameterTypes":[] }, - {"name":"testFileScheme","parameterTypes":[] }, - {"name":"testHttpUnix","parameterTypes":[] }, - {"name":"testParseURIandBack","parameterTypes":[] }, - {"name":"testSchemesAvailable","parameterTypes":[] }, - {"name":"testSocatString","parameterTypes":[] }, - {"name":"testURITemplate","parameterTypes":[] }, - {"name":"testURITemplateWithPortNumber","parameterTypes":[] }, - {"name":"testUnixScheme","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testAbstractNamespace","parameterTypes":[] }, {"name":"testFileScheme","parameterTypes":[] }, {"name":"testHttpUnix","parameterTypes":[] }, {"name":"testParseURIandBack","parameterTypes":[] }, {"name":"testSchemesAvailable","parameterTypes":[] }, {"name":"testSerialize","parameterTypes":[] }, {"name":"testSocatString","parameterTypes":[] }, {"name":"testURITemplate","parameterTypes":[] }, {"name":"testURITemplateWithPortNumber","parameterTypes":[] }, {"name":"testUnixScheme","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.AFVSOCKSocketAddress", @@ -118,11 +147,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testAcceptWithoutBindToService","parameterTypes":[] }, - {"name":"testCatchTimeout","parameterTypes":[] }, - {"name":"testTimeoutAfterDelay","parameterTypes":[] } - ] + "methods":[{"name":"testAcceptWithoutBindToService","parameterTypes":[] }, {"name":"testCatchTimeout","parameterTypes":[] }, {"name":"testTimeoutAfterDelay","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.AncillaryMessageTest", @@ -137,10 +162,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testAvailableAtClient","parameterTypes":[] }, - {"name":"testAvailableAtServer","parameterTypes":[] } - ] + "methods":[{"name":"testAvailableAtClient","parameterTypes":[] }, {"name":"testAvailableAtServer","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.BufferOverflowTest", @@ -148,13 +170,16 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"readOutOfBounds","parameterTypes":[] }, - {"name":"readUpTo","parameterTypes":[] }, - {"name":"setUp","parameterTypes":[] }, - {"name":"tearDown","parameterTypes":[] }, - {"name":"writeOverflow","parameterTypes":[] } - ] + "methods":[{"name":"readOutOfBounds","parameterTypes":[] }, {"name":"readUpTo","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }, {"name":"writeOverflow","parameterTypes":[] }] +}, +{ + "name":"org.newsclub.net.unix.BuildPropertiesTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testHasProperties","parameterTypes":[] }, {"name":"testNotEmpty","parameterTypes":[] }, {"name":"testResolved","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.CancelAcceptTest", @@ -170,11 +195,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testBindConnect","parameterTypes":[] }, - {"name":"testPeekTimeout","parameterTypes":[] }, - {"name":"testReadTimeout","parameterTypes":[] } - ] + "methods":[{"name":"testBindConnect","parameterTypes":[] }, {"name":"testPeekTimeout","parameterTypes":[] }, {"name":"testReadTimeout","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.EndOfFileTest", @@ -182,17 +203,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"bidirectionalSanity","parameterTypes":[] }, - {"name":"clientReadEof","parameterTypes":[] }, - {"name":"clientWriteToSocketClosedByClient","parameterTypes":[] }, - {"name":"clientWriteToSocketClosedByServer","parameterTypes":[] }, - {"name":"serverReadEof","parameterTypes":[] }, - {"name":"serverWriteToSocketClosedByClient","parameterTypes":[] }, - {"name":"serverWriteToSocketClosedByServer","parameterTypes":[] }, - {"name":"setUp","parameterTypes":[] }, - {"name":"tearDown","parameterTypes":[] } - ] + "methods":[{"name":"bidirectionalSanity","parameterTypes":[] }, {"name":"clientReadEof","parameterTypes":[] }, {"name":"clientWriteToSocketClosedByClient","parameterTypes":[] }, {"name":"clientWriteToSocketClosedByServer","parameterTypes":[] }, {"name":"serverReadEof","parameterTypes":[] }, {"name":"serverWriteToSocketClosedByClient","parameterTypes":[] }, {"name":"serverWriteToSocketClosedByServer","parameterTypes":[] }, {"name":"setUp","parameterTypes":[] }, {"name":"tearDown","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.FileDescriptorCastTest", @@ -201,14 +212,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testAvailableTypes","parameterTypes":[] }, - {"name":"testInvalidFileDescriptor","parameterTypes":[] }, - {"name":"testPipe","parameterTypes":[] }, - {"name":"testRandomAccessFile","parameterTypes":[] }, - {"name":"testStdout","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testAvailableTypes","parameterTypes":[] }, {"name":"testCastAsInteger","parameterTypes":[] }, {"name":"testInvalidFileDescriptor","parameterTypes":[] }, {"name":"testPipe","parameterTypes":[] }, {"name":"testRandomAccessFile","parameterTypes":[] }, {"name":"testStdout","parameterTypes":[] }, {"name":"testUnsafeCast","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.InetAddressTest", @@ -217,13 +221,10 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testFromToBytes","parameterTypes":[] }, - {"name":"testHostnameString","parameterTypes":[] }, - {"name":"testHostnameStringEndsWithJunixSocket","parameterTypes":[] }, - {"name":"testIsLoopbackAddress","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testFromToBytes","parameterTypes":[] }, {"name":"testHostnameString","parameterTypes":[] }, {"name":"testHostnameStringEndsWithJunixSocket","parameterTypes":[] }, {"name":"testIsLoopbackAddress","parameterTypes":[] }] +}, +{ + "name":"org.newsclub.net.unix.OperationNotSupportedSocketException" }, { "name":"org.newsclub.net.unix.PipeTest", @@ -232,11 +233,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testPipe","parameterTypes":[] }, - {"name":"testPipeRecvHang","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testPipe","parameterTypes":[] }, {"name":"testPipeRecvHang","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.ReadWriteTest", @@ -244,12 +241,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testReceiveDataByteForByteSendByteForByte","parameterTypes":[] }, - {"name":"testReceiveDataByteForByteSendWithByteArray","parameterTypes":[] }, - {"name":"testReceiveWithByteArraySendByteForByte","parameterTypes":[] }, - {"name":"testReceiveWithByteArraySendWithByteArray","parameterTypes":[] } - ] + "methods":[{"name":"testReceiveDataByteForByteSendByteForByte","parameterTypes":[] }, {"name":"testReceiveDataByteForByteSendWithByteArray","parameterTypes":[] }, {"name":"testReceiveWithByteArraySendByteForByte","parameterTypes":[] }, {"name":"testReceiveWithByteArraySendWithByteArray","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SelectorTest", @@ -257,20 +249,11 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testCancelSelect","parameterTypes":[] }, - {"name":"testClosedSelectorSelect","parameterTypes":[] }, - {"name":"testClosedSelectorWakeup","parameterTypes":[] }, - {"name":"testConnectionCloseEventualClientDisconnect","parameterTypes":[] }, - {"name":"testConnectionCloseEventualClientDisconnectKeepLooping","parameterTypes":[] }, - {"name":"testConnectionCloseImmediateClientDisconnect","parameterTypes":[] }, - {"name":"testConnectionCloseImmediateClientDisconnectKeepLooping","parameterTypes":[] }, - {"name":"testNonBlockingAccept","parameterTypes":[] } - ] + "methods":[{"name":"testCancelSelect","parameterTypes":[] }, {"name":"testClosedSelectorSelect","parameterTypes":[] }, {"name":"testClosedSelectorWakeup","parameterTypes":[] }, {"name":"testConnectionCloseEventualClientDisconnect","parameterTypes":[] }, {"name":"testConnectionCloseEventualClientDisconnectKeepLooping","parameterTypes":[] }, {"name":"testConnectionCloseImmediateClientDisconnect","parameterTypes":[] }, {"name":"testConnectionCloseImmediateClientDisconnectKeepLooping","parameterTypes":[] }, {"name":"testNonBlockingAccept","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SelftestDiagnosticsHelper", - "methods":[{"name":"initError","parameterTypes":[] }] + "methods":[{"name":"buildProperties","parameterTypes":[] }, {"name":"initError","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.ServerSocketCloseTest", @@ -278,10 +261,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testUnblockAcceptsWithSoTimeout","parameterTypes":[] }, - {"name":"testUnblockAcceptsWithoutSoTimeout","parameterTypes":[] } - ] + "methods":[{"name":"testUnblockAcceptsWithSoTimeout","parameterTypes":[] }, {"name":"testUnblockAcceptsWithoutSoTimeout","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.ServerSocketTest", @@ -289,11 +269,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testBindBadArguments","parameterTypes":[] }, - {"name":"testCloseable","parameterTypes":[] }, - {"name":"testUnboundServerSocket","parameterTypes":[] } - ] + "methods":[{"name":"testBindBadArguments","parameterTypes":[] }, {"name":"testCloseable","parameterTypes":[] }, {"name":"testUnboundServerSocket","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SoTimeoutTest", @@ -301,12 +277,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"issue14Fail","parameterTypes":[] }, - {"name":"issue14Pass","parameterTypes":[] }, - {"name":"testSocketTimeoutExceptionRead","parameterTypes":[] }, - {"name":"testSocketTimeoutExceptionWrite","parameterTypes":[] } - ] + "methods":[{"name":"issue14Fail","parameterTypes":[] }, {"name":"issue14Pass","parameterTypes":[] }, {"name":"testSocketTimeoutExceptionRead","parameterTypes":[] }, {"name":"testSocketTimeoutExceptionWrite","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SocketChannelTest", @@ -314,11 +285,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testDoubleBindAddressNotReusable","parameterTypes":[] }, - {"name":"testDoubleBindAddressReusable","parameterTypes":[] }, - {"name":"testNonBlockingConnect","parameterTypes":[] } - ] + "methods":[{"name":"testByteBufferWithPositionOffset","parameterTypes":[] }, {"name":"testDoubleBindAddressNotReusable","parameterTypes":[] }, {"name":"testDoubleBindAddressReusable","parameterTypes":[] }, {"name":"testNonBlockingConnect","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SocketOptionsTest", @@ -333,10 +300,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testDatagramPair","parameterTypes":[] }, - {"name":"testSocketPair","parameterTypes":[] } - ] + "methods":[{"name":"testDatagramPair","parameterTypes":[] }, {"name":"testSocketPair","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SocketTest", @@ -344,12 +308,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testBindBadArguments","parameterTypes":[] }, - {"name":"testCloseable","parameterTypes":[] }, - {"name":"testConnectBadArguments","parameterTypes":[] }, - {"name":"testUnconnectedSocket","parameterTypes":[] } - ] + "methods":[{"name":"testBindBadArguments","parameterTypes":[] }, {"name":"testCloseable","parameterTypes":[] }, {"name":"testConnectBadArguments","parameterTypes":[] }, {"name":"testUnconnectedSocket","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.SocketTestBase", @@ -357,10 +316,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"ensureSocketFileIsDeleted","parameterTypes":[] }, - {"name":"tearDownClass","parameterTypes":[] } - ] + "methods":[{"name":"ensureSocketFileIsDeleted","parameterTypes":[] }, {"name":"tearDownClass","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.StandardSocketOptionsTest", @@ -368,12 +324,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"afterEach","parameterTypes":[] }, - {"name":"beforeEach","parameterTypes":[] }, - {"name":"testSocketOptions","parameterTypes":[] }, - {"name":"testUnconnectedServerSocketOptions","parameterTypes":[] } - ] + "methods":[{"name":"afterEach","parameterTypes":[] }, {"name":"beforeEach","parameterTypes":[] }, {"name":"testSocketOptions","parameterTypes":[] }, {"name":"testUnconnectedServerSocketOptions","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.TcpNoDelayTest", @@ -381,10 +332,7 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testDefaultImpl","parameterTypes":[] }, - {"name":"testStrictImpl","parameterTypes":[] } - ] + "methods":[{"name":"testDefaultImpl","parameterTypes":[] }, {"name":"testStrictImpl","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.ThroughputTest", @@ -392,16 +340,28 @@ "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, - "methods":[ - {"name":"testDatagramChannel","parameterTypes":[] }, - {"name":"testDatagramChannelDirect","parameterTypes":[] }, - {"name":"testDatagramChannelNonBlocking","parameterTypes":[] }, - {"name":"testDatagramChannelNonBlockingDirect","parameterTypes":[] }, - {"name":"testDatagramPacket","parameterTypes":[] }, - {"name":"testSocket","parameterTypes":[] }, - {"name":"testSocketChannel","parameterTypes":[] }, - {"name":"testSocketChannelDirectBuffer","parameterTypes":[] } - ] + "methods":[{"name":"testDatagramChannel","parameterTypes":[] }, {"name":"testDatagramChannelDirect","parameterTypes":[] }, {"name":"testDatagramChannelNonBlocking","parameterTypes":[] }, {"name":"testDatagramChannelNonBlockingDirect","parameterTypes":[] }, {"name":"testDatagramPacket","parameterTypes":[] }, {"name":"testSocket","parameterTypes":[] }, {"name":"testSocketChannel","parameterTypes":[] }, {"name":"testSocketChannelDirectBuffer","parameterTypes":[] }] +}, +{ + "name":"org.newsclub.net.unix.darwin.system.AFSYSTEMSelectorProvider" +}, +{ + "name":"org.newsclub.net.unix.darwin.system.KernelControlNamesTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testStandardKernelControlNames","parameterTypes":[] }] +}, +{ + "name":"org.newsclub.net.unix.darwin.system.UtunTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.AbstractNamespaceTest", @@ -472,18 +432,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testDatagramFileChannel","parameterTypes":[] }, - {"name":"testDatagramPorts","parameterTypes":[] }, - {"name":"testDatagramSocket","parameterTypes":[] }, - {"name":"testForkedVMRedirectStdin","parameterTypes":[] }, - {"name":"testServer","parameterTypes":[] }, - {"name":"testSocketPair","parameterTypes":[] }, - {"name":"testSocketPairNative","parameterTypes":[] }, - {"name":"testSocketPorts","parameterTypes":[] }, - {"name":"testUnconnectedServerAsSocket","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testDatagramFileChannel","parameterTypes":[] }, {"name":"testDatagramPorts","parameterTypes":[] }, {"name":"testDatagramSocket","parameterTypes":[] }, {"name":"testForkedVMRedirectStdin","parameterTypes":[] }, {"name":"testServer","parameterTypes":[] }, {"name":"testSocketPair","parameterTypes":[] }, {"name":"testSocketPairNative","parameterTypes":[] }, {"name":"testSocketPorts","parameterTypes":[] }, {"name":"testUnconnectedServerAsSocket","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.FileDescriptorsTest", @@ -492,18 +441,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testAncillaryReceiveBufferTooSmall","parameterTypes":[] }, - {"name":"testBadFileDescriptor","parameterTypes":[] }, - {"name":"testDatagramSocket","parameterTypes":[] }, - {"name":"testEmptyFileDescriptorArray","parameterTypes":[] }, - {"name":"testFileInputStream","parameterTypes":[] }, - {"name":"testFileInputStreamPartiallyConsumed","parameterTypes":[] }, - {"name":"testNoAncillaryReceiveBuffer","parameterTypes":[] }, - {"name":"testNullFileDescriptorArray","parameterTypes":[] }, - {"name":"testSendRecvFileDescriptors","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testAncillaryReceiveBufferTooSmall","parameterTypes":[] }, {"name":"testBadFileDescriptor","parameterTypes":[] }, {"name":"testDatagramSocket","parameterTypes":[] }, {"name":"testEmptyFileDescriptorArray","parameterTypes":[] }, {"name":"testFileInputStream","parameterTypes":[] }, {"name":"testFileInputStreamPartiallyConsumed","parameterTypes":[] }, {"name":"testNoAncillaryReceiveBuffer","parameterTypes":[] }, {"name":"testNullFileDescriptorArray","parameterTypes":[] }, {"name":"testSendRecvFileDescriptors","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.PeerCredentialsTest", @@ -512,12 +450,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"ensureSameCreds","parameterTypes":[] }, - {"name":"testDatagramSocket","parameterTypes":[] }, - {"name":"testSocketsSameProcess","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"ensureSameCreds","parameterTypes":[] }, {"name":"testDatagramSocket","parameterTypes":[] }, {"name":"testSocketsSameProcess","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.ReadWriteTest", @@ -553,10 +486,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testSupported","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testSupported","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.SoTimeoutTest", @@ -574,16 +504,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testAbstractNamespace","parameterTypes":[] }, - {"name":"testByteConstructor","parameterTypes":[] }, - {"name":"testEmptyAddress","parameterTypes":[] }, - {"name":"testInetAddress","parameterTypes":[] }, - {"name":"testLegacyConstructor","parameterTypes":[] }, - {"name":"testPath","parameterTypes":[] }, - {"name":"testPort","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testAbstractNamespace","parameterTypes":[] }, {"name":"testByteConstructor","parameterTypes":[] }, {"name":"testEmptyAddress","parameterTypes":[] }, {"name":"testInetAddress","parameterTypes":[] }, {"name":"testLargePort","parameterTypes":[] }, {"name":"testLegacyConstructor","parameterTypes":[] }, {"name":"testPath","parameterTypes":[] }, {"name":"testPort","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.SocketChannelTest", @@ -601,15 +522,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testFactoryArg","parameterTypes":[] }, - {"name":"testSystemProperty","parameterTypes":[] }, - {"name":"testURISchemeCeateSocketThenConnect","parameterTypes":[] }, - {"name":"testURISchemeCeateSocketWithHostnameValidCases","parameterTypes":[] }, - {"name":"testURISchemeCeateSocketWithIllegalArguments","parameterTypes":[] }, - {"name":"testURISchemeCeateSocketWithInvalidHostname","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testFactoryArg","parameterTypes":[] }, {"name":"testSystemProperty","parameterTypes":[] }, {"name":"testURISchemeCeateSocketThenConnect","parameterTypes":[] }, {"name":"testURISchemeCeateSocketWithHostnameValidCases","parameterTypes":[] }, {"name":"testURISchemeCeateSocketWithIllegalArguments","parameterTypes":[] }, {"name":"testURISchemeCeateSocketWithInvalidHostname","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.SocketPairTest", @@ -627,15 +540,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testLoadedLibrary","parameterTypes":[] }, - {"name":"testMain","parameterTypes":[] }, - {"name":"testReceivedFileDescriptorsUnconnected","parameterTypes":[] }, - {"name":"testSupported","parameterTypes":[] }, - {"name":"testSupports","parameterTypes":[] }, - {"name":"testVersion","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testLoadedLibrary","parameterTypes":[] }, {"name":"testMain","parameterTypes":[] }, {"name":"testReceivedFileDescriptorsUnconnected","parameterTypes":[] }, {"name":"testSupported","parameterTypes":[] }, {"name":"testSupports","parameterTypes":[] }, {"name":"testVersion","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.domain.StandardSocketOptionsTest", @@ -664,6 +569,23 @@ "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.newsclub.net.unix.domain.ThroughputTestShim", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"testJEP380","parameterTypes":[] }, {"name":"testJEP380direct","parameterTypes":[] }] +}, +{ + "name":"org.newsclub.net.unix.domain.UnixDomainSocketAddressTest", + "allDeclaredFields":true, + "allDeclaredClasses":true, + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"testConvertUnixDomainSocketAddress","parameterTypes":[] }] +}, { "name":"org.newsclub.net.unix.tipc.AFTIPCSelectorProvider" }, @@ -834,10 +756,7 @@ "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, - "methods":[ - {"name":"","parameterTypes":[] }, - {"name":"testGetLocalID","parameterTypes":[] } - ] + "methods":[{"name":"","parameterTypes":[] }, {"name":"testGetLocalID","parameterTypes":[] }] }, { "name":"org.newsclub.net.unix.vsock.AFVSOCKSelectorProvider" @@ -992,7 +911,11 @@ }, { "name":"sun.security.provider.NativePRNG", - "methods":[{"name":"","parameterTypes":[] }] + "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.NativePRNG$NonBlocking", + "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] }, { "name":"sun.security.provider.SHA", diff --git a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/resource-config.json b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/resource-config.json index dca265359..1bce2bc9f 100644 --- a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/resource-config.json +++ b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/resource-config.json @@ -1,27 +1,17 @@ { "resources":{ - "includes":[ - { - "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-common/pom.properties\\E" - }, - { - "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-common/pom.properties\\E" - }, - { - "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-custom/pom.properties\\E" - }, - { - "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-selftest/git.properties\\E" - }, - { - "pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E" - }, - { - "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E" - }, - { - "pattern":"\\Qlib/aarch64-MacOSX-clang/jni/libjunixsocket-native-2.6.1-SNAPSHOT.dylib\\E" - } - ]}, + "includes":[{ + "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-common/pom.properties\\E" + }, { + "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-common/pom.properties\\E" + }, { + "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-native-custom/pom.properties\\E" + }, { + "pattern":"\\QMETA-INF/maven/com.kohlschutter.junixsocket/junixsocket-selftest/git.properties\\E" + }, { + "pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E" + }, { + "pattern":"\\Qlib/aarch64-MacOSX-clang/jni/libjunixsocket-native-2.7.0-SNAPSHOT.dylib\\E" + }]}, "bundles":[] } \ No newline at end of file diff --git a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/serialization-config.json b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/serialization-config.json index d35fed355..6a715a1a1 100644 --- a/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/serialization-config.json +++ b/junixsocket-native-graalvm/output/META-INF/native-image/com.kohlschutter.junixsocket/junixsocket-native-graalvm/serialization-config.json @@ -1,12 +1,32 @@ { "types":[ + { + "name":"byte[]" + }, + { + "name":"java.lang.String" + }, { "name":"java.net.InetAddress" }, + { + "name":"java.net.InetSocketAddress" + }, + { + "name":"java.net.SocketAddress" + }, { "name":"org.junit.platform.launcher.TestIdentifier$SerializedForm" + }, + { + "name":"org.newsclub.net.unix.AFSocketAddress" + }, + { + "name":"org.newsclub.net.unix.AFUNIXSocketAddress" } ], "lambdaCapturingTypes":[ + ], + "proxies":[ ] } \ No newline at end of file diff --git a/junixsocket-native/.gitignore b/junixsocket-native/.gitignore index 7b9e6c964..4b4506e14 100644 --- a/junixsocket-native/.gitignore +++ b/junixsocket-native/.gitignore @@ -1,3 +1,4 @@ /*.xcodeproj/xcuserdata /*.xcodeproj/project.xcworkspace/xcuserdata /target-eclipse/ +src/**/*.jar diff --git a/junixsocket-native/crossclang/bin/clang b/junixsocket-native/crossclang/bin/clang index 97aa0cfef..5b7e5ddd5 100755 --- a/junixsocket-native/crossclang/bin/clang +++ b/junixsocket-native/crossclang/bin/clang @@ -80,6 +80,9 @@ hideUnknownWarningWarnings=0 hasExportDynamic=0 ignoreExportDynamic=0 +hasReproducible=0 +ignoreReproducible=0 + skipGccArg=0 analyze=0 @@ -111,10 +114,12 @@ while [ $# -gt 0 ]; do -Xcrossclang-llvm-name ) llvmName="$1"; shift ; skipArg=1 ;; -Xcrossclang-use-ldshim ) useLdShim=1 ; skipArg=1 ;; -Xcrossclang-use-gcc=* ) gccMode=1 ; compiler="${v#-Xcrossclang-use-gcc=}" ; skipArg=1 ;; + -Xcrossclang-use-clang=* ) compiler="${v#-Xcrossclang-use-clang=}" ; skipArg=1 ;; -Xcrossclang-output-strip-lib-prefix ) outputStripLibPrefix=1 ; skipArg=1 ;; -Xcrossclang-ld64-sdk-version ) ld64_sdk_version="$1"; shift ; skipArg=1 ;; -Xcrossclang-hide-unknown-warning-warnings ) hideUnknownWarningWarnings=1 ; skipArg=1 ;; -Xcrossclang-ignore-export-dynamic) ignoreExportDynamic=1 ; skipArg=1 ;; + -Xcrossclang-ignore-reproducible) ignoreReproducible=1 ; skipArg=1 ;; # -index-store-path ) shift ; skipArg=1 ;; # if you're hitting this, set "Enable Index-While-Building Functionality=No" in Xcode build settings instead -filelist ) fileList="$1" ; shift; skipArg=1 ;; -current_version ) shift ; skipArg=1 ;; @@ -155,6 +160,8 @@ while [ $# -gt 0 ]; do -Xlinker ) if [[ "$1" == "-export_dynamic" ]]; then hasExportDynamic=1 + elif [[ "$1" == "-reproducible" ]]; then + hasReproducible=1 elif [[ -n "$1" ]]; then clangArgs+=("-Xlinker" "$1") gccArgs+=("-Xlinker" "$1") @@ -186,6 +193,11 @@ if [[ $hasExportDynamic -eq 1 && $ignoreExportDynamic -eq 0 ]]; then gccArgs+=("-Xlinker" "-export_dynamic") fi +if [[ $hasReproducible -eq 1 && $ignoreReproducible -eq 0 ]]; then + clangArgs+=("-Xlinker" "-reproducible") + # gccArgs+=("-Xlinker" "-reproducible") +fi + if [[ $analyze -gt 0 && $gccMode -gt 0 ]]; then # When using the static code analyzer, switch back to clang compiler=clang @@ -262,13 +274,14 @@ if [[ $gccMode -gt 0 ]]; then clangArgs=( ${gccArgs[@]} ) fi -whichComp=$(which $compiler) +whichComp=$(which "$compiler") if [[ -z "$whichComp" ]]; then echo "crossclang could not find compiler $compiler in path $PATH, giving up" >&2 exit 1 fi -clangDir=$(cd $(dirname "$whichComp"); pwd) +clangDir=$(dirname "$whichComp") +clangDir=$(cd "$clangDir"; pwd) if [ "$currentDir" == "$clangDir" ]; then echo "crossclang detected in PATH. Can't find real clang, giving up" >&2 exit 1 @@ -627,7 +640,7 @@ if [[ $gccMode -gt 0 ]]; then fi -compilerBinary="$(which $compiler)" +compilerBinary=$(which "$compiler") if [ -z "$compilerBinary" ]; then echo "Cannot compile -- compiler not found: $compiler" >&2 exit 1 diff --git a/junixsocket-native/junixsocket-native.xcodeproj/project.pbxproj b/junixsocket-native/junixsocket-native.xcodeproj/project.pbxproj index a1bb6f52a..dab2ce37d 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/project.pbxproj +++ b/junixsocket-native/junixsocket-native.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 53; objects = { /* Begin PBXAggregateTarget section */ @@ -13,6 +13,10 @@ buildPhases = ( ); dependencies = ( + 435B551B2A3F6658001011B8 /* PBXTargetDependency */, + 435B54DF2A3F6617001011B8 /* PBXTargetDependency */, + 435B54A32A3F65ED001011B8 /* PBXTargetDependency */, + 43EE6B472A3B3D0700E6F8F2 /* PBXTargetDependency */, 436B5AF32827E55000574CD4 /* PBXTargetDependency */, 436B5AB928244D5700574CD4 /* PBXTargetDependency */, 43B3E076266756780024822F /* PBXTargetDependency */, @@ -630,6 +634,156 @@ 4355476E28F6E6310042E830 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; 4355476F28F6E6310042E830 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; 4355477028F6E6310042E830 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; + 435B546A2A3F65D2001011B8 /* tipc.h in Headers */ = {isa = PBXBuildFile; fileRef = 436E165F27FBB70B00A553EC /* tipc.h */; }; + 435B546B2A3F65D2001011B8 /* jni.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C02650AC3200823167 /* jni.h */; }; + 435B546C2A3F65D2001011B8 /* ancillary.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C52650AB4E00823167 /* ancillary.h */; }; + 435B546D2A3F65D2001011B8 /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; + 435B546E2A3F65D2001011B8 /* init.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BE2650AB4D00823167 /* init.h */; }; + 435B546F2A3F65D2001011B8 /* send.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AA2650AB4A00823167 /* send.h */; }; + 435B54702A3F65D2001011B8 /* listen.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B12650AB4B00823167 /* listen.h */; }; + 435B54712A3F65D2001011B8 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A22650AB4900823167 /* config.h */; }; + 435B54722A3F65D2001011B8 /* socketpair.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DFB6266082000024822F /* socketpair.h */; }; + 435B54732A3F65D2001011B8 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; + 435B54742A3F65D2001011B8 /* connect.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A72650AB4A00823167 /* connect.h */; }; + 435B54752A3F65D2001011B8 /* jniutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A12650AB4900823167 /* jniutil.h */; }; + 435B54762A3F65D2001011B8 /* socketoptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339D2650AB4900823167 /* socketoptions.h */; }; + 435B54772A3F65D2001011B8 /* filedescriptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AC2650AB4A00823167 /* filedescriptors.h */; }; + 435B54782A3F65D2001011B8 /* exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A82650AB4A00823167 /* exceptions.h */; }; + 435B54792A3F65D2001011B8 /* capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A42650AB4900823167 /* capabilities.h */; }; + 435B547A2A3F65D2001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C02650AB4D00823167 /* org_newsclub_net_unix_NativeUnixSocket.h */; }; + 435B547B2A3F65D2001011B8 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B42650AB4B00823167 /* socket.h */; }; + 435B547C2A3F65D2001011B8 /* ckmacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BF2650AB4D00823167 /* ckmacros.h */; }; + 435B547D2A3F65D2001011B8 /* address.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B92650AB4C00823167 /* address.h */; }; + 435B547E2A3F65D2001011B8 /* polling.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A92650AB4A00823167 /* polling.h */; }; + 435B547F2A3F65D2001011B8 /* accept.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BB2650AB4C00823167 /* accept.h */; }; + 435B54802A3F65D2001011B8 /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AD2650AB4A00823167 /* reflection.h */; }; + 435B54812A3F65D2001011B8 /* jniport.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C12650AC3200823167 /* jniport.h */; }; + 435B54822A3F65D2001011B8 /* receive.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A62650AB4A00823167 /* receive.h */; }; + 435B54832A3F65D2001011B8 /* credentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AE2650AB4A00823167 /* credentials.h */; }; + 435B54842A3F65D2001011B8 /* bind.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339F2650AB4900823167 /* bind.h */; }; + 435B54862A3F65D2001011B8 /* filedescriptors.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C42650AB4E00823167 /* filedescriptors.c */; }; + 435B54872A3F65D2001011B8 /* bind.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C32650AB4D00823167 /* bind.c */; }; + 435B54882A3F65D2001011B8 /* address.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339C2650AB4900823167 /* address.c */; }; + 435B54892A3F65D2001011B8 /* vsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 4355474728F6E6310042E830 /* vsock.c */; }; + 435B548A2A3F65D2001011B8 /* send.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B22650AB4B00823167 /* send.c */; }; + 435B548B2A3F65D2001011B8 /* socket.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C22650AB4D00823167 /* socket.c */; }; + 435B548C2A3F65D2001011B8 /* pipe.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DF82265EEC930024822F /* pipe.c */; }; + 435B548D2A3F65D2001011B8 /* connect.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BC2650AB4C00823167 /* connect.c */; }; + 435B548E2A3F65D2001011B8 /* capabilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B02650AB4B00823167 /* capabilities.c */; }; + 435B548F2A3F65D2001011B8 /* socketoptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B52650AB4B00823167 /* socketoptions.c */; }; + 435B54902A3F65D2001011B8 /* polling.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C12650AB4D00823167 /* polling.c */; }; + 435B54912A3F65D2001011B8 /* receive.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AB2650AB4A00823167 /* receive.c */; }; + 435B54922A3F65D2001011B8 /* jniutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A32650AB4900823167 /* jniutil.c */; }; + 435B54932A3F65D2001011B8 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B32650AB4B00823167 /* init.c */; }; + 435B54942A3F65D2001011B8 /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; + 435B54952A3F65D2001011B8 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A02650AB4900823167 /* config.c */; }; + 435B54962A3F65D2001011B8 /* credentials.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C62650AB4E00823167 /* credentials.c */; }; + 435B54972A3F65D2001011B8 /* socketpair.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DFB5266082000024822F /* socketpair.c */; }; + 435B54982A3F65D2001011B8 /* exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BD2650AB4C00823167 /* exceptions.c */; }; + 435B54992A3F65D2001011B8 /* reflection.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BA2650AB4C00823167 /* reflection.c */; }; + 435B549A2A3F65D2001011B8 /* listen.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AF2650AB4B00823167 /* listen.c */; }; + 435B549B2A3F65D2001011B8 /* ancillary.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A52650AB4900823167 /* ancillary.c */; }; + 435B549C2A3F65D2001011B8 /* accept.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339E2650AB4900823167 /* accept.c */; }; + 435B54A62A3F65F4001011B8 /* tipc.h in Headers */ = {isa = PBXBuildFile; fileRef = 436E165F27FBB70B00A553EC /* tipc.h */; }; + 435B54A72A3F65F4001011B8 /* jni.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C02650AC3200823167 /* jni.h */; }; + 435B54A82A3F65F4001011B8 /* ancillary.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C52650AB4E00823167 /* ancillary.h */; }; + 435B54A92A3F65F4001011B8 /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; + 435B54AA2A3F65F4001011B8 /* init.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BE2650AB4D00823167 /* init.h */; }; + 435B54AB2A3F65F4001011B8 /* send.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AA2650AB4A00823167 /* send.h */; }; + 435B54AC2A3F65F4001011B8 /* listen.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B12650AB4B00823167 /* listen.h */; }; + 435B54AD2A3F65F4001011B8 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A22650AB4900823167 /* config.h */; }; + 435B54AE2A3F65F4001011B8 /* socketpair.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DFB6266082000024822F /* socketpair.h */; }; + 435B54AF2A3F65F4001011B8 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; + 435B54B02A3F65F4001011B8 /* connect.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A72650AB4A00823167 /* connect.h */; }; + 435B54B12A3F65F4001011B8 /* jniutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A12650AB4900823167 /* jniutil.h */; }; + 435B54B22A3F65F4001011B8 /* socketoptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339D2650AB4900823167 /* socketoptions.h */; }; + 435B54B32A3F65F4001011B8 /* filedescriptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AC2650AB4A00823167 /* filedescriptors.h */; }; + 435B54B42A3F65F4001011B8 /* exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A82650AB4A00823167 /* exceptions.h */; }; + 435B54B52A3F65F4001011B8 /* capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A42650AB4900823167 /* capabilities.h */; }; + 435B54B62A3F65F4001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C02650AB4D00823167 /* org_newsclub_net_unix_NativeUnixSocket.h */; }; + 435B54B72A3F65F4001011B8 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B42650AB4B00823167 /* socket.h */; }; + 435B54B82A3F65F4001011B8 /* ckmacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BF2650AB4D00823167 /* ckmacros.h */; }; + 435B54B92A3F65F4001011B8 /* address.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B92650AB4C00823167 /* address.h */; }; + 435B54BA2A3F65F4001011B8 /* polling.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A92650AB4A00823167 /* polling.h */; }; + 435B54BB2A3F65F4001011B8 /* accept.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BB2650AB4C00823167 /* accept.h */; }; + 435B54BC2A3F65F4001011B8 /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AD2650AB4A00823167 /* reflection.h */; }; + 435B54BD2A3F65F4001011B8 /* jniport.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C12650AC3200823167 /* jniport.h */; }; + 435B54BE2A3F65F4001011B8 /* receive.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A62650AB4A00823167 /* receive.h */; }; + 435B54BF2A3F65F4001011B8 /* credentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AE2650AB4A00823167 /* credentials.h */; }; + 435B54C02A3F65F4001011B8 /* bind.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339F2650AB4900823167 /* bind.h */; }; + 435B54C22A3F65F4001011B8 /* filedescriptors.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C42650AB4E00823167 /* filedescriptors.c */; }; + 435B54C32A3F65F4001011B8 /* bind.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C32650AB4D00823167 /* bind.c */; }; + 435B54C42A3F65F4001011B8 /* address.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339C2650AB4900823167 /* address.c */; }; + 435B54C52A3F65F4001011B8 /* vsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 4355474728F6E6310042E830 /* vsock.c */; }; + 435B54C62A3F65F4001011B8 /* send.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B22650AB4B00823167 /* send.c */; }; + 435B54C72A3F65F4001011B8 /* socket.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C22650AB4D00823167 /* socket.c */; }; + 435B54C82A3F65F4001011B8 /* pipe.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DF82265EEC930024822F /* pipe.c */; }; + 435B54C92A3F65F4001011B8 /* connect.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BC2650AB4C00823167 /* connect.c */; }; + 435B54CA2A3F65F4001011B8 /* capabilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B02650AB4B00823167 /* capabilities.c */; }; + 435B54CB2A3F65F4001011B8 /* socketoptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B52650AB4B00823167 /* socketoptions.c */; }; + 435B54CC2A3F65F4001011B8 /* polling.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C12650AB4D00823167 /* polling.c */; }; + 435B54CD2A3F65F4001011B8 /* receive.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AB2650AB4A00823167 /* receive.c */; }; + 435B54CE2A3F65F4001011B8 /* jniutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A32650AB4900823167 /* jniutil.c */; }; + 435B54CF2A3F65F4001011B8 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B32650AB4B00823167 /* init.c */; }; + 435B54D02A3F65F4001011B8 /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; + 435B54D12A3F65F4001011B8 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A02650AB4900823167 /* config.c */; }; + 435B54D22A3F65F4001011B8 /* credentials.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C62650AB4E00823167 /* credentials.c */; }; + 435B54D32A3F65F4001011B8 /* socketpair.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DFB5266082000024822F /* socketpair.c */; }; + 435B54D42A3F65F4001011B8 /* exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BD2650AB4C00823167 /* exceptions.c */; }; + 435B54D52A3F65F4001011B8 /* reflection.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BA2650AB4C00823167 /* reflection.c */; }; + 435B54D62A3F65F4001011B8 /* listen.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AF2650AB4B00823167 /* listen.c */; }; + 435B54D72A3F65F4001011B8 /* ancillary.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A52650AB4900823167 /* ancillary.c */; }; + 435B54D82A3F65F4001011B8 /* accept.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339E2650AB4900823167 /* accept.c */; }; + 435B54E22A3F661F001011B8 /* tipc.h in Headers */ = {isa = PBXBuildFile; fileRef = 436E165F27FBB70B00A553EC /* tipc.h */; }; + 435B54E32A3F661F001011B8 /* jni.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C02650AC3200823167 /* jni.h */; }; + 435B54E42A3F661F001011B8 /* ancillary.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C52650AB4E00823167 /* ancillary.h */; }; + 435B54E52A3F661F001011B8 /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; + 435B54E62A3F661F001011B8 /* init.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BE2650AB4D00823167 /* init.h */; }; + 435B54E72A3F661F001011B8 /* send.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AA2650AB4A00823167 /* send.h */; }; + 435B54E82A3F661F001011B8 /* listen.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B12650AB4B00823167 /* listen.h */; }; + 435B54E92A3F661F001011B8 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A22650AB4900823167 /* config.h */; }; + 435B54EA2A3F661F001011B8 /* socketpair.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DFB6266082000024822F /* socketpair.h */; }; + 435B54EB2A3F661F001011B8 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; + 435B54EC2A3F661F001011B8 /* connect.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A72650AB4A00823167 /* connect.h */; }; + 435B54ED2A3F661F001011B8 /* jniutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A12650AB4900823167 /* jniutil.h */; }; + 435B54EE2A3F661F001011B8 /* socketoptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339D2650AB4900823167 /* socketoptions.h */; }; + 435B54EF2A3F661F001011B8 /* filedescriptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AC2650AB4A00823167 /* filedescriptors.h */; }; + 435B54F02A3F661F001011B8 /* exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A82650AB4A00823167 /* exceptions.h */; }; + 435B54F12A3F661F001011B8 /* capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A42650AB4900823167 /* capabilities.h */; }; + 435B54F22A3F661F001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C02650AB4D00823167 /* org_newsclub_net_unix_NativeUnixSocket.h */; }; + 435B54F32A3F661F001011B8 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B42650AB4B00823167 /* socket.h */; }; + 435B54F42A3F661F001011B8 /* ckmacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BF2650AB4D00823167 /* ckmacros.h */; }; + 435B54F52A3F661F001011B8 /* address.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B92650AB4C00823167 /* address.h */; }; + 435B54F62A3F661F001011B8 /* polling.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A92650AB4A00823167 /* polling.h */; }; + 435B54F72A3F661F001011B8 /* accept.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BB2650AB4C00823167 /* accept.h */; }; + 435B54F82A3F661F001011B8 /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AD2650AB4A00823167 /* reflection.h */; }; + 435B54F92A3F661F001011B8 /* jniport.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C12650AC3200823167 /* jniport.h */; }; + 435B54FA2A3F661F001011B8 /* receive.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A62650AB4A00823167 /* receive.h */; }; + 435B54FB2A3F661F001011B8 /* credentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AE2650AB4A00823167 /* credentials.h */; }; + 435B54FC2A3F661F001011B8 /* bind.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339F2650AB4900823167 /* bind.h */; }; + 435B54FE2A3F661F001011B8 /* filedescriptors.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C42650AB4E00823167 /* filedescriptors.c */; }; + 435B54FF2A3F661F001011B8 /* bind.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C32650AB4D00823167 /* bind.c */; }; + 435B55002A3F661F001011B8 /* address.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339C2650AB4900823167 /* address.c */; }; + 435B55012A3F661F001011B8 /* vsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 4355474728F6E6310042E830 /* vsock.c */; }; + 435B55022A3F661F001011B8 /* send.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B22650AB4B00823167 /* send.c */; }; + 435B55032A3F661F001011B8 /* socket.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C22650AB4D00823167 /* socket.c */; }; + 435B55042A3F661F001011B8 /* pipe.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DF82265EEC930024822F /* pipe.c */; }; + 435B55052A3F661F001011B8 /* connect.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BC2650AB4C00823167 /* connect.c */; }; + 435B55062A3F661F001011B8 /* capabilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B02650AB4B00823167 /* capabilities.c */; }; + 435B55072A3F661F001011B8 /* socketoptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B52650AB4B00823167 /* socketoptions.c */; }; + 435B55082A3F661F001011B8 /* polling.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C12650AB4D00823167 /* polling.c */; }; + 435B55092A3F661F001011B8 /* receive.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AB2650AB4A00823167 /* receive.c */; }; + 435B550A2A3F661F001011B8 /* jniutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A32650AB4900823167 /* jniutil.c */; }; + 435B550B2A3F661F001011B8 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B32650AB4B00823167 /* init.c */; }; + 435B550C2A3F661F001011B8 /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; + 435B550D2A3F661F001011B8 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A02650AB4900823167 /* config.c */; }; + 435B550E2A3F661F001011B8 /* credentials.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C62650AB4E00823167 /* credentials.c */; }; + 435B550F2A3F661F001011B8 /* socketpair.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DFB5266082000024822F /* socketpair.c */; }; + 435B55102A3F661F001011B8 /* exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BD2650AB4C00823167 /* exceptions.c */; }; + 435B55112A3F661F001011B8 /* reflection.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BA2650AB4C00823167 /* reflection.c */; }; + 435B55122A3F661F001011B8 /* listen.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AF2650AB4B00823167 /* listen.c */; }; + 435B55132A3F661F001011B8 /* ancillary.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A52650AB4900823167 /* ancillary.c */; }; + 435B55142A3F661F001011B8 /* accept.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339E2650AB4900823167 /* accept.c */; }; 43684D82282EE24B005F2989 /* tipc.h in Headers */ = {isa = PBXBuildFile; fileRef = 436E165F27FBB70B00A553EC /* tipc.h */; }; 43684D83282EE24B005F2989 /* jni.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C02650AC3200823167 /* jni.h */; }; 43684D84282EE24B005F2989 /* ancillary.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C52650AB4E00823167 /* ancillary.h */; }; @@ -808,6 +962,54 @@ 436E168027FBB70B00A553EC /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; 436E168127FBB70B00A553EC /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; 436E168227FBB70B00A553EC /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; + 438F4D972A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D982A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D992A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9A2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9B2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9C2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9D2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9E2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4D9F2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA02A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA12A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA22A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA32A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA42A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA52A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA62A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA72A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA82A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DA92A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAA2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAB2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAC2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAD2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAE2A6AF05000546E2C /* afsystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 438F4D952A6AF05000546E2C /* afsystem.c */; }; + 438F4DAF2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB02A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB12A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB22A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB32A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB42A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB52A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB62A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB72A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB82A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DB92A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBA2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBB2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBC2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBD2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBE2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DBF2A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC02A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC12A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC22A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC32A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC42A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC52A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; + 438F4DC62A6AF05100546E2C /* afsystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 438F4D962A6AF05000546E2C /* afsystem.h */; }; 43B3DF83265EEC930024822F /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; 43B3DF84265EEC930024822F /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; 43B3DF85265EEC930024822F /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; @@ -1040,6 +1242,56 @@ 43B3E0A5266971630024822F /* listen.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AF2650AB4B00823167 /* listen.c */; }; 43B3E0A6266971630024822F /* ancillary.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A52650AB4900823167 /* ancillary.c */; }; 43B3E0A7266971630024822F /* accept.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339E2650AB4900823167 /* accept.c */; }; + 43C57CC72A38F370001DEA62 /* tipc.h in Headers */ = {isa = PBXBuildFile; fileRef = 436E165F27FBB70B00A553EC /* tipc.h */; }; + 43C57CC82A38F370001DEA62 /* jni.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C02650AC3200823167 /* jni.h */; }; + 43C57CC92A38F370001DEA62 /* ancillary.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C52650AB4E00823167 /* ancillary.h */; }; + 43C57CCA2A38F370001DEA62 /* pipe.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DF81265EEC930024822F /* pipe.h */; }; + 43C57CCB2A38F370001DEA62 /* init.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BE2650AB4D00823167 /* init.h */; }; + 43C57CCC2A38F370001DEA62 /* send.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AA2650AB4A00823167 /* send.h */; }; + 43C57CCD2A38F370001DEA62 /* listen.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B12650AB4B00823167 /* listen.h */; }; + 43C57CCE2A38F370001DEA62 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A22650AB4900823167 /* config.h */; }; + 43C57CCF2A38F370001DEA62 /* socketpair.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B3DFB6266082000024822F /* socketpair.h */; }; + 43C57CD02A38F370001DEA62 /* vsock.h in Headers */ = {isa = PBXBuildFile; fileRef = 4355474828F6E6310042E830 /* vsock.h */; }; + 43C57CD12A38F370001DEA62 /* connect.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A72650AB4A00823167 /* connect.h */; }; + 43C57CD22A38F370001DEA62 /* jniutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A12650AB4900823167 /* jniutil.h */; }; + 43C57CD32A38F370001DEA62 /* socketoptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339D2650AB4900823167 /* socketoptions.h */; }; + 43C57CD42A38F370001DEA62 /* filedescriptors.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AC2650AB4A00823167 /* filedescriptors.h */; }; + 43C57CD52A38F370001DEA62 /* exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A82650AB4A00823167 /* exceptions.h */; }; + 43C57CD62A38F370001DEA62 /* capabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A42650AB4900823167 /* capabilities.h */; }; + 43C57CD72A38F370001DEA62 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233C02650AB4D00823167 /* org_newsclub_net_unix_NativeUnixSocket.h */; }; + 43C57CD82A38F370001DEA62 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B42650AB4B00823167 /* socket.h */; }; + 43C57CD92A38F370001DEA62 /* ckmacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BF2650AB4D00823167 /* ckmacros.h */; }; + 43C57CDA2A38F370001DEA62 /* address.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233B92650AB4C00823167 /* address.h */; }; + 43C57CDB2A38F370001DEA62 /* polling.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A92650AB4A00823167 /* polling.h */; }; + 43C57CDC2A38F370001DEA62 /* accept.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233BB2650AB4C00823167 /* accept.h */; }; + 43C57CDD2A38F370001DEA62 /* reflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AD2650AB4A00823167 /* reflection.h */; }; + 43C57CDE2A38F370001DEA62 /* jniport.h in Headers */ = {isa = PBXBuildFile; fileRef = 430235C12650AC3200823167 /* jniport.h */; }; + 43C57CDF2A38F370001DEA62 /* receive.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233A62650AB4A00823167 /* receive.h */; }; + 43C57CE02A38F370001DEA62 /* credentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 430233AE2650AB4A00823167 /* credentials.h */; }; + 43C57CE12A38F370001DEA62 /* bind.h in Headers */ = {isa = PBXBuildFile; fileRef = 4302339F2650AB4900823167 /* bind.h */; }; + 43C57CE32A38F370001DEA62 /* filedescriptors.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C42650AB4E00823167 /* filedescriptors.c */; }; + 43C57CE42A38F370001DEA62 /* bind.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C32650AB4D00823167 /* bind.c */; }; + 43C57CE52A38F370001DEA62 /* address.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339C2650AB4900823167 /* address.c */; }; + 43C57CE62A38F370001DEA62 /* vsock.c in Sources */ = {isa = PBXBuildFile; fileRef = 4355474728F6E6310042E830 /* vsock.c */; }; + 43C57CE72A38F370001DEA62 /* send.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B22650AB4B00823167 /* send.c */; }; + 43C57CE82A38F370001DEA62 /* socket.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C22650AB4D00823167 /* socket.c */; }; + 43C57CE92A38F370001DEA62 /* pipe.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DF82265EEC930024822F /* pipe.c */; }; + 43C57CEA2A38F370001DEA62 /* connect.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BC2650AB4C00823167 /* connect.c */; }; + 43C57CEB2A38F370001DEA62 /* capabilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B02650AB4B00823167 /* capabilities.c */; }; + 43C57CEC2A38F370001DEA62 /* socketoptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B52650AB4B00823167 /* socketoptions.c */; }; + 43C57CED2A38F370001DEA62 /* polling.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C12650AB4D00823167 /* polling.c */; }; + 43C57CEE2A38F370001DEA62 /* receive.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AB2650AB4A00823167 /* receive.c */; }; + 43C57CEF2A38F370001DEA62 /* jniutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A32650AB4900823167 /* jniutil.c */; }; + 43C57CF02A38F370001DEA62 /* init.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233B32650AB4B00823167 /* init.c */; }; + 43C57CF12A38F370001DEA62 /* tipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 436E166027FBB70B00A553EC /* tipc.c */; }; + 43C57CF22A38F370001DEA62 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A02650AB4900823167 /* config.c */; }; + 43C57CF32A38F370001DEA62 /* credentials.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233C62650AB4E00823167 /* credentials.c */; }; + 43C57CF42A38F370001DEA62 /* socketpair.c in Sources */ = {isa = PBXBuildFile; fileRef = 43B3DFB5266082000024822F /* socketpair.c */; }; + 43C57CF52A38F370001DEA62 /* exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BD2650AB4C00823167 /* exceptions.c */; }; + 43C57CF62A38F370001DEA62 /* reflection.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233BA2650AB4C00823167 /* reflection.c */; }; + 43C57CF72A38F370001DEA62 /* listen.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233AF2650AB4B00823167 /* listen.c */; }; + 43C57CF82A38F370001DEA62 /* ancillary.c in Sources */ = {isa = PBXBuildFile; fileRef = 430233A52650AB4900823167 /* ancillary.c */; }; + 43C57CF92A38F370001DEA62 /* accept.c in Sources */ = {isa = PBXBuildFile; fileRef = 4302339E2650AB4900823167 /* accept.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1127,6 +1379,27 @@ remoteGlobalIDString = 432EF1F7261ADEDD00F4A583; remoteInfo = "armv6--linux-gnueabihf"; }; + 435B54A22A3F65ED001011B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43D436B0261A55D0002D0C12 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 435B54682A3F65D2001011B8; + remoteInfo = "x86_64-linux-android30"; + }; + 435B54DE2A3F6617001011B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43D436B0261A55D0002D0C12 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 435B54A42A3F65F4001011B8; + remoteInfo = "i686-linux-android30"; + }; + 435B551A2A3F6658001011B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43D436B0261A55D0002D0C12 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 435B54E02A3F661F001011B8; + remoteInfo = "arm-linux-androideabi30"; + }; 43684DB8282EE2CA005F2989 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 43D436B0261A55D0002D0C12 /* Project object */; @@ -1183,6 +1456,13 @@ remoteGlobalIDString = 432EF206261AE29200F4A583; remoteInfo = "x86_64-apple-darwin18.2.0"; }; + 43EE6B462A3B3D0700E6F8F2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 43D436B0261A55D0002D0C12 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 43C57CC52A38F370001DEA62; + remoteInfo = "aarch64-linux-android30"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -1240,12 +1520,17 @@ 432EF212261AE29200F4A583 /* libjunixsocket-x86_64-apple-darwin18.2.0.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-x86_64-apple-darwin18.2.0.dylib"; sourceTree = BUILT_PRODUCTS_DIR; }; 4355474728F6E6310042E830 /* vsock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vsock.c; path = src/main/c/vsock.c; sourceTree = ""; }; 4355474828F6E6310042E830 /* vsock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vsock.h; path = src/main/c/vsock.h; sourceTree = ""; }; + 435B54A12A3F65D2001011B8 /* libjunixsocket-x86_64-linux-android30.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-x86_64-linux-android30.so"; sourceTree = BUILT_PRODUCTS_DIR; }; + 435B54DD2A3F65F4001011B8 /* libjunixsocket-i686-linux-android30.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-i686-linux-android30.so"; sourceTree = BUILT_PRODUCTS_DIR; }; + 435B55192A3F661F001011B8 /* libjunixsocket-arm-linux-androideabi30.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-arm-linux-androideabi30.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 43663EE2261AECC400CEED08 /* libjunixsocket-current.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-current.dylib"; sourceTree = BUILT_PRODUCTS_DIR; }; 43684DB7282EE24B005F2989 /* libjunixsocket-aarch64-w64-mingw32.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-aarch64-w64-mingw32.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 436B5AB728244CB500574CD4 /* libjunixsocket-powerpc-ibm-aix7.2.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-powerpc-ibm-aix7.2.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 436B5AF12826F49A00574CD4 /* libjunixsocket-powerpc-ibm-os400.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-powerpc-ibm-os400.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 436E165F27FBB70B00A553EC /* tipc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = tipc.h; path = src/main/c/tipc.h; sourceTree = ""; }; 436E166027FBB70B00A553EC /* tipc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = tipc.c; path = src/main/c/tipc.c; sourceTree = ""; }; + 438F4D952A6AF05000546E2C /* afsystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = afsystem.c; path = src/main/c/afsystem.c; sourceTree = ""; }; + 438F4D962A6AF05000546E2C /* afsystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = afsystem.h; path = src/main/c/afsystem.h; sourceTree = ""; }; 43B3DF81265EEC930024822F /* pipe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = pipe.h; path = src/main/c/pipe.h; sourceTree = ""; }; 43B3DF82265EEC930024822F /* pipe.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = pipe.c; path = src/main/c/pipe.c; sourceTree = ""; }; 43B3DFB5266082000024822F /* socketpair.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = socketpair.c; path = src/main/c/socketpair.c; sourceTree = ""; }; @@ -1254,6 +1539,7 @@ 43B3E03C26672FD30024822F /* libjunixsocket-x86_64-unknown-linux-gnu.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-x86_64-unknown-linux-gnu.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 43B3E074266756540024822F /* libjunixsocket-amd64-unknown-openbsd6.9.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-amd64-unknown-openbsd6.9.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 43B3E0AC266971630024822F /* libjunixsocket-sparc-sun-solaris2.11.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-sparc-sun-solaris2.11.so"; sourceTree = BUILT_PRODUCTS_DIR; }; + 43C57CFE2A38F370001DEA62 /* libjunixsocket-aarch64-linux-android30.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-aarch64-linux-android30.so"; sourceTree = BUILT_PRODUCTS_DIR; }; 43C5FB7D261AC3700088E301 /* libjunixsocket-arm64-apple-macos11.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-arm64-apple-macos11.dylib"; sourceTree = BUILT_PRODUCTS_DIR; }; 43D436BC261A564C002D0C12 /* libjunixsocket-x86_64-alpine-linux-musl.so */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjunixsocket-x86_64-alpine-linux-musl.so"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -1329,6 +1615,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 435B549D2A3F65D2001011B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54D92A3F65F4001011B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B55152A3F661F001011B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 43663EDE261AECC400CEED08 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1385,6 +1692,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 43C57CFA2A38F370001DEA62 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 43C5FB79261AC3700088E301 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1430,6 +1744,8 @@ 430233BB2650AB4C00823167 /* accept.h */, 4302339C2650AB4900823167 /* address.c */, 430233B92650AB4C00823167 /* address.h */, + 438F4D952A6AF05000546E2C /* afsystem.c */, + 438F4D962A6AF05000546E2C /* afsystem.h */, 430233A52650AB4900823167 /* ancillary.c */, 430233C52650AB4E00823167 /* ancillary.h */, 430233C32650AB4D00823167 /* bind.c */, @@ -1515,6 +1831,10 @@ 436B5AB728244CB500574CD4 /* libjunixsocket-powerpc-ibm-aix7.2.so */, 436B5AF12826F49A00574CD4 /* libjunixsocket-powerpc-ibm-os400.so */, 43684DB7282EE24B005F2989 /* libjunixsocket-aarch64-w64-mingw32.so */, + 43C57CFE2A38F370001DEA62 /* libjunixsocket-aarch64-linux-android30.so */, + 435B54A12A3F65D2001011B8 /* libjunixsocket-x86_64-linux-android30.so */, + 435B54DD2A3F65F4001011B8 /* libjunixsocket-i686-linux-android30.so */, + 435B55192A3F661F001011B8 /* libjunixsocket-arm-linux-androideabi30.so */, ); name = Products; sourceTree = ""; @@ -1546,6 +1866,7 @@ 4304A602266DFCEF003BCEDF /* socket.h in Headers */, 4304A603266DFCEF003BCEDF /* ckmacros.h in Headers */, 4304A604266DFCEF003BCEDF /* address.h in Headers */, + 438F4DC22A6AF05100546E2C /* afsystem.h in Headers */, 4304A605266DFCEF003BCEDF /* polling.h in Headers */, 4304A606266DFCEF003BCEDF /* accept.h in Headers */, 4304A607266DFCEF003BCEDF /* reflection.h in Headers */, @@ -1580,6 +1901,7 @@ 430234E82650AB4F00823167 /* socket.h in Headers */, 430235602650AB4F00823167 /* ckmacros.h in Headers */, 430235182650AB4F00823167 /* address.h in Headers */, + 438F4DB02A6AF05100546E2C /* afsystem.h in Headers */, 430234642650AB4E00823167 /* polling.h in Headers */, 430235302650AB4F00823167 /* accept.h in Headers */, 430234942650AB4E00823167 /* reflection.h in Headers */, @@ -1614,6 +1936,7 @@ 430234F22650AB4F00823167 /* socket.h in Headers */, 4302356A2650AB4F00823167 /* ckmacros.h in Headers */, 430235222650AB4F00823167 /* address.h in Headers */, + 438F4DBE2A6AF05100546E2C /* afsystem.h in Headers */, 4302346E2650AB4E00823167 /* polling.h in Headers */, 4302353A2650AB4F00823167 /* accept.h in Headers */, 4302349E2650AB4E00823167 /* reflection.h in Headers */, @@ -1648,6 +1971,7 @@ 430234EB2650AB4F00823167 /* socket.h in Headers */, 430235632650AB4F00823167 /* ckmacros.h in Headers */, 4302351B2650AB4F00823167 /* address.h in Headers */, + 438F4DB42A6AF05100546E2C /* afsystem.h in Headers */, 430234672650AB4E00823167 /* polling.h in Headers */, 430235332650AB4F00823167 /* accept.h in Headers */, 430234972650AB4E00823167 /* reflection.h in Headers */, @@ -1682,6 +2006,7 @@ 430234EE2650AB4F00823167 /* socket.h in Headers */, 430235662650AB4F00823167 /* ckmacros.h in Headers */, 4302351E2650AB4F00823167 /* address.h in Headers */, + 438F4DB92A6AF05100546E2C /* afsystem.h in Headers */, 4302346A2650AB4E00823167 /* polling.h in Headers */, 430235362650AB4F00823167 /* accept.h in Headers */, 4302349A2650AB4E00823167 /* reflection.h in Headers */, @@ -1716,6 +2041,7 @@ 430234F12650AB4F00823167 /* socket.h in Headers */, 430235692650AB4F00823167 /* ckmacros.h in Headers */, 430235212650AB4F00823167 /* address.h in Headers */, + 438F4DBC2A6AF05100546E2C /* afsystem.h in Headers */, 4302346D2650AB4E00823167 /* polling.h in Headers */, 430235392650AB4F00823167 /* accept.h in Headers */, 4302349D2650AB4E00823167 /* reflection.h in Headers */, @@ -1750,6 +2076,7 @@ 430234EC2650AB4F00823167 /* socket.h in Headers */, 430235642650AB4F00823167 /* ckmacros.h in Headers */, 4302351C2650AB4F00823167 /* address.h in Headers */, + 438F4DB52A6AF05100546E2C /* afsystem.h in Headers */, 430234682650AB4E00823167 /* polling.h in Headers */, 430235342650AB4F00823167 /* accept.h in Headers */, 430234982650AB4E00823167 /* reflection.h in Headers */, @@ -1784,6 +2111,7 @@ 430234ED2650AB4F00823167 /* socket.h in Headers */, 430235652650AB4F00823167 /* ckmacros.h in Headers */, 4302351D2650AB4F00823167 /* address.h in Headers */, + 438F4DB82A6AF05100546E2C /* afsystem.h in Headers */, 430234692650AB4E00823167 /* polling.h in Headers */, 430235352650AB4F00823167 /* accept.h in Headers */, 430234992650AB4E00823167 /* reflection.h in Headers */, @@ -1818,6 +2146,7 @@ 430234EA2650AB4F00823167 /* socket.h in Headers */, 430235622650AB4F00823167 /* ckmacros.h in Headers */, 4302351A2650AB4F00823167 /* address.h in Headers */, + 438F4DB32A6AF05100546E2C /* afsystem.h in Headers */, 430234662650AB4E00823167 /* polling.h in Headers */, 430235322650AB4F00823167 /* accept.h in Headers */, 430234962650AB4E00823167 /* reflection.h in Headers */, @@ -1852,6 +2181,7 @@ 430234F02650AB4F00823167 /* socket.h in Headers */, 430235682650AB4F00823167 /* ckmacros.h in Headers */, 430235202650AB4F00823167 /* address.h in Headers */, + 438F4DBB2A6AF05100546E2C /* afsystem.h in Headers */, 4302346C2650AB4E00823167 /* polling.h in Headers */, 430235382650AB4F00823167 /* accept.h in Headers */, 4302349C2650AB4E00823167 /* reflection.h in Headers */, @@ -1862,6 +2192,111 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 435B54692A3F65D2001011B8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B546A2A3F65D2001011B8 /* tipc.h in Headers */, + 435B546B2A3F65D2001011B8 /* jni.h in Headers */, + 435B546C2A3F65D2001011B8 /* ancillary.h in Headers */, + 435B546D2A3F65D2001011B8 /* pipe.h in Headers */, + 435B546E2A3F65D2001011B8 /* init.h in Headers */, + 435B546F2A3F65D2001011B8 /* send.h in Headers */, + 435B54702A3F65D2001011B8 /* listen.h in Headers */, + 435B54712A3F65D2001011B8 /* config.h in Headers */, + 435B54722A3F65D2001011B8 /* socketpair.h in Headers */, + 435B54732A3F65D2001011B8 /* vsock.h in Headers */, + 435B54742A3F65D2001011B8 /* connect.h in Headers */, + 435B54752A3F65D2001011B8 /* jniutil.h in Headers */, + 435B54762A3F65D2001011B8 /* socketoptions.h in Headers */, + 435B54772A3F65D2001011B8 /* filedescriptors.h in Headers */, + 435B54782A3F65D2001011B8 /* exceptions.h in Headers */, + 435B54792A3F65D2001011B8 /* capabilities.h in Headers */, + 435B547A2A3F65D2001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */, + 435B547B2A3F65D2001011B8 /* socket.h in Headers */, + 435B547C2A3F65D2001011B8 /* ckmacros.h in Headers */, + 435B547D2A3F65D2001011B8 /* address.h in Headers */, + 438F4DC42A6AF05100546E2C /* afsystem.h in Headers */, + 435B547E2A3F65D2001011B8 /* polling.h in Headers */, + 435B547F2A3F65D2001011B8 /* accept.h in Headers */, + 435B54802A3F65D2001011B8 /* reflection.h in Headers */, + 435B54812A3F65D2001011B8 /* jniport.h in Headers */, + 435B54822A3F65D2001011B8 /* receive.h in Headers */, + 435B54832A3F65D2001011B8 /* credentials.h in Headers */, + 435B54842A3F65D2001011B8 /* bind.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54A52A3F65F4001011B8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B54A62A3F65F4001011B8 /* tipc.h in Headers */, + 435B54A72A3F65F4001011B8 /* jni.h in Headers */, + 435B54A82A3F65F4001011B8 /* ancillary.h in Headers */, + 435B54A92A3F65F4001011B8 /* pipe.h in Headers */, + 435B54AA2A3F65F4001011B8 /* init.h in Headers */, + 435B54AB2A3F65F4001011B8 /* send.h in Headers */, + 435B54AC2A3F65F4001011B8 /* listen.h in Headers */, + 435B54AD2A3F65F4001011B8 /* config.h in Headers */, + 435B54AE2A3F65F4001011B8 /* socketpair.h in Headers */, + 435B54AF2A3F65F4001011B8 /* vsock.h in Headers */, + 435B54B02A3F65F4001011B8 /* connect.h in Headers */, + 435B54B12A3F65F4001011B8 /* jniutil.h in Headers */, + 435B54B22A3F65F4001011B8 /* socketoptions.h in Headers */, + 435B54B32A3F65F4001011B8 /* filedescriptors.h in Headers */, + 435B54B42A3F65F4001011B8 /* exceptions.h in Headers */, + 435B54B52A3F65F4001011B8 /* capabilities.h in Headers */, + 435B54B62A3F65F4001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */, + 435B54B72A3F65F4001011B8 /* socket.h in Headers */, + 435B54B82A3F65F4001011B8 /* ckmacros.h in Headers */, + 435B54B92A3F65F4001011B8 /* address.h in Headers */, + 438F4DC52A6AF05100546E2C /* afsystem.h in Headers */, + 435B54BA2A3F65F4001011B8 /* polling.h in Headers */, + 435B54BB2A3F65F4001011B8 /* accept.h in Headers */, + 435B54BC2A3F65F4001011B8 /* reflection.h in Headers */, + 435B54BD2A3F65F4001011B8 /* jniport.h in Headers */, + 435B54BE2A3F65F4001011B8 /* receive.h in Headers */, + 435B54BF2A3F65F4001011B8 /* credentials.h in Headers */, + 435B54C02A3F65F4001011B8 /* bind.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54E12A3F661F001011B8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B54E22A3F661F001011B8 /* tipc.h in Headers */, + 435B54E32A3F661F001011B8 /* jni.h in Headers */, + 435B54E42A3F661F001011B8 /* ancillary.h in Headers */, + 435B54E52A3F661F001011B8 /* pipe.h in Headers */, + 435B54E62A3F661F001011B8 /* init.h in Headers */, + 435B54E72A3F661F001011B8 /* send.h in Headers */, + 435B54E82A3F661F001011B8 /* listen.h in Headers */, + 435B54E92A3F661F001011B8 /* config.h in Headers */, + 435B54EA2A3F661F001011B8 /* socketpair.h in Headers */, + 435B54EB2A3F661F001011B8 /* vsock.h in Headers */, + 435B54EC2A3F661F001011B8 /* connect.h in Headers */, + 435B54ED2A3F661F001011B8 /* jniutil.h in Headers */, + 435B54EE2A3F661F001011B8 /* socketoptions.h in Headers */, + 435B54EF2A3F661F001011B8 /* filedescriptors.h in Headers */, + 435B54F02A3F661F001011B8 /* exceptions.h in Headers */, + 435B54F12A3F661F001011B8 /* capabilities.h in Headers */, + 435B54F22A3F661F001011B8 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */, + 435B54F32A3F661F001011B8 /* socket.h in Headers */, + 435B54F42A3F661F001011B8 /* ckmacros.h in Headers */, + 435B54F52A3F661F001011B8 /* address.h in Headers */, + 438F4DC62A6AF05100546E2C /* afsystem.h in Headers */, + 435B54F62A3F661F001011B8 /* polling.h in Headers */, + 435B54F72A3F661F001011B8 /* accept.h in Headers */, + 435B54F82A3F661F001011B8 /* reflection.h in Headers */, + 435B54F92A3F661F001011B8 /* jniport.h in Headers */, + 435B54FA2A3F661F001011B8 /* receive.h in Headers */, + 435B54FB2A3F661F001011B8 /* credentials.h in Headers */, + 435B54FC2A3F661F001011B8 /* bind.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 43663ED7261AECC400CEED08 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1886,6 +2321,7 @@ 430234E72650AB4F00823167 /* socket.h in Headers */, 4302355F2650AB4F00823167 /* ckmacros.h in Headers */, 430235172650AB4F00823167 /* address.h in Headers */, + 438F4DAF2A6AF05100546E2C /* afsystem.h in Headers */, 430234632650AB4E00823167 /* polling.h in Headers */, 4302352F2650AB4F00823167 /* accept.h in Headers */, 430234932650AB4E00823167 /* reflection.h in Headers */, @@ -1920,6 +2356,7 @@ 43684D92282EE24B005F2989 /* socket.h in Headers */, 43684D93282EE24B005F2989 /* ckmacros.h in Headers */, 43684D94282EE24B005F2989 /* address.h in Headers */, + 438F4DBD2A6AF05100546E2C /* afsystem.h in Headers */, 43684D95282EE24B005F2989 /* polling.h in Headers */, 43684D96282EE24B005F2989 /* accept.h in Headers */, 43684D97282EE24B005F2989 /* reflection.h in Headers */, @@ -1954,6 +2391,7 @@ 436B5A9228244CB500574CD4 /* socket.h in Headers */, 436B5A9328244CB500574CD4 /* ckmacros.h in Headers */, 436B5A9428244CB500574CD4 /* address.h in Headers */, + 438F4DB62A6AF05100546E2C /* afsystem.h in Headers */, 436B5A9528244CB500574CD4 /* polling.h in Headers */, 436B5A9628244CB500574CD4 /* accept.h in Headers */, 436B5A9728244CB500574CD4 /* reflection.h in Headers */, @@ -1988,6 +2426,7 @@ 436B5ACC2826F49A00574CD4 /* socket.h in Headers */, 436B5ACD2826F49A00574CD4 /* ckmacros.h in Headers */, 436B5ACE2826F49A00574CD4 /* address.h in Headers */, + 438F4DB72A6AF05100546E2C /* afsystem.h in Headers */, 436B5ACF2826F49A00574CD4 /* polling.h in Headers */, 436B5AD02826F49A00574CD4 /* accept.h in Headers */, 436B5AD12826F49A00574CD4 /* reflection.h in Headers */, @@ -2022,6 +2461,7 @@ 43B3DFE026660A2D0024822F /* socket.h in Headers */, 43B3DFE126660A2D0024822F /* ckmacros.h in Headers */, 43B3DFE226660A2D0024822F /* address.h in Headers */, + 438F4DBF2A6AF05100546E2C /* afsystem.h in Headers */, 43B3DFE326660A2D0024822F /* polling.h in Headers */, 43B3DFE426660A2D0024822F /* accept.h in Headers */, 43B3DFE526660A2D0024822F /* reflection.h in Headers */, @@ -2056,6 +2496,7 @@ 43B3E01826672FD30024822F /* socket.h in Headers */, 43B3E01926672FD30024822F /* ckmacros.h in Headers */, 43B3E01A26672FD30024822F /* address.h in Headers */, + 438F4DC02A6AF05100546E2C /* afsystem.h in Headers */, 43B3E01B26672FD30024822F /* polling.h in Headers */, 43B3E01C26672FD30024822F /* accept.h in Headers */, 43B3E01D26672FD30024822F /* reflection.h in Headers */, @@ -2090,6 +2531,7 @@ 43B3E050266756540024822F /* socket.h in Headers */, 43B3E051266756540024822F /* ckmacros.h in Headers */, 43B3E052266756540024822F /* address.h in Headers */, + 438F4DB12A6AF05100546E2C /* afsystem.h in Headers */, 43B3E053266756540024822F /* polling.h in Headers */, 43B3E054266756540024822F /* accept.h in Headers */, 43B3E055266756540024822F /* reflection.h in Headers */, @@ -2124,6 +2566,7 @@ 43B3E088266971630024822F /* socket.h in Headers */, 43B3E089266971630024822F /* ckmacros.h in Headers */, 43B3E08A266971630024822F /* address.h in Headers */, + 438F4DC12A6AF05100546E2C /* afsystem.h in Headers */, 43B3E08B266971630024822F /* polling.h in Headers */, 43B3E08C266971630024822F /* accept.h in Headers */, 43B3E08D266971630024822F /* reflection.h in Headers */, @@ -2134,6 +2577,41 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 43C57CC62A38F370001DEA62 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 43C57CC72A38F370001DEA62 /* tipc.h in Headers */, + 43C57CC82A38F370001DEA62 /* jni.h in Headers */, + 43C57CC92A38F370001DEA62 /* ancillary.h in Headers */, + 43C57CCA2A38F370001DEA62 /* pipe.h in Headers */, + 43C57CCB2A38F370001DEA62 /* init.h in Headers */, + 43C57CCC2A38F370001DEA62 /* send.h in Headers */, + 43C57CCD2A38F370001DEA62 /* listen.h in Headers */, + 43C57CCE2A38F370001DEA62 /* config.h in Headers */, + 43C57CCF2A38F370001DEA62 /* socketpair.h in Headers */, + 43C57CD02A38F370001DEA62 /* vsock.h in Headers */, + 43C57CD12A38F370001DEA62 /* connect.h in Headers */, + 43C57CD22A38F370001DEA62 /* jniutil.h in Headers */, + 43C57CD32A38F370001DEA62 /* socketoptions.h in Headers */, + 43C57CD42A38F370001DEA62 /* filedescriptors.h in Headers */, + 43C57CD52A38F370001DEA62 /* exceptions.h in Headers */, + 43C57CD62A38F370001DEA62 /* capabilities.h in Headers */, + 43C57CD72A38F370001DEA62 /* org_newsclub_net_unix_NativeUnixSocket.h in Headers */, + 43C57CD82A38F370001DEA62 /* socket.h in Headers */, + 43C57CD92A38F370001DEA62 /* ckmacros.h in Headers */, + 43C57CDA2A38F370001DEA62 /* address.h in Headers */, + 438F4DC32A6AF05100546E2C /* afsystem.h in Headers */, + 43C57CDB2A38F370001DEA62 /* polling.h in Headers */, + 43C57CDC2A38F370001DEA62 /* accept.h in Headers */, + 43C57CDD2A38F370001DEA62 /* reflection.h in Headers */, + 43C57CDE2A38F370001DEA62 /* jniport.h in Headers */, + 43C57CDF2A38F370001DEA62 /* receive.h in Headers */, + 43C57CE02A38F370001DEA62 /* credentials.h in Headers */, + 43C57CE12A38F370001DEA62 /* bind.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 43C5FB72261AC3700088E301 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -2158,6 +2636,7 @@ 430234E92650AB4F00823167 /* socket.h in Headers */, 430235612650AB4F00823167 /* ckmacros.h in Headers */, 430235192650AB4F00823167 /* address.h in Headers */, + 438F4DB22A6AF05100546E2C /* afsystem.h in Headers */, 430234652650AB4E00823167 /* polling.h in Headers */, 430235312650AB4F00823167 /* accept.h in Headers */, 430234952650AB4E00823167 /* reflection.h in Headers */, @@ -2192,6 +2671,7 @@ 430234EF2650AB4F00823167 /* socket.h in Headers */, 430235672650AB4F00823167 /* ckmacros.h in Headers */, 4302351F2650AB4F00823167 /* address.h in Headers */, + 438F4DBA2A6AF05100546E2C /* afsystem.h in Headers */, 4302346B2650AB4E00823167 /* polling.h in Headers */, 430235372650AB4F00823167 /* accept.h in Headers */, 4302349B2650AB4E00823167 /* reflection.h in Headers */, @@ -2375,6 +2855,57 @@ productReference = 432EF212261AE29200F4A583 /* libjunixsocket-x86_64-apple-darwin18.2.0.dylib */; productType = "com.apple.product-type.library.dynamic"; }; + 435B54682A3F65D2001011B8 /* x86_64-linux-android30 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 435B549E2A3F65D2001011B8 /* Build configuration list for PBXNativeTarget "x86_64-linux-android30" */; + buildPhases = ( + 435B54692A3F65D2001011B8 /* Headers */, + 435B54852A3F65D2001011B8 /* Sources */, + 435B549D2A3F65D2001011B8 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "x86_64-linux-android30"; + productName = "junixsocket-native"; + productReference = 435B54A12A3F65D2001011B8 /* libjunixsocket-x86_64-linux-android30.so */; + productType = "com.apple.product-type.library.dynamic"; + }; + 435B54A42A3F65F4001011B8 /* i686-linux-android30 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 435B54DA2A3F65F4001011B8 /* Build configuration list for PBXNativeTarget "i686-linux-android30" */; + buildPhases = ( + 435B54A52A3F65F4001011B8 /* Headers */, + 435B54C12A3F65F4001011B8 /* Sources */, + 435B54D92A3F65F4001011B8 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "i686-linux-android30"; + productName = "junixsocket-native"; + productReference = 435B54DD2A3F65F4001011B8 /* libjunixsocket-i686-linux-android30.so */; + productType = "com.apple.product-type.library.dynamic"; + }; + 435B54E02A3F661F001011B8 /* arm-linux-androideabi30 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 435B55162A3F661F001011B8 /* Build configuration list for PBXNativeTarget "arm-linux-androideabi30" */; + buildPhases = ( + 435B54E12A3F661F001011B8 /* Headers */, + 435B54FD2A3F661F001011B8 /* Sources */, + 435B55152A3F661F001011B8 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "arm-linux-androideabi30"; + productName = "junixsocket-native"; + productReference = 435B55192A3F661F001011B8 /* libjunixsocket-arm-linux-androideabi30.so */; + productType = "com.apple.product-type.library.dynamic"; + }; 43663ED6261AECC400CEED08 /* current */ = { isa = PBXNativeTarget; buildConfigurationList = 43663EDF261AECC400CEED08 /* Build configuration list for PBXNativeTarget "current" */; @@ -2511,6 +3042,23 @@ productReference = 43B3E0AC266971630024822F /* libjunixsocket-sparc-sun-solaris2.11.so */; productType = "com.apple.product-type.library.dynamic"; }; + 43C57CC52A38F370001DEA62 /* aarch64-linux-android30 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 43C57CFB2A38F370001DEA62 /* Build configuration list for PBXNativeTarget "aarch64-linux-android30" */; + buildPhases = ( + 43C57CC62A38F370001DEA62 /* Headers */, + 43C57CE22A38F370001DEA62 /* Sources */, + 43C57CFA2A38F370001DEA62 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "aarch64-linux-android30"; + productName = "junixsocket-native"; + productReference = 43C57CFE2A38F370001DEA62 /* libjunixsocket-aarch64-linux-android30.so */; + productType = "com.apple.product-type.library.dynamic"; + }; 43C5FB71261AC3700088E301 /* arm64-apple-macos11 */ = { isa = PBXNativeTarget; buildConfigurationList = 43C5FB7A261AC3700088E301 /* Build configuration list for PBXNativeTarget "arm64-apple-macos11" */; @@ -2551,7 +3099,8 @@ 43D436B0261A55D0002D0C12 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1320; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; TargetAttributes = { 432EF186261AD52100F4A583 = { CreatedOnToolsVersion = 11.3.1; @@ -2595,6 +3144,10 @@ 43B3E00726672FD30024822F /* x86_64-unknown-linux-gnu */, 43B3E077266971630024822F /* sparc-sun-solaris2.11 */, 4304A5F1266DFCEF003BCEDF /* x86_64-portbld-dragonfly6.0 */, + 43C57CC52A38F370001DEA62 /* aarch64-linux-android30 */, + 435B54682A3F65D2001011B8 /* x86_64-linux-android30 */, + 435B54A42A3F65F4001011B8 /* i686-linux-android30 */, + 435B54E02A3F661F001011B8 /* arm-linux-androideabi30 */, ); }; /* End PBXProject section */ @@ -2627,6 +3180,7 @@ 4304A61F266DFCEF003BCEDF /* listen.c in Sources */, 4304A620266DFCEF003BCEDF /* ancillary.c in Sources */, 4304A621266DFCEF003BCEDF /* accept.c in Sources */, + 438F4DAA2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2657,6 +3211,7 @@ 430234AC2650AB4E00823167 /* listen.c in Sources */, 430234342650AB4E00823167 /* ancillary.c in Sources */, 430233E02650AB4E00823167 /* accept.c in Sources */, + 438F4D982A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2687,6 +3242,7 @@ 430234B62650AB4E00823167 /* listen.c in Sources */, 4302343E2650AB4E00823167 /* ancillary.c in Sources */, 430233EA2650AB4E00823167 /* accept.c in Sources */, + 438F4DA62A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2717,6 +3273,7 @@ 430234AF2650AB4E00823167 /* listen.c in Sources */, 430234372650AB4E00823167 /* ancillary.c in Sources */, 430233E32650AB4E00823167 /* accept.c in Sources */, + 438F4D9C2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2747,6 +3304,7 @@ 430234B22650AB4E00823167 /* listen.c in Sources */, 4302343A2650AB4E00823167 /* ancillary.c in Sources */, 430233E62650AB4E00823167 /* accept.c in Sources */, + 438F4DA12A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2777,6 +3335,7 @@ 430234B52650AB4E00823167 /* listen.c in Sources */, 4302343D2650AB4E00823167 /* ancillary.c in Sources */, 430233E92650AB4E00823167 /* accept.c in Sources */, + 438F4DA42A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2807,6 +3366,7 @@ 430234B02650AB4E00823167 /* listen.c in Sources */, 430234382650AB4E00823167 /* ancillary.c in Sources */, 430233E42650AB4E00823167 /* accept.c in Sources */, + 438F4D9D2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2837,6 +3397,7 @@ 430234B12650AB4E00823167 /* listen.c in Sources */, 430234392650AB4E00823167 /* ancillary.c in Sources */, 430233E52650AB4E00823167 /* accept.c in Sources */, + 438F4DA02A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2867,6 +3428,7 @@ 430234AE2650AB4E00823167 /* listen.c in Sources */, 430234362650AB4E00823167 /* ancillary.c in Sources */, 430233E22650AB4E00823167 /* accept.c in Sources */, + 438F4D9B2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2897,6 +3459,100 @@ 430234B42650AB4E00823167 /* listen.c in Sources */, 4302343C2650AB4E00823167 /* ancillary.c in Sources */, 430233E82650AB4E00823167 /* accept.c in Sources */, + 438F4DA32A6AF05000546E2C /* afsystem.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54852A3F65D2001011B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B54862A3F65D2001011B8 /* filedescriptors.c in Sources */, + 435B54872A3F65D2001011B8 /* bind.c in Sources */, + 435B54882A3F65D2001011B8 /* address.c in Sources */, + 435B54892A3F65D2001011B8 /* vsock.c in Sources */, + 435B548A2A3F65D2001011B8 /* send.c in Sources */, + 435B548B2A3F65D2001011B8 /* socket.c in Sources */, + 435B548C2A3F65D2001011B8 /* pipe.c in Sources */, + 435B548D2A3F65D2001011B8 /* connect.c in Sources */, + 435B548E2A3F65D2001011B8 /* capabilities.c in Sources */, + 435B548F2A3F65D2001011B8 /* socketoptions.c in Sources */, + 435B54902A3F65D2001011B8 /* polling.c in Sources */, + 435B54912A3F65D2001011B8 /* receive.c in Sources */, + 435B54922A3F65D2001011B8 /* jniutil.c in Sources */, + 435B54932A3F65D2001011B8 /* init.c in Sources */, + 435B54942A3F65D2001011B8 /* tipc.c in Sources */, + 435B54952A3F65D2001011B8 /* config.c in Sources */, + 435B54962A3F65D2001011B8 /* credentials.c in Sources */, + 435B54972A3F65D2001011B8 /* socketpair.c in Sources */, + 435B54982A3F65D2001011B8 /* exceptions.c in Sources */, + 435B54992A3F65D2001011B8 /* reflection.c in Sources */, + 435B549A2A3F65D2001011B8 /* listen.c in Sources */, + 435B549B2A3F65D2001011B8 /* ancillary.c in Sources */, + 435B549C2A3F65D2001011B8 /* accept.c in Sources */, + 438F4DAC2A6AF05000546E2C /* afsystem.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54C12A3F65F4001011B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B54C22A3F65F4001011B8 /* filedescriptors.c in Sources */, + 435B54C32A3F65F4001011B8 /* bind.c in Sources */, + 435B54C42A3F65F4001011B8 /* address.c in Sources */, + 435B54C52A3F65F4001011B8 /* vsock.c in Sources */, + 435B54C62A3F65F4001011B8 /* send.c in Sources */, + 435B54C72A3F65F4001011B8 /* socket.c in Sources */, + 435B54C82A3F65F4001011B8 /* pipe.c in Sources */, + 435B54C92A3F65F4001011B8 /* connect.c in Sources */, + 435B54CA2A3F65F4001011B8 /* capabilities.c in Sources */, + 435B54CB2A3F65F4001011B8 /* socketoptions.c in Sources */, + 435B54CC2A3F65F4001011B8 /* polling.c in Sources */, + 435B54CD2A3F65F4001011B8 /* receive.c in Sources */, + 435B54CE2A3F65F4001011B8 /* jniutil.c in Sources */, + 435B54CF2A3F65F4001011B8 /* init.c in Sources */, + 435B54D02A3F65F4001011B8 /* tipc.c in Sources */, + 435B54D12A3F65F4001011B8 /* config.c in Sources */, + 435B54D22A3F65F4001011B8 /* credentials.c in Sources */, + 435B54D32A3F65F4001011B8 /* socketpair.c in Sources */, + 435B54D42A3F65F4001011B8 /* exceptions.c in Sources */, + 435B54D52A3F65F4001011B8 /* reflection.c in Sources */, + 435B54D62A3F65F4001011B8 /* listen.c in Sources */, + 435B54D72A3F65F4001011B8 /* ancillary.c in Sources */, + 435B54D82A3F65F4001011B8 /* accept.c in Sources */, + 438F4DAD2A6AF05000546E2C /* afsystem.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 435B54FD2A3F661F001011B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 435B54FE2A3F661F001011B8 /* filedescriptors.c in Sources */, + 435B54FF2A3F661F001011B8 /* bind.c in Sources */, + 435B55002A3F661F001011B8 /* address.c in Sources */, + 435B55012A3F661F001011B8 /* vsock.c in Sources */, + 435B55022A3F661F001011B8 /* send.c in Sources */, + 435B55032A3F661F001011B8 /* socket.c in Sources */, + 435B55042A3F661F001011B8 /* pipe.c in Sources */, + 435B55052A3F661F001011B8 /* connect.c in Sources */, + 435B55062A3F661F001011B8 /* capabilities.c in Sources */, + 435B55072A3F661F001011B8 /* socketoptions.c in Sources */, + 435B55082A3F661F001011B8 /* polling.c in Sources */, + 435B55092A3F661F001011B8 /* receive.c in Sources */, + 435B550A2A3F661F001011B8 /* jniutil.c in Sources */, + 435B550B2A3F661F001011B8 /* init.c in Sources */, + 435B550C2A3F661F001011B8 /* tipc.c in Sources */, + 435B550D2A3F661F001011B8 /* config.c in Sources */, + 435B550E2A3F661F001011B8 /* credentials.c in Sources */, + 435B550F2A3F661F001011B8 /* socketpair.c in Sources */, + 435B55102A3F661F001011B8 /* exceptions.c in Sources */, + 435B55112A3F661F001011B8 /* reflection.c in Sources */, + 435B55122A3F661F001011B8 /* listen.c in Sources */, + 435B55132A3F661F001011B8 /* ancillary.c in Sources */, + 435B55142A3F661F001011B8 /* accept.c in Sources */, + 438F4DAE2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2927,6 +3583,7 @@ 430234AB2650AB4E00823167 /* listen.c in Sources */, 430234332650AB4E00823167 /* ancillary.c in Sources */, 430233DF2650AB4E00823167 /* accept.c in Sources */, + 438F4D972A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2957,6 +3614,7 @@ 43684DB0282EE24B005F2989 /* listen.c in Sources */, 43684DB1282EE24B005F2989 /* ancillary.c in Sources */, 43684DB2282EE24B005F2989 /* accept.c in Sources */, + 438F4DA52A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2987,6 +3645,7 @@ 436B5AB028244CB500574CD4 /* listen.c in Sources */, 436B5AB128244CB500574CD4 /* ancillary.c in Sources */, 436B5AB228244CB500574CD4 /* accept.c in Sources */, + 438F4D9E2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3017,6 +3676,7 @@ 436B5AEA2826F49A00574CD4 /* listen.c in Sources */, 436B5AEB2826F49A00574CD4 /* ancillary.c in Sources */, 436B5AEC2826F49A00574CD4 /* accept.c in Sources */, + 438F4D9F2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3047,6 +3707,7 @@ 43B3DFFD26660A2D0024822F /* listen.c in Sources */, 43B3DFFE26660A2D0024822F /* ancillary.c in Sources */, 43B3DFFF26660A2D0024822F /* accept.c in Sources */, + 438F4DA72A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3077,6 +3738,7 @@ 43B3E03526672FD30024822F /* listen.c in Sources */, 43B3E03626672FD30024822F /* ancillary.c in Sources */, 43B3E03726672FD30024822F /* accept.c in Sources */, + 438F4DA82A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3107,6 +3769,7 @@ 43B3E06D266756540024822F /* listen.c in Sources */, 43B3E06E266756540024822F /* ancillary.c in Sources */, 43B3E06F266756540024822F /* accept.c in Sources */, + 438F4D992A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3137,6 +3800,38 @@ 43B3E0A5266971630024822F /* listen.c in Sources */, 43B3E0A6266971630024822F /* ancillary.c in Sources */, 43B3E0A7266971630024822F /* accept.c in Sources */, + 438F4DA92A6AF05000546E2C /* afsystem.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43C57CE22A38F370001DEA62 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 43C57CE32A38F370001DEA62 /* filedescriptors.c in Sources */, + 43C57CE42A38F370001DEA62 /* bind.c in Sources */, + 43C57CE52A38F370001DEA62 /* address.c in Sources */, + 43C57CE62A38F370001DEA62 /* vsock.c in Sources */, + 43C57CE72A38F370001DEA62 /* send.c in Sources */, + 43C57CE82A38F370001DEA62 /* socket.c in Sources */, + 43C57CE92A38F370001DEA62 /* pipe.c in Sources */, + 43C57CEA2A38F370001DEA62 /* connect.c in Sources */, + 43C57CEB2A38F370001DEA62 /* capabilities.c in Sources */, + 43C57CEC2A38F370001DEA62 /* socketoptions.c in Sources */, + 43C57CED2A38F370001DEA62 /* polling.c in Sources */, + 43C57CEE2A38F370001DEA62 /* receive.c in Sources */, + 43C57CEF2A38F370001DEA62 /* jniutil.c in Sources */, + 43C57CF02A38F370001DEA62 /* init.c in Sources */, + 43C57CF12A38F370001DEA62 /* tipc.c in Sources */, + 43C57CF22A38F370001DEA62 /* config.c in Sources */, + 43C57CF32A38F370001DEA62 /* credentials.c in Sources */, + 43C57CF42A38F370001DEA62 /* socketpair.c in Sources */, + 43C57CF52A38F370001DEA62 /* exceptions.c in Sources */, + 43C57CF62A38F370001DEA62 /* reflection.c in Sources */, + 43C57CF72A38F370001DEA62 /* listen.c in Sources */, + 43C57CF82A38F370001DEA62 /* ancillary.c in Sources */, + 43C57CF92A38F370001DEA62 /* accept.c in Sources */, + 438F4DAB2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3167,6 +3862,7 @@ 430234AD2650AB4E00823167 /* listen.c in Sources */, 430234352650AB4E00823167 /* ancillary.c in Sources */, 430233E12650AB4E00823167 /* accept.c in Sources */, + 438F4D9A2A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3197,6 +3893,7 @@ 430234B32650AB4E00823167 /* listen.c in Sources */, 4302343B2650AB4E00823167 /* ancillary.c in Sources */, 430233E72650AB4E00823167 /* accept.c in Sources */, + 438F4DA22A6AF05000546E2C /* afsystem.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3263,6 +3960,21 @@ target = 432EF1F7261ADEDD00F4A583 /* armv6--linux-gnueabihf */; targetProxy = 432EF204261AE01000F4A583 /* PBXContainerItemProxy */; }; + 435B54A32A3F65ED001011B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 435B54682A3F65D2001011B8 /* x86_64-linux-android30 */; + targetProxy = 435B54A22A3F65ED001011B8 /* PBXContainerItemProxy */; + }; + 435B54DF2A3F6617001011B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 435B54A42A3F65F4001011B8 /* i686-linux-android30 */; + targetProxy = 435B54DE2A3F6617001011B8 /* PBXContainerItemProxy */; + }; + 435B551B2A3F6658001011B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 435B54E02A3F661F001011B8 /* arm-linux-androideabi30 */; + targetProxy = 435B551A2A3F6658001011B8 /* PBXContainerItemProxy */; + }; 43684DB9282EE2CA005F2989 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 43684D80282EE24B005F2989 /* aarch64-w64-mingw32 */; @@ -3303,6 +4015,11 @@ target = 432EF206261AE29200F4A583 /* x86_64-apple-darwin18.2.0 */; targetProxy = 43D0E25B261AE48B008F2FFE /* PBXContainerItemProxy */; }; + 43EE6B472A3B3D0700E6F8F2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 43C57CC52A38F370001DEA62 /* aarch64-linux-android30 */; + targetProxy = 43EE6B462A3B3D0700E6F8F2 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -3314,7 +4031,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3343,7 +4060,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3369,6 +4086,8 @@ buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_STYLE = Automatic; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_LDFLAGS = "-Xcrossclang-ignore-reproducible"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -3378,6 +4097,8 @@ buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_STYLE = Automatic; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_LDFLAGS = "-Xcrossclang-ignore-reproducible"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -3390,7 +4111,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3415,7 +4136,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3440,7 +4161,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3470,7 +4191,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3500,7 +4221,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3527,7 +4248,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3554,7 +4275,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3580,7 +4301,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3606,7 +4327,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3631,7 +4352,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3656,7 +4377,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3681,7 +4402,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3706,7 +4427,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3735,7 +4456,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3764,7 +4485,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3789,7 +4510,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3810,12 +4531,13 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = "x86_64-apple-darwin18.2.0"; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; LLVM_LTO = YES; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3837,12 +4559,13 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = "x86_64-apple-darwin18.2.0"; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; LLVM_LTO = YES; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3860,15 +4583,178 @@ }; name = Release; }; + 435B549F2A3F65D2001011B8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "x86_64-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Debug; + }; + 435B54A02A3F65D2001011B8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "x86_64-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Release; + }; + 435B54DB2A3F65F4001011B8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "i686-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Debug; + }; + 435B54DC2A3F65F4001011B8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "i686-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Release; + }; + 435B55172A3F661F001011B8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "arm-linux-androideabi30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Debug; + }; + 435B55182A3F661F001011B8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "arm-linux-androideabi30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Release; + }; 43663EE0261AECC400CEED08 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = current; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3888,11 +4774,12 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = current; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3916,7 +4803,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3947,7 +4834,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -3983,7 +4870,7 @@ ); LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4019,7 +4906,7 @@ ); LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4057,7 +4944,7 @@ ); LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4094,7 +4981,7 @@ ); LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4126,7 +5013,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4152,7 +5039,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4178,7 +5065,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4203,7 +5090,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4228,7 +5115,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4254,7 +5141,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4281,7 +5168,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-fno-lto", @@ -4314,7 +5201,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-fno-lto", @@ -4338,15 +5225,70 @@ }; name = Release; }; + 43C57CFC2A38F370001DEA62 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "aarch64-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Debug; + }; + 43C57CFD2A38F370001DEA62 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CROSSCLANG_TARGET = "aarch64-linux-android30"; + EXECUTABLE_EXTENSION = so; + LD_DEPENDENCY_INFO_FILE = ""; + LD_LTO_OBJECT_FILE = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + OTHER_CFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-fPIC", + "-Wno-unknown-warning-option", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-target", + "$(CROSSCLANG_TARGET)", + "-Qunused-arguments", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = crossclang.macosx1.0; + }; + name = Release; + }; 43C5FB7B261AC3700088E301 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = "arm64-apple-macos11"; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4366,11 +5308,12 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "-"; CROSSCLANG_TARGET = "arm64-apple-macos11"; EXECUTABLE_EXTENSION = dylib; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4414,6 +5357,7 @@ CODE_SIGN_STYLE = Manual; COMPILER_INDEX_STORE_ENABLE = NO; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; EXECUTABLE_PREFIX = "libjunixsocket-"; @@ -4429,7 +5373,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INTERMEDIATE_TEXT_BASED_STUBS = NO; HEADER_SEARCH_PATHS = src/main/c/jni; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "-fno-strict-overflow", @@ -4441,7 +5387,7 @@ "-fno-common", "-fvisibility=hidden", ); - OTHER_LDFLAGS = ""; + OTHER_LDFLAGS = "-Xcrossclang-ignore-reproducible"; RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; WARNING_CFLAGS = ( @@ -4463,6 +5409,7 @@ "-Wno-unused-command-line-argument", "-Werror-implicit-function-declaration", "-Wno-declaration-after-statement", + "-Wno-unsafe-buffer-usage", ); }; name = Debug; @@ -4495,6 +5442,7 @@ CODE_SIGN_STYLE = Manual; COMPILER_INDEX_STORE_ENABLE = NO; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; EXECUTABLE_PREFIX = "libjunixsocket-"; GCC_CW_ASM_SYNTAX = NO; @@ -4508,7 +5456,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INTERMEDIATE_TEXT_BASED_STUBS = NO; HEADER_SEARCH_PATHS = src/main/c/jni; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "-fno-strict-overflow", @@ -4520,7 +5470,7 @@ "-fno-common", "-fvisibility=hidden", ); - OTHER_LDFLAGS = ""; + OTHER_LDFLAGS = "-Xcrossclang-ignore-reproducible"; RUN_CLANG_STATIC_ANALYZER = YES; SKIP_INSTALL = YES; WARNING_CFLAGS = ( @@ -4542,6 +5492,7 @@ "-Wno-unused-command-line-argument", "-Werror-implicit-function-declaration", "-Wno-declaration-after-statement", + "-Wno-unsafe-buffer-usage", ); }; name = Release; @@ -4554,7 +5505,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4586,7 +5537,7 @@ EXECUTABLE_EXTENSION = so; LD_DEPENDENCY_INFO_FILE = ""; LD_LTO_OBJECT_FILE = ""; - MACOSX_DEPLOYMENT_TARGET = ""; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CFLAGS = ( "$(inherited)", "-target", @@ -4712,6 +5663,33 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 435B549E2A3F65D2001011B8 /* Build configuration list for PBXNativeTarget "x86_64-linux-android30" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 435B549F2A3F65D2001011B8 /* Debug */, + 435B54A02A3F65D2001011B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 435B54DA2A3F65F4001011B8 /* Build configuration list for PBXNativeTarget "i686-linux-android30" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 435B54DB2A3F65F4001011B8 /* Debug */, + 435B54DC2A3F65F4001011B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 435B55162A3F661F001011B8 /* Build configuration list for PBXNativeTarget "arm-linux-androideabi30" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 435B55172A3F661F001011B8 /* Debug */, + 435B55182A3F661F001011B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 43663EDF261AECC400CEED08 /* Build configuration list for PBXNativeTarget "current" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -4784,6 +5762,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 43C57CFB2A38F370001DEA62 /* Build configuration list for PBXNativeTarget "aarch64-linux-android30" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 43C57CFC2A38F370001DEA62 /* Debug */, + 43C57CFD2A38F370001DEA62 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 43C5FB7A261AC3700088E301 /* Build configuration list for PBXNativeTarget "arm64-apple-macos11" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/All Architectures.xcscheme b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/All Architectures.xcscheme index 9f879e63b..a3388611f 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/All Architectures.xcscheme +++ b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/All Architectures.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/aarch64-linux-gnu.xcscheme b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/aarch64-linux-gnu.xcscheme index 22e93454e..ca2c4659f 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/aarch64-linux-gnu.xcscheme +++ b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/aarch64-linux-gnu.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/arm64-apple-macos11.xcscheme b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/arm64-apple-macos11.xcscheme index 868ef8c02..4c0b691b9 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/arm64-apple-macos11.xcscheme +++ b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/arm64-apple-macos11.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/powerpc-ibm-aix7.2.xcscheme b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/powerpc-ibm-aix7.2.xcscheme index b3d78f413..f517e6dcc 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/powerpc-ibm-aix7.2.xcscheme +++ b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/powerpc-ibm-aix7.2.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/x86_64-portbld-dragonfly6.0.xcscheme b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/x86_64-portbld-dragonfly6.0.xcscheme index 3ec1048a4..eadd19d05 100644 --- a/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/x86_64-portbld-dragonfly6.0.xcscheme +++ b/junixsocket-native/junixsocket-native.xcodeproj/xcshareddata/xcschemes/x86_64-portbld-dragonfly6.0.xcscheme @@ -1,6 +1,6 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-native @@ -28,7 +28,7 @@ ${junixsocket.native.default.options} - + ${junixsocket.native.default.c.options} ${junixsocket.native.default.cpp.options} ${junixsocket.native.aol} @@ -83,6 +83,11 @@ llvm + + + !crossclang.disable + + @@ -144,6 +149,7 @@ codeql-nar-compile + compile nar-compile @@ -164,6 +170,71 @@ + + fix-jnilib + + + !m2e.version + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + fix-jnilib + prepare-package + + + + + + + + + + + + + + + + + + + + + + + + + + { +"resources":{ +"includes":[ +{ +"pattern":"^lib/${junixsocket.native.aol.llvm}/jni/.*" +} +]}, +"bundles":[] +} + + + + + + + + run + + + + + + + @@ -215,61 +286,6 @@ - - org.apache.maven.plugins - maven-antrun-plugin - - - fix-jnilib - prepare-package - - - - - - - - - - - - - - - - - - - - - - - - - - - { - "resources":{ - "includes":[ - { - "pattern":"^lib/${junixsocket.native.aol.llvm}/jni/.*" - } - ]}, - "bundles":[] -} - - - - - - - - run - - - - org.apache.maven.plugins maven-install-plugin diff --git a/junixsocket-native/src/main/c/.gitignore b/junixsocket-native/src/main/c/.gitignore new file mode 100644 index 000000000..1854c34c1 --- /dev/null +++ b/junixsocket-native/src/main/c/.gitignore @@ -0,0 +1,8 @@ +# Ignore assembled libraries +*.so +*.dylib +*.dll + +# Ignore assembly code +*.a +*.lst diff --git a/junixsocket-native/src/main/c/address.c b/junixsocket-native/src/main/c/address.c index 161918a85..32691e289 100644 --- a/junixsocket-native/src/main/c/address.c +++ b/junixsocket-native/src/main/c/address.c @@ -42,6 +42,17 @@ struct __attribute__((__packed__)) jux_vsock_addr { jint cid; }; +struct __attribute__((__packed__)) jux_system_addr { + jint sysaddr; + jint id; + jint unit; + jint reserved0; + jint reserved1; + jint reserved2; + jint reserved3; + jint reserved4; +}; + int domainToNative(int domain) { switch(domain) { case org_newsclub_net_unix_NativeUnixSocket_DOMAIN_UNIX: @@ -53,6 +64,10 @@ int domainToNative(int domain) { #if junixsocket_have_vsock case org_newsclub_net_unix_NativeUnixSocket_DOMAIN_VSOCK: return AF_VSOCK; +#endif +#if junixsocket_have_system + case org_newsclub_net_unix_NativeUnixSocket_DOMAIN_SYSTEM: + return AF_SYSTEM; #endif default: // do not throw: _throwException(env, kExceptionSocketException, "Unsupported domain"); @@ -131,6 +146,10 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sockAddrLengt #if junixsocket_have_vsock case AF_VSOCK: return sizeof(struct sockaddr_vm); +#endif +#if junixsocket_have_system + case AF_SYSTEM: + return sizeof(struct sockaddr_ctl); #endif default: _throwException(env, kExceptionSocketException, "Unsupported domain"); @@ -139,7 +158,7 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sockAddrLengt } static jbyteArray sockAddrUnToBytes(JNIEnv *env, struct sockaddr_un *addr, socklen_t len) { - if(len <= 0) { + if(len <= 0 || addr == NULL) { return NULL; } #if defined(junixsocket_have_sun_len) @@ -224,6 +243,29 @@ static jbyteArray sockAddrVsockToBytes(JNIEnv *env, struct sockaddr_vm *addr) { #endif +#if junixsocket_have_system + +static jbyteArray sockAddrSystemToBytes(JNIEnv *env, struct sockaddr_ctl *addr) { + CK_IGNORE_RESERVED_IDENTIFIER_BEGIN // htonl + struct jux_system_addr buf = { + .sysaddr = htonl(addr->ss_sysaddr), + .id = htonl(addr->sc_id), + .unit = htonl(addr->sc_unit), + .reserved0 = htonl(addr->sc_reserved[0]), + .reserved1 = htonl(addr->sc_reserved[1]), + .reserved2 = htonl(addr->sc_reserved[2]), + .reserved3 = htonl(addr->sc_reserved[3]), + .reserved4 = htonl(addr->sc_reserved[4]) + }; + CK_IGNORE_RESERVED_IDENTIFIER_END + + jbyteArray array = (*env)->NewByteArray(env, sizeof(typeof(buf))); + (*env)->SetByteArrayRegion(env, array, 0, sizeof(typeof(buf)), (jbyte*)&buf); + return array; +} + +#endif + /* * Class: org_newsclub_net_unix_NativeUnixSocket * Method: sockname @@ -327,6 +369,15 @@ JNIEXPORT jbyteArray JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_socknam return NULL; } return sockAddrVsockToBytes(env, &addr.vsock); +#endif +#if junixsocket_have_system + case AF_SYSTEM: + if(len > (socklen_t)sizeof(struct sockaddr_ctl)) { + _throwException(env, kExceptionSocketException, + peerName ? "peer sockname too long" : "sockname too long"); + return NULL; + } + return sockAddrSystemToBytes(env, &addr.system); #endif default: _throwException(env, kExceptionSocketException, @@ -357,6 +408,11 @@ JNIEXPORT jbyteArray JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sockAdd case AF_VSOCK: sockAddrLen = sizeof(struct sockaddr_vm); break; +#endif +#if junixsocket_have_system + case AF_SYSTEM: + sockAddrLen = sizeof(struct sockaddr_ctl); + break; #endif default: _throwException(env, kExceptionSocketException, "Unsupported domain"); @@ -396,6 +452,10 @@ JNIEXPORT jbyteArray JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sockAdd #if junixsocket_have_vsock case AF_VSOCK: return sockAddrVsockToBytes(env, (struct sockaddr_vm *)addr); +#endif +#if junixsocket_have_system + case AF_SYSTEM: + return sockAddrSystemToBytes(env, (struct sockaddr_ctl *)addr); #endif default: _throwException(env, kExceptionSocketException, "Unsupported domain"); @@ -426,6 +486,11 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_bytesToSockAd case AF_VSOCK: sockAddrLen = sizeof(struct sockaddr_vm); break; +#endif +#if junixsocket_have_system + case AF_SYSTEM: + sockAddrLen = sizeof(struct sockaddr_ctl); + break; #endif default: // do not throw: _throwException(env, kExceptionSocketException, "Unsupported domain"); @@ -502,6 +567,26 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_bytesToSockAd CK_IGNORE_RESERVED_IDENTIFIER_END } break; +#endif +#if junixsocket_have_system + case AF_SYSTEM: + { + struct jux_system_addr jaddr = {0}; + (*env)->GetByteArrayRegion(env, addressBytes, 0, sizeof(struct jux_system_addr), (void*)&jaddr); + + CK_IGNORE_RESERVED_IDENTIFIER_BEGIN // ntohl + addr->system.sc_len = sizeof(struct sockaddr_ctl); + addr->system.ss_sysaddr = (jshort)ntohl(jaddr.sysaddr); + addr->system.sc_id = ntohl(jaddr.id); + addr->system.sc_unit = ntohl(jaddr.unit); + addr->system.sc_reserved[0] = ntohl(jaddr.reserved0); + addr->system.sc_reserved[1] = ntohl(jaddr.reserved1); + addr->system.sc_reserved[2] = ntohl(jaddr.reserved2); + addr->system.sc_reserved[3] = ntohl(jaddr.reserved3); + addr->system.sc_reserved[4] = ntohl(jaddr.reserved4); + CK_IGNORE_RESERVED_IDENTIFIER_END + } + break; #endif } @@ -552,7 +637,7 @@ bool fixupSocketAddressPostError(int handle, jux_sockaddr_t *sa, socklen_t addrL CK_ARGUMENT_POTENTIALLY_UNUSED(errnum); #if defined(__linux__) && junixsocket_have_vsock - if(sa != NULL && addrLen >= sizeof(struct sockaddr_vm) && sa->addr.sa_family == AF_VSOCK) { + if(sa != NULL && addrLen >= (socklen_t)sizeof(struct sockaddr_vm) && sa->addr.sa_family == AF_VSOCK) { switch(errnum) { case EINVAL: case EADDRNOTAVAIL: diff --git a/junixsocket-native/src/main/c/address.h b/junixsocket-native/src/main/c/address.h index 7593f3ab4..1eef06e60 100644 --- a/junixsocket-native/src/main/c/address.h +++ b/junixsocket-native/src/main/c/address.h @@ -29,6 +29,9 @@ typedef union { #endif #if junixsocket_have_vsock struct sockaddr_vm vsock; +#endif +#if junixsocket_have_system + struct sockaddr_ctl system; #endif char bytes[128]; } jux_sockaddr_t; diff --git a/junixsocket-native/src/main/c/afsystem.c b/junixsocket-native/src/main/c/afsystem.c new file mode 100644 index 000000000..13578e6ab --- /dev/null +++ b/junixsocket-native/src/main/c/afsystem.c @@ -0,0 +1,76 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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. + */ + +#include "afsystem.h" + +#include "exceptions.h" +#include "filedescriptors.h" + +#if junixsocket_have_system + +static inline char* java_to_char(JNIEnv* env, jstring string) { + jsize len = (*env)->GetStringLength(env, string); + size_t bytes = (*env)->GetStringUTFLength(env, string); + char* chars = (char*) malloc(bytes + 1); + (*env)->GetStringUTFRegion(env, string, 0, len, chars); + chars[bytes] = 0; + return chars; +} + +#endif + +/* + * Class: org_newsclub_net_unix_NativeUnixSocket + * Method: systemResolveCtlId + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_systemResolveCtlId +(JNIEnv *env, jclass klazz CK_UNUSED, jobject fd, jstring ctlName) { + CK_ARGUMENT_POTENTIALLY_UNUSED(env); + CK_ARGUMENT_POTENTIALLY_UNUSED(fd); + CK_ARGUMENT_POTENTIALLY_UNUSED(ctlName); + +#if junixsocket_have_system + + struct ctl_info info = {0}; + + char *name = java_to_char(env, ctlName); + + size_t nameLen = strlen(name); + + strlcpy(info.ctl_name, name, MIN(nameLen + 1, sizeof(info.ctl_name))); + + free(name); + +// (*env)->ReleaseStringUTFChars(env, ctlName, name); + + int fdHandle = _getFD(env, fd); + + if(ioctl(fdHandle, CTLIOCGINFO, &info) != 0) { + _throwErrnumException(env, errno, NULL); + return -1; + } + + return info.ctl_id; + +#else + _throwException(env, kExceptionSocketException, "AF_SYSTEM is not supported"); + return -1; +#endif +} + diff --git a/junixsocket-native/src/main/c/afsystem.h b/junixsocket-native/src/main/c/afsystem.h new file mode 100644 index 000000000..97b2052b8 --- /dev/null +++ b/junixsocket-native/src/main/c/afsystem.h @@ -0,0 +1,24 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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 afsystem_h +#define afsystem_h + +#include "config.h" + +#endif /* afsystem_h */ diff --git a/junixsocket-native/src/main/c/bind.c b/junixsocket-native/src/main/c/bind.c index 4f94c28dc..abde64390 100644 --- a/junixsocket-native/src/main/c/bind.c +++ b/junixsocket-native/src/main/c/bind.c @@ -215,7 +215,8 @@ JNIEXPORT jlong JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_bind #endif int bindRes; - if(attempt == 0 && !reuse && addr->addr.sa_family == AF_UNIX) { + if(attempt == 0 && !reuse && addr->addr.sa_family == AF_UNIX && + (options & org_newsclub_net_unix_NativeUnixSocket_OPT_DGRAM_MODE) == 0) { // if we're not going to reuse the socket, let's try to connect first. // This avoids changing file metadata (e.g. ctime!) bindRes = -1; diff --git a/junixsocket-native/src/main/c/capabilities.c b/junixsocket-native/src/main/c/capabilities.c index a94b1347d..664322ec4 100644 --- a/junixsocket-native/src/main/c/capabilities.c +++ b/junixsocket-native/src/main/c/capabilities.c @@ -21,21 +21,25 @@ #include "filedescriptors.h" #include "init.h" +#include "reflection.h" CK_IGNORE_UNUSED_VARIABLE_BEGIN // see AFSocketCapability.java in junixsocket-common -static int CAPABILITY_PEER_CREDENTIALS = (1 << 0); -static int CAPABILITY_ANCILLARY_MESSAGES = (1 << 1); -static int CAPABILITY_FILE_DESCRIPTORS = (1 << 2); -static int CAPABILITY_ABSTRACT_NAMESPACE = (1 << 3); -static int CAPABILITY_UNIX_DATAGRAMS = (1 << 4); -static int CAPABILITY_NATIVE_SOCKETPAIR = (1 << 5); -static int CAPABILITY_FD_AS_REDIRECT = (1 << 6); -static int CAPABILITY_TIPC = (1 << 7); -static int CAPABILITY_UNIX_DOMAIN = (1 << 8); -static int CAPABILITY_VSOCK = (1 << 9); -static int CAPABILITY_VSOCK_DGRAM = (1 << 10); -static int CAPABILITY_ZERO_LENGTH_SEND = (1 << 11); +static jint CAPABILITY_PEER_CREDENTIALS = (1 << 0); +static jint CAPABILITY_ANCILLARY_MESSAGES = (1 << 1); +static jint CAPABILITY_FILE_DESCRIPTORS = (1 << 2); +static jint CAPABILITY_ABSTRACT_NAMESPACE = (1 << 3); +static jint CAPABILITY_UNIX_DATAGRAMS = (1 << 4); +static jint CAPABILITY_NATIVE_SOCKETPAIR = (1 << 5); +static jint CAPABILITY_FD_AS_REDIRECT = (1 << 6); +static jint CAPABILITY_TIPC = (1 << 7); +static jint CAPABILITY_UNIX_DOMAIN = (1 << 8); +static jint CAPABILITY_VSOCK = (1 << 9); +static jint CAPABILITY_VSOCK_DGRAM = (1 << 10); +static jint CAPABILITY_ZERO_LENGTH_SEND = (1 << 11); +static jint CAPABILITY_UNSAFE = (1 << 12); +static jint CAPABILITY_LARGE_PORTS = (1 << 13); +static jint CAPABILITY_DARWIN = (1 << 14); CK_IGNORE_UNUSED_VARIABLE_END void init_capabilities(JNIEnv *env CK_UNUSED) { @@ -52,7 +56,11 @@ void destroy_capabilities(JNIEnv *env CK_UNUSED) { JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_capabilities( JNIEnv *env CK_UNUSED, jclass clazz CK_UNUSED) { - int capabilities = 0; + jint capabilities = 0; + +#if !defined(_WIN32) + capabilities |= CAPABILITY_UNSAFE; +#endif if(supportsUNIX()) { capabilities |= CAPABILITY_UNIX_DOMAIN; @@ -108,5 +116,12 @@ defined(SO_PEERCRED) || defined(SO_PEERID) || defined(__NetBSD__) || defined(__s capabilities |= CAPABILITY_ZERO_LENGTH_SEND; } + if(supportsLargePorts()) { + capabilities |= CAPABILITY_LARGE_PORTS; + } + +#if junixsocket_have_system + capabilities |= CAPABILITY_DARWIN; +#endif return capabilities; } diff --git a/junixsocket-native/src/main/c/config.h b/junixsocket-native/src/main/c/config.h index 777db6485..214c7251f 100644 --- a/junixsocket-native/src/main/c/config.h +++ b/junixsocket-native/src/main/c/config.h @@ -46,6 +46,12 @@ CK_IGNORE_RESERVED_IDENTIFIER_END # define _POSIX_SOURCE #endif +#if _TPF_SOURCE +// s390x-ibm-tpf-gcc may errneously generate these +# undef __GLIBC__ +# undef __linux__ +#endif + #include #include #if __TOS_MVS__ @@ -73,6 +79,16 @@ CK_IGNORE_UNUSED_MACROS_END #endif #if __TOS_MVS__ +// z/OS +# undef junixsocket_have_ancillary +# undef junixsocket_have_pipe2 +# define junixsocket_use_poll_for_read +# define junixsocket_use_poll_for_accept +#endif + +#if _TPF_SOURCE +// z/TPF +# undef junixsocket_have_sun_len # undef junixsocket_have_ancillary # undef junixsocket_have_pipe2 # define junixsocket_use_poll_for_read @@ -87,6 +103,11 @@ CK_IGNORE_UNUSED_MACROS_END # define JUNIXSOCKET_HARDEN_CMSG_NXTHDR 1 #endif +#if defined(__HAIKU__) +# define junixsocket_use_poll_for_accept +# undef junixsocket_have_pipe2 +#endif + #if !defined(uint64_t) && !defined(_INT64_TYPE) && !defined(_UINT64_T) && !defined(_UINT64_T_DEFINED_) # ifdef _LP64 typedef unsigned long uint64_t; @@ -205,23 +226,34 @@ extern "C" { // Linux #ifdef __linux__ -#undef junixsocket_have_sun_len +# undef junixsocket_have_sun_len -#if !defined(JUNIXSOCKET_HARDEN_CMSG_NXTHDR) +# if !defined(JUNIXSOCKET_HARDEN_CMSG_NXTHDR) // workaround for systems using musl libc -# define JUNIXSOCKET_HARDEN_CMSG_NXTHDR 1 -#endif +# define JUNIXSOCKET_HARDEN_CMSG_NXTHDR 1 +# endif -#include -#include -#define junixsocket_have_tipc 1 +# include +# include +# define junixsocket_have_tipc 1 +#endif // __linux__ + +#if __TOS_MVS__ +// z/OS XLC doesn't have __has_include +#else #if defined(AF_VSOCK) && __has_include() #include #define junixsocket_have_vsock 1 #endif + +#if defined(__MACH__) && __has_include() +# include +# define junixsocket_have_system 1 #endif +#endif // not __TOS_MVS_ + // Solaris #if defined(__sun) || defined(__sun__) #undef junixsocket_have_sun_len diff --git a/junixsocket-native/src/main/c/credentials.c b/junixsocket-native/src/main/c/credentials.c index 7725f635d..1542eccd4 100644 --- a/junixsocket-native/src/main/c/credentials.c +++ b/junixsocket-native/src/main/c/credentials.c @@ -55,8 +55,10 @@ static int ucredFromPid(pid_t pid, struct xucred* cr) cr->cr_uid = process.kp_eproc.e_ucred.cr_uid; cr->cr_ngroups = process.kp_eproc.e_ucred.cr_ngroups; + + gid_t *groups = process.kp_eproc.e_ucred.cr_groups; for(int i=cr->cr_ngroups-1;i>=0;i--) { - cr->cr_groups[i] =process.kp_eproc.e_ucred.cr_groups[i]; + cr->cr_groups[i] = groups[i]; } return ret; diff --git a/junixsocket-native/src/main/c/exceptions.c b/junixsocket-native/src/main/c/exceptions.c index 3d1a28d9a..d8f10bdc6 100644 --- a/junixsocket-native/src/main/c/exceptions.c +++ b/junixsocket-native/src/main/c/exceptions.c @@ -34,6 +34,7 @@ static char *kExceptionClassnames[kExceptionMaxExcl] = { "org/newsclub/net/unix/InvalidArgumentSocketException", // kExceptionInvalidArgumentSocketException "org/newsclub/net/unix/AddressUnavailableSocketException", // kExceptionAddressUnavailableSocketException "org/newsclub/net/unix/OperationNotSupportedSocketException", // kExceptionOperationNotSupportedSocketException + "org/newsclub/net/unix/NoSuchDeviceSocketException", // kExceptionNoSuchDeviceSocketException }; static jclass *kExceptionClasses; @@ -46,6 +47,12 @@ void init_exceptions(JNIEnv *env) { for (int i=0; iGetMethodID(env, exc, "", "(Ljava/lang/String;)V"); @@ -97,6 +104,13 @@ void _throwException(JNIEnv* env, ExceptionType exceptionType, char* message) void _throwErrnumException(JNIEnv* env, int errnum, jobject fdToClose) { ExceptionType exceptionType; + +#if ENOTSUP + if(errnum == ENOTSUP) { + errnum = EOPNOTSUPP; + } +#endif + switch(errnum) { case EAGAIN: case ETIMEDOUT: @@ -115,7 +129,25 @@ void _throwErrnumException(JNIEnv* env, int errnum, jobject fdToClose) exceptionType = kExceptionAddressUnavailableSocketException; break; case EOPNOTSUPP: - exceptionType = kExceptionAddressUnavailableSocketException; +#if EPROTOTYPE + case EPROTOTYPE: +#endif +#if EPROTONOSUPPORT + case EPROTONOSUPPORT: +#endif +#if ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: +#endif +#if EPFNOSUPPORT + case EPFNOSUPPORT: +#endif +#if EAFNOSUPPORT + case EAFNOSUPPORT: +#endif + exceptionType = kExceptionOperationNotSupportedSocketException; + break; + case ENODEV: + exceptionType = kExceptionNoSuchDeviceSocketException; break; case EPIPE: case EBADF: @@ -135,7 +167,7 @@ void _throwErrnumException(JNIEnv* env, int errnum, jobject fdToClose) #ifdef __linux__ __auto_type otherBuf = strerror_r(errnum, message, buflen); - if(CK_IGNORE_CAST_BEGIN (int)otherBuf CK_IGNORE_CAST_END > 255) { + if(CK_IGNORE_CAST_BEGIN (unsigned int)otherBuf CK_IGNORE_CAST_END > 255) { // strerror_r is ill-defined. strncpy(message, CK_IGNORE_CAST_BEGIN (char *)otherBuf CK_IGNORE_CAST_END, diff --git a/junixsocket-native/src/main/c/exceptions.h b/junixsocket-native/src/main/c/exceptions.h index 48e61a24d..33e679c5f 100644 --- a/junixsocket-native/src/main/c/exceptions.h +++ b/junixsocket-native/src/main/c/exceptions.h @@ -32,6 +32,7 @@ typedef enum { kExceptionInvalidArgumentSocketException, kExceptionAddressUnavailableSocketException, kExceptionOperationNotSupportedSocketException, + kExceptionNoSuchDeviceSocketException, kExceptionMaxExcl } ExceptionType; diff --git a/junixsocket-native/src/main/c/filedescriptors.c b/junixsocket-native/src/main/c/filedescriptors.c index 1a5ca92be..0e3df008d 100644 --- a/junixsocket-native/src/main/c/filedescriptors.c +++ b/junixsocket-native/src/main/c/filedescriptors.c @@ -24,7 +24,10 @@ #include "address.h" static jclass class_FileDescriptor = NULL; + static jfieldID fieldID_fd = NULL; +static jmethodID methodID_getFd = NULL; +static jmethodID methodID_setFd = NULL; #if defined(_WIN32) static jfieldID fieldID_handle = NULL; @@ -44,6 +47,10 @@ typedef enum { #if junixsocket_have_vsock kFDTypeAFVSOCKStreamSocket, kFDTypeAFVSOCKDatagramSocket, +#endif +#if junixsocket_have_system + kFDTypeAFSYSTEMStreamSocket, + kFDTypeAFSYSTEMDatagramSocket, #endif kFDTypeMaxExcl } FileDescriptorType; @@ -56,6 +63,10 @@ static char* const kClassnameAFTIPCDatagramSocket = "org/newsclub/net/unix/tipc/ static char* const kClassnameAFVSOCKSocket = "org/newsclub/net/unix/vsock/AFVSOCKSocket"; static char* const kClassnameAFVSOCKDatagramSocket = "org/newsclub/net/unix/vsock/AFVSOCKDatagramSocket"; #endif +#if junixsocket_have_system +static char* const kClassnameAFSYSTEMSocket = "org/newsclub/net/unix/darwin/system/AFSYSTEMSocket"; +static char* const kClassnameAFSYSTEMDatagramSocket = "org/newsclub/net/unix/darwin/system/AFSYSTEMDatagramSocket"; +#endif // NOTE: The exceptions must all be either inherit from IOException or RuntimeException/Error static char *kFDTypeClassNames[kFDTypeMaxExcl] = { @@ -73,6 +84,10 @@ static char *kFDTypeClassNames[kFDTypeMaxExcl] = { kClassnameAFVSOCKSocket, kClassnameAFVSOCKDatagramSocket, #endif +#if junixsocket_have_system + kClassnameAFSYSTEMSocket, + kClassnameAFSYSTEMDatagramSocket, +#endif }; static jclass *kFDTypeClasses; @@ -102,11 +117,30 @@ void init_filedescriptors(JNIEnv *env) { // therefore it's OK if these classes are missing || (classname == kClassnameAFVSOCKSocket || classname == kClassnameAFVSOCKDatagramSocket) #endif +#if junixsocket_have_system + // Even if AF_SYSTEM is technically available, the junixsocket-system jar may not be in the classpath, + // therefore it's OK if these classes are missing + || (classname == kClassnameAFSYSTEMSocket || classname == kClassnameAFSYSTEMDatagramSocket) +#endif ); } class_FileDescriptor = kFDTypeClasses[0]; fieldID_fd = (*env)->GetFieldID(env, class_FileDescriptor, "fd", "I"); + if (fieldID_fd == NULL) { + (*env)->ExceptionClear(env); + + // https://github.com/AndroidSDKSources/android-sdk-sources-for-api-level-33/blob/master/java/io/FileDescriptor.java + methodID_getFd = (*env)->GetMethodID(env, class_FileDescriptor, "getInt$", "()I"); + (*env)->ExceptionClear(env); + methodID_setFd = (*env)->GetMethodID(env, class_FileDescriptor, "setInt$", "(I)V"); + + if(methodID_getFd == NULL || methodID_setFd == NULL) { + (*env)->ExceptionClear(env); + fieldID_fd = (*env)->GetFieldID(env, class_FileDescriptor, "descriptor", "I"); + } + } + #if defined(_WIN32) fieldID_handle = (*env)->GetFieldID(env, class_FileDescriptor, "handle", "J"); #endif @@ -147,11 +181,25 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_getFD jint _getFD(JNIEnv * env, jobject fd) { + if(fieldID_fd == NULL && methodID_getFd != NULL) { + // Android + return (*env)->CallIntMethod(env, fd, methodID_getFd); + } + return (*env)->GetIntField(env, fd, fieldID_fd); } void _initFD(JNIEnv * env, jobject fd, jint handle) { + if(fieldID_fd == NULL && methodID_setFd != NULL) { + // Android + (*env)->CallVoidMethod(env, fd, methodID_setFd, handle); + if((*env)->ExceptionCheck(env)) { + return; + } + return; + } + (*env)->SetIntField(env, fd, fieldID_fd, handle); } @@ -277,9 +325,13 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_shutdown int ret = shutdown(handle, mode); if(ret == -1) { int errnum = socket_errno; - if(errnum == ENOTCONN || errnum == EINVAL || errnum == EBADF) { - // ignore - return; + switch(errnum) { + case ENOTCONN: + case EINVAL: + case EBADF: + case EPIPE: + // ignore + return; } _throwErrnumException(env, errnum, fd); return; @@ -399,11 +451,30 @@ JNIEXPORT jclass JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_primaryType ret = getsockname(handle, addr, &len); if(ret != 0) { int errnum = socket_errno; - if(errnum == ENOTSOCK) { - return kFDTypeClasses[kFDTypeOther]; + switch(errnum) { + case ENOTSOCK: + return kFDTypeClasses[kFDTypeOther]; + case ENOTCONN: + break; + case EOPNOTSUPP: + ret = getpeername(handle, addr, &len); + if(ret != 0) { + switch(errnum) { + case ENOTSOCK: + return kFDTypeClasses[kFDTypeOther]; + case EOPNOTSUPP: + case ENOTCONN: + break; + default: + _throwErrnumException(env, errnum, fd); + return NULL; + } + } + break; + default: + _throwErrnumException(env, errnum, fd); + return NULL; } - _throwErrnumException(env, errnum, fd); - return NULL; } #endif @@ -412,6 +483,7 @@ JNIEXPORT jclass JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_primaryType (char*) #endif &type, &typeLen); + if(ret != 0) { _throwErrnumException(env, socket_errno, fd); return NULL; @@ -450,6 +522,17 @@ JNIEXPORT jclass JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_primaryType default: return kFDTypeClasses[kFDTypeOtherSocket]; } +#endif +#if junixsocket_have_system + case AF_SYSTEM: + switch(type) { + case SOCK_STREAM: + return kFDTypeClasses[kFDTypeAFSYSTEMStreamSocket]; + case SOCK_DGRAM: + return kFDTypeClasses[kFDTypeAFSYSTEMDatagramSocket]; + default: + return kFDTypeClasses[kFDTypeAFSYSTEMDatagramSocket]; + } #endif default: switch(type) { diff --git a/junixsocket-native/src/main/c/init.c b/junixsocket-native/src/main/c/init.c index 9a48dedcf..5c286b463 100644 --- a/junixsocket-native/src/main/c/init.c +++ b/junixsocket-native/src/main/c/init.c @@ -131,6 +131,7 @@ jboolean supportsZeroLengthSend(void) { JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_init (JNIEnv *env, jclass clazz CK_UNUSED) { + (*env)->EnsureLocalCapacity(env, 20); #if defined(_WIN32) WSADATA wsaData; int ret = WSAStartup(MAKEWORD(2,2), &wsaData); @@ -140,8 +141,8 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_init } #endif - init_exceptions(env); - init_capabilities(env); + init_exceptions(env); // should be first + init_reflection(env); init_unix(); init_filedescriptors(env); @@ -157,6 +158,8 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_init #endif init_poll(env); init_socketoptions(env); + + init_capabilities(env); // should be last } /* @@ -181,3 +184,14 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_destroy destroy_poll(env); destroy_socketoptions(env); } + +/* + * Class: org_newsclub_net_unix_NativeUnixSocket + * Method: noop + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_noop +(JNIEnv *env CK_UNUSED, jclass clazz CK_UNUSED) { + // no-op + return; +} diff --git a/junixsocket-native/src/main/c/jniutil.c b/junixsocket-native/src/main/c/jniutil.c index e52a08135..c4f270f59 100644 --- a/junixsocket-native/src/main/c/jniutil.c +++ b/junixsocket-native/src/main/c/jniutil.c @@ -23,24 +23,31 @@ void handleFieldNotFound(JNIEnv *env, jobject instance, char *fieldName) { + (*env)->ExceptionClear(env); jmethodID classMethodId = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, instance), "getClass", "()Ljava/lang/Class;"); jobject classObject = (*env)->CallObjectMethod(env, instance, classMethodId); + (*env)->ExceptionClear(env); jmethodID methodId = (*env)->GetMethodID(env, - (*env)->GetObjectClass(env, classObject), "getSimpleName", + (*env)->GetObjectClass(env, classObject), "getName", "()Ljava/lang/String;"); jstring className = (jstring)(*env)->CallObjectMethod(env, classObject, methodId); + if ((*env)->ExceptionCheck(env)) { + return; + } + const char* classNameStr = (*env)->GetStringUTFChars(env, className, NULL); if(classNameStr == NULL) { return; // OOME } #define handleFieldNotFound_error_message_template "Cannot find '%s' in class %s" + size_t buflen = strlen(handleFieldNotFound_error_message_template) + strlen(fieldName) + strlen(classNameStr); char *message = calloc(1, buflen); CK_IGNORE_USED_BUT_MARKED_UNUSED_BEGIN @@ -68,7 +75,10 @@ void callObjectSetter(JNIEnv *env, jobject instance, char *methodName, } __attribute__((aligned(8))) jobject array[] = {value}; - (*env)->CallObjectMethodA(env, instance, methodId, (jvalue*)array); + (*env)->CallVoidMethodA(env, instance, methodId, (jvalue*)array); + if ((*env)->ExceptionCheck(env)) { + return; + } } void setObjectFieldValue(JNIEnv *env, jobject instance, char *fieldName, diff --git a/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h b/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h index b165e1d27..f2bd8ab10 100644 --- a/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h +++ b/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h @@ -13,10 +13,14 @@ extern "C" { #define org_newsclub_net_unix_NativeUnixSocket_DOMAIN_TIPC 30L #undef org_newsclub_net_unix_NativeUnixSocket_DOMAIN_VSOCK #define org_newsclub_net_unix_NativeUnixSocket_DOMAIN_VSOCK 40L +#undef org_newsclub_net_unix_NativeUnixSocket_DOMAIN_SYSTEM +#define org_newsclub_net_unix_NativeUnixSocket_DOMAIN_SYSTEM 32L #undef org_newsclub_net_unix_NativeUnixSocket_SOCK_STREAM #define org_newsclub_net_unix_NativeUnixSocket_SOCK_STREAM 1L #undef org_newsclub_net_unix_NativeUnixSocket_SOCK_DGRAM #define org_newsclub_net_unix_NativeUnixSocket_SOCK_DGRAM 2L +#undef org_newsclub_net_unix_NativeUnixSocket_SOCK_RAW +#define org_newsclub_net_unix_NativeUnixSocket_SOCK_RAW 3L #undef org_newsclub_net_unix_NativeUnixSocket_SOCK_RDM #define org_newsclub_net_unix_NativeUnixSocket_SOCK_RDM 4L #undef org_newsclub_net_unix_NativeUnixSocket_SOCK_SEQPACKET @@ -57,6 +61,14 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_init JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_destroy (JNIEnv *, jclass); +/* + * Class: org_newsclub_net_unix_NativeUnixSocket + * Method: noop + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_noop + (JNIEnv *, jclass); + /* * Class: org_newsclub_net_unix_NativeUnixSocket * Method: capabilities @@ -433,6 +445,14 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_sockTypeToNat JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_vsockGetLocalCID (JNIEnv *, jclass); +/* + * Class: org_newsclub_net_unix_NativeUnixSocket + * Method: systemResolveCtlId + * Signature: (Ljava/io/FileDescriptor;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_systemResolveCtlId + (JNIEnv *, jclass, jobject, jstring); + #ifdef __cplusplus } #endif diff --git a/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketInputStream.h b/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketInputStream.h new file mode 100644 index 000000000..f1ae12049 --- /dev/null +++ b/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketInputStream.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_newsclub_net_unix_android_SocketInputStream */ + +#ifndef _Included_org_newsclub_net_unix_android_SocketInputStream +#define _Included_org_newsclub_net_unix_android_SocketInputStream +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_newsclub_net_unix_android_SocketInputStream + * Method: socketRead0 + * Signature: (Ljava/io/FileDescriptor;[BIII)I + */ +JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_android_SocketInputStream_socketRead0 + (JNIEnv *, jobject, jobject, jbyteArray, jint, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketOutputStream.h b/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketOutputStream.h new file mode 100644 index 000000000..0fc60c877 --- /dev/null +++ b/junixsocket-native/src/main/c/org_newsclub_net_unix_android_SocketOutputStream.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_newsclub_net_unix_android_SocketOutputStream */ + +#ifndef _Included_org_newsclub_net_unix_android_SocketOutputStream +#define _Included_org_newsclub_net_unix_android_SocketOutputStream +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_newsclub_net_unix_android_SocketOutputStream + * Method: socketWrite0 + * Signature: (Ljava/io/FileDescriptor;[BII)V + */ +JNIEXPORT void JNICALL Java_org_newsclub_net_unix_android_SocketOutputStream_socketWrite0 + (JNIEnv *, jobject, jobject, jbyteArray, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/junixsocket-native/src/main/c/polling.c b/junixsocket-native/src/main/c/polling.c index 371993a15..e5fdbf3dc 100644 --- a/junixsocket-native/src/main/c/polling.c +++ b/junixsocket-native/src/main/c/polling.c @@ -355,8 +355,15 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_poll jintArray ropsObj = (*env)->GetObjectField(env, pollFdObj, fieldID_rops); struct pollfd* pollFd = calloc(nfds, sizeof(struct pollfd)); + if(pollFd == NULL) { + return 0; + } jint *buf = calloc(nfds, sizeof(jint)); + if(buf == NULL) { + free(pollFd); + return 0; + } (*env)->GetIntArrayRegion(env, opsObj, 0, nfds, buf); for(int i=0; iCallVoidMethod(env, ancSupp, kSetTipcErrorInfo, errInfo->errorCode, errInfo->dataLength); + if((*env)->ExceptionCheck(env)) { + return -1; + } } } else if(cmsg->cmsg_level == SOL_TIPC && cmsg->cmsg_type == TIPC_DESTNAME && len == 12) { CK_IGNORE_CAST_ALIGN_BEGIN @@ -189,6 +192,9 @@ static ssize_t recvmsg_wrapper(JNIEnv * env, int handle, jbyte *buf, jint length jmethodID kSetTipcDestName = getMethodID_setTipcDestName(); if(kSetTipcDestName != NULL) { (*env)->CallVoidMethod(env, ancSupp, kSetTipcDestName, addr->type, addr->lower, addr->upper); + if((*env)->ExceptionCheck(env)) { + return -1; + } } #endif } else { diff --git a/junixsocket-native/src/main/c/reflection.c b/junixsocket-native/src/main/c/reflection.c index dbc83d162..23383a2a0 100644 --- a/junixsocket-native/src/main/c/reflection.c +++ b/junixsocket-native/src/main/c/reflection.c @@ -27,12 +27,30 @@ static jboolean doSetServerSocket = true; static jclass kClassAbstractSelectableChannel; static jmethodID kMethodRemoveKey; +static jboolean cap_largePorts = false; + +static jboolean checkCapLargePorts(JNIEnv *env); + +static jboolean dontInitServerImpl = false; + void init_reflection(JNIEnv *env) { kClassAbstractSelectableChannel = findClassAndGlobalRef(env, "java/nio/channels/spi/AbstractSelectableChannel"); if(kClassAbstractSelectableChannel) { kMethodRemoveKey = (*env)->GetMethodID(env, kClassAbstractSelectableChannel, "removeKey", "(Ljava/nio/channels/SelectionKey;)V"); + if(kMethodRemoveKey == NULL) { + (*env)->ExceptionClear(env); + + // https://android.googlesource.com/platform/libcore/+/cff1616/luni/src/main/java/java/nio/channels/spi/AbstractSelectableChannel.java + kMethodRemoveKey = (*env)->GetMethodID(env, kClassAbstractSelectableChannel, "deregister", "(Ljava/nio/channels/SelectionKey;)V"); + if(kMethodRemoveKey == NULL) { + (*env)->ExceptionClear(env); + } + } } + + cap_largePorts = checkCapLargePorts(env); } + void destroy_reflection(JNIEnv *env) { releaseClassGlobalRef(env, kClassAbstractSelectableChannel); } @@ -45,10 +63,26 @@ void destroy_reflection(JNIEnv *env) { JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_initServerImpl( JNIEnv * env, jclass clazz CK_UNUSED, jobject serverSocket, jobject impl) { + if(dontInitServerImpl) { + return; + } + + callObjectSetter(env, serverSocket, "", "(Ljava/net/SocketImpl;)V", impl); + if(!(*env)->ExceptionCheck(env)) { + // all done + return; + } + (*env)->ExceptionClear(env); + setObjectFieldValue(env, serverSocket, "impl", "Ljava/net/SocketImpl;", impl); + if((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + // cannot access impl (probably Android) - if (doSetServerSocket) { + dontInitServerImpl = true; + return; + } else if(doSetServerSocket) { // no longer present in Java 16 doSetServerSocket = setObjectFieldValueIfPossible(env, impl, "serverSocket", "Ljava/net/ServerSocket;", serverSocket); @@ -63,21 +97,22 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_initServerImp JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_setPort( JNIEnv * env, jclass clazz CK_UNUSED, jobject addr, jint port) { - jclass fileDescriptorClass = (*env)->GetObjectClass(env, addr); + jclass clsInetSocketAddress = (*env)->GetObjectClass(env, addr); jobject fieldObject = addr; jfieldID portField; - jfieldID holderField = (*env)->GetFieldID(env, fileDescriptorClass, + jfieldID holderField = (*env)->GetFieldID(env, clsInetSocketAddress, "holder", "Ljava/net/InetSocketAddress$InetSocketAddressHolder;"); if(holderField != NULL) { fieldObject = (*env)->GetObjectField(env, addr, holderField); jclass holderClass = (*env)->GetObjectClass(env, fieldObject); portField = (*env)->GetFieldID(env, holderClass, "port", "I"); } else { - portField = (*env)->GetFieldID(env, fileDescriptorClass, "port", "I"); + portField = (*env)->GetFieldID(env, clsInetSocketAddress, "port", "I"); } if(portField == NULL) { + (*env)->ExceptionClear(env); _throwException(env, kExceptionSocketException, "Cannot find field \"port\" in java.net.InetSocketAddress. Unsupported JVM?"); return; @@ -129,7 +164,7 @@ JNIEXPORT jobject JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_currentRMI } jobject connHandler = (*env)->CallObjectMethod(env, tl, tlGet); - if(connHandler == NULL) { + if((*env)->ExceptionCheck(env) || connHandler == NULL) { return NULL; } jclass connHandlerClass = (*env)->GetObjectClass(env, connHandler); @@ -159,7 +194,43 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_deregisterSel } if(kMethodRemoveKey != NULL) { (*env)->CallVoidMethod(env, chann, kMethodRemoveKey, key); + if((*env)->ExceptionCheck(env)) { + return; + } } else { // FIXME } } + +jboolean supportsLargePorts(void) { + return cap_largePorts; +} + +static jboolean checkCapLargePorts(JNIEnv *env) { + jclass cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + if((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return false; + } + + jmethodID constructor = (*env)->GetMethodID(env, cls, "", "(I)V"); + if((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return false; + } + + jobject instance = (*env)->NewObject(env, cls, constructor, 0); + if(instance == NULL) { + (*env)->ExceptionClear(env); + return false; + } + + Java_org_newsclub_net_unix_NativeUnixSocket_setPort(env, cls, instance, 65536); + + if((*env)->ExceptionCheck(env)) { + (*env)->ExceptionClear(env); + return false; + } else { + return true; + } +} diff --git a/junixsocket-native/src/main/c/reflection.h b/junixsocket-native/src/main/c/reflection.h index 8010c6d21..8bca68a6c 100644 --- a/junixsocket-native/src/main/c/reflection.h +++ b/junixsocket-native/src/main/c/reflection.h @@ -24,4 +24,6 @@ void init_reflection(JNIEnv *env); void destroy_reflection(JNIEnv *env); +jboolean supportsLargePorts(void); + #endif /* reflection_h */ diff --git a/junixsocket-native/src/main/c/send.c b/junixsocket-native/src/main/c/send.c index 5e14fc8ee..888e44898 100644 --- a/junixsocket-native/src/main/c/send.c +++ b/junixsocket-native/src/main/c/send.c @@ -76,6 +76,7 @@ ssize_t send_wrapper(int handle, jbyte *buf, jint length, jux_sockaddr_t *sendTo count = write(handle, (char*)buf, length); } } + // on macOS/BSD, send seems to not block if the send buffer is full, so we have to handle ENOBUFS if(count >= 0) { break; @@ -96,22 +97,13 @@ ssize_t send_wrapper(int handle, jbyte *buf, jint length, jux_sockaddr_t *sendTo break; } count = 0; // don't throw -#if defined(sched_yield) - sched_yield(); + + // see https://github.com/virtualsquare/vde-2/issues/19 + // and https://stackoverflow.com/questions/16555101/sendto-dgrams-do-not-block-for-enobufs-on-osx +#if defined(_WIN32) + SwitchToThread(); #else - struct pollfd fds[] = { - { - .fd = handle, - .events = (POLLOUT), - .revents = 0 - } - }; - -# if defined(_WIN32) - WSAPoll(fds, 1, -1); -# else - poll(fds, 1, -1); -# endif + sched_yield(); #endif continue; } @@ -155,7 +147,7 @@ ssize_t sendmsg_wrapper(JNIEnv * env, int handle, jbyte *buf, jint length, jux_s if(ancFdsLen > 0) { ancBuf = (*env)->GetIntArrayElements(env, ancFds, NULL); memcpy(data, ancBuf, ancFdsLen * sizeof(jint)); - (*env)->ReleaseIntArrayElements(env, ancFds, ancBuf, 0); + (*env)->ReleaseIntArrayElements(env, ancFds, ancBuf, JNI_ABORT); // ancFds is unmodified } cmsg = junixsocket_CMSG_NXTHDR(&msg, cmsg); diff --git a/junixsocket-native/src/main/c/socket.c b/junixsocket-native/src/main/c/socket.c index 5f5ebdcca..0d7beafe3 100644 --- a/junixsocket-native/src/main/c/socket.c +++ b/junixsocket-native/src/main/c/socket.c @@ -83,10 +83,18 @@ int sockTypeToNative(JNIEnv *env, int type) { return SOCK_STREAM; case org_newsclub_net_unix_NativeUnixSocket_SOCK_DGRAM: return SOCK_DGRAM; + case org_newsclub_net_unix_NativeUnixSocket_SOCK_RAW: + return SOCK_RAW; +#if defined(SOCK_RDM) + case org_newsclub_net_unix_NativeUnixSocket_SOCK_RDM: + return SOCK_RDM; +#endif +#if defined(SOCK_SEQPACKET) case org_newsclub_net_unix_NativeUnixSocket_SOCK_SEQPACKET: return SOCK_SEQPACKET; +#endif default: - _throwException(env, kExceptionSocketException, "Illegal type"); + _throwException(env, kExceptionSocketException, "Illegal or unsupported type"); return -1; } } @@ -127,10 +135,12 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_createSocket return; } + int protocol = 0; + #if defined(junixsocket_have_socket_cloexec) - handle = socket(domain, type | SOCK_CLOEXEC, 0); + handle = socket(domain, type | SOCK_CLOEXEC, protocol); if(handle == -1 && errno == EPROTONOSUPPORT) { - handle = socket(domain, type, 0); + handle = socket(domain, type, protocol); if(handle > 0) { # if defined(FD_CLOEXEC) fcntl(handle, F_SETFD, FD_CLOEXEC); // best effort @@ -138,7 +148,7 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_createSocket } } #else - handle = socket(domain, type, 0); + handle = socket(domain, type, protocol); #endif if(handle < 0) { _throwErrnumException(env, socket_errno, fd); @@ -180,6 +190,7 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_socketStatus if(ret != 0) { int errnum = socket_errno; switch(errnum) { + case EOPNOTSUPP: case EINVAL: case ENOTCONN: case ENOTSOCK: // OSv socketpair @@ -196,6 +207,7 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_socketStatus if(ret != 0) { int errnum = socket_errno; switch(errnum) { + case EOPNOTSUPP: case EINVAL: case ENOTCONN: case ENOTSOCK: // OSv socketpair diff --git a/junixsocket-native/src/main/c/socketoptions.c b/junixsocket-native/src/main/c/socketoptions.c index 0d71b07c8..edfb73ee0 100644 --- a/junixsocket-native/src/main/c/socketoptions.c +++ b/junixsocket-native/src/main/c/socketoptions.c @@ -257,8 +257,8 @@ struct tipc_group_req { # endif static jobject groupReqToJava(JNIEnv *env, void* valPtr, socklen_t valLen) { - if(valLen < sizeof(struct tipc_group_req)) { - if(valLen == sizeof(jint)) { + if(valLen < (socklen_t)sizeof(struct tipc_group_req)) { + if(valLen == (socklen_t)sizeof(jint)) { // response is just the group type return (*env)->CallStaticObjectMethod(env, kAFTIPCGroupRequestClass, kAFTIPCGroupRequestFromNative, *((jint*)valPtr),0,0,0); } diff --git a/junixsocket-native/src/main/c/socketpair.c b/junixsocket-native/src/main/c/socketpair.c index 9ec7eabc1..8ff31a864 100644 --- a/junixsocket-native/src/main/c/socketpair.c +++ b/junixsocket-native/src/main/c/socketpair.c @@ -175,11 +175,13 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_socketPair .svm_cid = VMADDR_CID_RESERVED # endif }; +CK_IGNORE_CAST_ALIGN_BEGIN if(simulateSocketPair(env, domain, type, fd1, fd2, (jux_sockaddr_t*)&addr, sizeof(struct sockaddr_vm)) != 0) { _throwErrnumException(env, myerr, NULL); return; } +CK_IGNORE_CAST_ALIGN_END return; } #endif diff --git a/junixsocket-native/src/main/nar/aol.properties b/junixsocket-native/src/main/nar/aol.properties index e8dcabf24..44e8b92cc 100644 --- a/junixsocket-native/src/main/nar/aol.properties +++ b/junixsocket-native/src/main/nar/aol.properties @@ -116,6 +116,94 @@ aarch64.Linux.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target aarch64.Linux.clang.cpp.includes=${junixsocket.native.default.cpp.includes} aarch64.Linux.clang.cpp.excludes= +aarch64.Android.clang.linker.name=clang +aarch64.Android.clang.linker.options=${junixsocket.native.default.linker.options} -target ${junixsocket.native.llvm.target} +aarch64.Android.clang.c.compiler=clang +aarch64.Android.clang.c.defines=_GNU_SOURCE +aarch64.Android.clang.c.options=${junixsocket.native.clang.c.options} -I${project.basedir}/src/main/c/jni -target ${junixsocket.native.llvm.target} +aarch64.Android.clang.c.includes=${junixsocket.native.default.c.includes} +aarch64.Android.clang.c.excludes= +aarch64.Android.clang.java.include= +aarch64.Android.clang.java.runtimeDirectory=IGNORED +aarch64.Android.clang.lib.prefix=lib +aarch64.Android.clang.shared.prefix=lib +aarch64.Android.clang.static.extension=a +aarch64.Android.clang.shared.extension=so +aarch64.Android.clang.plugin.extension=so +aarch64.Android.clang.jni.extension=so +aarch64.Android.clang.executable.extension= +aarch64.Android.clang.cpp.compiler=clang++ +aarch64.Android.clang.cpp.defines=_GNU_SOURCE +aarch64.Android.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target ${junixsocket.native.llvm.target} +aarch64.Android.clang.cpp.includes=${junixsocket.native.default.cpp.includes} +aarch64.Android.clang.cpp.excludes= + +i686.Android.clang.linker.name=clang +i686.Android.clang.linker.options=${junixsocket.native.default.linker.options} -target ${junixsocket.native.llvm.target} +i686.Android.clang.c.compiler=clang +i686.Android.clang.c.defines=_GNU_SOURCE +i686.Android.clang.c.options=${junixsocket.native.clang.c.options} -I${project.basedir}/src/main/c/jni -target ${junixsocket.native.llvm.target} +i686.Android.clang.c.includes=${junixsocket.native.default.c.includes} +i686.Android.clang.c.excludes= +i686.Android.clang.java.include= +i686.Android.clang.java.runtimeDirectory=IGNORED +i686.Android.clang.lib.prefix=lib +i686.Android.clang.shared.prefix=lib +i686.Android.clang.static.extension=a +i686.Android.clang.shared.extension=so +i686.Android.clang.plugin.extension=so +i686.Android.clang.jni.extension=so +i686.Android.clang.executable.extension= +i686.Android.clang.cpp.compiler=clang++ +i686.Android.clang.cpp.defines=_GNU_SOURCE +i686.Android.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target ${junixsocket.native.llvm.target} +i686.Android.clang.cpp.includes=${junixsocket.native.default.cpp.includes} +i686.Android.clang.cpp.excludes= + +x86_64.Android.clang.linker.name=clang +x86_64.Android.clang.linker.options=${junixsocket.native.default.linker.options} -target ${junixsocket.native.llvm.target} +x86_64.Android.clang.c.compiler=clang +x86_64.Android.clang.c.defines=_GNU_SOURCE +x86_64.Android.clang.c.options=${junixsocket.native.clang.c.options} -I${project.basedir}/src/main/c/jni -target ${junixsocket.native.llvm.target} +x86_64.Android.clang.c.includes=${junixsocket.native.default.c.includes} +x86_64.Android.clang.c.excludes= +x86_64.Android.clang.java.include= +x86_64.Android.clang.java.runtimeDirectory=IGNORED +x86_64.Android.clang.lib.prefix=lib +x86_64.Android.clang.shared.prefix=lib +x86_64.Android.clang.static.extension=a +x86_64.Android.clang.shared.extension=so +x86_64.Android.clang.plugin.extension=so +x86_64.Android.clang.jni.extension=so +x86_64.Android.clang.executable.extension= +x86_64.Android.clang.cpp.compiler=clang++ +x86_64.Android.clang.cpp.defines=_GNU_SOURCE +x86_64.Android.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target ${junixsocket.native.llvm.target} +x86_64.Android.clang.cpp.includes=${junixsocket.native.default.cpp.includes} +x86_64.Android.clang.cpp.excludes= + +arm.Android.clang.linker.name=clang +arm.Android.clang.linker.options=${junixsocket.native.default.linker.options} -target ${junixsocket.native.llvm.target} +arm.Android.clang.c.compiler=clang +arm.Android.clang.c.defines=_GNU_SOURCE +arm.Android.clang.c.options=${junixsocket.native.clang.c.options} -I${project.basedir}/src/main/c/jni -target ${junixsocket.native.llvm.target} +arm.Android.clang.c.includes=${junixsocket.native.default.c.includes} +arm.Android.clang.c.excludes= +arm.Android.clang.java.include= +arm.Android.clang.java.runtimeDirectory=IGNORED +arm.Android.clang.lib.prefix=lib +arm.Android.clang.shared.prefix=lib +arm.Android.clang.static.extension=a +arm.Android.clang.shared.extension=so +arm.Android.clang.plugin.extension=so +arm.Android.clang.jni.extension=so +arm.Android.clang.executable.extension= +arm.Android.clang.cpp.compiler=clang++ +arm.Android.clang.cpp.defines=_GNU_SOURCE +arm.Android.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target ${junixsocket.native.llvm.target} +arm.Android.clang.cpp.includes=${junixsocket.native.default.cpp.includes} +arm.Android.clang.cpp.excludes= + ppc64.AIX.clang.linker.name=clang ppc64.AIX.clang.linker.options=${junixsocket.native.default.linker.options} -target ${junixsocket.native.llvm.target} -maix64 -Xcrossclang-use-gcc=powerpc-ibm-aix7.2-gcc -static-libgcc -s ppc64.AIX.clang.c.compiler=clang @@ -427,7 +515,7 @@ amd64.NetBSD.clang.cpp.defines=_GNU_SOURCE amd64.NetBSD.clang.cpp.options=${junixsocket.native.clang.cpp.options} -target ${junixsocket.native.llvm.target} amd64.NetBSD.clang.cpp.includes=${junixsocket.native.default.cpp.includes} amd64.NetBSD.clang.cpp.excludes= -` + amd64.OpenBSD.linker=clang amd64.OpenBSD.clang.c.compiler=clang amd64.OpenBSD.clang.linker.name=clang diff --git a/junixsocket-rmi/pom.xml b/junixsocket-rmi/pom.xml index 3e2d19a0d..4789ee532 100644 --- a/junixsocket-rmi/pom.xml +++ b/junixsocket-rmi/pom.xml @@ -6,7 +6,7 @@ com.kohlschutter.junixsocket junixsocket - 2.6.1 + 2.7.2 ../pom.xml junixsocket-rmi @@ -36,12 +36,26 @@ ${project.version} test - - com.kohlschutter.junixsocket - junixsocket-native-custom - ${project.version} - test - default - + + + + with-native-custom + + + !junixsocket.native-custom.skip + + + + + com.kohlschutter.junixsocket + junixsocket-native-custom + ${project.version} + test + default + true + + + + diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNaming.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNaming.java index 9bb43c164..0e014a089 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNaming.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNaming.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ /** * The {@link AFSocket}-compatible equivalent of {@link Naming}. Use this class for accessing RMI * registries that are reachable by {@link AFSocket}s. - * + * * @author Christian Kohlschütter */ public abstract class AFNaming extends AFRegistryAccess { @@ -61,13 +61,13 @@ public abstract class AFNaming extends AFRegistryAccess { private final int registryPort; private final int servicePort; AFRMISocketFactory socketFactory; - private boolean remoteShutdownAllowed = true; + private final AtomicBoolean remoteShutdownAllowed = new AtomicBoolean(true); private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false); private final AtomicBoolean addedShutdownHook = new AtomicBoolean(false); /** * Creates a new naming instance with the given ports. - * + * * @param registryPort The registry port. * @param servicePort The port for AFRMIService. * @throws IOException on error. @@ -80,7 +80,7 @@ protected AFNaming(final int registryPort, final int servicePort) throws IOExcep /** * Creates a new {@link AFRegistry} given a {@link Registry} implementation. - * + * * @param impl The implementation. * @return The new {@link AFRegistry} instance. * @throws RemoteException on error. @@ -89,7 +89,7 @@ protected AFNaming(final int registryPort, final int servicePort) throws IOExcep /** * Creates or returns the {@link AFRMISocketFactory} to be used with this instance. - * + * * @return The socket factory. * @throws IOException on error. */ @@ -107,7 +107,9 @@ static T getInstance(final int registryPort, try { instance = provider.newInstance(registryPort); Objects.requireNonNull(instance); - instance.socketFactory = instance.initSocketFactory(); + synchronized (instance) { + instance.socketFactory = instance.initSocketFactory(); + } } catch (RemoteException e) { throw e; } catch (IOException e) { @@ -121,7 +123,7 @@ static T getInstance(final int registryPort, /** * Returns the {@link AFRMISocketFactory} associated with this instance. - * + * * @return The {@link AFRMISocketFactory}. */ @SuppressFBWarnings("EI_EXPOSE_REP") @@ -131,7 +133,7 @@ public synchronized AFRMISocketFactory getSocketFactory() { /** * Returns the registry port. - * + * * @return The port. */ public final int getRegistryPort() { @@ -142,7 +144,8 @@ AFRMIService getRMIService() throws RemoteException, NotBoundException { return getRMIService(getRegistry()); } - AFRMIService getRMIService(AFRegistry reg) throws RemoteException, NotBoundException { + synchronized AFRMIService getRMIService(AFRegistry reg) throws RemoteException, + NotBoundException { if (rmiService == null) { this.rmiService = getRMIServiceFromRegistry(reg); } @@ -152,7 +155,7 @@ AFRMIService getRMIService(AFRegistry reg) throws RemoteException, NotBoundExcep AFRMIService getRMIServiceFromRegistry(AFRegistry reg) throws RemoteException, NotBoundException { AFRMIService service; service = (AFRMIService) reg.lookup(RMI_SERVICE_NAME, 5, TimeUnit.SECONDS); - this.remoteShutdownAllowed = service.isShutdownAllowed(); + this.remoteShutdownAllowed.set(service.isShutdownAllowed()); return service; } @@ -161,18 +164,17 @@ private void closeUponRuntimeShutdown() { ShutdownHookSupport.addWeakShutdownHook(new ShutdownHook() { @Override - public void onRuntimeShutdown(Thread thread) throws IOException { - synchronized (AFNaming.class) { - if (registry != null && registry.isLocal()) { - shutdownRegistry(); - } + @SuppressWarnings("LockOnNonEnclosingClassLiteral" /* errorprone */) + public synchronized void onRuntimeShutdown(Thread thread) throws IOException { + if (registry != null && registry.isLocal()) { + shutdownRegistry(); } } }); } } - private void rebindRMIService(final AFRMIService assigner) throws RemoteException { + private synchronized void rebindRMIService(final AFRMIService assigner) throws RemoteException { rmiService = assigner; getRegistry().rebind(RMI_SERVICE_NAME, assigner); } @@ -184,10 +186,10 @@ public AFRegistry getRegistry() throws RemoteException { /** * Returns a reference to the existing RMI registry. - * + * * If there's no registry running at this port after waiting for up to the given time, an * exception is thrown. - * + * * @param timeout The timeout value. * @param unit The timeout unit. * @return The registry. @@ -197,7 +199,7 @@ public AFRegistry getRegistry(long timeout, TimeUnit unit) throws RemoteExceptio if (shutdownInProgress.get()) { throw new ShutdownException(); } - synchronized (AFNaming.class) { + synchronized (this) { AFRegistry reg = getRegistry(false); if (reg == null) { reg = openRegistry(timeout, unit); @@ -208,7 +210,7 @@ public AFRegistry getRegistry(long timeout, TimeUnit unit) throws RemoteExceptio /** * Tries to access the registry, waiting some time if necessary. - * + * * @param timeout The timeout. * @param unit The unit for the timeout. * @return The registry instance. @@ -221,7 +223,7 @@ public AFRegistry getRegistry(long timeout, TimeUnit unit) throws RemoteExceptio * * If there's no registry running at this port, and {@code create} is set to {@code true}, a new * one is created; when {@code create} is set to {@code false}, {@code null} is returned. - * + * * @param create {@code true} if a new register may be created if necessary. * @return The registry, or {@code null} * @throws RemoteException If there was a problem. @@ -230,7 +232,7 @@ public AFRegistry getRegistry(boolean create) throws RemoteException { if (shutdownInProgress.get()) { throw new ShutdownException(); } - synchronized (AFNaming.class) { + synchronized (this) { if (registry != null) { return registry; } else if (!socketFactory.hasRegisteredPort(registryPort)) { @@ -262,11 +264,11 @@ private AFRegistry locateRegistry() throws RemoteException { /** * Shuts this RMI Registry down. - * + * * @throws RemoteException if the operation fails. */ public void shutdownRegistry() throws RemoteException { - synchronized (AFNaming.class) { + synchronized (this) { if (registry == null) { return; } @@ -309,8 +311,8 @@ public void shutdownRegistry() throws RemoteException { */ protected abstract void shutdownRegistryFinishingTouches(); - private void unexportRMIService(AFRegistry reg, AFRMIServiceImpl serv) throws AccessException, - RemoteException { + private synchronized void unexportRMIService(AFRegistry reg, AFRMIServiceImpl serv) + throws AccessException, RemoteException { if (serv != null) { serv.shutdownRegisteredCloseables(); } @@ -359,81 +361,79 @@ private void shutdownViaRMIService(AFRegistry reg, AFRMIService serv) throws Rem /** * Creates a new RMI {@link Registry}. - * + * * If there already was a registry created previously, it is shut down and replaced by the current * one. - * + * * Use {@link #getRegistry()} to try to reuse an existing registry. - * + * * @return The registry * @throws RemoteException if the operation fails. * @see #getRegistry() */ - public AFRegistry createRegistry() throws RemoteException { - synchronized (AFNaming.class) { - AFRegistry existingRegistry = registry; - if (existingRegistry == null) { - try { - existingRegistry = getRegistry(false); - } catch (ServerException e) { - Throwable cause = e.getCause(); - if (cause instanceof NotBoundException || cause instanceof ConnectIOException) { - existingRegistry = null; - } else { - throw e; - } + public synchronized AFRegistry createRegistry() throws RemoteException { + AFRegistry existingRegistry = registry; + if (existingRegistry == null) { + try { + existingRegistry = getRegistry(false); + } catch (ServerException e) { + Throwable cause = e.getCause(); + if (cause instanceof NotBoundException || cause instanceof ConnectIOException) { + existingRegistry = null; + } else { + throw e; } } - if (existingRegistry != null) { - if (!isRemoteShutdownAllowed()) { - throw new ServerException("The server refuses to be shutdown remotely"); - } - shutdownRegistry(); + } + if (existingRegistry != null) { + if (!isRemoteShutdownAllowed()) { + throw new ServerException("The server refuses to be shutdown remotely"); } + shutdownRegistry(); + } - initRegistryPrerequisites(); - - setRegistry(newAFRegistry(LocateRegistry.createRegistry(registryPort, socketFactory, - socketFactory))); + initRegistryPrerequisites(); + AFRegistry newAFRegistry = newAFRegistry(LocateRegistry.createRegistry(registryPort, + socketFactory, socketFactory)); + setRegistry(newAFRegistry); - final AFRMIService service = new AFRMIServiceImpl(this); - UnicastRemoteObject.exportObject(service, servicePort, socketFactory, socketFactory); + final AFRMIService service = new AFRMIServiceImpl(this); + UnicastRemoteObject.exportObject(service, servicePort, socketFactory, socketFactory); - rebindRMIService(service); + rebindRMIService(service); - return registry; - } + return registry; } /** * Called by {@link #createRegistry()} right before creating/setting the registry. - * + * * @throws ServerException on error. */ protected abstract void initRegistryPrerequisites() throws ServerException; /** * Checks if this {@link AFNaming} instance can be shut down remotely. - * + * * @return {@code true} if remote shutdown is allowed. */ public boolean isRemoteShutdownAllowed() { - return remoteShutdownAllowed; + return remoteShutdownAllowed.get(); } /** * Controls whether this {@link AFNaming} instance can be shut down remotely. - * + * * @param remoteShutdownAllowed {@code true} if remote shutdown is allowed. */ public void setRemoteShutdownAllowed(boolean remoteShutdownAllowed) { - this.remoteShutdownAllowed = remoteShutdownAllowed; + this.remoteShutdownAllowed.set(remoteShutdownAllowed); } /** * Exports and binds the given Remote object to the given name, using the given {@link AFNaming} * setup. - * + * * @param name The name to use to bind the object in the registry. * @param obj The object to export and bind. * @throws RemoteException if the operation fails. @@ -448,7 +448,7 @@ public void exportAndBind(String name, Remote obj) throws RemoteException, Alrea /** * Exports and re-binds the given Remote object to the given name, using the given * {@link AFNaming} setup. - * + * * @param name The name to use to bind the object in the registry. * @param obj The object to export and bind. * @throws RemoteException if the operation fails. @@ -478,9 +478,9 @@ public void unexportAndUnbind(String name, Remote obj) throws RemoteException { /** * Exports the given Remote object, using the given socket factory and a randomly assigned port. - * + * * NOTE: This helper function can also be used for regular RMI servers. - * + * * @param obj The object to export. * @param socketFactory The socket factory to use. * @return The remote stub. @@ -494,9 +494,9 @@ public static Remote exportObject(Remote obj, RMISocketFactory socketFactory) /** * Forcibly un-exports the given object, if it exists (otherwise returns without an error). This * should be called upon closing a {@link Closeable} {@link Remote} object. - * + * * NOTE: This helper function can also be used for regular RMI servers. - * + * * @param obj The object to un-export. */ public static void unexportObject(Remote obj) { @@ -507,14 +507,12 @@ public static void unexportObject(Remote obj) { } } - private void setRegistry(AFRegistry registry) { - synchronized (AFNaming.class) { - this.registry = registry; - if (registry == null) { - rmiService = null; - } else if (registry.isLocal()) { - closeUponRuntimeShutdown(); - } + private synchronized void setRegistry(AFRegistry registry) { + this.registry = registry; + if (registry == null) { + rmiService = null; + } else if (registry.isLocal()) { + closeUponRuntimeShutdown(); } } } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingProvider.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingProvider.java index e412a1396..8d24a4471 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingProvider.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,17 +21,17 @@ /** * The key to accessing to {@link AFNaming} instances. - * + * * Implementors must guarantee that {@link #hashCode()} and {@link #equals(Object)} correctly * identify duplicate providers. - * + * * @param The actual {@link AFNaming} subclass. * @author Christian Kohlschütter */ public interface AFNamingProvider { /** * Creates a new {@link AFNaming} instance using the given registry port. - * + * * @param registryPort The registry port. * @return The {@link AFNaming} instance. * @throws IOException on error. diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingRef.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingRef.java index 92f010b97..276991eeb 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingRef.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFNamingRef.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * A reference to a {@link AFNaming} instance. - * + * * @author Christian Kohlschütter */ final class AFNamingRef { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIService.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIService.java index 10d7edd85..6b654ec29 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIService.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIService.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,16 +25,16 @@ /** * The {@link AFRMIService} assigns and keeps track of anonymous ports, among other things. - * + * * This feature is to be used by {@link AFRMISocketFactory} only. - * + * * @author Christian Kohlschütter */ public interface AFRMIService extends Remote { /** * Registers a new anonymous port and returns it. When the port is not required anymore, it must * be returned via {@link #returnPort(int)}. - * + * * @return The new port. * @throws IOException if the operation fails. */ @@ -43,7 +43,7 @@ public interface AFRMIService extends Remote { /** * Returns a previously registered port. No error is thrown if the given port has not been * assigned before. - * + * * @param port The port. * @throws IOException if the operation fails. */ @@ -51,7 +51,7 @@ public interface AFRMIService extends Remote { /** * Returns a stream of open ports. - * + * * @return A sequence of open ports. * @throws RemoteException if the operation fails. */ @@ -59,9 +59,9 @@ public interface AFRMIService extends Remote { /** * Indicates whether a remote-shutdown of the RMI registry is allowed. - * + * * NOTE: A call to {@link #shutdown()} may or may not succeed regardless. - * + * * @return Indication of whether a remote-shutdown of the RMI registry is allowed. * @throws RemoteException if the operation fails. */ @@ -69,7 +69,7 @@ public interface AFRMIService extends Remote { /** * Asks that this RMI registry gets shut down. - * + * * @throws RemoteException if the operation fails. */ void shutdown() throws RemoteException; @@ -77,7 +77,7 @@ public interface AFRMIService extends Remote { /** * Adds the given {@link Closeable} to the list of instances to be closed upon shutdown of the RMI * registry. - * + * * @param closeable The instance. * @throws RemoteException if the operation fails. */ @@ -86,9 +86,9 @@ public interface AFRMIService extends Remote { /** * Removes the given {@link Closeable} from the list of instances to be closed upon shutdown of * the RMI registry. - * + * * No error is returned if the given element was not registered before. - * + * * @param closeable The instance. * @throws RemoteException if the operation fails. */ diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIServiceImpl.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIServiceImpl.java index 01d5083bb..b6fb4dbda 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIServiceImpl.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMIServiceImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,11 +34,13 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.stream.IntStream; +import org.newsclub.net.unix.StackTraceUtil; + import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; /** * A very simple implementation of a {@link AFRMIService}. - * + * * @author Christian Kohlschütter */ final class AFRMIServiceImpl implements AFRMIService { @@ -120,7 +122,7 @@ public boolean isShutdownAllowed() throws RemoteException { public void registerForShutdown(Closeable closeable) throws RemoteException { synchronized (closeAtShutdown) { unregisterForShutdown(closeable); - closeAtShutdown.add(new WeakReference(closeable)); + closeAtShutdown.add(new WeakReference<>(closeable)); } } @@ -146,21 +148,18 @@ void shutdownRegisteredCloseables() { ExecutorService executor = Executors.newCachedThreadPool(); for (WeakReference ref : list) { - executor.submit(new Runnable() { - @Override - public void run() { - @SuppressWarnings("resource") - Closeable cl = ref.get(); - if (cl == null) { - return; - } - try { - cl.close(); - } catch (NoSuchObjectException e) { - // ignore - } catch (IOException e) { - e.printStackTrace(); - } + executor.execute(() -> { + @SuppressWarnings("resource") + Closeable cl = ref.get(); + if (cl == null) { + return; + } + try { + cl.close(); + } catch (NoSuchObjectException e) { + // ignore + } catch (IOException e) { + StackTraceUtil.printStackTrace(e); } }); } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMISocketFactory.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMISocketFactory.java index 820fdfa5d..1c17b83ef 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMISocketFactory.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRMISocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,55 +36,61 @@ import org.newsclub.net.unix.AFServerSocket; import org.newsclub.net.unix.AFSocket; import org.newsclub.net.unix.AFSocketAddress; +import org.newsclub.net.unix.StackTraceUtil; import org.newsclub.net.unix.rmi.ShutdownHookSupport.ShutdownHook; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; /** * An {@link RMISocketFactory} that supports {@link AFSocket}s. - * + * * @author Christian Kohlschütter */ public abstract class AFRMISocketFactory extends RMISocketFactory implements Externalizable, Closeable { private static final long serialVersionUID = 1L; - private RMIClientSocketFactory defaultClientFactory; - private RMIServerSocketFactory defaultServerFactory; + private transient AFRMIService rmiService = null; - private AFNaming naming; + private Externables externables; + private final transient Map> openServerSockets = new HashMap<>(); + private final transient Set> openSockets = new HashSet<>(); - private AFRMIService rmiService = null; + private static final class Externables { + private final AFNaming naming; + private final RMIClientSocketFactory defaultClientFactory; + private final RMIServerSocketFactory defaultServerFactory; - private final Map> openServerSockets = new HashMap<>(); - private final Set> openSockets = new HashSet<>(); + private Externables(AFNaming naming, RMIClientSocketFactory defaultClientFactory, + RMIServerSocketFactory defaultServerFactory) { + this.naming = naming; + this.defaultClientFactory = defaultClientFactory; + this.defaultServerFactory = defaultServerFactory; + } + } /** * Constructor required per definition. - * + * * @see RMISocketFactory */ public AFRMISocketFactory() { - super(); - closeUponRuntimeShutdown(); + this(null, null, null); } /** * Creates a new socket factory. - * + * * @param naming The {@link AFNaming} instance to use. * @param defaultClientFactory The default {@link RMIClientSocketFactory}. * @param defaultServerFactory The default {@link RMIServerSocketFactory}. - * @throws IOException on error. */ @SuppressFBWarnings("EI_EXPOSE_REP2") public AFRMISocketFactory(final AFNaming naming, final RMIClientSocketFactory defaultClientFactory, - final RMIServerSocketFactory defaultServerFactory) throws IOException { + final RMIServerSocketFactory defaultServerFactory) { super(); - this.naming = naming; - this.defaultClientFactory = defaultClientFactory; - this.defaultServerFactory = defaultServerFactory; + this.externables = new Externables(naming, defaultClientFactory, defaultServerFactory); closeUponRuntimeShutdown(); } @@ -106,7 +112,7 @@ public void onRuntimeShutdown(Thread thread) { /** * Creates a new socket address for the given RMI port. - * + * * @param port The port. * @return The socket address. * @throws IOException on error. @@ -115,16 +121,24 @@ public void onRuntimeShutdown(Thread thread) { /** * Creates a new socket that is connected to the given socket address. - * + * * @param addr The socket address. * @return The connected socket. * @throws IOException on error. */ protected abstract AFSocket newConnectedSocket(AFSocketAddress addr) throws IOException; + private synchronized Externables getExternables() { + return externables; + } + + private synchronized void setExternable(Externables externable) { + this.externables = externable; + } + @Override public Socket createSocket(String host, int port) throws IOException { - final RMIClientSocketFactory cf = defaultClientFactory; + final RMIClientSocketFactory cf = getExternables().defaultClientFactory; if (cf != null && port < RMIPorts.AF_PORT_BASE) { return cf.createSocket(host, port); } @@ -145,7 +159,7 @@ public Socket createSocket(String host, int port) throws IOException { @Override public void close() throws IOException { - synchronized (naming) { + synchronized (getExternables().naming) { rmiService = null; closeServerSockets(); closeSockets(); @@ -153,6 +167,7 @@ public void close() throws IOException { } private AFRMIService getRmiService() throws IOException { + AFNaming naming = getExternables().naming; synchronized (naming) { if (rmiService == null) { try { @@ -167,35 +182,98 @@ private AFRMIService getRmiService() throws IOException { /** * Returns a new free port. - * + * * @return The new port. * @throws IOException on error. * @see #returnPort(int) + * @deprecated use {@link #newPortLease()}. */ + @Deprecated protected int newPort() throws IOException { return getRmiService().newPort(); } + /** + * Returns a new free port. + * + * @return The new port, wrapped as a {@link PortLease}. Closing the lease will return the port. + * @throws IOException on error. + */ + protected PortLease newPortLease() throws IOException { + AFRMIService service = getRmiService(); + int port = service.newPort(); + return new PortLease(port, service); + } + /** * Returns a port that was previously returned by {@link #newPort()}. - * + * + * Note that this may call may stall unnecessarily upon shutdown due to locking issues. + * * @param port The port to return. * @throws IOException on error. + * @deprecated use {@link #newPortLease()} */ + @Deprecated protected void returnPort(int port) throws IOException { - getRmiService().returnPort(port); + try { + getRmiService().returnPort(port); + } catch (ShutdownException e) { + // ignore + } catch (IOException e) { + StackTraceUtil.printStackTrace(e); + } + } + + /** + * A lease on a registered port; closing the lease will return the port. + * + * @author Christian Kohlschütter + */ + protected static final class PortLease implements Closeable { + private final int port; + private final AFRMIService rmiService; + + private PortLease(int port, AFRMIService rmiService) { + this.port = port; + this.rmiService = rmiService; + } + + /** + * Closes the lease, returning the port to the {@link AFRMIService} it was leased from. + */ + @Override + public void close() throws IOException { + rmiService.returnPort(getPort()); + } + + /** + * Returns the port number. + * + * @return the port number. + */ + public int getPort() { + return port; + } + + /** + * Returns the service the port was leased from. + * + * @return The service. + */ + public AFRMIService getRmiService() { + return rmiService; + } } @Override public ServerSocket createServerSocket(int port) throws IOException { if (port == 0) { - port = newPort(); - final int returnPort = port; + PortLease portLease = newPortLease(); + port = portLease.getPort(); final AFSocketAddress addr = newSocketAddress(port); AFServerSocket ass = addr.getAddressFamily().newServerSocket(); - ass.addCloseable(() -> { - returnPort(returnPort); - }); + ass.addCloseable(portLease); ass.setReuseAddress(true); ass.setDeleteOnClose(true); ass.bind(addr); @@ -206,7 +284,7 @@ public ServerSocket createServerSocket(int port) throws IOException { return ass; } - final RMIServerSocketFactory sf = defaultServerFactory; + final RMIServerSocketFactory sf = getExternables().defaultServerFactory; if (sf != null && port < RMIPorts.AF_PORT_BASE) { return sf.createServerSocket(port); } @@ -284,21 +362,23 @@ public void close() throws IOException { @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - naming = readNamingInstance(in); - defaultClientFactory = (RMIClientSocketFactory) in.readObject(); - defaultServerFactory = (RMIServerSocketFactory) in.readObject(); + setExternable(new Externables(readNamingInstance(in), // + (RMIClientSocketFactory) in.readObject(), // + (RMIServerSocketFactory) in.readObject())); } @Override public void writeExternal(ObjectOutput out) throws IOException { - writeNamingInstance(out, naming); - out.writeObject(defaultClientFactory); - out.writeObject(defaultServerFactory); + Externables ext = getExternables(); + + writeNamingInstance(out, ext.naming); + out.writeObject(ext.defaultClientFactory); + out.writeObject(ext.defaultServerFactory); } /** * Deserializes information necessary to instantiate the {@link AFNaming} instance. - * + * * @param in The stream. * @return The {@link AFNaming} instance. * @throws IOException on error. @@ -307,7 +387,7 @@ public void writeExternal(ObjectOutput out) throws IOException { /** * Serializes information necessary to instantiate the given {@link AFNaming} instance. - * + * * @param out The stream. * @param namingInstance The {@link AFNaming} instance. * @throws IOException on error. @@ -317,7 +397,7 @@ protected abstract void writeNamingInstance(ObjectOutput out, AFNaming namingIns /** * Checks if the given port refers to a local server port. - * + * * @param port The port to check. * @return {@code true} if the given port is a local server. */ @@ -332,16 +412,16 @@ public boolean isLocalServer(int port) { /** * The naming instance. - * + * * @return The instance. */ protected AFNaming getNaming() { - return naming; + return getExternables().naming; } /** * Checks if this socket factory has some knowledge about the given port. - * + * * @param port The port. * @return {@code true} if registered. */ diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistry.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistry.java index c2402bb0b..24bb24f51 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistry.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistry.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.newsclub.net.unix.rmi.ShutdownHookSupport.ShutdownHook; @@ -39,7 +40,7 @@ /** * A wrapper for RMI registries, both remote and local, to allow for a clean removal of bound * resources upon shutdown. - * + * * @author Christian Kohlschütter */ public abstract class AFRegistry implements Registry { @@ -48,7 +49,9 @@ public abstract class AFRegistry implements Registry { private final Registry impl; private final Map bound = new HashMap<>(); private final AFNaming naming; - private boolean boundCloserExported = false; + private final AtomicBoolean boundCloserExported = new AtomicBoolean(false); + + private AFRMIService rmiService = null; AFRegistry(AFNaming naming, Registry impl) throws RemoteException { this.naming = naming; @@ -78,7 +81,7 @@ public void onRuntimeShutdown(Thread thread) { /** * Returns {@code true} if the wrapped Registry instance is a locally created * {@link RemoteServer}. - * + * * @return {@code true} if wrapped instance is a locally created {@link RemoteServer}. * @see #isLocal() */ @@ -89,7 +92,7 @@ public boolean isRemoteServer() { /** * Returns {@code true} if the wrapped Registry instance is locally created. - * + * * @return {@code true} if wrapped instance is locally created. */ public final boolean isLocal() { @@ -98,7 +101,7 @@ public final boolean isLocal() { /** * Returns the {@link AFNaming} instance responsible for this registry. - * + * * @return The {@link AFNaming} instance. */ @SuppressFBWarnings("EI_EXPOSE_REP") @@ -246,12 +249,10 @@ private void checkBound() throws RemoteException { empty = bound.isEmpty(); } if (empty) { - if (boundCloserExported) { - boundCloserExported = false; - + if (boundCloserExported.compareAndSet(true, false)) { AFRMIService service; try { - service = naming.getRMIService(this); + service = getRMIService(); service.unregisterForShutdown(boundCloser); } catch (NoSuchObjectException | NotBoundException e) { return; @@ -259,17 +260,23 @@ private void checkBound() throws RemoteException { AFNaming.unexportObject(boundCloser); } } - } else if (!boundCloserExported) { + } else if (boundCloserExported.compareAndSet(false, true)) { AFNaming.exportObject(boundCloser, naming.getSocketFactory()); - boundCloserExported = true; AFRMIService service; try { - service = naming.getRMIService(this); + service = getRMIService(); } catch (NotBoundException e) { return; } service.registerForShutdown(boundCloser); } } + + private AFRMIService getRMIService() throws RemoteException, NotBoundException { + if (rmiService == null) { + rmiService = naming.getRMIService(this); + } + return rmiService; + } } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistryAccess.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistryAccess.java index 24d08c92f..80a428805 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistryAccess.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFRegistryAccess.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ abstract class AFRegistryAccess { /** * Returns a reference to the existing RMI registry. - * + * * If there's no registry running at this port, an exception is thrown. - * + * * @return The registry. * @throws RemoteException If there was a problem. */ @@ -38,7 +38,7 @@ abstract class AFRegistryAccess { /** * Convenience method for {@code getRegistry().lookup}. - * + * * @param name the name for the remote reference to look up * @return The instance * @throws NotBoundException upon error @@ -53,7 +53,7 @@ public Remote lookup(String name) throws NotBoundException, MalformedURLExceptio /** * Convenience method for {@code getRegistry().lookup}. - * + * * @param name the name for the remote reference to look up * @param timeout The timeout value. * @param unit The timeout unit. @@ -70,7 +70,7 @@ public Remote lookup(String name, long timeout, TimeUnit unit) throws NotBoundEx /** * Convenience method for {@code getRegistry().unbind}. - * + * * @param name the name for the remote reference to unbind * @throws RemoteException upon error * @throws NotBoundException upon error @@ -83,7 +83,7 @@ public void unbind(String name) throws RemoteException, NotBoundException, Malfo /** * Convenience method for {@code getRegistry().bind}. - * + * * @param name the name for the remote reference to bind * @param obj the remote reference to bind * @throws RemoteException upon error @@ -98,7 +98,7 @@ public void bind(String name, Remote obj) throws AlreadyBoundException, Malforme /** * Convenience method for {@code getRegistry().rebind}. - * + * * @param name the name for the remote reference to rebind * @param obj the remote reference to rebind * @throws RemoteException upon error @@ -111,10 +111,10 @@ public void rebind(String name, Remote obj) throws MalformedURLException, Remote /** * Convenience method for {@code getRegistry().list}. - * + * * Unlike {@link AFRegistry#list()}, in case the registry has been shut down already, an empty * array is returned. - * + * * @return an array of the names bound in this registry * @throws RemoteException upon error * @throws AccessException upon error diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXNaming.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXNaming.java index 10566abef..0288b6695 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXNaming.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXNaming.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ /** * The {@link AFUNIXSocket}-compatible equivalent of {@link Naming}. Use this class for accessing * RMI registries that are reachable by {@link AFUNIXSocket}s. - * + * * @author Christian Kohlschütter */ public final class AFUNIXNaming extends AFNaming { @@ -66,10 +66,10 @@ private AFUNIXNaming(File socketDir, int registryPort, String socketPrefix, Stri /** * Returns the directory where RMI sockets are stored by default. - * + * * You can configure this location by setting the System property * {@code org.newsclub.net.unix.rmi.socketdir} upon start. - * + * * @return The directory. */ public static File getDefaultSocketDirectory() { @@ -79,7 +79,7 @@ public static File getDefaultSocketDirectory() { /** * Returns a new private instance that resides in a custom location, to avoid any collisions with * existing instances. - * + * * @return The private {@link AFNaming} instance. * @throws IOException if the operation fails. */ @@ -98,7 +98,7 @@ public static AFUNIXNaming newPrivateInstance() throws IOException { /** * Returns the default instance of {@link AFUNIXNaming}. Sockets are stored in * java.io.tmpdir. - * + * * @return The default instance. * @throws IOException if the operation fails. */ @@ -109,7 +109,7 @@ public static AFUNIXNaming getInstance() throws IOException { /** * Returns a {@link AFUNIXNaming} instance which support several socket files that can be stored * under the same, given directory. - * + * * @param socketDir The directory to store sockets in. * @return The instance. * @throws RemoteException if the operation fails. @@ -121,10 +121,10 @@ public static AFUNIXNaming getInstance(final File socketDir) throws RemoteExcept /** * Returns a {@link AFUNIXNaming} instance which support several socket files that can be stored * under the same, given directory. - * + * * A custom "registry port" can be specified. Typically, AF-UNIX specific ports should be above * {@code 100000}. - * + * * @param socketDir The directory to store sockets in. * @param registryPort The registry port. Should be above {@code 100000}. * @return The instance. @@ -138,10 +138,10 @@ public static AFUNIXNaming getInstance(File socketDir, final int registryPort) /** * Returns a {@link AFUNIXNaming} instance which support several socket files that can be stored * under the same, given directory. - * + * * A custom "registry port" can be specified. Typically, AF-UNIX specific ports should be above * {@code 100000}. - * + * * @param socketDir The directory to store sockets in. * @param registryPort The registry port. Should be above {@code 100000}. * @param socketPrefix A string to be inserted at the beginning of each socket filename, or @@ -183,7 +183,7 @@ public AFUNIXNaming newInstance(int port) throws IOException { /** * Returns an {@link AFUNIXNaming} instance which only supports one file. (Probably only useful * when you want/can access the exported {@link UnicastRemoteObject} directly) - * + * * @param socketFile The socket file. * @return The instance. * @throws IOException if the operation fails. @@ -221,7 +221,7 @@ public AFUNIXRegistry createRegistry() throws RemoteException { * Returns the socket file which is used to control the RMI registry. * * The file is usually in the directory returned by {@link #getRegistrySocketDir()}. - * + * * @return The directory. */ public File getRegistrySocketFile() { @@ -285,7 +285,7 @@ protected void shutdownRegistryFinishingTouches() { /** * Returns the directory in which sockets used by this registry are located. - * + * * @return The directory. */ public File getRegistrySocketDir() { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRMISocketFactory.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRMISocketFactory.java index 4f101db97..806a64140 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRMISocketFactory.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRMISocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ /** * An {@link RMISocketFactory} that supports {@link AFUNIXSocket}s. - * + * * @author Christian Kohlschütter */ public class AFUNIXRMISocketFactory extends AFRMISocketFactory { @@ -52,11 +52,11 @@ public class AFUNIXRMISocketFactory extends AFRMISocketFactory { private String socketPrefix; private String socketSuffix; - private final Map credentials = new HashMap<>(); + private final transient Map credentials = new HashMap<>(); /** * Constructor required per definition. - * + * * @see RMISocketFactory */ public AFUNIXRMISocketFactory() { @@ -65,7 +65,7 @@ public AFUNIXRMISocketFactory() { /** * Creates a new socket factory. - * + * * @param naming The {@link AFNaming} instance to use. * @param socketDir The directory to store the sockets in. * @param defaultClientFactory The default {@link RMIClientSocketFactory}. @@ -90,7 +90,7 @@ public AFUNIXRMISocketFactory(final AFNaming naming, final File socketDir, /** * Creates a new socket factory. - * + * * @param naming The {@link AFNaming} instance to use. * @param socketDir The directory to store the sockets in. * @param defaultClientFactory The default {@link RMIClientSocketFactory}. @@ -105,7 +105,7 @@ public AFUNIXRMISocketFactory(AFNaming naming, File socketDir, /** * Creates a new socket factory. - * + * * @param naming The {@link AFNaming} instance to use. * @param socketDir The directory to store the sockets in. * @throws IOException on error. @@ -164,7 +164,7 @@ public boolean equals(Object other) { /** * The directory in which socket files are stored. - * + * * @return The directory. */ public File getSocketDir() { @@ -222,7 +222,9 @@ public String toString() { @Override public void close() throws IOException { - credentials.clear(); + synchronized (credentials) { + credentials.clear(); + } super.close(); } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRegistry.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRegistry.java index 7f94a6042..ebac7dc66 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRegistry.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/AFUNIXRegistry.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +23,9 @@ /** * A wrapper for RMI registries, both remote and local, to allow for a clean removal of bound * resources upon shutdown. - * + * * This subclass mostly exists for backwards compatibility. - * + * * @author Christian Kohlschütter */ public class AFUNIXRegistry extends AFRegistry { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIClientSocketFactory.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIClientSocketFactory.java index a7b3cb4ca..a66a456ff 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIClientSocketFactory.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIClientSocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +22,10 @@ import java.net.Socket; import java.rmi.server.RMIClientSocketFactory; -import org.newsclub.net.unix.AFSocket; - /** * An implementation of {@link RMIClientSocketFactory}. - * - * @see AFRMISocketFactory where we deal with {@link AFSocket}s. + * + * @see AFRMISocketFactory */ public final class DefaultRMIClientSocketFactory implements RMIClientSocketFactory, Serializable { private static final long serialVersionUID = 1L; @@ -38,7 +36,7 @@ private DefaultRMIClientSocketFactory() { /** * Returns the singleton instance for DefaultRMIClientSocketFactory. - * + * * @return The singleton. */ public static DefaultRMIClientSocketFactory getInstance() { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIServerSocketFactory.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIServerSocketFactory.java index 55c688763..5278d6ca4 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIServerSocketFactory.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/DefaultRMIServerSocketFactory.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +22,10 @@ import java.net.ServerSocket; import java.rmi.server.RMIServerSocketFactory; -import org.newsclub.net.unix.AFSocket; - /** * An implementation of {@link RMIServerSocketFactory}. - * - * @see AFRMISocketFactory where we deal with {@link AFSocket}. + * + * @see AFRMISocketFactory */ public class DefaultRMIServerSocketFactory implements RMIServerSocketFactory, Serializable { private static final long serialVersionUID = 1L; @@ -42,7 +40,7 @@ public DefaultRMIServerSocketFactory() { /** * Returns the singleton instance for DefaultRMIServerSocketFactory. - * + * * @return The singleton. */ public static DefaultRMIServerSocketFactory getInstance() { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RMIPorts.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RMIPorts.java index 246e302c9..bf6415b6e 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RMIPorts.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RMIPorts.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * Contains some default ports used by junixsocket for RMI-over-AF_UNIX etc. - * + * * @author Christian Kohlschütter * @see AFRMISocketFactory * @see AFRMIService @@ -42,14 +42,14 @@ final class RMIPorts { /** * This is the port reserved for the port assigner. - * + * * @see AFRMIService */ static final int RMI_SERVICE_PORT = 100002; /** * This is the base for anonymous ports. Any anonymous port will be higher than this number. - * + * * @see AFRMIService */ static final int ANONYMOUS_PORT_BASE = 110000; @@ -60,7 +60,7 @@ final class RMIPorts { */ static final int PLAIN_FILE_SOCKET = Integer.MAX_VALUE; - @ExcludeFromCodeCoverageGeneratedReport + @ExcludeFromCodeCoverageGeneratedReport(reason = "unreachable") private RMIPorts() { throw new UnsupportedOperationException("No instances"); } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseable.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseable.java index 54aa9fc48..7f8004fa9 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseable.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseable.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,17 +24,18 @@ /** * A resource that can be exposed remotely, and closed locally as well as remotely. - * + * * @author Christian Kohlschütter + * @param The resource type. */ public interface RemoteCloseable extends Remote, Closeable { /** * Returns the resource (or the Remote instance of it). - * + * * If the returned resource is {@link Closeable}, then closing via {@code get().close()}} will * affect the client-side (local), but not necessarily the server-side as well (the exact behavior * depends on the resource). - * + * * @return The wrapped resource. * @throws NoSuchObjectException if this instance has been closed already. * @throws IOException if there was a problem. @@ -44,7 +45,7 @@ public interface RemoteCloseable extends Remote, Closeable { /** * Closes the resource on the server-side (i.e., where it was created), and — as long as the * wrapped resource returned by {@link #get()} supports it — locally as well. - * + * * @throws IOException if there was a problem. */ @Override diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseableImpl.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseableImpl.java index 2eb581474..87a1a9763 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseableImpl.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteCloseableImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,13 +27,14 @@ * * @author Christian Kohlschütter * @see RemoteCloseable + * @param The resource type. */ -public class RemoteCloseableImpl implements RemoteCloseable { +public final class RemoteCloseableImpl implements RemoteCloseable { private final T remote; /** * Created a new instance. - * + * * @param socketFactory The socket factory. * @param obj The object to wrap. * @throws RemoteException on error. @@ -44,18 +45,18 @@ public RemoteCloseableImpl(RMISocketFactory socketFactory, T obj) throws RemoteE } @Override - public final void close() throws IOException { + public void close() throws IOException { AFNaming.unexportObject(this); doClose(remote); } /** * Closes the given object. - * + * * @param obj The object to close. * @throws IOException on error. */ - protected void doClose(T obj) throws IOException { + private void doClose(T obj) throws IOException { if (obj instanceof Closeable) { ((Closeable) obj).close(); } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptor.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptor.java index 42406fe4f..6ce008973 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptor.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptor.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * A generic (unspecific) {@link FileDescriptor} reference. - * + * * @author Christian Kohlschütter * @see RemoteFileInput * @see RemoteFileOutput @@ -34,7 +34,7 @@ public final class RemoteFileDescriptor extends RemoteFileDescriptorBase { /** * Creates an uninitialized instance; used for externalization. - * + * * @see #readExternal(ObjectInput) */ public RemoteFileDescriptor() { @@ -44,7 +44,7 @@ public RemoteFileDescriptor() { /** * Creates a new {@link RemoteFileOutput} instance, encapsulating a generic {@link FileDescriptor} * so that it can be shared with other processes via RMI. - * + * * @param socketFactory The socket factory. * @param fd The {@link FileDescriptor}. */ @@ -56,7 +56,7 @@ public RemoteFileDescriptor(AFUNIXRMISocketFactory socketFactory, FileDescriptor public synchronized void close() throws IOException { FileDescriptor fd = getFileDescriptor(); if (fd != null && fd.valid()) { - try (FileInputStream fin = new FileInputStream(fd)) { + try (FileInputStream unused = new FileInputStream(fd)) { // should succeed } } diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorBase.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorBase.java index 32a4a1224..001fe1e41 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorBase.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,6 @@ import java.io.DataOutputStream; import java.io.Externalizable; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; @@ -31,6 +29,7 @@ import java.io.OutputStream; import java.net.SocketException; import java.util.Objects; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -46,10 +45,11 @@ /** * A wrapper that allows a {@link FileDescriptor} be sent via RMI over AF_UNIX sockets. - * + * * @author Christian Kohlschütter - * @see RemoteFileInput subclass for sending {@link FileInputStream}s. - * @see RemoteFileOutput subclass for sending {@link FileOutputStream}s. + * @param The resource type. + * @see RemoteFileInput + * @see RemoteFileOutput */ public abstract class RemoteFileDescriptorBase implements Externalizable, Closeable, FileDescriptorAccess { @@ -75,22 +75,22 @@ public abstract class RemoteFileDescriptorBase implements Externalizable, Clo /** * An optional, closeable resource that is related to this instance. If the reference is non-null, * this will be closed upon {@link #close()}. - * + * * For unidirectional implementations, this could be the corresponding input/output stream. For * bidirectional implementations (e.g., a Socket, Pipe, etc.), this should close both directions. */ protected final transient AtomicReference resource = new AtomicReference<>(); private int magicValue; - private FileDescriptor fd; + private transient FileDescriptor fd; private AFUNIXRMISocketFactory socketFactory; /** * Creates an uninitialized instance; used for externalization. - * + * * @see #readExternal(ObjectInput) */ - RemoteFileDescriptorBase() { + public RemoteFileDescriptorBase() { } RemoteFileDescriptorBase(AFUNIXRMISocketFactory socketFactory, T stream, FileDescriptor fd, @@ -153,7 +153,9 @@ protected void onServerStopped(AFServerSocket socket) { } }; - server.startThenStopAfter(SERVER_TIMEOUT, TimeUnit.MILLISECONDS); + @SuppressWarnings("unused") + ScheduledFuture unused = server.startThenStopAfter(SERVER_TIMEOUT, + TimeUnit.MILLISECONDS); } catch (IOException e) { objOut.writeObject(e); throw e; @@ -223,10 +225,10 @@ public final void readExternal(ObjectInput objIn) throws IOException, ClassNotFo /** * Returns the file descriptor. - * + * * This is either the original one that was specified in the constructor or a copy that was sent * via RMI over an AF_UNIX connection as part of an ancillary message. - * + * * @return The file descriptor. */ @Override @@ -237,11 +239,11 @@ public final FileDescriptor getFileDescriptor() { /** * Returns the "magic value" for this type of file descriptor. - * + * * The magic value consists of an indicator ("this is a file descriptor") as well as its * capabilities (read/write). It is used to prevent, for example, converting an output stream to * an input stream. - * + * * @return The magic value. */ protected final int getMagicValue() { diff --git a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileInput.java b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileInput.java index bad662be2..e6388977e 100644 --- a/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileInput.java +++ b/junixsocket-rmi/src/main/java/org/newsclub/net/unix/rmi/RemoteFileInput.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * A specialized subclass of {@link RemoteFileDescriptorBase}, specifically for * {@link FileInputStream}s. - * + * * @author Christian Kohlschütter */ public final class RemoteFileInput extends RemoteFileDescriptorBase implements @@ -34,7 +34,7 @@ public final class RemoteFileInput extends RemoteFileDescriptorBase { @@ -33,7 +33,7 @@ public final class RemoteFileOutput extends RemoteFileDescriptorBase list; @@ -68,19 +69,19 @@ static void runHooks() { /** * Something that wants to be called upon Runtime shutdown. - * + * * @author Christian Kohlschütter */ interface ShutdownHook { /** * Called upon Runtime shutdown. - * + * * When you implement this method, make sure to check that the given Thread matches the current * thread, e.g.: * if (thread != Thread.currentThread() || !(thread instanceof ShutdownThread)) { * throw new IllegalStateException("Illegal caller"); } * - * + * * @param thread The current Thread. * @throws Exception Most likely ignored */ @@ -90,7 +91,7 @@ interface ShutdownHook { /** * The Thread that will be called upon Runtime shutdown. - * + * * @author Christian Kohlschütter */ static final class ShutdownThread extends Thread { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityCondition.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityCondition.java index 92d875e16..ad341b9bd 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityCondition.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityCondition.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,12 @@ // This is a deliberate copy of the same class from junixsocket-common's tests. // CPD-OFF public class AFSocketCapabilityCondition implements ExecutionCondition { + /** + * Constructs a new {@link AFSocketCapabilityCondition}. + */ + public AFSocketCapabilityCondition() { + } + @SuppressWarnings("exports") @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityRequirement.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityRequirement.java index 7ba3a0c30..70af5f39e 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityRequirement.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/AFSocketCapabilityRequirement.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/Hello.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/Hello.java index f8dca96f1..2588a4f5e 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/Hello.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/Hello.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,13 @@ /** * A very simple "hello" service. - * + * * @author Christian Kohlschütter */ public interface Hello extends Remote { /** * Returns "Hello". - * + * * @return "Hello" * @throws IOException if the operation fails. */ diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/HelloImpl.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/HelloImpl.java index 813f61658..38b13b1d1 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/HelloImpl.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/HelloImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ /** * The implementation of the very simple {@link Hello} service. - * + * * @author Christian Kohlschütter */ public class HelloImpl implements Hello { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/JunixsocketVersionTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/JunixsocketVersionTest.java index b8c4092ab..aedc8361e 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/JunixsocketVersionTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/JunixsocketVersionTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,24 +18,22 @@ package org.newsclub.net.unix.rmi; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; import org.newsclub.net.unix.AFSocket; /** * Tests whether we can get the junixsocket version (which is encoded in a properties file). - * + * * This can't easily be tested from within the Maven build for junixsocket-common, which is why we * have it in junixsocket-rmi. - * + * * @author Christian Kohlschütter */ public class JunixsocketVersionTest { @Test public void testVersion() { String version = AFSocket.getVersion(); - assertNotNull(version); assertNotEquals("", version); } } diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemote.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemote.java index 57575f843..e71cbf376 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemote.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemote.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,10 +25,10 @@ /** * Exposes a {@link FileInputStream}'s basic functionality over RMI. - * + * * As opposed to {@link RemoteFileDescriptorBase}, all data is read, then copied and serialized via * RMI. - * + * * @author Christian Kohlschütter * @see RemoteFileDescriptorBase for a better way. */ diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemoteImpl.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemoteImpl.java index 36090a9db..4da892334 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemoteImpl.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/NaiveFileInputStreamRemoteImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RMIPeerCredentialsTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RMIPeerCredentialsTest.java index 5446de81f..89e562806 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RMIPeerCredentialsTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RMIPeerCredentialsTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ /** * Verifies that peer credentials are properly set when communicating over RMI. - * + * * @author Christian Kohlschütter */ @AFSocketCapabilityRequirement({ diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RegistryTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RegistryTest.java index bbc00fc73..4d4891eaf 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RegistryTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RegistryTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableTest.java index 961aa72e7..db2b2beaf 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ /** * Tests {@link RemoteCloseable}. - * + * * @author Christian Kohlschütter */ @SuppressFBWarnings({ @@ -54,7 +54,7 @@ public void testRemoteCloseableWithACloseableThing() throws IOException, NotBoun try (RemoteCloseable remoteCloseable = svc.remoteCloseable( IsCloseable.class)) { - try (IsCloseable testCloseable = remoteCloseable.get()) { + try (IsCloseable unused = remoteCloseable.get()) { assertEquals(0, svc.remoteCloseableThingNumberOfCloseCalls(IsCloseable.class)); // forcibly calling close here unexports the RemoteCloseable remoteCloseable.close(); @@ -73,7 +73,7 @@ public void testRemoteCloseableWithACloseableThing() throws IOException, NotBoun try (RemoteCloseable remoteCloseable = svc.remoteCloseable( IsCloseable.class)) { - try (IsCloseable testCloseable = remoteCloseable.get()) { + try (IsCloseable unused = remoteCloseable.get()) { // no exception thrown } } @@ -109,8 +109,7 @@ public void testRemoteCloseableWithANotCloseableThing() throws IOException, NotB } assertEquals(0, svc.remoteCloseableThingNumberOfCloseCalls(NotCloseable.class)); - try (RemoteCloseable remoteCloseable = svc.remoteCloseable( - NotCloseable.class)) { + try (RemoteCloseable unused = svc.remoteCloseable(NotCloseable.class)) { // no exception thrown } diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThing.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThing.java index 47b7e7894..c95d6dd83 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThing.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThing.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ /** * To be used by {@link RemoteCloseableTest}. - * + * * @author Christian Kohlschütter */ public interface RemoteCloseableThing extends Serializable { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThingImpl.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThingImpl.java index a904ca45b..c5d1885ff 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThingImpl.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteCloseableThingImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ /** * To be used by {@link RemoteCloseableTest}. - * + * * @author Christian Kohlschütter */ abstract class RemoteCloseableThingImpl implements RemoteCloseableThing { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorTest.java index 8cc8b3ffb..653b2a22e 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteFileDescriptorTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import java.nio.charset.StandardCharsets; import java.rmi.NotBoundException; import java.rmi.server.RMISocketFactory; +import java.util.Objects; import org.junit.jupiter.api.Test; import org.newsclub.net.unix.AFSocketCapability; @@ -63,7 +64,7 @@ public void testRemoteStdout() throws IOException, NotBoundException { public void testRemoteStdoutNoop() throws IOException, NotBoundException { TestService svc = lookupTestService(); - try (RemoteFileDescriptor stdout = svc.stdout()) { + try (RemoteFileDescriptor unused = svc.stdout()) { // not doing anything here should trigger descriptor cleanup in RemoteFileDescriptor#close } } @@ -83,7 +84,8 @@ public void testWriteAndReadHello() throws IOException, NotBoundException { } try (NaiveFileInputStreamRemote rfis = svc.naiveInputStreamRemote(); - FileInputStream fin = rfis.getRemoteFileDescriptor().asFileInputStream()) { + FileInputStream fin = Objects.requireNonNull(rfis.getRemoteFileDescriptor() + .asFileInputStream())) { assertEquals('H', rfis.read()); assertEquals('e', fin.read()); assertEquals('l', fin.read()); diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteRegistryTest.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteRegistryTest.java index f6b47ef64..d5eac0110 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteRegistryTest.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/RemoteRegistryTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,35 +42,56 @@ import com.kohlschutter.testutil.ForkedVMRequirement; import com.kohlschutter.testutil.OutputBridge; import com.kohlschutter.testutil.OutputBridge.ProcessStream; +import com.kohlschutter.testutil.TestAsyncUtil; -@ForkedVMRequirement(forkSupported = true) @SuppressFBWarnings({ "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"}) public class RemoteRegistryTest { @Test + @ForkedVMRequirement(forkSupported = true) public void testRemoteRegistry() throws Exception { File socketDir = tempSocketDir(); + try { + try (SpawnedRegistryAccess sra = new SpawnedRegistryAccess(TestRegistryServer.class + .getSimpleName(), socketDir) { - try (SpawnedRegistryAccess sra = new SpawnedRegistryAccess(TestRegistryServer.class - .getSimpleName(), socketDir) { + @Override + protected int exportHelloDelayMillis() { + return 500; + } - @Override - protected int exportHelloDelayMillis() { - return 500; + }) { + tryToSayHello(sra); + sra.shutdown(); + } catch (Exception e) { + throw e; } - }) { - tryToSayHello(sra); - sra.shutdown(); - } catch (Exception e) { - throw e; + assertEquals(0, countRMIFiles(socketDir), "There shouldn't be any RMI socket files in " + + socketDir); + } finally { + assertTrue(deleteDirectory(socketDir), "Should be able to delete temporary directory: " + + socketDir); } + } - assertEquals(0, countRMIFiles(socketDir)); - assertTrue(socketDir.delete()); + private boolean deleteDirectory(File f) { + if (!f.exists()) { + return true; + } + File[] files = f.listFiles(); + if (files != null) { + for (File d : files) { + if (!d.delete() || d.isDirectory()) { + deleteDirectory(f); + } + } + } + return f.delete(); } @Test + @ForkedVMRequirement(forkSupported = true) public void testRemoteRegistryStandardPath() throws Exception { File socketDir = AFUNIXNaming.getDefaultSocketDirectory(); assumeTrue(countRMIFiles(socketDir) == 0, @@ -96,32 +117,55 @@ protected AFNaming getNamingInstance() throws IOException { } @Test + @ForkedVMRequirement(forkSupported = true) + @SuppressWarnings("PMD.UnusedAssignment") public void testRemoteShutdownNotAllowed() throws Exception { File socketDir = tempSocketDir(); + try { + try (SpawnedRegistryAccess sra = new SpawnedRegistryAccess(TestRegistryServer.class + .getSimpleName(), socketDir) { - try (SpawnedRegistryAccess sra = new SpawnedRegistryAccess(TestRegistryServer.class - .getSimpleName(), socketDir) { + @Override + protected boolean remoteShutdownAllowed() { + return false; + } - @Override - protected boolean remoteShutdownAllowed() { - return false; - } + @Override + protected int shutdownAfterSecs() { + return 5; + } + }) { + AFRegistry registry = sra.getRegistry(); + assertNotNull(registry, "Could not access the AFUNIXRegistry created by the forked VM"); - @Override - protected int shutdownAfterSecs() { - return 30; + assertThrows(ServerException.class, () -> sra.getRegistry().getNaming().shutdownRegistry()); + + sra.shutdownAndWait(false); + if (!awaitNoRMIFiles(socketDir, 5)) { + sra.shutdownAndWait(true); + } + } catch (Exception e) { + throw e; } - }) { - AFRegistry registry = sra.getRegistry(); - assertNotNull(registry, "Could not access the AFUNIXRegistry created by the forked VM"); - assertThrows(ServerException.class, () -> sra.getRegistry().getNaming().shutdownRegistry()); + assertTrue(awaitNoRMIFiles(socketDir, 5), "There shouldn't be any RMI socket files in " + + socketDir); + } finally { + assertTrue(deleteDirectory(socketDir), "Should be able to delete temporary directory: " + + socketDir); + } + } - sra.shutdownAndWait(true); - } catch (Exception e) { - throw e; + private boolean awaitNoRMIFiles(File socketDir, int loops) throws InterruptedException { + int count = 0; + for (int i = 0; i < loops; i++) { + count = countRMIFiles(socketDir); + if (count == 0) { + return true; + } + Thread.sleep(100); } - assertEquals(0, countRMIFiles(socketDir)); + return count == 0; } private void tryToSayHello(SpawnedRegistryAccess sra) throws Exception { @@ -235,7 +279,7 @@ protected int shutdownAfterSecs() { } private void watchProcessAsync() { - executors.submit(() -> { + TestAsyncUtil.runAsync(executors, () -> { try { registryProcess.waitFor(); } catch (InterruptedException e) { @@ -249,12 +293,12 @@ private void watchProcessAsync() { "The spawned VM has terminated with RC=" + registryProcess.exitValue())); } }); - executors.submit(bridgeOut); - executors.submit(bridgeErr); + TestAsyncUtil.runAsync(executors, bridgeOut); + TestAsyncUtil.runAsync(executors, bridgeErr); } private void asyncGetRegistry() { - executors.submit(() -> { + TestAsyncUtil.runAsync(executors, () -> { try { AFNaming naming = getNamingInstance(); AFRegistry registry = naming.getRegistry(30, TimeUnit.SECONDS); diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/SelftestProvider.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/SelftestProvider.java index 263ce5b39..f1b96660e 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/SelftestProvider.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/SelftestProvider.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ */ package org.newsclub.net.unix.rmi; +import java.io.PrintWriter; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -25,7 +26,7 @@ /** * Provides references to all "junixsocket-rmi" tests that should be included in * junixsocket-selftest. - * + * * @author Christian Kohlschütter */ public class SelftestProvider { @@ -45,4 +46,7 @@ public Map[]> tests() { public Set modulesDisabledByDefault() { return Collections.emptySet(); } + + public void printAdditionalProperties(PrintWriter out) { + } } diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/ShutdownHookTestBase.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/ShutdownHookTestBase.java index a8a29993d..1655dd0e8 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/ShutdownHookTestBase.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/ShutdownHookTestBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestBase.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestBase.java index 367e2f9d3..e5419a03c 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestBase.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestBase.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestRegistryServer.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestRegistryServer.java index ca636f569..edb137205 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestRegistryServer.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestRegistryServer.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,14 +23,15 @@ import java.rmi.RemoteException; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; +import com.kohlschutter.testutil.TestStackTraceUtil; import com.kohlschutter.util.SystemPropertyUtil; /** * A simple RMI Registry that is launched as forked Java VM from unit tests such as * {@link RemoteRegistryTest}. - * + * * Important: The server will terminate ca. 10 seconds after starting. - * + * * @author Christian Kohlschütter */ public class TestRegistryServer { @@ -89,7 +90,7 @@ public void run() { try { naming.shutdownRegistry(); } catch (RemoteException e) { - e.printStackTrace(); + TestStackTraceUtil.printStackTrace(e); } System.exit(0); // NOPMD diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestService.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestService.java index 05fd71389..4e47c0328 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestService.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestService.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ /** * A test service. - * + * * @author Christian Kohlschütter */ public interface TestService extends Remote { diff --git a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestServiceImpl.java b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestServiceImpl.java index c1d9d2092..346118cf0 100644 --- a/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestServiceImpl.java +++ b/junixsocket-rmi/src/test/java/org/newsclub/net/unix/rmi/TestServiceImpl.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2022 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,10 +37,10 @@ /** * The implementation for the test service. - * + * * @author Christian Kohlschütter */ -public class TestServiceImpl implements TestService, Closeable { +public final class TestServiceImpl implements TestService, Closeable { private final File tmpFile; private final AFUNIXRMISocketFactory socketFactory; private final RemoteCloseableThingImpl.NotCloseableImpl testNotCloseableImpl = diff --git a/junixsocket-rmi/src/test/java8/org/newsclub/net/unix/rmi/RemoteRegistryTest.java b/junixsocket-rmi/src/test/java8/org/newsclub/net/unix/rmi/RemoteRegistryTest.java index b99e8d00e..e2d1e0cd2 100644 --- a/junixsocket-rmi/src/test/java8/org/newsclub/net/unix/rmi/RemoteRegistryTest.java +++ b/junixsocket-rmi/src/test/java8/org/newsclub/net/unix/rmi/RemoteRegistryTest.java @@ -1,7 +1,7 @@ /* * junixsocket * - * Copyright 2009-2021 Christian Kohlschütter + * Copyright 2009-2023 Christian Kohlschütter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/junixsocket-selftest-android/README.md b/junixsocket-selftest-android/README.md new file mode 100644 index 000000000..2cbdb2f72 --- /dev/null +++ b/junixsocket-selftest-android/README.md @@ -0,0 +1,30 @@ + +![](SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png) + +# junixsocket Selftest app for Android + +This app runs the junixsocket-selftest code on Android. + +## Installation instructions + +Use Android Studio to build and run. + +## Screenshot + +![junixsocket Selftest results](screenshot.png) + +## License + +Copyright 2023 Christian Kohlschütter + +Licensed 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. diff --git a/junixsocket-selftest-android/SelftestApp/.gitignore b/junixsocket-selftest-android/SelftestApp/.gitignore new file mode 100644 index 000000000..aa724b770 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/junixsocket-selftest-android/SelftestApp/app/.gitignore b/junixsocket-selftest-android/SelftestApp/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/build.gradle b/junixsocket-selftest-android/SelftestApp/app/build.gradle new file mode 100644 index 000000000..8884cb51f --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/build.gradle @@ -0,0 +1,76 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.kohlschutter.junixsocket.selftest.android' + compileSdk 33 + ndkVersion '25.2.9519653' + + defaultConfig { + applicationId "com.kohlschutter.junixsocket.selftest.android" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + viewBinding true + } + + packagingOptions { + // junit jars contain duplicate META-INF/LICENSE.md files + resources.excludes += "META-INF/LICENSE*" + + // exclude GraalVM config (not necessary on Android) + resources.excludes += "META-INF/native-image/**/*" + + // exclude other native image files erroneously included + resources.excludes += "lib/*/jni/*" + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + + implementation 'com.kohlschutter.junixsocket:junixsocket-native-android:2.7.2@aar' + + implementation 'com.kohlschutter.junixsocket:junixsocket-common:2.7.2' + + implementation 'com.kohlschutter.junixsocket:junixsocket-tipc:2.7.2' + implementation 'com.kohlschutter.junixsocket:junixsocket-vsock:2.7.2' + implementation 'com.kohlschutter.junixsocket:junixsocket-rmi:2.7.2' + implementation 'com.kohlschutter.junixsocket:junixsocket-darwin:2.7.2' + + // selftest-specifics + implementation('com.kohlschutter.junixsocket:junixsocket-selftest:2.7.2') { + exclude group: "com.kohlschutter.junixsocket", module: "junixsocket-core" + exclude group: "com.kohlschutter.junixsocket", module: "junixsocket-native-common" + exclude group: "com.kohlschutter.junixsocket", module: "junixsocket-native-custom" + } + implementation 'com.kohlschutter:kohlschutter-util:1.5.6' + implementation 'com.kohlschutter:kohlschutter-test-util:1.5.6' + implementation 'org.junit.platform:junit-platform-console:1.9.3' + implementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3' + implementation 'org.junit.jupiter:junit-jupiter-api:5.9.3' + + // testImplementation 'junit:junit:4.13.2' + //androidTestImplementation 'androidx.test.ext:junit:1.1.4' + //androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' +} diff --git a/junixsocket-selftest-android/SelftestApp/app/proguard-rules.pro b/junixsocket-selftest-android/SelftestApp/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/AndroidManifest.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..27ab08364 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/ic_launcher-playstore.png b/junixsocket-selftest-android/SelftestApp/app/src/main/ic_launcher-playstore.png new file mode 100644 index 000000000..63affd6d3 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/ic_launcher-playstore.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/java/com/kohlschutter/junixsocket/selftest/android/ScrollingActivity.java b/junixsocket-selftest-android/SelftestApp/app/src/main/java/com/kohlschutter/junixsocket/selftest/android/ScrollingActivity.java new file mode 100644 index 000000000..19f4b9808 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/java/com/kohlschutter/junixsocket/selftest/android/ScrollingActivity.java @@ -0,0 +1,123 @@ +package com.kohlschutter.junixsocket.selftest.android; + +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.widget.NestedScrollView; + +import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; +import com.kohlschutter.junixsocket.selftest.android.databinding.ActivityScrollingBinding; + +import org.newsclub.net.unix.AFSocket; +import org.newsclub.net.unix.selftest.Selftest; + +import java.io.IOException; +import java.io.Writer; +import java.util.concurrent.CompletableFuture; + +public class ScrollingActivity extends AppCompatActivity { + + private ActivityScrollingBinding binding; + + private static final class TextViewWriter extends Writer { + + private final NestedScrollView sv; + private final TextView tv; + + TextViewWriter(NestedScrollView sv, TextView tv, boolean clear) { + this.sv = sv; + this.tv = tv; + sv.setOverScrollMode(NestedScrollView.OVER_SCROLL_ALWAYS); + + if (clear) { + tv.post(() -> tv.setText("")); + flush(); + } + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + String s = new String(cbuf, off, len); + tv.post(() -> tv.append(s)); + } + + @Override + public void flush() { +// sv.post(() -> { +// sv.fullScroll(View.FOCUS_DOWN); +// }); + } + + @Override + public void close() { + flush(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + System.out.println("junixsocket supported: "+AFSocket.isSupported()); + binding = ActivityScrollingBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + Toolbar toolbar = binding.toolbar; + setSupportActionBar(toolbar); + CollapsingToolbarLayout toolBarLayout = binding.toolbarLayout; + toolBarLayout.setTitle(getTitle()); + binding.selftestText.selftestScrollView.fullScroll(View.FOCUS_DOWN); + + NestedScrollView scrollView = binding.selftestText.selftestScrollView; + TextView textView = binding.selftestText.selftestText; + + try { + CompletableFuture.runAsync(() -> { + try { + Selftest.runSelftest(new TextViewWriter(scrollView, textView, true)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + + FloatingActionButton fab = binding.fab; + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar.make(view, "Not yet implemented", Snackbar.LENGTH_LONG) + .setAction("Action", null).show(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_scrolling, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + return super.onOptionsItemSelected(item); + } +} \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable/ic_launcher_background.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w1240dp/content_scrolling.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w1240dp/content_scrolling.xml new file mode 100644 index 000000000..98efc6102 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w1240dp/content_scrolling.xml @@ -0,0 +1,23 @@ + + + + diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w936dp/content_scrolling.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w936dp/content_scrolling.xml new file mode 100644 index 000000000..35f2b3967 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout-w936dp/content_scrolling.xml @@ -0,0 +1,21 @@ + + + + diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/activity_scrolling.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/activity_scrolling.xml new file mode 100644 index 000000000..194301cdc --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/activity_scrolling.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/content_scrolling.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/content_scrolling.xml new file mode 100644 index 000000000..1b54760f0 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/layout/content_scrolling.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/menu/menu_scrolling.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/menu/menu_scrolling.xml new file mode 100644 index 000000000..36cabcab9 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/menu/menu_scrolling.xml @@ -0,0 +1,10 @@ +

+ + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..aa63b16a1 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..e6e2a6e7a Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..21b772f67 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..1590b239e Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..087ddce0a Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..cfcc92a40 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..72d31a1bd Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..9f0a0893c Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..1b33c67ed Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..f6762b457 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..d256c1b1b Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..492db1289 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..467c68cd8 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..cc4cae5ab Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..bbb135078 Binary files /dev/null and b/junixsocket-selftest-android/SelftestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-land/dimens.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-land/dimens.xml new file mode 100644 index 000000000..96d25a4e7 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,4 @@ + + 48dp + 48dp + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-night/themes.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-night/themes.xml new file mode 100644 index 000000000..54202f550 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w1240dp/dimens.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 000000000..d73f4a359 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,3 @@ + + 200dp + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w600dp/dimens.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 000000000..96d25a4e7 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,4 @@ + + 48dp + 48dp + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/colors.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..f8c6127d3 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/dimens.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..89ef4658d --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + 180dp + 16dp + 16dp + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/ic_launcher_background.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 000000000..a76c72f5e --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #3DDCDC + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/strings.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..6e20f6eab --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/strings.xml @@ -0,0 +1,93 @@ + + My Application + + "Material is the metaphor.\n\n" + + "A material metaphor is the unifying theory of a rationalized space and a system of motion." + "The material is grounded in tactile reality, inspired by the study of paper and ink, yet " + "technologically advanced and open to imagination and magic.\n" + "Surfaces and edges of the material provide visual cues that are grounded in reality. The " + "use of familiar tactile attributes helps users quickly understand affordances. Yet the " + "flexibility of the material creates new affordances that supercede those in the physical " + "world, without breaking the rules of physics.\n" + "The fundamentals of light, surface, and movement are key to conveying how objects move, " + "interact, and exist in space and in relation to each other. Realistic lighting shows " + "seams, divides space, and indicates moving parts.\n\n" + + "Bold, graphic, intentional.\n\n" + + "The foundational elements of print based design typography, grids, space, scale, color, " + "and use of imagery guide visual treatments. These elements do far more than please the " + "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge " + "imagery, large scale typography, and intentional white space create a bold and graphic " + "interface that immerse the user in the experience.\n" + "An emphasis on user actions makes core functionality immediately apparent and provides " + "waypoints for the user.\n\n" + + "Motion provides meaning.\n\n" + + "Motion respects and reinforces the user as the prime mover. Primary user actions are " + "inflection points that initiate motion, transforming the whole design.\n" + "All action takes place in a single environment. Objects are presented to the user without " + "breaking the continuity of experience even as they transform and reorganize.\n" + "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. " + "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n" + + "3D world.\n\n" + + "The material environment is a 3D space, which means all objects have x, y, and z " + "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the " + "positive z-axis extending towards the viewer. Every sheet of material occupies a single " + "position along the z-axis and has a standard 1dp thickness.\n" + "On the web, the z-axis is used for layering and not for perspective. The 3D world is " + "emulated by manipulating the y-axis.\n\n" + + "Light and shadow.\n\n" + + "Within the material environment, virtual lights illuminate the scene. Key lights create " + "directional shadows, while ambient light creates soft shadows from all angles.\n" + "Shadows in the material environment are cast by these two light sources. In Android " + "development, shadows occur when light sources are blocked by sheets of material at " + "various positions along the z-axis. On the web, shadows are depicted by manipulating the " + "y-axis only. The following example shows the card with a height of 6dp.\n\n" + + "Resting elevation.\n\n" + + "All material objects, regardless of size, have a resting elevation, or default elevation " + "that does not change. If an object changes elevation, it should return to its resting " + "elevation as soon as possible.\n\n" + + "Component elevations.\n\n" + + "The resting elevation for a component type is consistent across apps (e.g., FAB elevation " + "does not vary from 6dp in one app to 16dp in another app).\n" + "Components may have different resting elevations across platforms, depending on the depth " + "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n" + + "Responsive elevation and dynamic elevation offsets.\n\n" + + "Some component types have responsive elevation, meaning they change elevation in response " + "to user input (e.g., normal, focused, and pressed) or system events. These elevation " + "changes are consistently implemented using dynamic elevation offsets.\n" + "Dynamic elevation offsets are the goal elevation that a component moves towards, relative " + "to the component’s resting state. They ensure that elevation changes are consistent " + "across actions and component types. For example, all components that lift on press have " + "the same elevation change relative to their resting elevation.\n" + "Once the input event is completed or cancelled, the component will return to its resting " + "elevation.\n\n" + + "Avoiding elevation interference.\n\n" + + "Components with responsive elevations may encounter other components as they move between " + "their resting elevations and dynamic elevation offsets. Because material cannot pass " + "through other material, components avoid interfering with one another any number of ways, " + "whether on a per component basis or using the entire app layout.\n" + "On a component level, components can move or be removed before they cause interference. " + "For example, a floating action button (FAB) can disappear or move off screen before a " + "user picks up a card, or it can move if a snackbar appears.\n" + "On the layout level, design your app layout to minimize opportunities for interference. " + "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere " + "when a user tries to pick up one of cards.\n\n" + + Settings + \ No newline at end of file diff --git a/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/themes.xml b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..b4611aa13 --- /dev/null +++ b/junixsocket-selftest-android/SelftestApp/app/src/main/res/values/themes.xml @@ -0,0 +1,25 @@ + + + + + + +