summaryrefslogtreecommitdiff
path: root/src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java
diff options
context:
space:
mode:
authorBrenton Bostick <[email protected]>2022-12-09 06:40:06 -0500
committerBrenton Bostick <[email protected]>2023-05-22 15:36:21 -0400
commit48adf41cc1ca35e82d81f510a1910e82e222e264 (patch)
treefa37703386129e941401fbb106b93229d2a4aed4 /src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java
parent71c2f97056b61a1d030d40d4e33e49a80a7f0ae6 (diff)
Move java files to com/zerotier/sockets subfolder
This adheres to standard Java style and fixes: Package name 'com.zerotier.sockets' does not correspond to the file path '' in Android Studio
Diffstat (limited to 'src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java')
-rw-r--r--src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java b/src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java
new file mode 100644
index 0000000..8d23d88
--- /dev/null
+++ b/src/bindings/java/com/zerotier/sockets/ZeroTierInputStream.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c)2013-2021 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2026-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+package com.zerotier.sockets;
+
+import com.zerotier.sockets.ZeroTierNative;
+import java.io.*;
+import java.util.Objects;
+
+/**
+ * Extends InputStream using ZeroTier as a transport
+ */
+public class ZeroTierInputStream extends InputStream {
+ /**
+ * File descriptor used by lower native layer
+ */
+ public int zfd = -1;
+
+ /**
+ * Close the ZeroTierInputStream
+ * @exception IOException when an I/O error occurs
+ */
+ public void close​() throws IOException
+ {
+ /* Note: this operation currently only stops RX on a socket that is shared
+ between both I/OStreams. This means that closing this stream will only shutdown
+ that aspect of the socket but not actually close it and free resources. Resources
+ will be properly freed when the socket implementation's close() is called or if
+ both I/OStreams are closed separately */
+ ZeroTierNative.zts_bsd_shutdown(zfd, ZeroTierNative.ZTS_SHUT_RD);
+ zfd = -1;
+ }
+
+ /**
+ * Transfer bytes from this stream to another
+ * @param destStream Unused
+ * @return Number of bytes transferred
+ * @exception IOException when an I/O error occurs
+ */
+ public long transferTo​(OutputStream destStream) throws IOException
+ {
+ Objects.requireNonNull(destStream, "destStream must not be null");
+ int bytesTransferred = 0, bytesRead;
+ byte[] buf = new byte[8192];
+ while (((bytesRead = ZeroTierNative.zts_bsd_read(zfd, buf)) >= 0)) {
+ destStream.write(buf, 0, bytesRead);
+ bytesTransferred += bytesRead;
+ }
+ return bytesTransferred;
+ }
+
+ /**
+ * Read a single byte from the stream
+ * @return Single byte read
+ * @exception IOException when an I/O error occurs
+ */
+ public int read​() throws IOException
+ {
+ byte[] buf = new byte[1];
+ // Unlike a native read(), if nothing is read we should return -1
+ int retval = ZeroTierNative.zts_bsd_read(zfd, buf);
+ if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
+ return -1;
+ }
+ if (retval < 0) {
+ throw new IOException("read​(), errno=" + retval);
+ }
+ return buf[0];
+ }
+
+ /**
+ * Read from stream into buffer
+ * @param destBuffer Destination buffer
+ * @return Number of bytes read
+ * @exception IOException when an I/O error occurs
+ */
+ public int read​(byte[] destBuffer) throws IOException
+ {
+ Objects.requireNonNull(destBuffer, "input byte array must not be null");
+ // Unlike a native read(), if nothing is read we should return -1
+ int retval = ZeroTierNative.zts_bsd_read(zfd, destBuffer);
+ if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
+ return -1;
+ }
+ if (retval < 0) {
+ throw new IOException("read​(destBuffer), errno=" + retval);
+ }
+ return retval;
+ }
+
+ /**
+ * Read from stream into buffer at offset
+ * @param destBuffer Destination buffer
+ * @param offset Where in the destination buffer bytes should be written
+ * @param numBytes Number of bytes to read
+ * @return Number of bytes read.
+ * @exception IOException when an I/O error occurs
+ */
+ public int read​(byte[] destBuffer, int offset, int numBytes) throws IOException
+ {
+ Objects.requireNonNull(destBuffer, "input byte array must not be null");
+ if (offset < 0) {
+ throw new IndexOutOfBoundsException("offset < 0");
+ }
+ if (numBytes < 0) {
+ throw new IndexOutOfBoundsException("numBytes < 0");
+ }
+ if (numBytes > (destBuffer.length - offset)) {
+ throw new IndexOutOfBoundsException("numBytes > (destBuffer.length - offset");
+ }
+ if (numBytes == 0) {
+ return 0;
+ }
+ // Unlike a native read(), if nothing is read we should return -1
+ int retval = ZeroTierNative.zts_bsd_read_offset(zfd, destBuffer, offset, numBytes);
+ if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
+ return -1;
+ }
+ if (retval < 0) {
+ throw new IOException("read​(destBuffer, offset, numBytes), errno=" + retval);
+ }
+ return retval;
+ }
+
+ /**
+ * Read all available data from stream
+ * @return Array of bytes
+ * @exception IOException when an I/O error occurs
+ */
+ public byte[] readAllBytes​() throws IOException
+ {
+ int pendingDataSize = ZeroTierNative.zts_get_pending_data_size(zfd);
+ byte[] buf = new byte[pendingDataSize];
+ int retval = ZeroTierNative.zts_bsd_read(zfd, buf);
+ if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
+ // No action needed
+ }
+ if (retval < 0) {
+ throw new IOException("readAllBytes​(), errno=" + retval);
+ }
+ return buf;
+ }
+
+ /**
+ * Read a given number of bytes from the stream into a buffer
+ * @param destBuffer Destination buffer
+ * @param offset Where in the destination buffer bytes should be written
+ * @param numBytes Number of bytes to read
+ * @return Nothing.
+ * @exception IOException when an I/O error occurs
+ */
+ public int readNBytes​(byte[] destBuffer, int offset, int numBytes) throws IOException
+ {
+ Objects.requireNonNull(destBuffer, "input byte array must not be null");
+ if (offset < 0) {
+ throw new IndexOutOfBoundsException("offset < 0");
+ }
+ if (numBytes < 0) {
+ throw new IndexOutOfBoundsException("numBytes < 0");
+ }
+ if (numBytes > (destBuffer.length - offset)) {
+ throw new IndexOutOfBoundsException("numBytes > destBuffer.length - offset");
+ }
+ if (numBytes == 0) {
+ return 0;
+ }
+ int retval = ZeroTierNative.zts_bsd_read_offset(zfd, destBuffer, offset, numBytes);
+ if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
+ // No action needed
+ }
+ if (retval < 0) {
+ throw new IOException("readNBytes​(destBuffer, offset, numBytes), errno=" + retval);
+ }
+ return retval;
+ }
+
+ /**
+ * Skip a certain number of bytes
+ * @param numBytes Unused
+ * @return Nothing.
+ * @exception IOException when an I/O error occurs
+ */
+ public long skip​(long numBytes) throws IOException
+ {
+ if (numBytes <= 0) {
+ return 0;
+ }
+ long bytesRemaining = numBytes, bytesRead;
+ int bufSize = (int)Math.min(2048, bytesRemaining);
+ byte[] buf = new byte[bufSize];
+ while (bytesRemaining > 0) {
+ if ((bytesRead = ZeroTierNative.zts_bsd_read_length(zfd, buf, (int)Math.min(bufSize, bytesRemaining)))
+ < 0) {
+ break;
+ }
+ bytesRemaining -= bytesRead;
+ }
+ return numBytes - bytesRemaining;
+ }
+}