-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathEncoder.kt
108 lines (86 loc) · 3.06 KB
/
Encoder.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package kotlinx.sockets.examples.dns
import kotlinx.sockets.channels.*
import java.nio.charset.*
suspend fun BufferedWriteChannel.write(message: Message, encoder: CharsetEncoder, tcp: Boolean = true) {
ensureCapacity(12 + if (tcp) 2 else 0)
if (tcp) {
putUShort(12 + message.questions.sumBy { it.measure() }
+ message.answers.sumBy { it.measure() }
+ message.nameServers.sumBy { it.measure() }
+ message.additional.sumBy { it.measure() }
)
}
putShort(message.header.id)
putUByte(
(bit(7, !message.header.isQuery) or
bits(4, 4, message.header.opcode.value) or
bit(3, message.header.authoritativeAnswer) or
bit(1, message.header.truncation) or
bit(0, message.header.recursionDesired))
)
putUByte(bits(4, 0, message.header.responseCode.value)
or bit(7, message.header.recursionAvailable)
or bit(5, message.header.authenticData)
or bit(4, message.header.checkingDisabled)
)
putUShort(message.header.questionsCount)
putUShort(message.header.answersCount)
putUShort(message.header.nameServersCount)
putUShort(message.header.additionalResourcesCount)
message.questions.forEach { q ->
encodeStringsSequence(q.name, encoder)
ensureCapacity(4)
putUShort(q.type.value)
putUShort(q.qclass.value)
}
message.answers.forEach { a ->
writeResource(a, encoder)
}
message.nameServers.forEach { a ->
writeResource(a, encoder)
}
message.additional.forEach { a ->
writeResource(a, encoder)
}
// TODO resources, name servers and additional resources
}
private suspend fun BufferedWriteChannel.writeResource(resource: Resource<*>, encoder: CharsetEncoder) {
encodeStringsSequence(resource.name, encoder)
ensureCapacity(10)
putUShort(resource.type.value)
when (resource) {
is Resource.Opt -> {
putUShort(resource.udpPayloadSize)
putByte(resource.extendedRCode)
putByte(resource.version)
putShort(0) // D0 bit = 0
}
else -> throw IllegalArgumentException("resource of type ${resource.type} is not supported")
}
if (resource.length != 0) {
TODO()
}
putUShort(resource.length)
}
private fun Question.measure(): Int {
return 4 + name.sumBy { 1 + it.length } + 1
}
private fun Resource<*>.measure(): Int {
return name.sumBy { 1 + it.length } + 1 + 10 + length
}
private suspend fun BufferedWriteChannel.encodeStringsSequence(items: Iterable<String>, encoder: CharsetEncoder) {
for (s in items) {
ensureCapacity(1)
putUByte(s.length)
putString(s, encoder)
}
ensureCapacity(1)
putUByte(0)
}
private fun bit(shift: Int, value: Boolean): Int {
return bits(1, shift, if (value) 1 else 0)
}
private fun bits(size: Int, shift: Int, value: Int): Int {
val mask = (1..size).fold(0) { acc, _ -> (acc shl 1) or 1 }
return (value and mask) shl shift
}