blob: a89df4de496ded85165a5ecd26f00cf302a71c02 [file]
/*
* Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import sockets.*
fun main() {
memScoped {
val bufferLength = 100L
val buffer = allocArray<ByteVar>(bufferLength)
val serverAddr = alloc<sockaddr_in>()
val listenFd = socket(AF_INET, SOCK_STREAM, 0)
.toInt().ensureUnixCallResult { it >= 0 }
with(serverAddr) {
memset(this.ptr, 0, sockaddr_in.size.convert())
sin_family = AF_INET.convert()
sin_addr.s_addr = htons(0u).convert()
sin_port = htons(0u)
}
bind(listenFd, serverAddr.ptr.reinterpret(), sockaddr_in.size.toUInt())
.toInt().ensureUnixCallResult { it == 0 }
listen(listenFd, 10)
.toInt().ensureUnixCallResult { it == 0 }
val actualAddr = alloc<sockaddr_in>()
val actualAddrLen = alloc<UIntVar>()
val port = with(actualAddr) {
actualAddrLen.value = sockaddr_in.size.convert()
memset(this.ptr, 0, sockaddr_in.size.convert())
getsockname(listenFd, actualAddr.ptr.reinterpret(), actualAddrLen.ptr)
.toInt().ensureUnixCallResult { it == 0 }
interop_ntohs(sin_port.toShort()).toUShort()
}
println(port)
val commFd = accept(listenFd, null, null)
.toInt().ensureUnixCallResult { it >= 0 }
while (true) {
val length = read(commFd, buffer, bufferLength.convert())
.toInt().ensureUnixCallResult { it >= 0 }
if (length == 0) {
break
}
write(commFd, buffer, length.convert())
.toInt().ensureUnixCallResult { it >= 0 }
}
}
}
// Not available through interop because declared as macro:
fun htons(value: UShort) = ((value.toInt() ushr 8) or (value.toInt() shl 8)).toUShort()
fun throwUnixError(): Nothing {
perror(null) // TODO: store error message to exception instead.
throw Error("UNIX call failed")
}
inline fun Int.ensureUnixCallResult(predicate: (Int) -> Boolean): Int {
if (!predicate(this)) {
throwUnixError()
}
return this
}