CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-kohlschutter-junixsocket--junixsocket-core

Java Unix Domain Socket implementation providing AF_UNIX, AF_TIPC, AF_VSOCK, and AF_SYSTEM socket support with traditional and NIO APIs

Pending
Overview
Eval results
Files

file-descriptors.mddocs/

File Descriptor Operations

Advanced Unix Domain Socket features including file descriptor passing, peer credentials, and ancillary message handling for sophisticated inter-process communication scenarios.

Core Imports

import java.io.*;
import java.net.*;
import org.newsclub.net.unix.AFUNIXSocket;
import org.newsclub.net.unix.AFUNIXSocketChannel;
import org.newsclub.net.unix.AFUNIXSocketCredentials;
import org.newsclub.net.unix.AFUNIXSocketExtensions;
import org.newsclub.net.unix.AFSocketCapability;
import org.newsclub.net.unix.FileDescriptorAccess;

Capabilities

AFUNIXSocketExtensions

Interface defining advanced Unix Domain Socket extensions beyond the standard Socket API, including file descriptor passing and credential access.

/**
 * Advanced Unix Domain Socket extensions interface
 */
public interface AFUNIXSocketExtensions {
    
    /**
     * Gets file descriptors received via ancillary messages
     * File descriptors are received automatically with regular data
     * @return Array of received file descriptors, never null
     * @throws IOException if retrieval fails
     */
    FileDescriptor[] getReceivedFileDescriptors() throws IOException;
    
    /**
     * Clears the queue of received file descriptors
     * Should be called after processing received file descriptors
     */
    void clearReceivedFileDescriptors();
    
    /**
     * Sets file descriptors to be sent via ancillary messages
     * File descriptors will be sent with the next write operation
     * @param fds File descriptors to send (can be empty to clear)
     * @throws IOException if setting fails
     */
    void setOutboundFileDescriptors(FileDescriptor... fds) throws IOException;
    
    /**
     * Checks if outbound file descriptors are pending
     * @return true if file descriptors are queued for sending
     */
    boolean hasOutboundFileDescriptors();
    
    /**
     * Gets credentials of the peer process
     * @return Peer credentials information
     * @throws IOException if retrieval fails or not supported
     */
    AFUNIXSocketCredentials getPeerCredentials() throws IOException;
}

Usage Examples:

import java.io.*;
import java.nio.charset.StandardCharsets;
import org.newsclub.net.unix.*;

// Server: Receiving file descriptors
File serverSocket = new File("/tmp/fd-server.sock");
try (AFUNIXServerSocket server = AFUNIXServerSocket.bindOn(AFUNIXSocketAddress.of(serverSocket))) {
    System.out.println("Server waiting for connections...");
    
    try (AFUNIXSocket clientSocket = server.accept()) {
        System.out.println("Client connected");
        
        // Read regular data and file descriptors
        InputStream is = clientSocket.getInputStream();
        byte[] buffer = new byte[1024];
        int bytesRead = is.read(buffer);
        String message = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
        System.out.println("Received message: " + message);
        
        // Check for received file descriptors
        FileDescriptor[] receivedFds = clientSocket.getReceivedFileDescriptors();
        System.out.println("Received " + receivedFds.length + " file descriptors");
        
        for (int i = 0; i < receivedFds.length; i++) {
            // Use the received file descriptor
            try (FileInputStream fis = new FileInputStream(receivedFds[i])) {
                byte[] fileBuffer = new byte[1024];
                int fileBytes = fis.read(fileBuffer);
                String fileContent = new String(fileBuffer, 0, fileBytes, StandardCharsets.UTF_8);
                System.out.println("Content from FD " + i + ": " + fileContent);
            }
        }
        
        // Clear received file descriptors after processing
        clientSocket.clearReceivedFileDescriptors();
        
        // Get peer credentials
        AFUNIXSocketCredentials peerCreds = clientSocket.getPeerCredentials();
        System.out.println("Peer process ID: " + peerCreds.getPid());
        System.out.println("Peer user ID: " + peerCreds.getUid());
        System.out.println("Peer group ID: " + peerCreds.getGid());
    }
}

