From fead9d4b20649cd15aac177fc66b49a56766ef73 Mon Sep 17 00:00:00 2001 From: saddydead Date: Wed, 27 May 2026 20:49:34 +0300 Subject: [PATCH] work blyattt --- build.gradle.kts | 1 + src/main/c/tun.c | 270 +++++++++++++++++++++++++++++++++++ src/main/kotlin/Client.kt | 89 ++++++++---- src/main/kotlin/Config.kt | 2 + src/main/kotlin/Main.kt | 4 +- src/main/kotlin/Server.kt | 100 ++++++++----- src/main/kotlin/TunDevice.kt | 32 ++++- 7 files changed, 428 insertions(+), 70 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 68c63cd..1cdc23f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -68,6 +68,7 @@ tasks.register("generateTestServerConfig") { { "type": "server", "address": "10.1.0.1", + "outInterface": "enp9s0", "port": 9093 } """.trimIndent()) diff --git a/src/main/c/tun.c b/src/main/c/tun.c index ec15fee..1e5ff6a 100644 --- a/src/main/c/tun.c +++ b/src/main/c/tun.c @@ -1,5 +1,15 @@ #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -10,6 +20,81 @@ #include #include #include +#include + +JNIEXPORT jint JNICALL +Java_su_sonoma_TunDevice_addDefaultRoute( + JNIEnv *env, + jobject obj, + jstring ifname +) { + + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + char buf[4096]; + } req; + + memset(&req, 0, sizeof(req)); + + const char *c_ifname = (*env)->GetStringUTFChars(env, ifname, 0); + + if (c_ifname == NULL) { + return -1; + } + + int ifindex = if_nametoindex(c_ifname); + + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_type = RTM_NEWROUTE; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; + req.nlh.nlmsg_seq = 1; + req.nlh.nlmsg_pid = getpid(); + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_table = RT_TABLE_MAIN; + req.rtm.rtm_protocol = RTPROT_STATIC; + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNICAST; + req.rtm.rtm_dst_len = 0; + + struct rtattr *rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.nlh.nlmsg_len)); + rta->rta_type = RTA_OIF; + rta->rta_len = RTA_LENGTH(sizeof(int)); + *(int *) RTA_DATA(rta) = ifindex; + + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + RTA_LENGTH(sizeof(int)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + perror("socket"); + return -1; + } + + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + close(sock); + return -1; + } + + struct sockaddr_nl kernel; + memset(&kernel, 0, sizeof(kernel)); + kernel.nl_family = AF_NETLINK; + + if (sendto(sock, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr *)&kernel, sizeof(kernel)) < 0) { + perror("sendto"); + close(sock); + return -1; + } + + close(sock); + return 0; +} static int tun_set_up(const char *ifname) { struct ifreq ifr; @@ -99,3 +184,188 @@ Java_su_sonoma_TunDevice_ioctlTun( return result; } + +static int add_route(const char *ifname, const char *cidr) { + + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + char buf[256]; + } req; + + memset(&req, 0, sizeof(req)); + + int ifindex = if_nametoindex(ifname); + + if (ifindex == 0) { + perror("if_nametoindex"); + return -1; + } + + char ip[64]; + int prefix; + + if (sscanf(cidr, "%63[^/]/%d", ip, &prefix) != 2) { + fprintf(stderr, "Invalid CIDR\n"); + return -1; + } + + struct in_addr dst; + + if (inet_pton(AF_INET, ip, &dst) != 1) { + perror("inet_pton"); + return -1; + } + + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_type = RTM_NEWROUTE; + + req.nlh.nlmsg_flags = + NLM_F_REQUEST | + NLM_F_CREATE | + NLM_F_EXCL; + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_table = RT_TABLE_MAIN; + req.rtm.rtm_protocol = RTPROT_STATIC; + req.rtm.rtm_scope = RT_SCOPE_LINK; + req.rtm.rtm_type = RTN_UNICAST; + + req.rtm.rtm_dst_len = prefix; + + struct rtattr *rta; + + rta = (struct rtattr *) + (((char *)&req) + NLMSG_ALIGN(req.nlh.nlmsg_len)); + + rta->rta_type = RTA_DST; + rta->rta_len = RTA_LENGTH(sizeof(dst)); + + memcpy(RTA_DATA(rta), &dst, sizeof(dst)); + + req.nlh.nlmsg_len = + NLMSG_ALIGN(req.nlh.nlmsg_len) + + RTA_LENGTH(sizeof(dst)); + + rta = (struct rtattr *) + (((char *)&req) + NLMSG_ALIGN(req.nlh.nlmsg_len)); + + rta->rta_type = RTA_OIF; + rta->rta_len = RTA_LENGTH(sizeof(int)); + + memcpy(RTA_DATA(rta), &ifindex, sizeof(int)); + + req.nlh.nlmsg_len = + NLMSG_ALIGN(req.nlh.nlmsg_len) + + RTA_LENGTH(sizeof(int)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (sock < 0) { + perror("socket"); + return -1; + } + + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + + nladdr.nl_family = AF_NETLINK; + + if (sendto( + sock, + &req, + req.nlh.nlmsg_len, + 0, + (struct sockaddr *)&nladdr, + sizeof(nladdr) + ) < 0) { + + perror("sendto"); + close(sock); + return -1; + } + + close(sock); + + return 0; +} + +JNIEXPORT jint JNICALL +Java_su_sonoma_TunDevice_addRoute( + JNIEnv *env, + jobject obj, + jstring ifname, + jstring cidr +) { + + const char *c_ifname = + (*env)->GetStringUTFChars(env, ifname, 0); + + const char *c_cidr = + (*env)->GetStringUTFChars(env, cidr, 0); + + if (!c_ifname || !c_cidr) { + return -1; + } + + int result = add_route(c_ifname, c_cidr); + + (*env)->ReleaseStringUTFChars(env, ifname, c_ifname); + (*env)->ReleaseStringUTFChars(env, cidr, c_cidr); + + return result; +} + +JNIEXPORT jint JNICALL +Java_su_sonoma_TunDevice_enableIpForwarding( + JNIEnv *env, + jobject obj +) { + + int fd = open( + "/proc/sys/net/ipv4/ip_forward", + O_WRONLY + ); + + if (fd < 0) { + return -1; + } + + const char *value = "1"; + + ssize_t result = write(fd, value, 1); + + close(fd); + + return result == 1 ? 0 : -1; +} + +JNIEXPORT jint JNICALL +Java_su_sonoma_TunDevice_enableMasquerade( + JNIEnv *env, + jobject obj, + jstring iface +) { + + const char *c_iface = + (*env)->GetStringUTFChars(env, iface, 0); + + if (c_iface == NULL) { + return -1; + } + + char command[256]; + + snprintf( + command, + sizeof(command), + "iptables -t nat -A POSTROUTING -o %s -j MASQUERADE", + c_iface + ); + + int result = system(command); + + (*env)->ReleaseStringUTFChars(env, iface, c_iface); + + return result; +} \ No newline at end of file diff --git a/src/main/kotlin/Client.kt b/src/main/kotlin/Client.kt index 578f948..addb5dd 100644 --- a/src/main/kotlin/Client.kt +++ b/src/main/kotlin/Client.kt @@ -1,48 +1,75 @@ package su.sonoma import kotlinx.coroutines.* +import java.io.BufferedInputStream +import java.io.BufferedOutputStream import java.io.DataInputStream import java.net.Socket -fun startClient(host: String, address: String, port: Int) = runBlocking { +fun startClient(host: String, address: String, port: Int, tun: String) = runBlocking { println("Client started") - val tun = TunDevice(address) + val tun = TunDevice(address, tun) + tun.addDefaultRoute() println("Client trying to connect - $host:$port") val socket = Socket(host, port) + println("Client trying connected - $host:$port") - val input = socket.getInputStream() - val output = socket.getOutputStream() - - println("Client trying to pair") - - launch { - val buffer = ByteArray(32768) - println("Client connected - $host:$port") - - while (true) { - val len = tun.input.read(buffer) - - if (len > 0) { - output.write(len shr 8) - output.write(len) - - output.write(buffer, 0, len) - } + launch(Dispatchers.IO) { + try { + handleClient(socket, tun) + } catch (e: Exception) { + e.printStackTrace() } } - launch { - val buffer = ByteArray(32768) - - while (true) { - val hi = input.read() - val lo = input.read() - val len = (hi shl 8) or lo - - DataInputStream(input).readFully(buffer, 0, len) - - tun.output.write(buffer, 0, len) + launch(Dispatchers.IO) { + try { + forwardClient(socket, tun) + } catch (e: Exception) { + e.printStackTrace() } } +} + +fun forwardClient(socket: Socket, tun: TunDevice) { + val buffer = ByteArray(1500) + val input = BufferedInputStream(socket.getInputStream()) + + while (true) { + + val hi = input.read() + if (hi == -1) break + + val lo = input.read() + if (lo == -1) break + + val len = (hi shl 8) or lo + //println("Message length: $len") + + var read = 0 + while (read < len) { + val r = input.read(buffer, read, len - read) + if (r == -1) break + read += r + } + + tun.output.write(buffer, 0, read) + tun.output.flush() + } +} + +fun handleClient(socket: Socket, tun: TunDevice) { + val output = BufferedOutputStream(socket.getOutputStream()) + val buffer = ByteArray(1500) + + while (true) { + val len = tun.input.read(buffer) + if (len <= 0) continue + + output.write((len shr 8) and 0xFF) + output.write(len and 0xFF) + output.write(buffer, 0, len) + output.flush() + } } \ No newline at end of file diff --git a/src/main/kotlin/Config.kt b/src/main/kotlin/Config.kt index 91ada65..464eef9 100644 --- a/src/main/kotlin/Config.kt +++ b/src/main/kotlin/Config.kt @@ -8,6 +8,8 @@ import java.io.File data class Config( val type: String, val host: String? = null, + val tun: String = "awake0", + val outInterface: String = "eth0", val address: String, val port: Int, ) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 7fbfe1b..f43a434 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -7,8 +7,8 @@ fun main(args: Array) { val config = loadConfig(File(configArg)) when (config.type) { - "server" -> startServer(config.address, config.port) - "client" -> startClient(config.host ?: error("Invalid config"), config.address, config.port) + "server" -> startServer(config.address, config.port, config.tun, config.outInterface) + "client" -> startClient(config.host ?: error("Invalid config"), config.address, config.port, config.tun) else -> { error("Invalid type") } diff --git a/src/main/kotlin/Server.kt b/src/main/kotlin/Server.kt index 2cad796..0b09f9f 100644 --- a/src/main/kotlin/Server.kt +++ b/src/main/kotlin/Server.kt @@ -1,12 +1,17 @@ package su.sonoma import kotlinx.coroutines.* +import java.io.BufferedInputStream +import java.io.BufferedOutputStream import java.net.ServerSocket import java.net.Socket import java.io.DataInputStream -fun startServer(address: String, port: Int) = runBlocking { - val tun = TunDevice(address) +fun startServer(address: String, port: Int, tun: String, outIntefrace: String) = runBlocking { + val tun = TunDevice(address, "awake0") + tun.addRoute() + tun.enableIpForwarding() + tun.enableMasquerade(outIntefrace) val server = ServerSocket(port) println("Server started on $port") @@ -21,55 +26,80 @@ fun startServer(address: String, port: Int) = runBlocking { try { handle(client, tun) } catch (e: Exception) { - println("${client.inetAddress} Client disconnected: ${e.message}") + e.printStackTrace() + } + } + + launch(Dispatchers.IO) { + try { + forward(client, tun) + } catch (e: Exception) { + e.printStackTrace() } } } } -suspend fun handle(socket: Socket, tun: TunDevice) { - val input = withContext(Dispatchers.IO) { - socket.getInputStream() - } - val output = withContext(Dispatchers.IO) { - socket.getOutputStream() - } +fun handle(socket: Socket, tun: TunDevice) { - val buffer = ByteArray(32768) + val input = BufferedInputStream(socket.getInputStream()) - withContext(Dispatchers.IO) { - while (true) { + val buffer = ByteArray(1500) - val hi = input.read() + while (true) { - val lo = input.read() + val hi = input.read() + val lo = input.read() - val len = (hi shl 8) or lo - - DataInputStream(input).readFully(buffer, 0, len) - - val packet = buffer.copyOf(len) - - val response = forwardPacket(packet) - - //output.write(response.size shr 8) - //output.write(response.size) - //output.write(response) - tun.output.write(packet, 0, len) + if (hi == -1 || lo == -1) { + println("${socket.inetAddress} Client disconnected") + break } + + val len = (hi shl 8) or lo + + //println("Message length: $len") + + if (len <= 0 || len > buffer.size) { + println("Invalid packet size: $len") + break + } + + var offset = 0 + while (offset < len) { + val r = input.read(buffer, offset, len - offset) + + //println("Read $r from client") + + if (r == -1) { + println("${socket.inetAddress} Client closed stream") + return + } + offset += r + } + + runCatching { + tun.output.write(buffer, 0, len) + tun.output.flush() + } + //println("Write in tun") } } -fun forwardPacket(packet: ByteArray): ByteArray { - val host = "localhost" - val port = 1080 - val socket = Socket(host, port) +fun forward(socket: Socket, tun: TunDevice) { - socket.getOutputStream().write(packet) + val readBuffer = ByteArray(1500) + val output = BufferedOutputStream(socket.getOutputStream()) - val response = socket.getInputStream().readBytes() + while (true) { + val responseLen = tun.input.read(readBuffer) + if (responseLen > 0) { - socket.close() + output.write((responseLen shr 8) and 0xFF) + output.write(responseLen and 0xFF) - return response + output.write(readBuffer, 0, responseLen) + output.flush() + } + } } \ No newline at end of file diff --git a/src/main/kotlin/TunDevice.kt b/src/main/kotlin/TunDevice.kt index 7ab8401..da695a1 100644 --- a/src/main/kotlin/TunDevice.kt +++ b/src/main/kotlin/TunDevice.kt @@ -5,11 +5,13 @@ import java.io.FileDescriptor import java.io.FileInputStream import java.io.FileOutputStream import java.io.RandomAccessFile +import java.nio.channels.DatagramChannel +import java.nio.channels.FileChannel class TunDevice( - private val ip: String + private val ip: String, + private val name: String ) { - val name: String = "tun0" companion object { init { @@ -46,4 +48,30 @@ class TunDevice( ip: String, flags: Int ): Int + + private external fun addDefaultRoute( + name: String, + ): Int + + fun addDefaultRoute() { + addDefaultRoute(name) + } + + private external fun addRoute(ifName: String, cidr: String): Int + + fun addRoute() { + addRoute(name, toCidr24(ip)) + } + + fun toCidr24(ip: String): String { + val parts = ip.split(".") + + require(parts.size == 4) + + return "${parts[0]}.${parts[1]}.${parts[2]}.0/24" + } + + external fun enableIpForwarding(): Int + + external fun enableMasquerade(iface: String): Int } \ No newline at end of file