diff --git a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4Option.kt b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4Option.kt index 04cfa50..2bd9bea 100644 --- a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4Option.kt +++ b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4Option.kt @@ -69,22 +69,28 @@ abstract class Ipv4Option( // we don't apply it directly to length because we want to construct the option // with the correct length which includes the first two fields val length = stream.get().toUByte() - if (kind == Ipv4OptionType.Security.kind) { - options.add(Ipv4OptionSecurity.fromStream(stream, isCopied, optionClass, length)) - } else { - if (stream.remaining() < length.toInt() - 2) { - throw PacketTooShortException("Can't parse ipv4 option because we don't have enough bytes left for the data") + if (stream.remaining() < length.toInt() - 2) { + throw PacketTooShortException("Can't parse ipv4 option because we don't have enough bytes left for the data") + } + when (kind) { + Ipv4OptionType.Security.kind -> { + options.add(Ipv4OptionSecurity.fromStream(stream, isCopied, optionClass, length)) + } + Ipv4OptionType.LooseSourceRouting.kind -> { + options.add(Ipv4OptionLooseSourceAndRecordRoute.fromStream(stream, isCopied, optionClass, length)) } - val data = ByteArray(length.toInt() - 2) - stream.get(data) + else -> { + val data = ByteArray(length.toInt() - 2) + stream.get(data) - val type = - try { - Ipv4OptionType.fromKind(kind) - } catch (e: NoSuchElementException) { - Ipv4OptionType.Unknown - } - options.add(Ipv4OptionUnknown(isCopied, optionClass, type, length, data)) + val type = + try { + Ipv4OptionType.fromKind(kind) + } catch (e: NoSuchElementException) { + Ipv4OptionType.Unknown + } + options.add(Ipv4OptionUnknown(isCopied, optionClass, type, length, data)) + } } } } diff --git a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionLooseSourceAndRecordRoute.kt b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionLooseSourceAndRecordRoute.kt index a495292..f0382c1 100644 --- a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionLooseSourceAndRecordRoute.kt +++ b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionLooseSourceAndRecordRoute.kt @@ -1,6 +1,7 @@ package com.jasonernst.knet.ip.options import com.jasonernst.knet.PacketTooShortException +import org.slf4j.LoggerFactory import java.nio.ByteBuffer import java.nio.ByteOrder @@ -21,32 +22,38 @@ import java.nio.ByteOrder * recorded route full) and the routing is to be based on the * destination address field. */ -class Ipv4OptionLooseSourceAndRecordRoute( +data class Ipv4OptionLooseSourceAndRecordRoute( override val isCopied: Boolean = true, override val optionClass: Ipv4OptionClassType = Ipv4OptionClassType.Control, override val type: Ipv4OptionType = Ipv4OptionType.LooseSourceRouting, - override val size: UByte = MIN_OPTION_SIZE, val pointer: UByte, val routeData: ByteArray = ByteArray(0), -) : Ipv4Option(isCopied, optionClass, type, size) { +) : Ipv4Option(isCopied, optionClass, type, size = (routeData.size.toUByte() + MIN_OPTION_SIZE).toUByte()) { companion object { val MIN_OPTION_SIZE: UByte = 3u + private val logger = LoggerFactory.getLogger(Ipv4OptionLooseSourceAndRecordRoute::class.java) fun fromStream( stream: ByteBuffer, - dataLength: Int, + isCopied: Boolean, + optionClass: Ipv4OptionClassType, + size: UByte, ): Ipv4OptionLooseSourceAndRecordRoute { - if (stream.remaining() < MIN_OPTION_SIZE.toInt() - 2) { + logger.debug("SIZE: $size, remaining: ${stream.remaining()}") + if (stream.remaining() < (size - 2u).toInt()) { throw PacketTooShortException( - "Stream must have at least ${MIN_OPTION_SIZE - 2u} " + + "Stream must have at least ${size - 2u} " + "remaining bytes remaining to parse Ipv4OptionLooseSourceAndRecordRoute, we only have " + "${stream.remaining()} bytes", ) } val pointer = stream.get().toUByte() + val dataLength = size.toInt() - MIN_OPTION_SIZE.toInt() val routingData = ByteArray(dataLength) stream.get(routingData) return Ipv4OptionLooseSourceAndRecordRoute( + isCopied = isCopied, + optionClass = optionClass, pointer = pointer, routeData = routingData, ) @@ -63,4 +70,28 @@ class Ipv4OptionLooseSourceAndRecordRoute( .put(routeData) return buffer.array() } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Ipv4OptionLooseSourceAndRecordRoute + + if (isCopied != other.isCopied) return false + if (optionClass != other.optionClass) return false + if (type != other.type) return false + if (pointer != other.pointer) return false + if (!routeData.contentEquals(other.routeData)) return false + + return true + } + + override fun hashCode(): Int { + var result = isCopied.hashCode() + result = 31 * result + optionClass.hashCode() + result = 31 * result + type.hashCode() + result = 31 * result + pointer.hashCode() + result = 31 * result + routeData.contentHashCode() + return result + } } diff --git a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionSecurity.kt b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionSecurity.kt index 9aec57d..52ebfd6 100644 --- a/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionSecurity.kt +++ b/knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionSecurity.kt @@ -78,6 +78,7 @@ data class Ipv4OptionSecurity( override fun toByteArray(order: ByteOrder): ByteArray { logger.debug("SIZE: $size") val buffer = ByteBuffer.allocate(size.toInt()) + buffer.order(order) buffer.put(super.toByteArray(order)) buffer.putShort(security.kind.toShort()) buffer.putShort(compartment.toShort()) diff --git a/knet/src/test/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionTest.kt b/knet/src/test/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionTest.kt index f16d54c..cb6642c 100644 --- a/knet/src/test/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionTest.kt +++ b/knet/src/test/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionTest.kt @@ -167,4 +167,43 @@ class Ipv4OptionTest { Ipv4Option.parseOptions(stream) } } + + @Test + fun ipv4OptionLooseSourceAndRecordRoute() { + val option = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + val stream = ByteBuffer.wrap(option.toByteArray()) + logger.debug("Stream length: ${stream.limit()}") + val parsedOptions = Ipv4Option.parseOptions(stream) + assertEquals(1, parsedOptions.size) + assertEquals(option, parsedOptions[0]) + } + + @Test fun ipv4OptionLooseSourceAndRecordRouteTooShort() { + val option = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + val stream = ByteBuffer.wrap(option.toByteArray()) + stream.limit(stream.limit() - 1) + stream.position(2) + assertThrows { + Ipv4OptionLooseSourceAndRecordRoute.fromStream(stream, true, Ipv4OptionClassType.DebuggingAndMeasurement, 6u) + } + } + + @Test fun ipv4OptionLooseSourceAndRecordRouteEquals() { + val option1 = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + val option2 = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + assertEquals(option1, option2) + + val option3 = Ipv4OptionLooseSourceAndRecordRoute(pointer = 1u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + assertNotEquals(option1, option3) + + val option4 = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x03)) + assertNotEquals(option1, option4) + } + + @Test fun ipv4OptionLooseSourceAndRecordRouteHashCode() { + val map: MutableMap = mutableMapOf() + val option1 = Ipv4OptionLooseSourceAndRecordRoute(pointer = 0u, routeData = byteArrayOf(0x00, 0x01, 0x02)) + map[option1] = "test" + assertTrue(map.containsKey(option1)) + } }