A high-performance, production-ready Elixir socket library optimized for TURN server usage. Provides unified support for multiple transport protocols with advanced features like rate limiting, connection management, and comprehensive telemetry.
- Multi-Protocol Support: UDP, TCP, TLS, DTLS, and SCTP protocols
- Production Optimized: Built specifically for TURN server requirements
- Rate Limiting: Per-IP rate limiting with configurable thresholds
- Connection Management: Automatic connection tracking and cleanup
- Telemetry Integration: Comprehensive metrics and monitoring
- Security Features: SSL/TLS support with configurable certificates
- Error Handling: Robust error handling with graceful degradation
- Performance Tuned: Optimized buffer sizes and connection pooling
- IPv4/IPv6 Support: Full dual-stack networking support
Add xturn_sockets
to your list of dependencies in mix.exs
:
def deps do
[
{:xturn_sockets, "~> 1.1.0"},
{:telemetry, "~> 1.0"} # For telemetry features
]
end
Configure the library in your config/config.exs
:
config :xturn_sockets,
# Connection Management
connection_timeout: 30_000, # 30 seconds
max_udp_connections: 10_000, # Max UDP connections
max_tcp_connections: 1_000, # Max TCP connections
max_sctp_connections: 1_000, # Max SCTP connections
# Rate Limiting
rate_limit_enabled: true,
rate_limit_window: 60_000, # 1 minute window (1000 requests per window is hardcoded)
# Buffer Configuration
buffer_size: 262_144, # 256KB buffers
# SSL/TLS Configuration
ssl_handshake_timeout: 10_000, # 10 seconds
# SCTP Configuration
sctp_nodelay: true,
sctp_autoclose: 0,
sctp_maxseg: 1400,
# Telemetry
telemetry_enabled: true
# Certificate configuration for TLS/DTLS
config :certs,
certfile: "path/to/cert.pem",
keyfile: "path/to/key.pem",
cacertfile: "path/to/ca.pem"
alias Xirsys.Sockets.Socket
# Open a UDP socket
{:ok, socket} = Socket.open_port({127, 0, 0, 1}, :random, [])
# Send a message
:ok = Socket.send(socket, "Hello UDP", {192, 168, 1, 100}, 5000)
# Get socket information
{:ok, {ip, port}} = Socket.sockname(socket)
# Close the socket
:ok = Socket.close(socket)
defmodule MyUDPHandler do
def process_buffer(data, client_ip, client_port, server_ip, server_port) do
# Process incoming data with full 5-tuple information for STUN/TURN protocols
# This is essential for RFC 5389 (STUN) and RFC 5766 (TURN) compliance
{data, <<>>} # Return {processed_data, remaining_buffer}
end
def dispatch(conn) do
# Handle the processed connection
IO.puts "Received: #{inspect(conn.message)}"
IO.puts "From: #{inspect(conn.client_ip)}:#{conn.client_port}"
end
end
# Start UDP listener
{:ok, pid} = Xirsys.Sockets.Listener.UDP.start_link(
MyUDPHandler,
{0, 0, 0, 0}, # Bind to all interfaces
8080 # Port
)
defmodule MyTCPHandler do
def process_buffer(data, client_ip, client_port, server_ip, server_port) do
# Process TCP data with 5-tuple information
{data, <<>>} # Return {processed_data, remaining_buffer}
end
def dispatch(conn) do
# Handle the processed connection
Socket.send(conn.client_socket, "Echo: #{conn.message}")
:ok
end
end
# Start TCP listener
{:ok, pid} = Xirsys.Sockets.Listener.TCP.start_link(
MyTCPHandler,
{0, 0, 0, 0},
8081
)
# Start DTLS listener (requires certificate configuration)
{:ok, pid} = Xirsys.Sockets.Listener.UDP.start_link(
MyUDPHandler,
{0, 0, 0, 0},
8443,
{:ssl, true} # Enable DTLS
)
defmodule MySCTPHandler do
def process_buffer(data, client_ip, client_port, server_ip, server_port) do
# Process SCTP data with 5-tuple information
{data, <<>>} # Return {processed_data, remaining_buffer}
end
def dispatch(conn) do
# Handle the processed SCTP connection with metadata
stream_id = conn.metadata[:stream_id] || 0
ppid = conn.metadata[:ppid] || 0
IO.puts "SCTP message on stream #{stream_id}, PPID #{ppid}"
IO.puts "Data: #{inspect(conn.message)}"
# Echo back on the same stream
Socket.send(conn.client_socket, "Echo: #{conn.message}")
:ok
end
end
# Start SCTP listener
{:ok, pid} = Xirsys.Sockets.Listener.SCTP.start_link(
MySCTPHandler,
{0, 0, 0, 0},
8082
)
# Create socket with specific options
{:ok, socket} = Socket.open_port(
{127, 0, 0, 1},
{:range, 50000, 50100}, # Port range
[
{:reuseaddr, true},
{:buffer, 65536}
]
)
# Set socket options
:ok = Socket.setopts(socket, [{:active, :once}, :binary])
# Get socket options
{:ok, opts} = Socket.getopts(socket)
# Check rate limiting
case Socket.check_rate_limit({192, 168, 1, 100}) do
:ok ->
# Process request
:ok
{:error, :rate_limited} ->
# Reject request
{:error, :too_many_requests}
end
The library provides comprehensive telemetry events for monitoring:
# Attach telemetry handlers
Xirsys.Sockets.Telemetry.attach_handlers()
# Get current metrics
metrics = Xirsys.Sockets.Telemetry.get_metrics()
# Example metrics structure:
%{
total_connections: 1500,
active_udp_connections: 800,
active_tcp_connections: 500,
active_sctp_connections: 200,
bytes_sent: 1_048_576,
bytes_received: 2_097_152,
rate_limit_hits: 45,
ssl_handshake_successes: 150,
ssl_handshake_errors: 5,
send_errors: 12,
last_updated: 1640995200
}
# Check health status
health = Xirsys.Sockets.Telemetry.health_status()
# Returns: :healthy | :degraded | :unhealthy | :under_attack
# Attach custom handler for connection events
:telemetry.attach(
"my-connection-handler",
[:xturn_sockets, :connection_accepted],
fn _name, measurements, metadata, _config ->
protocol = measurements[:protocol]
Logger.info("New #{protocol} connection from #{inspect(metadata[:ip])}")
end,
%{}
)
All callback modules must implement the following interface:
def process_buffer(data, client_ip, client_port, server_ip, server_port)
This interface provides the complete 5-tuple information required by:
- RFC 5389 (STUN): For XOR-MAPPED-ADDRESS responses
- RFC 5766 (TURN): For allocation and permission management
- WebRTC compatibility: Proper NAT traversal responses
STUN/TURN protocols require the complete connection context:
defmodule MySTUNHandler do
def process_buffer(data, client_ip, client_port, server_ip, server_port) do
case STUNPacket.parse(data) do
{:binding_request, transaction_id} ->
# Build XOR-MAPPED-ADDRESS using client IP/port
response = STUNPacket.binding_response(
transaction_id,
client_ip,
client_port,
server_ip, # Required for XOR calculation
server_port
)
{response, <<>>}
_ -> {nil, <<>>}
end
end
def dispatch(conn) do
# Send response back to client
Socket.send(conn.client_socket, conn.message, conn.client_ip, conn.client_port)
end
end
The process_buffer/5
function must return:
{packet, remaining_buffer}
- Complete packet with any remaining data{nil, buffer}
- Incomplete packet, wait for more data
- UDP: Connectionless, high-performance datagram transport
- TCP: Reliable, connection-oriented stream transport
- TLS: Secure TCP with SSL/TLS encryption
- DTLS: Secure UDP with datagram TLS encryption
- SCTP: Message-oriented transport with multi-streaming
Socket
: Core socket abstraction and operationsListener.UDP
: UDP packet listener with DTLS supportListener.TCP
: TCP connection listener with TLS supportListener.SCTP
: SCTP association listener with multi-streamingTelemetry
: Metrics collection and health monitoringClient
: Client connection managementConn
: Connection state and metadata
# Per-IP rate limiting (1000 requests/minute by default)
case Socket.check_rate_limit(client_ip) do
:ok ->
# Within limits
process_request()
{:error, :rate_limited} ->
# Rate limited
send_error_response()
end
# Configure certificates
config :certs,
certfile: "/path/to/server.crt",
keyfile: "/path/to/server.key",
cacertfile: "/path/to/ca.crt",
# Additional SSL options
versions: [:"tlsv1.2", :"tlsv1.3"],
ciphers: [:ecdhe_ecdsa_aes_256_gcm_sha384, :ecdhe_rsa_aes_256_gcm_sha384],
secure_renegotiate: true,
reuse_sessions: true
# Automatic connection limiting per protocol
config :xturn_sockets,
max_udp_connections: 10_000,
max_tcp_connections: 1_000,
max_sctp_connections: 1_000,
max_connections_per_ip: 100
Run the test suite:
# Run all tests
mix test
# Run tests for specific protocols
mix test --only udp
mix test --only tcp
mix test --only tls
mix test --only dtls
mix test --only sctp
# Run with coverage
mix test --cover
config :xturn_sockets,
buffer_size: 262_144, # 256KB (default)
# For high-throughput applications:
buffer_size: 1_048_576 # 1MB
config :xturn_sockets,
connection_cleanup_enabled: true,
connection_cleanup_interval: 300_000, # 5 minutes
connection_timeout: 30_000 # 30 seconds
config :xturn_sockets,
sctp_nodelay: true, # Disable Nagle algorithm
sctp_autoclose: 0, # Disable auto-close
sctp_maxseg: 1400, # Optimal segment size
sctp_streams: %{
num_ostreams: 10, # Outbound streams
max_instreams: 10, # Inbound streams
max_attempts: 4, # Connection attempts
max_init_timeo: 30_000 # Init timeout
}
-
SCTP Not Available
{:error, :sctp_not_supported}
Solution: Ensure SCTP support is compiled into your Erlang/OTP installation.
-
Certificate Errors
{:error, :no_certificates_configured}
Solution: Configure certificates in the
:certs
application environment. -
Rate Limiting False Positives
{:error, :rate_limited}
Solution: Adjust rate limiting configuration or disable for testing.
# Enable debug logging
config :logger, level: :debug
# Enable socket statistics
{:ok, pid} = Xirsys.Sockets.Listener.UDP.start_link(
MyHandler,
{0, 0, 0, 0},
8080,
false,
debug: [:statistics]
)
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
mix test
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Copyright (c) 2013 - 2020 Xirsys LLC
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Developing libraries takes time (mostly personal), so donations are welcome. Donations can be sent via PayPal:
For questions, suggestions, or support:
- Email: experts@xirsys.com
- Issues: GitHub Issues
- XTurn - TURN/STUN server implementation
- Xirsys Cloud - Managed TURN/STUN service