// Client: Sending file descriptors
File clientSocket = new File("/tmp/fd-client.sock");
try (AFUNIXSocket socket = AFUNIXSocket.connectTo(AFUNIXSocketAddress.of(serverSocket))) {
    
    // Create a temporary file to send
    File tempFile = File.createTempFile("test", ".txt");
    try (FileWriter writer = new FileWriter(tempFile)) {
        writer.write("Hello from shared file!");
    }
    
    // Open file descriptor to send
    FileInputStream fileInput = new FileInputStream(tempFile);
    FileDescriptor fd = fileInput.getFD();
    
    // Set file descriptor to be sent
    socket.setOutboundFileDescriptors(fd);
    
    // Send regular data (file descriptor will be sent automatically)
    OutputStream os = socket.getOutputStream();
    os.write("Sending file descriptor".getBytes(StandardCharsets.UTF_8));
    os.flush();
    
    System.out.println("File descriptor sent with message");
    
    // Clean up
    fileInput.close();
    tempFile.delete();
}

AFUNIXSocketCredentials

Container for Unix Domain Socket peer credentials, providing access to peer process information for authentication and authorization.

/**
 * Unix Domain Socket peer credentials
 */
public final class AFUNIXSocketCredentials {
    
    /**
     * Gets the process ID of the peer
     * @return Process ID (PID)
     */
    public long getPid();
    
    /**
     * Gets the user ID of the peer process
     * @return User ID (UID)
     */
    public long getUid();
    
    /**
     * Gets the primary group ID of the peer process
     * @return Primary group ID (GID)
     */
    public long getGid();
    
    /**
     * Gets all group IDs of the peer process
     * @return Array of all group IDs
     */
    public long[] getGids();
    
    /**
     * Gets the UUID of the peer process (if available)
     * @return Process UUID or null
     */
    public String getUUID();
    
    /**
     * Creates credentials for the current process
     * @return Current process credentials
     */
    public static AFUNIXSocketCredentials current();
    
    /**
     * Gets remote peer credentials from RMI context
     * Used in RMI scenarios to identify the calling process
     * @return Remote peer credentials or null
     */
    public static AFUNIXSocketCredentials remotePeerCredentials();
    
    /**
     * String representation of credentials
     * @return Human-readable credential information
     */
    public String toString();
}

Usage Examples:

import org.newsclub.net.unix.*;

// Authentication server using peer credentials
File authServerSocket = new File("/tmp/auth-server.sock");
try (AFUNIXServerSocket server = AFUNIXServerSocket.bindOn(AFUNIXSocketAddress.of(authServerSocket))) {
    
    while (true) {
        try (AFUNIXSocket client = server.accept()) {
            // Get peer credentials for authentication
            AFUNIXSocketCredentials peerCreds = client.getPeerCredentials();
            
            System.out.println("Connection from:");
            System.out.println("  PID: " + peerCreds.getPid());
            System.out.println("  UID: " + peerCreds.getUid());
            System.out.println("  GID: " + peerCreds.getGid());
            
            // Simple authorization check
            if (peerCreds.getUid() == 0) {
                System.out.println("Root user detected - full access granted");
                client.getOutputStream().write("ADMIN_ACCESS".getBytes());
            } else if (peerCreds.getUid() == getCurrentUserId()) {
                System.out.println("Same user - standard access granted");
                client.getOutputStream().write("USER_ACCESS".getBytes());
            } else {
                System.out.println("Different user - access denied");
                client.getOutputStream().write("ACCESS_DENIED".getBytes());
            }
            
            client.getOutputStream().flush();
        }
    }
}

// Check group memberships
AFUNIXSocketCredentials currentCreds = AFUNIXSocketCredentials.current();
long[] groups = currentCreds.getGids();
System.out.println("Current process belongs to " + groups.length + " groups:");
for (long gid : groups) {
    System.out.println("  GID: " + gid);
}

FileDescriptorAccess

Utility class for advanced file descriptor operations and access to native file descriptor values.

/**
 * Utility class for file descriptor access and operations
 */
public final class FileDescriptorAccess {
    
    /**
     * Gets the native file descriptor value from a FileDescriptor
     * @param fd The FileDescriptor instance
     * @return Native file descriptor number
     * @throws IOException if access fails
     */
    public static int getFileDescriptor(FileDescriptor fd) throws IOException;
    
    /**
     * Gets the file descriptor from an AFSocket
     * @param socket The socket instance
     * @return Socket's file descriptor
     * @throws IOException if access fails
     */
    public static FileDescriptor getFileDescriptor(AFSocket socket) throws IOException;
    
    /**
     * Checks if a file descriptor is valid
     * @param fd The FileDescriptor to check
     * @return true if the file descriptor is valid
     */
    public static boolean isValid(FileDescriptor fd);
    
