V7
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package tech.xvanturing.freeproxy.vpn.net
|
||||
|
||||
const val PROTO_ICMP = 1
|
||||
const val PROTO_TCP = 6
|
||||
const val PROTO_UDP = 17
|
||||
|
||||
/** IPv4 首部。选项字段不解析,但 [headerLength] 已把它算在内。 */
|
||||
class Ipv4Header(
|
||||
val headerLength: Int,
|
||||
val totalLength: Int,
|
||||
val protocol: Int,
|
||||
val sourceIp: Int,
|
||||
val destIp: Int,
|
||||
) {
|
||||
val payloadLength: Int get() = totalLength - headerLength
|
||||
|
||||
companion object {
|
||||
const val MIN_SIZE = 20
|
||||
|
||||
/** 解析失败返回 null(畸形包直接丢弃,不抛异常 —— 转发热路径上异常代价太高)。 */
|
||||
fun parse(buffer: ByteArray, length: Int): Ipv4Header? {
|
||||
if (length < MIN_SIZE) return null
|
||||
val versionAndIhl = buffer.u8(0)
|
||||
if ((versionAndIhl ushr 4) != 4) return null
|
||||
|
||||
val headerLength = (versionAndIhl and 0x0F) * 4
|
||||
if (headerLength < MIN_SIZE || headerLength > length) return null
|
||||
|
||||
val totalLength = buffer.u16(2)
|
||||
if (totalLength < headerLength || totalLength > length) return null
|
||||
|
||||
// 本栈不做分片重组:MF 置位或分片偏移非零的包一律丢弃。
|
||||
// TUN 的 MTU 由我们自己设定,正常流量不会走到这里。
|
||||
val fragmentField = buffer.u16(6)
|
||||
val moreFragments = (fragmentField and 0x2000) != 0
|
||||
val fragmentOffset = fragmentField and 0x1FFF
|
||||
if (moreFragments || fragmentOffset != 0) return null
|
||||
|
||||
return Ipv4Header(
|
||||
headerLength = headerLength,
|
||||
totalLength = totalLength,
|
||||
protocol = buffer.u8(9),
|
||||
sourceIp = buffer.ipv4(12),
|
||||
destIp = buffer.ipv4(16),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** TCP 首部。 */
|
||||
class TcpHeader(
|
||||
val sourcePort: Int,
|
||||
val destPort: Int,
|
||||
val sequence: Long,
|
||||
val acknowledgment: Long,
|
||||
val dataOffset: Int,
|
||||
val flags: Int,
|
||||
val window: Int,
|
||||
) {
|
||||
val isFin: Boolean get() = (flags and FIN) != 0
|
||||
val isSyn: Boolean get() = (flags and SYN) != 0
|
||||
val isRst: Boolean get() = (flags and RST) != 0
|
||||
val isAck: Boolean get() = (flags and ACK) != 0
|
||||
|
||||
override fun toString(): String = buildString {
|
||||
if (isSyn) append("SYN ")
|
||||
if (isAck) append("ACK ")
|
||||
if (isFin) append("FIN ")
|
||||
if (isRst) append("RST ")
|
||||
append("seq=").append(sequence).append(" ack=").append(acknowledgment)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MIN_SIZE = 20
|
||||
|
||||
const val FIN = 0x01
|
||||
const val SYN = 0x02
|
||||
const val RST = 0x04
|
||||
const val PSH = 0x08
|
||||
const val ACK = 0x10
|
||||
const val URG = 0x20
|
||||
|
||||
fun parse(buffer: ByteArray, offset: Int, length: Int): TcpHeader? {
|
||||
if (length < MIN_SIZE) return null
|
||||
val dataOffset = ((buffer.u8(offset + 12) ushr 4) and 0x0F) * 4
|
||||
if (dataOffset < MIN_SIZE || dataOffset > length) return null
|
||||
return TcpHeader(
|
||||
sourcePort = buffer.u16(offset),
|
||||
destPort = buffer.u16(offset + 2),
|
||||
sequence = buffer.u32(offset + 4),
|
||||
acknowledgment = buffer.u32(offset + 8),
|
||||
dataOffset = dataOffset,
|
||||
flags = buffer.u8(offset + 13),
|
||||
window = buffer.u16(offset + 14),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** UDP 首部。 */
|
||||
class UdpHeader(
|
||||
val sourcePort: Int,
|
||||
val destPort: Int,
|
||||
val length: Int,
|
||||
) {
|
||||
val payloadLength: Int get() = length - SIZE
|
||||
|
||||
companion object {
|
||||
const val SIZE = 8
|
||||
|
||||
fun parse(buffer: ByteArray, offset: Int, available: Int): UdpHeader? {
|
||||
if (available < SIZE) return null
|
||||
val length = buffer.u16(offset + 4)
|
||||
if (length < SIZE || length > available) return null
|
||||
return UdpHeader(
|
||||
sourcePort = buffer.u16(offset),
|
||||
destPort = buffer.u16(offset + 2),
|
||||
length = length,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user