Skip to content

Developer Guide

Ryan edited this page Mar 28, 2026 · 2 revisions

Developer Guide

Internal API reference for developers who want to understand, modify, or extend the script. This page documents the function signatures, return codes, and calling conventions.

Function Categories

The script is organized into these function groups (in order of appearance):

  1. Utility — logging, colors, formatting
  2. Validation — input sanitization and normalization
  3. Session Management — register, read, find, cleanup
  4. Command Builders — construct socat command strings
  5. Launch Infrastructure — launch sessions, watchdog
  6. Mode Handlers — one per operational mode
  7. Help — per-mode help text

Validation Functions

All validation functions return 0 on success, 1 on failure. They write normalized output to stdout where applicable.

validate_port

validate_port <port>
# Returns: 0 if valid (1-65535, numeric), 1 if invalid
# Output: none

validate_port_range

validate_port_range <range>
# Input: "8000-8010"
# Returns: 0 if valid (start < end, both valid ports, ≤1000 range), 1 if invalid
# Output: none

validate_port_list

validate_port_list <list>
# Input: "8080,8081,8082" or "8080;8081;8082" (semicolons normalized to commas)
# Returns: 0 if at least one valid port, 1 if entirely invalid
# Output: cleaned, comma-separated list with invalid entries removed

validate_hostname

validate_hostname <host>
# Accepts: IPv4, IPv6, RFC 1123 hostnames
# Rejects: shell metacharacters (;|`$)
# Returns: 0 if valid, 1 if invalid
# Output: none

validate_protocol

validate_protocol <proto>
# Input: tcp, tcp4, tcp6, udp, udp4, udp6 (case-insensitive)
# Returns: 0 if valid, 1 if invalid
# Output: normalized string (tcp → tcp4, udp → udp4)

validate_socat_opts

validate_socat_opts <opts>
# Whitelist: [a-zA-Z0-9=,.:/_-]
# Returns: 0 if safe, 1 if contains forbidden characters

validate_session_name

validate_session_name <name>
# Whitelist: [a-zA-Z0-9._-], max 64 characters
# Returns: 0 if valid, 1 if invalid

validate_file_path

validate_file_path <path>
# Rejects: path traversal (..), shell metacharacters (;|`$)
# Returns: 0 if valid, 1 if invalid
# Output: none

validate_session_id

validate_session_id <sid>
# Accepts: exactly 8 lowercase hex characters
# Returns: 0 if valid, 1 if invalid
# Output: none

Session Management Functions

generate_session_id

generate_session_id
# Returns: 0 on success, 1 if collision limit exceeded
# Output: 8-character lowercase hex string

Reads from /proc/sys/kernel/random/uuid, takes first 8 characters, lowercases. Retries up to 10 times on collision.

session_register

session_register <sid> <name> <pid> <pgid> <mode> <proto> <port> <socat_cmd> [rhost] [rport]
# Creates: sessions/<sid>.session with all metadata fields
# Permissions: 600 on the session file
# Returns: 0

session_unregister

session_unregister <sid>
# Removes: sessions/<sid>.session, sessions/<sid>.stop, sessions/<sid>.launching
# Returns: 0 (idempotent — no error if files don't exist)

session_read_field

session_read_field <sid> <field_name>
# Output: field value (empty string if field or file not found)
# Returns: 0
# Example: session_read_field "a1b2c3d4" "PID" → "12345"

session_find_by_name

session_find_by_name <name>
# Output: space-separated list of matching session IDs
# Returns: 0

session_find_by_port

session_find_by_port <port>
# Output: space-separated list of matching session IDs (may include both TCP and UDP)
# Returns: 0

session_find_by_pid

session_find_by_pid <pid>
# Output: matching session ID (or empty)
# Returns: 0

session_is_alive

session_is_alive <sid>
# Returns: 0 if PID from session file is running, 1 otherwise

session_get_all_ids

session_get_all_ids
# Output: space-separated list of all registered session IDs
# Returns: 0

session_cleanup_dead

session_cleanup_dead
# Removes session files for processes that are no longer running
# Output: log messages for each cleaned session
# Returns: 0

Command Builder Functions

Each builder constructs a socat command string. They do NOT execute it.

build_socat_listen_cmd

build_socat_listen_cmd <proto> <port> <logfile> [socat_opts] [capture]
# Output: complete socat command string
# Example output: "socat -v TCP4-LISTEN:8080,reuseaddr,fork OPEN:/tmp/log.txt,creat,append"

build_socat_forward_cmd