    /**
     * Creates a FileDescriptor from a native file descriptor number
     * @param fdNum Native file descriptor number
     * @return FileDescriptor instance
     * @throws IOException if creation fails
     */
    public static FileDescriptor fromNative(int fdNum) throws IOException;
}

Usage Examples:

import org.newsclub.net.unix.*;

// File descriptor introspection
try (AFUNIXSocket socket = AFUNIXSocket.newInstance()) {
    socket.connect(AFUNIXSocketAddress.of(new File("/tmp/test.sock")));
    
    // Get the socket's file descriptor
    FileDescriptor socketFd = FileDescriptorAccess.getFileDescriptor(socket);
    int nativeFd = FileDescriptorAccess.getFileDescriptor(socketFd);
    
    System.out.println("Socket file descriptor: " + nativeFd);
    System.out.println("File descriptor valid: " + FileDescriptorAccess.isValid(socketFd));
    
    // Create file descriptor for stdout
    FileDescriptor stdoutFd = FileDescriptorAccess.fromNative(1);
    socket.setOutboundFileDescriptors(stdoutFd);
    
    // Send message with stdout file descriptor
    socket.getOutputStream().write("Check your terminal!".getBytes());
    socket.getOutputStream().flush();
}

Capability Detection

Check for file descriptor and credential support before using advanced features:

/**
 * Capability constants for file descriptor features
 */
public enum AFSocketCapability {
    /** File descriptor passing support */
    CAPABILITY_FILE_DESCRIPTORS,
    
    /** Peer credential support */
    CAPABILITY_PEER_CREDENTIALS,
    
    /** Ancillary message support */
    CAPABILITY_ANCILLARY_MESSAGES
}

Feature Detection Examples:

import org.newsclub.net.unix.*;

public class FeatureDetection {
    public static void main(String[] args) {
        System.out.println("Advanced Unix Socket Features:");
        
        if (AFSocketCapability.CAPABILITY_FILE_DESCRIPTORS.isSupported()) {
            System.out.println("✓ File descriptor passing available");
            demonstrateFileDescriptorPassing();
        } else {
            System.out.println("✗ File descriptor passing not supported");
        }
        
        if (AFSocketCapability.CAPABILITY_PEER_CREDENTIALS.isSupported()) {
            System.out.println("✓ Peer credentials available");
            demonstratePeerCredentials();
        } else {
            System.out.println("✗ Peer credentials not supported");
        }
        
        if (AFSocketCapability.CAPABILITY_ANCILLARY_MESSAGES.isSupported()) {
            System.out.println("✓ Ancillary messages available");
        } else {
            System.out.println("✗ Ancillary messages not supported");
        }
    }
    
    private static void demonstrateFileDescriptorPassing() {
        // File descriptor passing implementation
        System.out.println("  → File descriptor passing is fully functional");
    }
    
    private static void demonstratePeerCredentials() {
        // Peer credentials implementation
        AFUNIXSocketCredentials current = AFUNIXSocketCredentials.current();
        System.out.println("  → Current process PID: " + current.getPid());
        System.out.println("  → Current process UID: " + current.getUid());
    }
}

Security Considerations

When using file descriptor passing and peer credentials:

  1. Validate Received File Descriptors: Always validate file descriptors before use
  2. Check Permissions: Verify peer credentials for authorization
  3. Resource Management: Properly close received file descriptors
  4. Error Handling: Handle cases where features are not supported
  5. Capability Testing: Always test capabilities before using advanced features
// Safe file descriptor handling
FileDescriptor[] receivedFds = socket.getReceivedFileDescriptors();
for (FileDescriptor fd : receivedFds) {
    if (FileDescriptorAccess.isValid(fd)) {
        try {
            // Use the file descriptor
            processFileDescriptor(fd);
        } finally {
            // Ensure proper cleanup
            try {
                // Close if it's a file we opened
                if (fd != FileDescriptor.in && fd != FileDescriptor.out && fd != FileDescriptor.err) {
                    // Close safely
                }
            } catch (Exception e) {
                // Log but don't fail
            }
        }
    }
}
socket.clearReceivedFileDescriptors();

Install with Tessl CLI

npx tessl i tessl/maven-com-kohlschutter-junixsocket--junixsocket-core

docs

addressing.md

capabilities.md

datagram-sockets.md

exceptions.md

file-descriptors.md

index.md

nio-channels.md

rmi.md

socket-pairs.md

unix-sockets.md

utilities.md

tile.json