| /* |
| * 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 |
| } |