DEV Community

Cover image for Playing with the embryonic connections in Java with the Foreign Function
Ulrich VACHON
Ulrich VACHON

Posted on

Playing with the embryonic connections in Java with the Foreign Function

Today we will use the Foreign Function (FFI) in Java 22 to call a native code aims to initiate embryonic connections against any servers.

💡An example is ready to run on my github.com/ulrich space available in the repository : https://github.com/ulrich/java-rawsocket

What is embryonic connections ? 🎓

We use embryonic connections capabilities when we want to initiate an aborted TCP three-way handshake for example. If you remember your Network courses at the Faculty (coucou Paris 8 ❤️) there are a couple of years (for me), you remember the following sequences while the TCP-IP tries to open a network connection :

1️⃣              SYN (seq 1000) -> |
2️⃣                                | <- SYN / ACK (seq 2000, ACK 1001)
3️⃣    ACK (seq 1001, ACK 2001) -> |
Enter fullscreen mode Exit fullscreen mode

When the process is achieved the hosts can talk together. If we don't need to establish a well formed connection between hosts, we can abort the sequence by sending a reset (RST) instruction at the end of the TCP three-way handshake, like this :

1️⃣              SYN (seq 1000) -> |
2️⃣                                | <- SYN / ACK (seq 2000, ACK 1001)
3️⃣                        RST ->  X
Enter fullscreen mode Exit fullscreen mode

There are several reasons to create embryonic connections and from my side I had to implement this feature for a client there is some years...

Anyway, if you want to explore Embryonic Connection go ahead in the dedicated Wikipedia page : https://en.wikipedia.org/wiki/TCP_half-open

The Foreign Function in Java ? 🎓

The best way to resume the FFI in Java is to mention the official definition from Oracle company : The Foreign Function and Memory (FFM) API enables Java programs to interoperate with code and data outside the Java runtime.

In other terms, we want "to bend the game" and forgot the ancestor ways (JNI, JNA, JNR...) to use native code outside the Java Virtal Machine.

The promise behind FFI is to ease the use cases when a Java needs to (down)call and (up)call a native program or library.

This article is not an introduction about FFI and the best to learn about it is to follow the Oracle documentation here : https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html

FFI for embryonic connection ? 🚀

I think you've guessed why we need native program to establish an embryonic connection. Indeed, in Java we unfortunately don't have the ability to create low-level network packets. So the use of the netinet/tcp.h library is reserved for lower-level languages such as C (our example) and so FFI comes to the rescue !

Please deep dive in the SocketTester.java code with me 🔍

public int run() throws Throwable {
    log.info("Running SocketTester for destination address {}:{}", destinationAddress, destinationPort);

1️⃣    try (Arena confinedArena = Arena.ofConfined()) {
        SymbolLookup symbolLookup =
                    SymbolLookup.libraryLookup(Config.getNativeLibraryFile(), confinedArena);

2️⃣        MemorySegment function =
                    symbolLookup.find(Config.getNativeFunctionName())
                            .orElseThrow(() -> new IllegalStateException("Unable to find the native function: " + Config.getNativeFunctionName()));

3️⃣       MethodHandle methodHandle = Linker.nativeLinker()
                .downcallHandle(
                            function,
                            Config.getNativeFunctionDescriptor());

4️⃣       return (int) methodHandle.invoke(
                    confinedArena.allocateFrom(sourceAddress),
                    confinedArena.allocateFrom(destinationAddress),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, destinationPort),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, readTimeout),
                    confinedArena.allocateFrom(ValueLayout.OfInt.JAVA_INT, writeTimeout));
    }
}
Enter fullscreen mode Exit fullscreen mode

At the point 1️⃣ we declare that we want to use the restricted Arena only available for the thread which creates the Arena. We can compare Arena with a closed box responsible to control the lifecycle of native memory segments. For the moment FFI allows the following scopes : Global, Automatic, Confined and Shared. Please take a look into Javadoc : https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/Arena.html

At the point 2️⃣ we have to load the native library under the .so form.

At the point 3️⃣ we initialize a downcall handler used to communicate from Java to Native code. FFI allows to use upcall handler stub which enable you to pass Java code as a function pointer to a foreign function.

At the point 4️⃣ we invoke the native code by passing the expected parameters previously created by the different allocateFrom methods.

To test the embryonic connection with Foreign Function code, you can use the mentioned project upper.

If all the process is successful, we will obtain the following trace in your terminal :

[INFO (java)] Running SocketTester for destination address 127.0.0.1:8080
[INFO (native)] Selected source port number: 35940
[INFO (native)] TCP header sequence number: 272214228
[INFO (native)] Successfully sent 60 bytes SYN!
[INFO (native)] Received bytes: 40
[INFO (native)] Destination port: 35940
[INFO (native)] Successfully received 40 bytes
[INFO (native)] Received syn: 0, ack: 1, rst: 1
[INFO (native)] TCP header sequence number response: 0
[INFO (native)] TCP header ack sequence number response: 272214229
[INFO (native)] tcph->syn: 0
[INFO (native)] tcph->ack: 16777216
[INFO (native)] SYN ACK received -> Success
Enter fullscreen mode Exit fullscreen mode

Have a good day.

Image par jacqueline macou de Pixabay

Top comments (0)