summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/kotlin/IPAddress.kt10
-rw-r--r--src/main/kotlin/NetworkPacketTransporter.kt10
-rw-r--r--src/main/kotlin/NetworkPacketTransporterJavaNet.kt23
-rw-r--r--src/main/kotlin/NinePConnection.kt47
-rw-r--r--src/main/kotlin/NinePMessage.kt27
-rw-r--r--src/main/kotlin/NinePMessageType.kt58
-rw-r--r--src/main/kotlin/NinePTranslator.kt2
7 files changed, 97 insertions, 80 deletions
diff --git a/src/main/kotlin/IPAddress.kt b/src/main/kotlin/IPAddress.kt
index 58d0aae..8eb7414 100644
--- a/src/main/kotlin/IPAddress.kt
+++ b/src/main/kotlin/IPAddress.kt
@@ -2,7 +2,7 @@
* An IP address (v4 or v6).
*/
class IPAddress {
- private var address: Array<Byte>
+ private var address: Array<UByte>
private var is4: Boolean
/**
@@ -19,15 +19,15 @@ class IPAddress {
val split4 = address.split('.')
val split6 = address.split(':')
- val bytes: List<Byte>
+ val bytes: List<UByte>
if (this.is4) {
- bytes = split4.map { it.toByte(10) }
+ bytes = split4.map { it.toUByte(10) }
} else {
val shorts = split6.map { it.toShort(16) }
bytes = shorts.flatMap {
listOf(
- it.toInt().shr(8).toByte(),
- it.toInt().and(0x00FF).toByte()
+ it.toInt().and(0xFF00).shr(0x08).toUByte(),
+ it.toInt().and(0x00FF).toUByte()
)
}
}
diff --git a/src/main/kotlin/NetworkPacketTransporter.kt b/src/main/kotlin/NetworkPacketTransporter.kt
index 4b11967..e12a6ac 100644
--- a/src/main/kotlin/NetworkPacketTransporter.kt
+++ b/src/main/kotlin/NetworkPacketTransporter.kt
@@ -10,7 +10,7 @@ import java.io.Closeable
* if the remote address is a domain name, but it cannot be resolved.
*
* Depending on the specific given implementation, the constructor of this class might throw other exceptions (e.g. the
- * [Socket] constructor from [java.net] in [NetworkPacketTransporterJavaNet]).
+ * [java.net.Socket] constructor in [NetworkPacketTransporterJavaNet]).
*/
abstract class NetworkPacketTransporter : Closeable {
val address: String
@@ -48,8 +48,8 @@ abstract class NetworkPacketTransporter : Closeable {
*
* @throws java.io.IOException if the message could not be correctly transmitted.
*/
- abstract fun transmit(payload: List<Byte>)
+ abstract fun transmit(payload: Array<UByte>)
/**
* Receive a payload until a byte occurs, which marks the end of the message. The byte is discarded after being read
* and is not returned.
@@ -64,11 +64,11 @@ abstract class NetworkPacketTransporter : Closeable {
abstract fun receiveUntil(untilByte: Byte): List<Byte>
/**
- * Receive a payload with fixed length.
+ * Receive a payload with fixed length. If zero, nothing is read.
*
* @param length The length of the message in bytes.
- *
+ * @return the received payload.
* @throws java.io.IOException if the message could not be correctly received.
*/
- abstract fun receiveFixed(length: ULong): List<Byte>
+ abstract fun receive(length: ULong): Array<UByte>
} \ No newline at end of file
diff --git a/src/main/kotlin/NetworkPacketTransporterJavaNet.kt b/src/main/kotlin/NetworkPacketTransporterJavaNet.kt
index 6badc3d..6855604 100644
--- a/src/main/kotlin/NetworkPacketTransporterJavaNet.kt
+++ b/src/main/kotlin/NetworkPacketTransporterJavaNet.kt
@@ -1,6 +1,7 @@
import java.io.InputStream
import java.io.OutputStream
import java.net.Socket
+import kotlin.math.min
/*
TODO:
@@ -36,8 +37,9 @@ class NetworkPacketTransporterJavaNet : NetworkPacketTransporter {
this.socket.close()
}
- override fun transmit(payload: List<Byte>) {
- this.outStream.write(payload.toByteArray())
+ override fun transmit(payload: Array<UByte>) {
+ val bytes = ByteArray(payload.size) { i -> payload[i].toByte() }
+ this.outStream.write(bytes)
}
override fun receiveUntil(untilByte: Byte): List<Byte> {
@@ -55,7 +57,20 @@ class NetworkPacketTransporterJavaNet : NetworkPacketTransporter {
return payload
}
- override fun receiveFixed(length: ULong): List<Byte> {
- return this.inStream.readNBytes(length.toInt()).toList()
+ override fun receive(length: ULong): Array<UByte> {
+ var length = length
+ val intMax = Int.MAX_VALUE.toULong()
+ val bytes: MutableList<Byte> = MutableList(0) { 0 }
+ // readNBytes only takes Int values, so it must be called multiple times if the length is greater than Int's
+ // maximum value
+ while (length > 0u) {
+ // the min function ensures that the value passed to readNBytes never exceeds Int's maximum value while also
+ // switching to the length variable when its value eventually becomes less than Int's maximum value, which
+ // avoids writing duplicated readNBytes calls in the code
+ val lenMin = min(length, intMax)
+ bytes += this.inStream.readNBytes(lenMin.toInt()).toMutableList()
+ length -= intMax
+ }
+ return Array(bytes.size) { i -> bytes[i].toUByte() }
}
} \ No newline at end of file
diff --git a/src/main/kotlin/NinePConnection.kt b/src/main/kotlin/NinePConnection.kt
index 5adb35b..13e2ecf 100644
--- a/src/main/kotlin/NinePConnection.kt
+++ b/src/main/kotlin/NinePConnection.kt
@@ -42,24 +42,21 @@ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator
}
/**
- * Read an [nBytes]-long unsigned integer number from the connection.
+ * Read an [len] bytes long unsigned integer number from the connection.
*
* In 9P, binary numbers (non-textual) are specified in little-endian order (least significant byte first).
*
- * @param nBytes The length of the integer number in bytes. It only works with unsigned numbers strictly greater
- * than zero (zero is excluded).
- * @return The number's value.
- * @throws IllegalArgumentException if [nBytes] is zero or negative.
+ * @param len The length of the integer number in bytes. If zero, nothing is read.
+ * @return the number's value.
+ * @throws java.io.IOException if the message could not be correctly received.
*/
- private fun readInteger(nBytes: Int): BigInteger {
- require(nBytes > 0)
- val bytes = this.npt.receiveFixed(nBytes.toULong())
- var number: BigInteger = BigInteger.valueOf(0)
+ private fun readInteger(len: ULong): BigInteger {
+ val bytes = this.npt.receive(len)
+ var value = 0.toBigInteger()
for (i in 0..<bytes.size) {
- number += bytes[i].toInt().toBigInteger().shl(i)
+ value += bytes[i].toInt().toBigInteger().shl(i*8)
}
-
- return number
+ return value
}
/**
@@ -80,12 +77,13 @@ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator
*
* @return A triple in which the first element is the message size in bytes, the second is the message type as a
* [NinePMessageType] constant, and the third element is the message tag.
+ * @throws java.io.IOException if the message could not be correctly received.
*/
private fun readSizeTypeTag(): Triple<UInt, NinePMessageType, UShort> {
return Triple(
- readInteger(4).toInt().toUInt(),
- NinePMessageType.fromByte(readInteger(1).toByte()),
- readInteger(2).toInt().toUShort()
+ readInteger(4u).toInt().toUInt(),
+ NinePMessageType.fromByte(readInteger(1u).toByte().toUByte()),
+ readInteger(2u).toShort().toUShort()
)
}
@@ -118,16 +116,17 @@ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator
/**
* Read a 9P message of type Rerror, after the message size and type have already been read.
*
- * @return A pair of: (1) the message tag and (2) the error message
+ * @return A pair of: (1) the message tag and (2) the error message.
+ * @throws java.io.IOException if the message could not be correctly received.
*/
- private fun readError(): Pair<SizedMessageField, String> {
- val tag = readInteger(2)
+ private fun readError(): Pair<UShort, String> {
+ val tag = readInteger(2u).toInt().toUShort()
val error = readString()
- return Pair(SizedMessageField(2, tag), error)
+ return Pair(tag, error)
}
/**
- * Send 9P request and check for exceptions or a Rerror response.
+ * Send 9P request and check for exceptions or an Rerror response.
*/
private fun responseError(msg: NinePMessage): String? {
try {
@@ -143,10 +142,10 @@ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator
return null
}
- override fun version(msize: SizedMessageField, version: String): String? {
+ override fun version(msize: UInt, version: String): String? {
val msg = NinePMessage(NinePMessageType.TVERSION, this.NOTAG, listOf("msize", "version"),
mapOf(
- "msize" to msize
+ "msize" to BigInteger(msize.toString())
),
mapOf(
"version" to version
@@ -157,10 +156,10 @@ class NinePConnection(netPackTrans: NetworkPacketTransporter) : NinePTranslator
if (error != null) {
return error
}
- val rmsize = readInteger(4)
+ val rmsize = readInteger(4u).toInt().toUInt()
val rversion = readString()
// this check should not be necessary, but you never know
- this.maxSize = ( if (rmsize < msize.value) msize.value else rmsize ).toInt().toUInt()
+ this.maxSize = ( if (rmsize < msize) msize else rmsize )
if (rversion == "unknown") {
return "Unknown version."
}
diff --git a/src/main/kotlin/NinePMessage.kt b/src/main/kotlin/NinePMessage.kt
index 87d59a5..225ec45 100644
--- a/src/main/kotlin/NinePMessage.kt
+++ b/src/main/kotlin/NinePMessage.kt
@@ -1,3 +1,4 @@
+import java.math.BigInteger
import kotlin.math.pow
/**
@@ -13,7 +14,7 @@ import kotlin.math.pow
* @param fieldValuesInt A map of each field name into its value. This map only stores integer values.
* @param fieldValuesStr A map of each field name into its value. This map only stores string values.
*/
-class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames: List<String>, val fieldValuesInt: Map<String, SizedMessageField>, val fieldValuesStr: Map<String, String>, val maxSize: UInt) {
+class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames: List<String>, val fieldValuesInt: Map<String, BigInteger>, val fieldValuesStr: Map<String, String>, val maxSize: UInt) {
/**
* Intersection between [fieldNames] and [fieldValuesInt]. In other words: the amount of integer fields that are
* going to be used when writing the message.
@@ -37,11 +38,11 @@ class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames:
require(insecInts.size == fieldNames.size - insecStrs.size)
val totalSize = size()
- if (totalSize > this.maxSize.toLong()) {
+ if (totalSize > this.maxSize) {
throw IllegalArgumentException("Message size exceeded.")
}
writeMessageSizeType(npt, totalSize, type)
- writeInteger(npt, SizedMessageField(2, tag.toInt().toBigInteger()))
+ writeInteger(npt, tag.toInt().toBigInteger())
for (field in fieldNames) {
if (field in insecInts) {
writeInteger(npt, fieldValuesInt[field]!!)
@@ -59,8 +60,9 @@ class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames:
* @param npt The networking API.
* @param value The number's value. [SizedMessageField] defines both its actual value and its size.
*/
- private fun writeInteger(npt: NetworkPacketTransporter, value: SizedMessageField) {
- npt.transmit(value.value.toByteArray().toList())
+ private fun writeInteger(npt: NetworkPacketTransporter, value: BigInteger) {
+ val bytes = value.toByteArray()
+ npt.transmit(Array(bytes.size) { i -> bytes[i].toUByte() })
}
/**
@@ -75,8 +77,9 @@ class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames:
*/
private fun writeString(npt: NetworkPacketTransporter, value: String) {
require(value.length <= 2.0.pow(16.0) - 1)
- writeInteger(npt, SizedMessageField(2, value.length.toBigInteger()))
- npt.transmit(value.toByteArray().toList())
+ writeInteger(npt, value.length.toBigInteger())
+ val bytes = value.toByteArray()
+ npt.transmit(Array(bytes.size) { i -> bytes[i].toUByte() })
}
/**
@@ -86,15 +89,15 @@ class NinePMessage(val type: NinePMessageType, val tag: UShort, val fieldNames:
* @param size The total message size, including the 4 bytes of this parameter and the type's byte.
* @param type The 9P message type as a [NinePMessageType] constant.
*/
- private fun writeMessageSizeType(npt: NetworkPacketTransporter, size: Int, type: NinePMessageType) {
- writeInteger(npt, SizedMessageField(4, size.toBigInteger()))
- writeInteger(npt, SizedMessageField(1, type.value.toInt().toBigInteger()))
+ private fun writeMessageSizeType(npt: NetworkPacketTransporter, size: UInt, type: NinePMessageType) {
+ writeInteger(npt, size.toInt().toBigInteger())
+ writeInteger(npt, type.value.toInt().toBigInteger())
}
/**
* Calculate the expected size of the message.
*/
- fun size(): Int {
- return 4 + 1 + 2 + this.insecInts.sumOf { this.fieldValuesInt[it]!!.size } + this.insecStrs.sumOf { 2 + this.fieldValuesStr[it]!!.length }
+ fun size(): UInt {
+ return 4u + 1u + 2u + this.insecInts.sumOf { this.fieldValuesInt[it]!!.bitLength().toUInt() } + this.insecStrs.sumOf { 2u + this.fieldValuesStr[it]!!.length.toUInt() }
}
} \ No newline at end of file
diff --git a/src/main/kotlin/NinePMessageType.kt b/src/main/kotlin/NinePMessageType.kt
index c18e7e9..fadd0fe 100644
--- a/src/main/kotlin/NinePMessageType.kt
+++ b/src/main/kotlin/NinePMessageType.kt
@@ -1,34 +1,34 @@
-enum class NinePMessageType(val value: Byte) {
- TVERSION(100),
- RVERSION(101),
- TAUTH(102),
- RAUTH(103),
- TATTACH(104),
- RATTACH(105),
+enum class NinePMessageType(val value: UByte) {
+ TVERSION(100u),
+ RVERSION(101u),
+ TAUTH(102u),
+ RAUTH(103u),
+ TATTACH(104u),
+ RATTACH(105u),
//TERROR(106), <--- illegal
- RERROR(107),
- TFLUSH(108),
- RFLUSH(109),
- TWALK(110),
- RWALK(111),
- TOPEN(112),
- ROPEN(113),
- TCREATE(114),
- RCREATE(115),
- TREAD(116),
- RREAD(117),
- TWRITE(118),
- RWRITE(119),
- TCLUNK(120),
- RCLUNK(121),
- TREMOVE(122),
- RREMOVE(123),
- TSTAT(124),
- RSTAT(125),
- TWSTAT(126),
- RWSTAT(127);
+ RERROR(107u),
+ TFLUSH(108u),
+ RFLUSH(109u),
+ TWALK(110u),
+ RWALK(111u),
+ TOPEN(112u),
+ ROPEN(113u),
+ TCREATE(114u),
+ RCREATE(115u),
+ TREAD(116u),
+ RREAD(117u),
+ TWRITE(118u),
+ RWRITE(119u),
+ TCLUNK(120u),
+ RCLUNK(121u),
+ TREMOVE(122u),
+ RREMOVE(123u),
+ TSTAT(124u),
+ RSTAT(125u),
+ TWSTAT(126u),
+ RWSTAT(127u);
companion object {
- fun fromByte(value: Byte) = NinePMessageType.entries.first { it.value == value }
+ fun fromByte(value: UByte) = NinePMessageType.entries.first { it.value == value }
}
} \ No newline at end of file
diff --git a/src/main/kotlin/NinePTranslator.kt b/src/main/kotlin/NinePTranslator.kt
index a42ea3c..02c8ef4 100644
--- a/src/main/kotlin/NinePTranslator.kt
+++ b/src/main/kotlin/NinePTranslator.kt
@@ -24,7 +24,7 @@ interface NinePTranslator {
* @param version Should be "9P2000", which is the only defined value.
* @return a possible error.
*/
- fun version(msize: SizedMessageField, version: String): String?
+ fun version(msize: UInt, version: String): String?
/**
* Perform authentication.