build_socat_forward_cmd <proto> <lport> <rhost> <rport> [remote_proto] [capture]
# Output: complete socat command string

build_socat_tunnel_cmd

build_socat_tunnel_cmd <lport> <rhost> <rport> <cert> <key> [capture]
# Output: complete socat command string with SSL options

build_socat_redirect_cmd

build_socat_redirect_cmd <proto> <lport> <rhost> <rport> [capture]
# Output: complete socat command string

Capture Flag

When capture is "true", the builder prepends -v to the socat command. This enables socat's verbose mode which produces bidirectional hex dump output on stderr.


Launch Infrastructure

launch_socat_session

launch_socat_session <name> <mode> <proto> <port> <socat_cmd> [rhost] [rport] [stderr_redirect]
# Sets: LAUNCH_SID global variable (session ID of the launched session)
# Creates: session file, PID staging file (cleaned up after launch)
# Returns: 0 on success, 1 on failure

This is the core launch function. It:

  1. Generates a session ID
  2. Creates a PID staging file
  3. Launches socat under setsid with PID-file handoff
  4. Waits for the PID file to be written (up to 2 seconds)
  5. Registers the session with all metadata
  6. Sets LAUNCH_SID for the caller

Important: Do NOT call this inside $() — it launches background processes. Use the LAUNCH_SID global variable instead.

get_alt_protocol

get_alt_protocol <proto>
# Output: the "other" protocol (tcp4 → udp4, udp4 → tcp4, tcp6 → udp6, udp6 → tcp6)
# Returns: 0

check_port_available

check_port_available <port> <proto>
# Returns: 0 if port is available, 1 if in use
# Uses: ss (preferred) or netstat (fallback)

check_port_freed

check_port_freed <port> <proto> [max_retries]
# Returns: 0 if port is freed, 1 if still in use after all retries
# Calls check_port_available in a retry loop

Adding a New Mode

To add a new operational mode:

  1. Create the mode handler function:

    mode_mymode() {
        local port="" rhost="" ...
        # Parse arguments
        while [[ $# -gt 0 ]]; do
            case "$1" in
                --port) port="$2"; shift 2 ;;
                --help|-h) show_mymode_help; return 0 ;;
                *) log_error "Unknown mymode option: $1"; return 1 ;;
            esac
        done
        # Validate inputs
        validate_port "${port}" || { log_error "Invalid port"; return 1; }
        # Build command
        local cmd
        cmd="$(build_socat_mymode_cmd ...)"
        # Launch
        launch_socat_session "mymode-${proto}-${port}" "mymode" "${proto}" "${port}" "${cmd}"
    }
  2. Create the command builder:

    build_socat_mymode_cmd() { ... echo "${cmd}"; }
  3. Create the help function:

    show_mymode_help() { cat << 'HELPEOF' ... HELPEOF }
  4. Register in the main dispatcher (the case block in main()):

    mymode) mode_mymode "${@:2}" ;;
  5. Add to main help (show_main_help)

  6. Add tests in a new file or in extended.bats

  7. Update documentation: README, USAGE_GUIDE, CHANGELOG, wiki


Code Style

  • Function headers: Every non-trivial function must have a comment block with Description, Parameters, Returns, and Output
  • Variable quoting: Quote every ${variable} expansion
  • Error handling: ((count++)) || true under set -e
  • No $() for launch functions: Use global variables for functions that spawn background processes
  • ShellCheck clean: No warnings at --severity=warning

Interactive Menu System

The menu system is implemented as a set of _menu_* functions that collect and validate input, then call main() with the constructed argument array.

Key Functions

Function Purpose
interactive_menu Root menu loop — displays options, dispatches to submenus
_menu_listen, _menu_batch, etc. Per-mode submenus with guided input
_menu_prompt Core input function — returns user input or _MENU_CANCEL on q/quit/cancel/back
_menu_prompt_port Port input with validation loop
_menu_prompt_host Hostname input with validation loop
_menu_prompt_protocol Protocol selection menu
_menu_prompt_yn Yes/no prompt with default
_menu_collect_common_flags Collects protocol, dual-stack, capture, watchdog, name for any mode
_menu_check_deps Dependency checker with version detection

Cancel Convention

All _menu_prompt* functions return exit code 2 on cancel. Submenus catch code 2 and call _menu_cancelled to display feedback and return to the root menu.


Return Code Conventions

Code Meaning
0 Success
1 Expected failure (port in use, validation failed, session not found)
2 Usage error (missing required flag, unknown option)

Clone this wiki locally