work blyattt
This commit is contained in:
@ -68,6 +68,7 @@ tasks.register("generateTestServerConfig") {
|
||||
{
|
||||
"type": "server",
|
||||
"address": "10.1.0.1",
|
||||
"outInterface": "enp9s0",
|
||||
"port": 9093
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
270
src/main/c/tun.c
270
src/main/c/tun.c
@ -1,5 +1,15 @@
|
||||
#include <jni.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -10,6 +20,81 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -7,8 +7,8 @@ fun main(args: Array<String>) {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
val buffer = ByteArray(1500)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
while (true) {
|
||||
|
||||
val hi = input.read()
|
||||
|
||||
val lo = input.read()
|
||||
|
||||
if (hi == -1 || lo == -1) {
|
||||
println("${socket.inetAddress} Client disconnected")
|
||||
break
|
||||
}
|
||||
|
||||
val len = (hi shl 8) or lo
|
||||
|
||||
DataInputStream(input).readFully(buffer, 0, len)
|
||||
//println("Message length: $len")
|
||||
|
||||
val packet = buffer.copyOf(len)
|
||||
if (len <= 0 || len > buffer.size) {
|
||||
println("Invalid packet size: $len")
|
||||
break
|
||||
}
|
||||
|
||||
val response = forwardPacket(packet)
|
||||
var offset = 0
|
||||
while (offset < len) {
|
||||
val r = input.read(buffer, offset, len - offset)
|
||||
|
||||
//output.write(response.size shr 8)
|
||||
//output.write(response.size)
|
||||
//output.write(response)
|
||||
tun.output.write(packet, 0, len)
|
||||
//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 forward(socket: Socket, tun: TunDevice) {
|
||||
|
||||
val readBuffer = ByteArray(1500)
|
||||
val output = BufferedOutputStream(socket.getOutputStream())
|
||||
|
||||
while (true) {
|
||||
val responseLen = tun.input.read(readBuffer)
|
||||
if (responseLen > 0) {
|
||||
|
||||
output.write((responseLen shr 8) and 0xFF)
|
||||
output.write(responseLen and 0xFF)
|
||||
|
||||
output.write(readBuffer, 0, responseLen)
|
||||
output.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
fun forwardPacket(packet: ByteArray): ByteArray {
|
||||
val host = "localhost"
|
||||
val port = 1080
|
||||
|
||||
val socket = Socket(host, port)
|
||||
|
||||
socket.getOutputStream().write(packet)
|
||||
|
||||
val response = socket.getInputStream().readBytes()
|
||||
|
||||
socket.close()
|
||||
|
||||
return response
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user