Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java, how do you obtain a Socket or DatagramSocket from the file descriptor of an already opened C socket?

Tags:

java

c

tcp

sockets

udp

I have a Linux program split into two parts.

One part does NAT traversal to obtain either a UDP socket (UDP hole punching) or a TCP socket (TCP hole punching). Part one is written in C to allow for native features which facilitate or enhance the NAT traversal process. Part two actually uses the connected socket obtained via the NAT traversal performed in part one.

Now here is the problem. I want the first part, the part that obtains the socket, to be independent of the second part, the part that uses the socket for an application specific purpose. For example, I want the first part to be reusable for a variety of different applications that all need UDP and TCP connections that were established between peers.

Right now, I would like the second part (the application part) to be written in Java rather than C or C++. I want the second part to use a socket connection that was obtained by the C code responsible for NAT traversal. Let's say the first part established a connection and then returns a struct:

// Represents a TCP or UDP connection that was obtained in part one.
struct ConnectionObtained {
    int socket_file_descriptor;
    int source_port;
    int destination_port;
    int source_address; // 4 byte ipv4 address
    int destination_address;
    int is_UDP; // 1 for UDP client socket, 0 for TCP client socket 
};

The C code in part one can provide this POD/struct to the Java code in part two either via JNI (Java Native Interface) or via inter-proceess communication.

I want the Java code to use that information to construct an object whose declared type is either java.net.DatagramSocket or java.net.Socket and then use that object wherever a DatagramSocket or Socket would be expected.

As a starting point, consider the following sample code...

/** 
 * Determines the Unix file descriptor number of the given  {@link ServerSocket}.
 */
private int getUnixFileDescriptor(ServerSocket ss) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
  Field $impl=ss.getClass().getDeclaredField("impl");
  $impl.setAccessible(true);
  SocketImpl socketImpl=(SocketImpl)$impl.get(ss);
  Method $getFileDescriptor=SocketImpl.class.getDeclaredMethod("getFileDescriptor");
  $getFileDescriptor.setAccessible(true);
  FileDescriptor fd=(FileDescriptor)$getFileDescriptor.invoke(socketImpl);
  Field $fd=fd.getClass().getDeclaredField("fd");
  $fd.setAccessible(true);
  return (Integer)$fd.get(fd);
}

The code makes it appear that it may be possible to "recreates a bound {@link ServerSocket} on the given file descriptor." Does this mean that it is possible to "recreates a bound {@link java.net.Socket} on the given file descriptor" as well? What about a bound {@link java.net.DatagramSocket}?

/** 
 * Recreates a bound  {@link ServerSocket} on the given file descriptor.
 */
private ServerSocket recreateServerSocket(int fdn) throws Exception {
  FileDescriptor fd=new FileDescriptor();
  Field $fd=FileDescriptor.class.getDeclaredField("fd");
  $fd.setAccessible(true);
  $fd.set(fd,fdn);
  Class $PlainSocketImpl=Class.forName("java.net.PlainSocketImpl");
  Constructor $init=$PlainSocketImpl.getDeclaredConstructor(FileDescriptor.class);
  $init.setAccessible(true);
  SocketImpl socketImpl=(SocketImpl)$init.newInstance(fd);
  ServerSocket ss=new ServerSocket();
  ss.bind(new InetSocketAddress(0));
  Field $impl=ServerSocket.class.getDeclaredField("impl");
  $impl.setAccessible(true);
  $impl.set(ss,socketImpl);
  return ss;
}
like image 940
Michael Lafayette Avatar asked Sep 26 '15 07:09

Michael Lafayette


People also ask

What is difference between socket and DatagramSocket?

A ServerSocket is for accepting incoming network connections on some stream protocol; e.g. TCP/IP. A datagram is bunch of information sent in a single logical packet. For example, a UDP packet.

What is DatagramSocket explain in detail with example?

A datagram socket is the sending or receiving point for a packet delivery service. Each packet sent or received on a datagram socket is individually addressed and routed. Multiple packets sent from one machine to another may be routed differently, and may arrive in any order.

What returns a new descriptor that is used to send and receive data?

File descriptors File descriptors are normally small non-negative integers that the kernel uses to identify the files being accessed by a particular process. Whenever it opens an existing file or creates a new file, the kernel returns a file descriptor that is used to read or write the file.


1 Answers

You're asking two different questions. Can you pass a bound socket from C code written in a separate process and can you pass a bound socket from C code written in the same process.

For the first part, no, it's not possible if the C code is in one application and the Java code is another because if it were possible, than multiple different applications would be able to pass around a socket (without SCM_RIGHTS). Killing the application that created and bound the socket initially would create problems for the other applications using/sharing that socket.

As for having the C code be in the native part of a Java application (i.e. via jni), in that case the operating system wouldn't be able to differentiate whether the socket is in the Java part of the user code or the C part, so you don't run into the problem introduced in the previous paragraph. It is possible to pass a socket (file descriptor int and native socket descriptor) between Java and native code (see link), but that doesn't tell you if it would be practical in this scenario.

As for making a java.net.Socket or a java.net.DatagramSocket from a bound socket file descriptor that came from jni code, I have no idea. You would have to try it yourself.

like image 108
Michael Lafayette Avatar answered Sep 27 '22 18:09

Michael Lafayette