Making the C version works...

parent 87e45f53
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
# Parse command line arguments # Parse command line arguments
BUILD_DEBIAN=false BUILD_DEBIAN=false
BUILD_DEBIAN_ONLY=false BUILD_DEBIAN_ONLY=false
BUILD_SERVER_ONLY=false
BUILD_NO_SERVER=false
BUILD_WSSSHTOOLS_ONLY=false
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
--debian) --debian)
...@@ -14,9 +17,32 @@ while [[ $# -gt 0 ]]; do ...@@ -14,9 +17,32 @@ while [[ $# -gt 0 ]]; do
BUILD_DEBIAN=true BUILD_DEBIAN=true
shift shift
;; ;;
--server-only)
BUILD_SERVER_ONLY=true
shift
;;
--no-server)
BUILD_NO_SERVER=true
shift
;;
--wssshtools-only)
BUILD_WSSSHTOOLS_ONLY=true
shift
;;
--help|-h)
echo "Usage: $0 [options]"
echo "Options:"
echo " --debian Build Debian package"
echo " --debian-only Build only Debian package (skip binaries)"
echo " --server-only Build only the server (wssshd)"
echo " --no-server Skip building the server (wssshd)"
echo " --wssshtools-only Build only the C tools (wssshtools)"
echo " --help, -h Show this help"
exit 0
;;
*) *)
echo "Unknown option: $1" echo "Unknown option: $1"
echo "Usage: $0 [--debian] [--debian-only]" echo "Usage: $0 [--debian] [--debian-only] [--server-only] [--no-server] [--wssshtools-only]"
exit 1 exit 1
;; ;;
esac esac
...@@ -41,8 +67,8 @@ pip3 install pyinstaller ...@@ -41,8 +67,8 @@ pip3 install pyinstaller
# Create dist directory if not exists # Create dist directory if not exists
mkdir -p dist mkdir -p dist
# Skip Python/C building if --debian-only is specified # Skip Python/C building if --debian-only or --wssshtools-only is specified
if [ "$BUILD_DEBIAN_ONLY" = false ]; then if [ "$BUILD_DEBIAN_ONLY" = false ] && [ "$BUILD_WSSSHTOOLS_ONLY" = false ]; then
# Generate SSL certificates if they don't exist # Generate SSL certificates if they don't exist
if [ ! -f "cert.pem" ] || [ ! -f "key.pem" ]; then if [ ! -f "cert.pem" ] || [ ! -f "key.pem" ]; then
echo "Generating SSL certificates..." echo "Generating SSL certificates..."
...@@ -76,17 +102,25 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then ...@@ -76,17 +102,25 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then
# Build Python binaries # Build Python binaries
echo "Building Python binaries..." echo "Building Python binaries..."
# Build wssshd (server) binary with certificates and web assets # Build wssshd (server) binary unless --no-server is specified
pyinstaller --onefile --distpath dist --add-data "cert.pem:." --add-data "key.pem:." --add-data "templates:templates" --add-data "static:static" --runtime-tmpdir /tmp --clean wssshd.py if [ "$BUILD_NO_SERVER" = false ]; then
pyinstaller --onefile --distpath dist --add-data "cert.pem:." --add-data "key.pem:." --add-data "templates:templates" --add-data "static:static" --runtime-tmpdir /tmp --clean wssshd.py
fi
# Build wssshc (client) binary # Build client binaries
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wssshc.py if [ "$BUILD_SERVER_ONLY" = false ]; then
# Build wssshc (client) binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wssshc.py
# Build wsssh binary # Build wsssh and wsscp binaries unless --no-server is specified
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsssh.py if [ "$BUILD_NO_SERVER" = false ]; then
# Build wsssh binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsssh.py
# Build wsscp binary # Build wsscp binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsscp.py pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsscp.py
fi
fi
# Build C version if wssshtools directory exists # Build C version if wssshtools directory exists
if [ -d "wssshtools" ]; then if [ -d "wssshtools" ]; then
...@@ -94,7 +128,15 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then ...@@ -94,7 +128,15 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then
cd wssshtools cd wssshtools
if [ -f "configure.sh" ]; then if [ -f "configure.sh" ]; then
./configure.sh ./configure.sh
make if [ "$BUILD_SERVER_ONLY" = true ]; then
# Build only wssshc (client) for server-only mode
make wssshc
elif [ "$BUILD_NO_SERVER" = true ]; then
# Build only wssshc for no-server mode
make wssshc
else
make
fi
cd .. cd ..
else else
echo "Warning: configure.sh not found in wssshtools/" echo "Warning: configure.sh not found in wssshtools/"
...@@ -145,18 +187,38 @@ if [ "$BUILD_DEBIAN_ONLY" = true ]; then ...@@ -145,18 +187,38 @@ if [ "$BUILD_DEBIAN_ONLY" = true ]; then
echo "Package available in dist/ directory:" echo "Package available in dist/ directory:"
echo "- dist/wsssh-tools*.deb (Debian package)" echo "- dist/wsssh-tools*.deb (Debian package)"
fi fi
else elif [ "$BUILD_WSSSHTOOLS_ONLY" = true ]; then
echo "Build complete. Binaries are in dist/ directory:" echo "C tools build complete."
echo "- dist/wssshd (server with web interface)"
echo "- dist/wssshc (client)"
echo "- dist/wsssh (SSH wrapper)"
echo "- dist/wsscp (SCP wrapper)"
if [ -d "wssshtools" ] && [ -f "wssshtools/wssshc" ]; then if [ -d "wssshtools" ] && [ -f "wssshtools/wssshc" ]; then
echo "C tools available in wssshtools/ directory:"
echo "- wssshtools/wssshc (C client)" echo "- wssshtools/wssshc (C client)"
echo "- wssshtools/wsssh (C SSH wrapper)" echo "- wssshtools/wsssh (C SSH wrapper)"
echo "- wssshtools/wsscp (C SCP wrapper)" echo "- wssshtools/wsscp (C SCP wrapper)"
fi fi
else
echo "Build complete. Binaries are in dist/ directory:"
if [ "$BUILD_NO_SERVER" = false ]; then
echo "- dist/wssshd (server with web interface)"
fi
if [ "$BUILD_SERVER_ONLY" = false ]; then
echo "- dist/wssshc (client)"
if [ "$BUILD_NO_SERVER" = false ]; then
echo "- dist/wsssh (SSH wrapper)"
echo "- dist/wsscp (SCP wrapper)"
fi
fi
if [ -d "wssshtools" ] && [ -f "wssshtools/wssshc" ]; then
if [ "$BUILD_SERVER_ONLY" = true ] || [ "$BUILD_NO_SERVER" = true ]; then
echo "- wssshtools/wssshc (C client)"
else
echo "- wssshtools/wssshc (C client)"
echo "- wssshtools/wsssh (C SSH wrapper)"
echo "- wssshtools/wsscp (C SCP wrapper)"
fi
fi
if [ "$BUILD_DEBIAN" = true ] && ls dist/wsssh-tools*.deb >/dev/null 2>&1; then if [ "$BUILD_DEBIAN" = true ] && ls dist/wsssh-tools*.deb >/dev/null 2>&1; then
echo "- dist/wsssh-tools*.deb (Debian package)" echo "- dist/wsssh-tools*.deb (Debian package)"
......
logos/banner-800x200.png

52.7 KB | W: | H:

logos/banner-800x200.png

52.7 KB | W: | H:

logos/banner-800x200.png
logos/banner-800x200.png
logos/banner-800x200.png
logos/banner-800x200.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-128.png

23.2 KB | W: | H:

logos/icon-128.png

23.2 KB | W: | H:

logos/icon-128.png
logos/icon-128.png
logos/icon-128.png
logos/icon-128.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-16.png

2.34 KB | W: | H:

logos/icon-16.png

2.34 KB | W: | H:

logos/icon-16.png
logos/icon-16.png
logos/icon-16.png
logos/icon-16.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-256.png

84.6 KB | W: | H:

logos/icon-256.png

84.6 KB | W: | H:

logos/icon-256.png
logos/icon-256.png
logos/icon-256.png
logos/icon-256.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-32.png

3.24 KB | W: | H:

logos/icon-32.png

3.24 KB | W: | H:

logos/icon-32.png
logos/icon-32.png
logos/icon-32.png
logos/icon-32.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-48.png

4.91 KB | W: | H:

logos/icon-48.png

4.91 KB | W: | H:

logos/icon-48.png
logos/icon-48.png
logos/icon-48.png
logos/icon-48.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-64.png

7.23 KB | W: | H:

logos/icon-64.png

7.23 KB | W: | H:

logos/icon-64.png
logos/icon-64.png
logos/icon-64.png
logos/icon-64.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-128.png

23.2 KB | W: | H:

logos/logo-128.png

23.2 KB | W: | H:

logos/logo-128.png
logos/logo-128.png
logos/logo-128.png
logos/logo-128.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-256.png

84.6 KB | W: | H:

logos/logo-256.png

84.6 KB | W: | H:

logos/logo-256.png
logos/logo-256.png
logos/logo-256.png
logos/logo-256.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-512.png

307 KB | W: | H:

logos/logo-512.png

307 KB | W: | H:

logos/logo-512.png
logos/logo-512.png
logos/logo-512.png
logos/logo-512.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-64.png

7.23 KB | W: | H:

logos/logo-64.png

7.23 KB | W: | H:

logos/logo-64.png
logos/logo-64.png
logos/logo-64.png
logos/logo-64.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -100,8 +100,11 @@ ...@@ -100,8 +100,11 @@
<div class="col-md-4 text-center mb-3"> <div class="col-md-4 text-center mb-3">
<h6><i class="fab fa-bitcoin text-warning"></i> Bitcoin</h6> <h6><i class="fab fa-bitcoin text-warning"></i> Bitcoin</h6>
<div class="input-group"> <div class="mb-2">
<input type="text" class="form-control form-control-sm" value="bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" readonly> <img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=bitcoin:bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" alt="BTC QR Code" class="img-fluid rounded">
</div>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm font-monospace" value="bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" readonly style="font-size: 0.75rem;">
<button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx')"> <button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx')">
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
</button> </button>
...@@ -110,8 +113,11 @@ ...@@ -110,8 +113,11 @@
<div class="col-md-4 text-center mb-3"> <div class="col-md-4 text-center mb-3">
<h6><i class="fab fa-ethereum text-secondary"></i> Ethereum</h6> <h6><i class="fab fa-ethereum text-secondary"></i> Ethereum</h6>
<div class="input-group"> <div class="mb-2">
<input type="text" class="form-control form-control-sm" value="0xdA6dAb526515b5cb556d20269207D43fcc760E51" readonly> <img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=ethereum:0xdA6dAb526515b5cb556d20269207D43fcc760E51" alt="ETH QR Code" class="img-fluid rounded">
</div>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm font-monospace" value="0xdA6dAb526515b5cb556d20269207D43fcc760E51" readonly style="font-size: 0.75rem;">
<button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('0xdA6dAb526515b5cb556d20269207D43fcc760E51')"> <button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('0xdA6dAb526515b5cb556d20269207D43fcc760E51')">
<i class="fas fa-copy"></i> <i class="fas fa-copy"></i>
</button> </button>
......
...@@ -66,11 +66,41 @@ ...@@ -66,11 +66,41 @@
</h3> </h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<p class="mb-1"><strong>WebSocket Port:</strong> {{ config.WEBSOCKET_PORT or 'N/A' }}</p> <p class="mb-1"><strong>WebSocket Port:</strong> <span id="websocket-port">{{ websocket_port or 'N/A' }}</span></p>
<p class="mb-1"><strong>Domain:</strong> {{ config.DOMAIN or 'N/A' }}</p> <p class="mb-1"><strong>Domain:</strong> <span id="domain">{{ domain or 'N/A' }}</span></p>
<p class="mb-0"><strong>Connected Clients:</strong> {{ clients|length }}</p> <p class="mb-0"><strong>Connected Clients:</strong> <span id="client-count">{{ clients|length }}</span></p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
{% block scripts %}
<script>
let currentClients = {{ clients|tojson }};
function updateClients() {
fetch('/api/clients')
.then(response => response.json())
.then(data => {
// Update client count
document.getElementById('client-count').textContent = data.count;
// Check if client list changed
if (JSON.stringify(data.clients.sort()) !== JSON.stringify(currentClients.sort())) {
// Reload the page to show updated client list
location.reload();
}
})
.catch(error => {
console.log('Error fetching client data:', error);
});
}
// Update every 5 seconds
setInterval(updateClients, 5000);
// Initial update after 1 second
setTimeout(updateClients, 1000);
</script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -10,9 +10,14 @@ ...@@ -10,9 +10,14 @@
<h3 class="card-title mb-0"> <h3 class="card-title mb-0">
<i class="fas fa-users"></i> User Management <i class="fas fa-users"></i> User Management
</h3> </h3>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addUserModal"> <div>
<i class="fas fa-plus"></i> Add User <a href="{{ url_for('index') }}" class="btn btn-outline-secondary btn-sm me-2">
</button> <i class="fas fa-home"></i> Back to Home
</a>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addUserModal">
<i class="fas fa-plus"></i> Add User
</button>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
......
...@@ -31,7 +31,7 @@ import uuid ...@@ -31,7 +31,7 @@ import uuid
debug = False debug = False
async def handle_tunnel(ws, local_port, request_id): async def handle_tunnel(ws, local_port, request_id, ready_event=None):
"""Handle tunnel data forwarding""" """Handle tunnel data forwarding"""
try: try:
# Open local TCP listener # Open local TCP listener
...@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id): ...@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id):
if debug: print(f"[DEBUG] Listening on localhost:{local_port}") if debug: print(f"[DEBUG] Listening on localhost:{local_port}")
# Signal that server is ready
if ready_event:
ready_event.set()
async with server: async with server:
await server.serve_forever() await server.serve_forever()
except Exception as e: except Exception as e:
...@@ -110,11 +114,15 @@ async def run_scp(server_ip, server_port, client_id, local_port, scp_args): ...@@ -110,11 +114,15 @@ async def run_scp(server_ip, server_port, client_id, local_port, scp_args):
if data.get('type') == 'tunnel_ack': if data.get('type') == 'tunnel_ack':
if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}") if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}")
# Start tunnel handler # Start tunnel handler (opens listening port)
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id)) ready_event = asyncio.Event()
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id, ready_event))
# Wait for tunnel to be ready (listening port opened)
await ready_event.wait()
# Launch SCP with modified arguments # Launch SCP with modified arguments
scp_cmd = ['scp'] + scp_args + ['-P', str(local_port)] scp_cmd = ['scp'] + scp_args
if debug: print(f"[DEBUG] Launching: {' '.join(scp_cmd)}") if debug: print(f"[DEBUG] Launching: {' '.join(scp_cmd)}")
# Run SCP process # Run SCP process
...@@ -171,8 +179,8 @@ def main(): ...@@ -171,8 +179,8 @@ def main():
global debug global debug
debug = args.debug debug = args.debug
# Automatically prepend 'scp' to the remaining arguments # Don't prepend 'scp' - we'll handle it in execvp
modified_args = ['scp'] + remaining modified_args = remaining
# Find host arguments and -P port option in modified args (SCP can have multiple host:paths) # Find host arguments and -P port option in modified args (SCP can have multiple host:paths)
hosts = [] hosts = []
...@@ -224,7 +232,13 @@ def main(): ...@@ -224,7 +232,13 @@ def main():
# Modify modified_args to use localhost:local_port # Modify modified_args to use localhost:local_port
final_args = [] final_args = []
for arg in modified_args: i = 0
while i < len(modified_args):
arg = modified_args[i]
if arg == '-P' and i + 1 < len(modified_args):
# Skip -P and the port value as it's for wssshd, not SCP
i += 2
continue
if ':' in arg: if ':' in arg:
host_part, path_part = arg.split(':', 1) host_part, path_part = arg.split(':', 1)
if '@' in host_part: if '@' in host_part:
...@@ -240,9 +254,10 @@ def main(): ...@@ -240,9 +254,10 @@ def main():
final_args.append(arg) final_args.append(arg)
else: else:
final_args.append(arg) final_args.append(arg)
i += 1
# Add port argument for local tunnel # Add port argument for local tunnel at the beginning
final_args.extend(['-P', str(local_port)]) final_args = ['-P', str(local_port)] + final_args
if debug: print(f"[DEBUG] Final SCP args: {final_args}") if debug: print(f"[DEBUG] Final SCP args: {final_args}")
......
...@@ -31,7 +31,7 @@ import uuid ...@@ -31,7 +31,7 @@ import uuid
debug = False debug = False
async def handle_tunnel(ws, local_port, request_id): async def handle_tunnel(ws, local_port, request_id, ready_event=None):
"""Handle tunnel data forwarding""" """Handle tunnel data forwarding"""
try: try:
# Open local TCP listener # Open local TCP listener
...@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id): ...@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id):
if debug: print(f"[DEBUG] Listening on localhost:{local_port}") if debug: print(f"[DEBUG] Listening on localhost:{local_port}")
# Signal that server is ready
if ready_event:
ready_event.set()
async with server: async with server:
await server.serve_forever() await server.serve_forever()
except Exception as e: except Exception as e:
...@@ -110,11 +114,15 @@ async def run_ssh(server_ip, server_port, client_id, local_port, ssh_args): ...@@ -110,11 +114,15 @@ async def run_ssh(server_ip, server_port, client_id, local_port, ssh_args):
if data.get('type') == 'tunnel_ack': if data.get('type') == 'tunnel_ack':
if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}") if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}")
# Start tunnel handler # Start tunnel handler (opens listening port)
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id)) ready_event = asyncio.Event()
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id, ready_event))
# Wait for tunnel to be ready (listening port opened)
await ready_event.wait()
# Launch SSH with modified arguments # Launch SSH with modified arguments
ssh_cmd = ['ssh'] + ssh_args + ['-p', str(local_port), 'localhost'] ssh_cmd = ['ssh'] + ssh_args
if debug: print(f"[DEBUG] Launching: {' '.join(ssh_cmd)}") if debug: print(f"[DEBUG] Launching: {' '.join(ssh_cmd)}")
# Run SSH process # Run SSH process
...@@ -171,8 +179,8 @@ def main(): ...@@ -171,8 +179,8 @@ def main():
global debug global debug
debug = args.debug debug = args.debug
# Automatically prepend 'ssh' to the remaining arguments # Don't prepend 'ssh' - we'll handle it in execvp
modified_args = ['ssh'] + remaining modified_args = remaining
# Find the host argument and -p port option in modified args # Find the host argument and -p port option in modified args
host = None host = None
...@@ -190,8 +198,8 @@ def main(): ...@@ -190,8 +198,8 @@ def main():
# Handle user@host format # Handle user@host format
host = arg.split('@', 1)[1] host = arg.split('@', 1)[1]
break break
elif not arg.startswith('-') and i == 1: # Skip 'ssh' at index 0 elif not arg.startswith('-') and i == 0:
# First non-option argument after 'ssh' might be host # First non-option argument might be host
host = arg host = arg
break break
......
...@@ -24,9 +24,33 @@ import asyncio ...@@ -24,9 +24,33 @@ import asyncio
import ssl import ssl
import websockets import websockets
import json import json
import socket
debug = False debug = False
# Active tunnels: request_id -> {'reader': tcp_reader, 'writer': tcp_writer}
active_tunnels = {}
async def forward_tcp_to_ws(tcp_reader, websocket, request_id):
try:
while True:
data = await tcp_reader.read(1024)
if not data:
break
if debug: print(f"[DEBUG] Sending tunnel_response, data len: {len(data)}, data: {data[:50].decode('utf-8', errors='replace') if data else 'empty'}, hex: {data.hex()[:100]}...")
await websocket.send(json.dumps({
"type": "tunnel_response",
"request_id": request_id,
"data": data.hex()
}))
except Exception as e:
if debug: print(f"[DEBUG] TCP to WS error: {e}")
finally:
# Cleanup tunnel
if request_id in active_tunnels:
del active_tunnels[request_id]
if debug: print(f"[DEBUG] Tunnel {request_id} closed")
async def connect_to_server(server_ip, port, client_id, password, interval): async def connect_to_server(server_ip, port, client_id, password, interval):
uri = f"wss://{server_ip}:{port}" uri = f"wss://{server_ip}:{port}"
ssl_context = ssl.create_default_context() ssl_context = ssl.create_default_context()
...@@ -53,14 +77,35 @@ async def connect_to_server(server_ip, port, client_id, password, interval): ...@@ -53,14 +77,35 @@ async def connect_to_server(server_ip, port, client_id, password, interval):
if debug: print(f"[DEBUG] WebSocket message: {message[:100]}...") if debug: print(f"[DEBUG] WebSocket message: {message[:100]}...")
data = json.loads(message) data = json.loads(message)
if data.get('type') == 'tunnel_request': if data.get('type') == 'tunnel_request':
print(f"Tunnel request received: {data['request_id']}") request_id = data['request_id']
# Client would handle tunnel setup here if debug: print(f"[DEBUG] Tunnel request received: {request_id}")
try:
# Connect to local SSH server
tcp_reader, tcp_writer = await asyncio.open_connection('localhost', 22)
active_tunnels[request_id] = {'reader': tcp_reader, 'writer': tcp_writer}
# Start forwarding from TCP to WS
asyncio.create_task(forward_tcp_to_ws(tcp_reader, websocket, request_id))
if debug: print(f"[DEBUG] Connected to local SSH server for tunnel {request_id}")
except Exception as e:
if debug: print(f"[DEBUG] Tunnel setup error: {e}")
elif data.get('type') == 'tunnel_data': elif data.get('type') == 'tunnel_data':
print(f"Tunnel data received: {len(data.get('data', ''))} bytes") request_id = data['request_id']
# Client would forward data here if request_id in active_tunnels:
# Forward data to local SSH
tunnel_data = bytes.fromhex(data['data'])
if debug: print(f"[DEBUG] Received tunnel_data, hex len: {len(data['data'])}, hex: {data['data'][:100]}..., decoded: {tunnel_data[:50].decode('utf-8', errors='replace') if tunnel_data else 'empty'}")
active_tunnels[request_id]['writer'].write(tunnel_data)
await active_tunnels[request_id]['writer'].drain()
elif data.get('type') == 'tunnel_close': elif data.get('type') == 'tunnel_close':
print(f"Tunnel closed: {data['request_id']}") request_id = data['request_id']
# Client would close tunnel here if request_id in active_tunnels:
# Close tunnel
active_tunnels[request_id]['writer'].close()
await active_tunnels[request_id]['writer'].wait_closed()
del active_tunnels[request_id]
if debug: print(f"[DEBUG] Tunnel {request_id} closed")
except Exception as e: except Exception as e:
print(f"Connection failed: {e}, retrying in {interval} seconds") print(f"Connection failed: {e}, retrying in {interval} seconds")
await asyncio.sleep(interval) await asyncio.sleep(interval)
......
...@@ -34,6 +34,8 @@ from werkzeug.security import generate_password_hash, check_password_hash ...@@ -34,6 +34,8 @@ from werkzeug.security import generate_password_hash, check_password_hash
# Client registry: id -> websocket # Client registry: id -> websocket
clients = {} clients = {}
# Active tunnels: request_id -> {'client_ws': ws, 'wsssh_ws': ws, 'client_id': id}
active_tunnels = {}
debug = False debug = False
server_password = None server_password = None
...@@ -72,7 +74,10 @@ with app.app_context(): ...@@ -72,7 +74,10 @@ with app.app_context():
@app.route('/') @app.route('/')
@login_required @login_required
def index(): def index():
return render_template('index.html', clients=list(clients.keys())) return render_template('index.html',
clients=list(clients.keys()),
websocket_port=args.port,
domain=args.domain)
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
...@@ -155,6 +160,14 @@ def terminal(client_id): ...@@ -155,6 +160,14 @@ def terminal(client_id):
return redirect(url_for('index')) return redirect(url_for('index'))
return render_template('terminal.html', client_id=client_id) return render_template('terminal.html', client_id=client_id)
@app.route('/api/clients')
@login_required
def get_clients():
return jsonify({
'clients': list(clients.keys()),
'count': len(clients)
})
async def handle_websocket(websocket, path=None): async def handle_websocket(websocket, path=None):
try: try:
async for message in websocket: async for message in websocket:
...@@ -172,46 +185,80 @@ async def handle_websocket(websocket, path=None): ...@@ -172,46 +185,80 @@ async def handle_websocket(websocket, path=None):
await websocket.send(json.dumps({"type": "registration_error", "error": "Invalid password"})) await websocket.send(json.dumps({"type": "registration_error", "error": "Invalid password"}))
elif data.get('type') == 'tunnel_request': elif data.get('type') == 'tunnel_request':
client_id = data['client_id'] client_id = data['client_id']
request_id = data['request_id']
if client_id in clients: if client_id in clients:
# Store tunnel mapping
active_tunnels[request_id] = {
'client_ws': clients[client_id],
'wsssh_ws': websocket,
'client_id': client_id
}
# Forward tunnel request to client # Forward tunnel request to client
await clients[client_id].send(json.dumps({ await clients[client_id].send(json.dumps({
"type": "tunnel_request", "type": "tunnel_request",
"request_id": data['request_id'] "request_id": request_id
})) }))
await websocket.send(json.dumps({ await websocket.send(json.dumps({
"type": "tunnel_ack", "type": "tunnel_ack",
"request_id": data['request_id'] "request_id": request_id
})) }))
else: else:
await websocket.send(json.dumps({ await websocket.send(json.dumps({
"type": "tunnel_error", "type": "tunnel_error",
"request_id": data['request_id'], "request_id": request_id,
"error": "Client not registered" "error": "Client not registered"
})) }))
elif data.get('type') == 'tunnel_data': elif data.get('type') == 'tunnel_data':
# Forward tunnel data to appropriate client # Forward tunnel data using active tunnel mapping
client_id = data.get('client_id') request_id = data['request_id']
if client_id and client_id in clients: if request_id in active_tunnels:
await clients[client_id].send(json.dumps({ tunnel = active_tunnels[request_id]
# Forward to client
await tunnel['client_ws'].send(json.dumps({
"type": "tunnel_data",
"request_id": request_id,
"data": data['data']
}))
elif data.get('type') == 'tunnel_response':
# Forward tunnel response from client to wsssh
request_id = data['request_id']
if request_id in active_tunnels:
tunnel = active_tunnels[request_id]
await tunnel['wsssh_ws'].send(json.dumps({
"type": "tunnel_data", "type": "tunnel_data",
"request_id": data['request_id'], "request_id": request_id,
"data": data['data'] "data": data['data']
})) }))
elif data.get('type') == 'tunnel_close': elif data.get('type') == 'tunnel_close':
client_id = data.get('client_id') request_id = data['request_id']
if client_id and client_id in clients: if request_id in active_tunnels:
await clients[client_id].send(json.dumps({ tunnel = active_tunnels[request_id]
# Forward close to client
await tunnel['client_ws'].send(json.dumps({
"type": "tunnel_close", "type": "tunnel_close",
"request_id": data['request_id'] "request_id": request_id
})) }))
# Clean up tunnel
del active_tunnels[request_id]
except websockets.exceptions.ConnectionClosed: except websockets.exceptions.ConnectionClosed:
# Remove from registry # Remove from registry and clean up tunnels
disconnected_client = None
for cid, ws in clients.items(): for cid, ws in clients.items():
if ws == websocket: if ws == websocket:
disconnected_client = cid
del clients[cid] del clients[cid]
print(f"Client {cid} disconnected") print(f"Client {cid} disconnected")
break break
# Clean up active tunnels for this client
if disconnected_client:
tunnels_to_remove = []
for request_id, tunnel in active_tunnels.items():
if tunnel['client_id'] == disconnected_client:
tunnels_to_remove.append(request_id)
for request_id in tunnels_to_remove:
del active_tunnels[request_id]
async def main(): async def main():
parser = argparse.ArgumentParser(description='WebSocket SSH Daemon (wssshd)') parser = argparse.ArgumentParser(description='WebSocket SSH Daemon (wssshd)')
parser.add_argument('--host', required=True, help='WebSocket server host') parser.add_argument('--host', required=True, help='WebSocket server host')
...@@ -220,6 +267,7 @@ async def main(): ...@@ -220,6 +267,7 @@ async def main():
parser.add_argument('--password', required=True, help='Registration password') parser.add_argument('--password', required=True, help='Registration password')
parser.add_argument('--web-host', help='Web interface host (optional)') parser.add_argument('--web-host', help='Web interface host (optional)')
parser.add_argument('--web-port', type=int, help='Web interface port (optional)') parser.add_argument('--web-port', type=int, help='Web interface port (optional)')
parser.add_argument('--web-https', action='store_true', help='Enable HTTPS for web interface')
parser.add_argument('--debug', action='store_true', help='Enable debug output') parser.add_argument('--debug', action='store_true', help='Enable debug output')
args = parser.parse_args() args = parser.parse_args()
...@@ -249,12 +297,29 @@ async def main(): ...@@ -249,12 +297,29 @@ async def main():
# Start web interface if specified # Start web interface if specified
if args.web_host and args.web_port: if args.web_host and args.web_port:
# Handle HTTPS setup
ssl_context = None
protocol = "http"
if args.web_https:
protocol = "https"
# Use config directory for web certificates
web_cert_path = os.path.join(config_dir, 'web-cert.pem')
web_key_path = os.path.join(config_dir, 'web-key.pem')
# Generate self-signed certificate if it doesn't exist
if not os.path.exists(web_cert_path) or not os.path.exists(web_key_path):
print("Generating self-signed certificate for web interface...")
os.system(f'openssl req -x509 -newkey rsa:4096 -keyout {web_key_path} -out {web_cert_path} -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"')
ssl_context = (web_cert_path, web_key_path)
def run_flask(): def run_flask():
app.run(host=args.web_host, port=args.web_port, debug=False, use_reloader=False) app.run(host=args.web_host, port=args.web_port, debug=debug, use_reloader=False, ssl_context=ssl_context)
flask_thread = threading.Thread(target=run_flask, daemon=True) flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start() flask_thread.start()
print(f"Web interface available at http://{args.web_host}:{args.web_port}") print(f"Web interface available at {protocol}://{args.web_host}:{args.web_port}")
await ws_server.wait_closed() await ws_server.wait_closed()
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <getopt.h> #include <getopt.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h>
#define BUFFER_SIZE 4096 #define BUFFER_SIZE 4096
#define DEFAULT_PORT 22 #define DEFAULT_PORT 22
...@@ -20,6 +21,23 @@ typedef struct { ...@@ -20,6 +21,23 @@ typedef struct {
int debug; int debug;
} wsssh_config_t; } wsssh_config_t;
typedef struct {
int local_sock; // Local TCP connection socket
char request_id[37]; // UUID string
int active;
SSL *ssl; // WebSocket SSL connection
} tunnel_t;
typedef struct {
SSL *ssl;
int debug;
} thread_args_t;
tunnel_t *active_tunnel = NULL;
pthread_mutex_t tunnel_mutex;
int send_websocket_frame(SSL *ssl, const char *data);
void print_trans_flag(void) { void print_trans_flag(void) {
// Transgender pride flag colors using ANSI escape codes // Transgender pride flag colors using ANSI escape codes
const char *colors[] = { const char *colors[] = {
...@@ -55,34 +73,38 @@ void print_usage(const char *program_name) { ...@@ -55,34 +73,38 @@ void print_usage(const char *program_name) {
fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n"); fprintf(stderr, " ETH: 0xdA6dAb526515b5cb556d20269207D43fcc760E51\n");
} }
int parse_args(int argc, char *argv[], wsssh_config_t *config) { int parse_args(int argc, char *argv[], wsssh_config_t *config, int *remaining_argc, char ***remaining_argv) {
static struct option long_options[] = { // Manually parse arguments to separate wsssh options from SSH options
{"local-port", required_argument, 0, 'l'}, int ssh_start = 1; // Skip argv[0]
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt; for (int i = 1; i < argc; i++) {
while ((opt = getopt_long(argc, argv, "l:dh", long_options, NULL)) != -1) { if (strcmp(argv[i], "--local-port") == 0 && i + 1 < argc) {
switch (opt) { config->local_port = strdup(argv[i + 1]);
case 'l': i++; // Skip the argument
config->local_port = strdup(optarg); } else if (strcmp(argv[i], "--debug") == 0) {
break; config->debug = 1;
case 'd': } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
config->debug = 1; print_usage(argv[0]);
break; return 0;
case 'h': } else if (argv[i][0] == '-') {
default: // Unknown option, treat as SSH option
print_usage(argv[0]); ssh_start = i;
return 0; break;
} else {
// Non-option argument, start of SSH args
ssh_start = i;
break;
} }
} }
// Return remaining arguments (SSH arguments)
*remaining_argc = argc - ssh_start;
*remaining_argv = &argv[ssh_start];
return 1; return 1;
} }
int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, int *wssshd_port) { int parse_hostname(const char *hostname, char **client_id, char **wssshd_host) {
char *at_pos = strchr(hostname, '@'); char *at_pos = strchr(hostname, '@');
if (!at_pos) { if (!at_pos) {
fprintf(stderr, "Error: Invalid hostname format. Expected user@host\n"); fprintf(stderr, "Error: Invalid hostname format. Expected user@host\n");
...@@ -90,29 +112,131 @@ int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, i ...@@ -90,29 +112,131 @@ int parse_hostname(const char *hostname, char **client_id, char **wssshd_host, i
} }
char *host_part = at_pos + 1; char *host_part = at_pos + 1;
char *colon_pos = strchr(host_part, ':'); char *host_copy = strdup(host_part);
if (!host_copy) {
return 0;
}
// Remove port if present
char *colon_pos = strchr(host_copy, ':');
if (colon_pos) { if (colon_pos) {
*colon_pos = '\0'; *colon_pos = '\0';
*wssshd_port = atoi(colon_pos + 1);
} else {
*wssshd_port = DEFAULT_PORT;
} }
// Split host by dots to extract client_id // Split host by dots to extract client_id
char *dot_pos = strchr(host_part, '.'); char *dot_pos = strchr(host_copy, '.');
if (!dot_pos) { if (!dot_pos) {
fprintf(stderr, "Error: Invalid hostname format. Expected client.domain format\n"); fprintf(stderr, "Error: Invalid hostname format. Expected client.domain format\n");
free(host_copy);
return 0; return 0;
} }
*dot_pos = '\0'; *dot_pos = '\0';
*client_id = strdup(host_part); *client_id = strdup(host_copy);
*wssshd_host = strdup(dot_pos + 1); *wssshd_host = strdup(dot_pos + 1);
free(host_copy);
return 1; return 1;
} }
int parse_ssh_args(int argc, char *argv[], char **host, int *ssh_port, int debug) {
*host = NULL;
*ssh_port = DEFAULT_PORT;
int skip_next = 0;
for (int i = 0; i < argc; i++) {
if (skip_next) {
skip_next = 0;
continue;
}
if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) && i + 1 < argc) {
*ssh_port = atoi(argv[i + 1]);
skip_next = 1;
if (debug) {
printf("[DEBUG] Found SSH port: %d\n", *ssh_port);
}
} else if (argv[i][0] != '-' && !*host) {
// First non-option argument should be the host
*host = argv[i];
if (debug) {
printf("[DEBUG] Found SSH host: %s\n", *host);
}
}
}
if (!*host) {
fprintf(stderr, "Error: Could not determine target host from SSH arguments\n");
return 0;
}
return 1;
}
char **modify_ssh_args(int argc, char *argv[], const char *original_host, int local_port, int *new_argc) {
// Allocate space for: ssh + -p + port + original args + NULL
char **new_args = malloc((argc + 4) * sizeof(char *));
if (!new_args) {
return NULL;
}
int idx = 0;
new_args[idx++] = "ssh";
// Add port argument for local tunnel
new_args[idx++] = "-p";
char *port_str = malloc(16);
if (!port_str) {
free(new_args);
return NULL;
}
snprintf(port_str, 16, "%d", local_port);
new_args[idx++] = port_str;
int skip_next = 0;
for (int i = 0; i < argc; i++) {
if (skip_next) {
skip_next = 0;
continue;
}
if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0 ||
strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--host") == 0) {
skip_next = 1;
continue;
}
// Check if this argument is the original host
if (strcmp(argv[i], original_host) == 0) {
// Replace user@host with user@localhost or host with localhost
char *at_pos = strchr(original_host, '@');
if (at_pos) {
// Has user part
size_t user_len = at_pos - original_host;
char *new_host = malloc(user_len + 10); // "@localhost"
if (!new_host) {
free(port_str);
free(new_args);
return NULL;
}
memcpy(new_host, original_host, user_len);
strcpy(new_host + user_len, "@localhost");
new_args[idx++] = new_host;
} else {
// No user part
new_args[idx++] = "localhost";
}
continue;
}
new_args[idx++] = argv[i];
}
new_args[idx] = NULL;
*new_argc = idx;
return new_args;
}
int find_available_port() { int find_available_port() {
struct sockaddr_in addr; struct sockaddr_in addr;
int sock; int sock;
...@@ -147,7 +271,15 @@ int find_available_port() { ...@@ -147,7 +271,15 @@ int find_available_port() {
return port; return port;
} }
int websocket_handshake(int sock, const char *host, int port, const char *path) { void generate_request_id(char *request_id, size_t size) {
const char charset[] = "0123456789abcdef";
for (size_t i = 0; i < size - 1; i++) {
request_id[i] = charset[rand() % (sizeof(charset) - 1)];
}
request_id[size - 1] = '\0';
}
int websocket_handshake(SSL *ssl, const char *host, int port, const char *path) {
char request[1024]; char request[1024];
char response[BUFFER_SIZE]; char response[BUFFER_SIZE];
int bytes_read; int bytes_read;
...@@ -163,15 +295,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -163,15 +295,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
"\r\n", "\r\n",
path, host, port); path, host, port);
if (send(sock, request, strlen(request), 0) < 0) { if (SSL_write(ssl, request, strlen(request)) <= 0) {
perror("WebSocket handshake send failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake send failed\n");
return 0; return 0;
} }
// Read response // Read response
bytes_read = recv(sock, response, sizeof(response) - 1, 0); bytes_read = SSL_read(ssl, response, sizeof(response) - 1);
if (bytes_read <= 0) { if (bytes_read <= 0) {
perror("WebSocket handshake recv failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake recv failed\n");
return 0; return 0;
} }
...@@ -186,21 +320,206 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -186,21 +320,206 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
return 1; return 1;
} }
int send_json_message(int sock, const char *type, const char *client_id) { int send_json_message(SSL *ssl, const char *type, const char *client_id, const char *request_id) {
char message[1024]; char message[1024];
snprintf(message, sizeof(message), if (request_id) {
"{\"type\":\"%s\",\"client_id\":\"%s\"}", snprintf(message, sizeof(message),
type, client_id); "{\"type\":\"%s\",\"client_id\":\"%s\",\"request_id\":\"%s\"}",
type, client_id, request_id);
} else {
snprintf(message, sizeof(message),
"{\"type\":\"%s\",\"client_id\":\"%s\"}",
type, client_id);
}
// Send as WebSocket frame
return send_websocket_frame(ssl, message);
}
int send_websocket_frame(SSL *ssl, const char *data) {
char frame[BUFFER_SIZE];
frame[0] = 0x81; // FIN + text opcode
int msg_len = strlen(data);
int header_len = 2;
if (msg_len <= 125) {
frame[1] = 0x80 | msg_len; // MASK + length
} else if (msg_len <= 65535) {
frame[1] = 0x80 | 126; // MASK + extended length
frame[2] = (msg_len >> 8) & 0xFF;
frame[3] = msg_len & 0xFF;
header_len = 4;
} else {
frame[1] = 0x80 | 127; // MASK + extended length
frame[2] = 0;
frame[3] = 0;
frame[4] = 0;
frame[5] = 0;
frame[6] = (msg_len >> 24) & 0xFF;
frame[7] = (msg_len >> 16) & 0xFF;
frame[8] = (msg_len >> 8) & 0xFF;
frame[9] = msg_len & 0xFF;
header_len = 10;
}
// Add mask key
char mask_key[4];
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[header_len + i] = mask_key[i];
}
header_len += 4;
// Mask payload
for (int i = 0; i < msg_len; i++) {
frame[header_len + i] = data[i] ^ mask_key[i % 4];
}
int frame_len = header_len + msg_len;
return SSL_write(ssl, frame, frame_len) > 0;
}
int parse_websocket_frame(const char *buffer, int bytes_read, char **payload, int *payload_len) {
if (bytes_read < 2 || ((buffer[0] & 0x8F) != 0x81 && (buffer[0] & 0x8F) != 0x82)) { // Not a text or binary frame
return 0;
}
int masked = buffer[1] & 0x80;
int len_indicator = buffer[1] & 0x7F;
int header_len = 2;
if (len_indicator <= 125) {
*payload_len = len_indicator;
} else if (len_indicator == 126) {
if (bytes_read < 4) return 0;
*payload_len = (buffer[2] << 8) | buffer[3];
header_len = 4;
} else if (len_indicator == 127) {
if (bytes_read < 10) return 0;
*payload_len = (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | buffer[9];
header_len = 10;
}
if (masked) {
header_len += 4;
}
if (send(sock, message, strlen(message), 0) < 0) { if (bytes_read < header_len + *payload_len) {
perror("Send failed");
return 0; return 0;
} }
*payload = (char *)buffer + header_len;
if (masked) {
char *mask_key = (char *)buffer + header_len - 4;
for (int i = 0; i < *payload_len; i++) {
(*payload)[i] ^= mask_key[i % 4];
}
}
return 1; return 1;
} }
void *forward_tcp_to_ws(void *arg) {
thread_args_t *args = (thread_args_t *)arg;
SSL *ssl = args->ssl;
int debug = args->debug;
char buffer[BUFFER_SIZE];
int bytes_read;
while (1) {
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || !active_tunnel->active) {
pthread_mutex_unlock(&tunnel_mutex);
break;
}
int sock = active_tunnel->local_sock;
char request_id[37];
strcpy(request_id, active_tunnel->request_id);
pthread_mutex_unlock(&tunnel_mutex);
bytes_read = recv(sock, buffer, sizeof(buffer), 0);
if (bytes_read <= 0) {
if (debug) {
printf("[DEBUG] TCP connection closed or error\n");
fflush(stdout);
}
break;
}
if (debug) {
printf("[DEBUG] Forwarding %d bytes from TCP to WebSocket\n", bytes_read);
fflush(stdout);
}
// Convert to hex
char hex_data[bytes_read * 2 + 1];
for (int i = 0; i < bytes_read; i++) {
sprintf(hex_data + i * 2, "%02x", (unsigned char)buffer[i]);
}
hex_data[bytes_read * 2] = '\0';
// Send as tunnel_data
char message[BUFFER_SIZE];
snprintf(message, sizeof(message),
"{\"type\":\"tunnel_data\",\"request_id\":\"%s\",\"data\":\"%s\"}",
request_id, hex_data);
if (!send_websocket_frame(ssl, message)) {
if (debug) {
printf("[DEBUG] Failed to send WebSocket frame\n");
fflush(stdout);
}
break;
}
}
if (debug) {
printf("[DEBUG] TCP to WebSocket forwarding thread exiting\n");
fflush(stdout);
}
free(args);
return NULL;
}
void handle_tunnel_data(SSL *ssl __attribute__((unused)), const char *request_id, const char *data_hex, int debug __attribute__((unused))) {
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || strcmp(active_tunnel->request_id, request_id) != 0) {
pthread_mutex_unlock(&tunnel_mutex);
return;
}
int sock = active_tunnel->local_sock;
pthread_mutex_unlock(&tunnel_mutex);
// Decode hex data
size_t data_len = strlen(data_hex) / 2;
char *data = malloc(data_len);
if (!data) return;
for (size_t i = 0; i < data_len; i++) {
sscanf(data_hex + i * 2, "%2hhx", &data[i]);
}
// Send to local socket
send(sock, data, data_len, 0);
free(data);
}
void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_id, int debug) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && strcmp(active_tunnel->request_id, request_id) == 0) {
active_tunnel->active = 0;
close(active_tunnel->local_sock);
free(active_tunnel);
active_tunnel = NULL;
if (debug) {
printf("[DEBUG] Tunnel %s closed\n", request_id);
}
}
pthread_mutex_unlock(&tunnel_mutex);
}
int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id, int local_port, int debug) { int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id, int local_port, int debug) {
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
struct hostent *he; struct hostent *he;
...@@ -210,6 +529,10 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -210,6 +529,10 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int bytes_read; int bytes_read;
// Generate request ID
char request_id[37];
generate_request_id(request_id, sizeof(request_id));
// Resolve hostname // Resolve hostname
if ((he = gethostbyname(wssshd_host)) == NULL) { if ((he = gethostbyname(wssshd_host)) == NULL) {
herror("gethostbyname"); herror("gethostbyname");
...@@ -252,24 +575,42 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -252,24 +575,42 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
SSL_set_fd(ssl, sock); SSL_set_fd(ssl, sock);
if (debug) {
printf("[DEBUG] Establishing SSL connection...\n");
fflush(stdout);
}
if (SSL_connect(ssl) <= 0) { if (SSL_connect(ssl) <= 0) {
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
fprintf(stderr, "SSL connection failed\n");
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return 0;
} }
if (debug) {
printf("[DEBUG] SSL connection established\n");
fflush(stdout);
}
// Perform WebSocket handshake // Perform WebSocket handshake
if (!websocket_handshake(sock, wssshd_host, wssshd_port, "/")) { if (debug) {
printf("[DEBUG] Performing WebSocket handshake to %s:%d\n", wssshd_host, wssshd_port);
fflush(stdout);
}
if (!websocket_handshake(ssl, wssshd_host, wssshd_port, "/")) {
fprintf(stderr, "WebSocket handshake failed\n");
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return 0;
} }
if (debug) {
printf("[DEBUG] WebSocket handshake successful\n");
fflush(stdout);
}
// Send tunnel request // Send tunnel request
if (!send_json_message(sock, "tunnel_request", client_id)) { if (!send_json_message(ssl, "tunnel_request", client_id, request_id)) {
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
...@@ -277,11 +618,11 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -277,11 +618,11 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
} }
if (debug) { if (debug) {
printf("[DEBUG] Tunnel request sent for client: %s\n", client_id); printf("[DEBUG] Tunnel request sent for client: %s, request_id: %s\n", client_id, request_id);
} }
// Read acknowledgment // Read acknowledgment
bytes_read = SSL_read(ssl, buffer, sizeof(buffer) - 1); bytes_read = SSL_read(ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) { if (bytes_read <= 0) {
if (debug) { if (debug) {
printf("[DEBUG] No acknowledgment received\n"); printf("[DEBUG] No acknowledgment received\n");
...@@ -292,15 +633,79 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -292,15 +633,79 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
return 0; return 0;
} }
buffer[bytes_read] = '\0'; if (debug) {
printf("[DEBUG] Read %d bytes acknowledgment\n", bytes_read);
printf("[DEBUG] Raw acknowledgment: ");
for (int i = 0; i < bytes_read && i < 50; i++) {
if (buffer[i] >= 32 && buffer[i] < 127) {
printf("%c", buffer[i]);
} else {
printf("\\x%02x", (unsigned char)buffer[i]);
}
}
printf("\n");
fflush(stdout);
}
// Check if it's a close frame
if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x88) {
if (debug) {
printf("[DEBUG] Server sent close frame\n");
if (bytes_read >= 6) {
int close_code = (buffer[2] << 8) | buffer[3];
printf("[DEBUG] Close code: %d\n", close_code);
if (bytes_read > 6) {
printf("[DEBUG] Close reason: %.*s\n", bytes_read - 6, buffer + 6);
}
}
fflush(stdout);
}
fprintf(stderr, "Tunnel request rejected by server\n");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Parse WebSocket frame
char *payload;
int payload_len;
if (!parse_websocket_frame(buffer, bytes_read, &payload, &payload_len)) {
fprintf(stderr, "Failed to parse WebSocket frame\n");
if (debug) {
printf("[DEBUG] Frame parsing failed. First few bytes: ");
for (int i = 0; i < bytes_read && i < 10; i++) {
printf("%02x ", (unsigned char)buffer[i]);
}
printf("\n");
fflush(stdout);
}
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Null terminate payload
payload[payload_len] = '\0';
if (debug) { if (debug) {
printf("[DEBUG] Received: %s\n", buffer); printf("[DEBUG] Received payload: '%s' (length: %d)\n", payload, payload_len);
printf("[DEBUG] Looking for tunnel_ack in: ");
for (int i = 0; i < payload_len && i < 50; i++) {
if (payload[i] >= 32 && payload[i] < 127) {
printf("%c", payload[i]);
} else {
printf("\\x%02x", (unsigned char)payload[i]);
}
}
printf("\n");
fflush(stdout);
} }
// Check for tunnel acknowledgment // Check for tunnel acknowledgment
if (strstr(buffer, "\"type\":\"tunnel_ack\"") == NULL) { if (strstr(payload, "tunnel_ack") == NULL) {
fprintf(stderr, "Tunnel request denied or failed\n"); fprintf(stderr, "Tunnel request denied or failed: %s\n", payload);
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
...@@ -311,16 +716,68 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id ...@@ -311,16 +716,68 @@ int setup_tunnel(const char *wssshd_host, int wssshd_port, const char *client_id
printf("[DEBUG] Tunnel established, local port: %d\n", local_port); printf("[DEBUG] Tunnel established, local port: %d\n", local_port);
} }
// Keep the connection alive for data transfer // Create tunnel structure
// In a real implementation, we'd need to handle bidirectional data transfer active_tunnel = malloc(sizeof(tunnel_t));
// For now, just keep the connection open if (!active_tunnel) {
perror("Memory allocation failed");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
// Cleanup (this would normally be handled differently) strcpy(active_tunnel->request_id, request_id);
SSL_free(ssl); active_tunnel->local_sock = -1;
SSL_CTX_free(ssl_ctx); active_tunnel->active = 1;
close(sock); active_tunnel->ssl = ssl;
// Start listening on local port
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
perror("Local socket creation failed");
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
return 1; struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(local_port);
local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(listen_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
perror("Local bind failed");
close(listen_sock);
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
if (listen(listen_sock, 1) < 0) {
perror("Local listen failed");
close(listen_sock);
free(active_tunnel);
active_tunnel = NULL;
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return 0;
}
if (debug) {
printf("[DEBUG] Listening on localhost:%d\n", local_port);
fflush(stdout);
}
// Return success - tunnel is set up and listening
return listen_sock;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
...@@ -335,11 +792,7 @@ int main(int argc, char *argv[]) { ...@@ -335,11 +792,7 @@ int main(int argc, char *argv[]) {
return 0; return 0;
} }
if (!parse_args(argc, argv, &config)) { // Handle --help before parsing
return 1;
}
// Handle --help
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
print_usage(argv[0]); print_usage(argv[0]);
...@@ -347,24 +800,44 @@ int main(int argc, char *argv[]) { ...@@ -347,24 +800,44 @@ int main(int argc, char *argv[]) {
} }
} }
// Need at least one argument (the target) pthread_mutex_init(&tunnel_mutex, NULL);
if (optind >= argc) {
// Parse wsssh arguments
int remaining_argc;
char **remaining_argv;
if (!parse_args(argc, argv, &config, &remaining_argc, &remaining_argv)) {
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
// Need at least one SSH argument (the target)
if (remaining_argc == 0) {
fprintf(stderr, "Error: No target specified\n"); fprintf(stderr, "Error: No target specified\n");
print_usage(argv[0]); print_usage(argv[0]);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
// Parse SSH arguments to extract host and port
char *ssh_host = NULL;
int ssh_port = DEFAULT_PORT;
if (!parse_ssh_args(remaining_argc, remaining_argv, &ssh_host, &ssh_port, config.debug)) {
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
char *target = argv[optind]; // Parse the SSH host to extract client_id and wssshd_host
char *client_id = NULL; char *client_id = NULL;
char *wssshd_host = NULL; char *wssshd_host = NULL;
int wssshd_port = DEFAULT_PORT; int wssshd_port = ssh_port; // The -p port becomes the WebSocket server port
if (!parse_hostname(target, &client_id, &wssshd_host, &wssshd_port)) { if (!parse_hostname(ssh_host, &client_id, &wssshd_host)) {
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
if (config.debug) { if (config.debug) {
printf("[DEBUG] Target: %s\n", target); printf("[DEBUG] SSH Host: %s\n", ssh_host);
printf("[DEBUG] Client ID: %s\n", client_id); printf("[DEBUG] Client ID: %s\n", client_id);
printf("[DEBUG] WSSSHD Host: %s\n", wssshd_host); printf("[DEBUG] WSSSHD Host: %s\n", wssshd_host);
printf("[DEBUG] WSSSHD Port: %d\n", wssshd_port); printf("[DEBUG] WSSSHD Port: %d\n", wssshd_port);
...@@ -376,6 +849,8 @@ int main(int argc, char *argv[]) { ...@@ -376,6 +849,8 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Error: Could not find available local port\n"); fprintf(stderr, "Error: Could not find available local port\n");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
...@@ -383,56 +858,295 @@ int main(int argc, char *argv[]) { ...@@ -383,56 +858,295 @@ int main(int argc, char *argv[]) {
printf("[DEBUG] Using local port: %d\n", local_port); printf("[DEBUG] Using local port: %d\n", local_port);
} }
// Setup tunnel // Modify SSH arguments
if (!setup_tunnel(wssshd_host, wssshd_port, client_id, local_port, config.debug)) { int new_ssh_argc;
fprintf(stderr, "Error: Failed to establish tunnel\n"); char **new_ssh_args = modify_ssh_args(remaining_argc, remaining_argv, ssh_host, local_port, &new_ssh_argc);
if (!new_ssh_args) {
fprintf(stderr, "Error: Failed to modify SSH arguments\n");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
// Prepare SSH arguments if (config.debug) {
char port_str[16]; printf("[DEBUG] Modified SSH command:");
snprintf(port_str, sizeof(port_str), "%d", local_port); for (int i = 0; new_ssh_args[i]; i++) {
printf(" %s", new_ssh_args[i]);
}
printf("\n");
fflush(stdout);
}
// Build SSH command // Parent process: setup tunnel FIRST
char **ssh_args = malloc((argc - optind + 4) * sizeof(char *)); if (config.debug) {
if (!ssh_args) { printf("[DEBUG] Parent process setting up tunnel...\n");
perror("Memory allocation failed"); fflush(stdout);
}
int listen_sock = setup_tunnel(wssshd_host, wssshd_port, client_id, local_port, config.debug);
if (listen_sock < 0) {
fprintf(stderr, "Error: Failed to establish tunnel\n");
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_ssh_argc; i++) {
if (new_ssh_args[i] != remaining_argv[i-1]) {
free(new_ssh_args[i]);
}
}
free(new_ssh_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
int arg_count = 0; // NOW fork to run SSH in background (after tunnel is ready)
ssh_args[arg_count++] = "ssh"; if (config.debug) {
ssh_args[arg_count++] = "-p"; printf("[DEBUG] About to fork SSH process...\n");
ssh_args[arg_count++] = port_str; fflush(stdout);
ssh_args[arg_count++] = "localhost"; }
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_ssh_argc; i++) {
if (new_ssh_args[i] != remaining_argv[i-1]) {
free(new_ssh_args[i]);
}
}
free(new_ssh_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
// Add remaining arguments if (pid == 0) {
for (int i = optind + 1; i < argc; i++) { // Child process: run SSH
ssh_args[arg_count++] = argv[i]; if (config.debug) {
printf("[DEBUG] Child process starting SSH...\n");
fflush(stdout);
}
execvp("ssh", new_ssh_args);
// If we reach here, execvp failed
perror("execvp failed");
free(client_id);
free(wssshd_host);
for (int i = 1; i < new_ssh_argc; i++) {
if (new_ssh_args[i] != remaining_argv[i-1]) {
free(new_ssh_args[i]);
}
}
free(new_ssh_args);
exit(1);
} }
ssh_args[arg_count] = NULL;
// Parent process: accept SSH connection and start forwarding
if (config.debug) { if (config.debug) {
printf("[DEBUG] SSH command:"); printf("[DEBUG] Waiting for SSH connection on localhost:%d...\n", local_port);
for (int i = 0; ssh_args[i]; i++) { fflush(stdout);
printf(" %s", ssh_args[i]); }
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
active_tunnel->local_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (active_tunnel->local_sock < 0) {
perror("Local accept failed");
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
close(listen_sock);
free(active_tunnel);
active_tunnel = NULL;
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_ssh_argc; i++) {
if (new_ssh_args[i] != remaining_argv[i-1]) {
free(new_ssh_args[i]);
}
} }
printf("\n"); free(new_ssh_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
} }
// Execute SSH close(listen_sock); // No longer needed
execvp("ssh", ssh_args);
if (config.debug) {
printf("[DEBUG] Local SSH connection accepted! Starting data forwarding...\n");
fflush(stdout);
}
// Start forwarding thread
thread_args_t *thread_args = malloc(sizeof(thread_args_t));
if (!thread_args) {
perror("Memory allocation failed for thread args");
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
close(active_tunnel->local_sock);
free(active_tunnel);
active_tunnel = NULL;
free(client_id);
free(wssshd_host);
free(config.local_port);
for (int i = 1; i < new_ssh_argc; i++) {
if (new_ssh_args[i] != remaining_argv[i-1]) {
free(new_ssh_args[i]);
}
}
free(new_ssh_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1;
}
thread_args->ssl = active_tunnel->ssl; // Need to store SSL in tunnel struct
thread_args->debug = config.debug;
pthread_t thread;
pthread_create(&thread, NULL, forward_tcp_to_ws, thread_args);
pthread_detach(thread);
// Main tunnel loop - handle WebSocket messages
char buffer[BUFFER_SIZE];
int bytes_read;
while (active_tunnel && active_tunnel->active) {
bytes_read = SSL_read(active_tunnel->ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) {
if (config.debug) {
printf("[DEBUG] WebSocket connection closed\n");
fflush(stdout);
}
break;
}
// Check if it's a close frame
if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x88) {
if (config.debug) {
printf("[DEBUG] Received close frame from server\n");
fflush(stdout);
}
break;
}
char *payload;
int payload_len;
if (!parse_websocket_frame(buffer, bytes_read, &payload, &payload_len)) {
if (config.debug) {
printf("[DEBUG] Failed to parse WebSocket frame\n");
fflush(stdout);
}
continue;
}
payload[payload_len] = '\0';
if (config.debug) {
printf("[DEBUG] Received: %s\n", payload);
fflush(stdout);
}
// Handle messages
if (strstr(payload, "tunnel_data")) {
if (config.debug) {
printf("[DEBUG] Received tunnel_data message\n");
fflush(stdout);
}
// Extract request_id and data
char *id_start = strstr(payload, "\"request_id\"");
char *data_start = strstr(payload, "\"data\"");
if (id_start && data_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
char *data_colon = strchr(data_start, ':');
if (data_colon) {
char *data_quote = strchr(data_colon, '"');
if (data_quote) {
data_start = data_quote + 1;
char *data_end = strchr(data_start, '"');
if (data_end) {
*data_end = '\0';
handle_tunnel_data(active_tunnel->ssl, id_start, data_start, config.debug);
}
}
}
}
}
}
}
} else if (strstr(payload, "tunnel_close")) {
if (config.debug) {
printf("[DEBUG] Received tunnel_close message\n");
fflush(stdout);
}
char *id_start = strstr(payload, "\"request_id\"");
if (id_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
handle_tunnel_close(active_tunnel->ssl, id_start, config.debug);
}
}
}
}
} else {
if (config.debug) {
printf("[DEBUG] Received unknown message type: %s\n", payload);
fflush(stdout);
}
}
}
if (config.debug) {
printf("[DEBUG] Tunnel loop ended, waiting for SSH to finish...\n");
fflush(stdout);
}
// Wait for SSH to finish
int status;
waitpid(pid, &status, 0);
if (config.debug) {
printf("[DEBUG] SSH process finished with status: %d\n", status);
fflush(stdout);
}
// Cleanup
if (active_tunnel) {
close(active_tunnel->local_sock);
SSL_free(active_tunnel->ssl);
free(active_tunnel);
active_tunnel = NULL;
}
// If we reach here, execvp failed
perror("execvp failed");
free(ssh_args);
free(client_id); free(client_id);
free(wssshd_host); free(wssshd_host);
free(config.local_port); free(config.local_port);
// Free allocated strings in new_ssh_args
for (int i = 0; i < new_ssh_argc; i++) {
// Free strings that were allocated with malloc/strdup
if (i == 2) { // The port string
free(new_ssh_args[i]);
} else if (i == 3) { // The user@localhost string (if allocated)
// Check if this was allocated (contains @localhost)
if (strstr(new_ssh_args[i], "@localhost")) {
free(new_ssh_args[i]);
}
}
}
free(new_ssh_args);
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 0;
} }
\ No newline at end of file
...@@ -9,10 +9,19 @@ ...@@ -9,10 +9,19 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <getopt.h> #include <getopt.h>
#include <pthread.h>
#define BUFFER_SIZE 4096 #define BUFFER_SIZE 65536
#define DEFAULT_PORT 9898 #define DEFAULT_PORT 9898
int global_debug = 0;
pthread_mutex_t tunnel_mutex;
typedef struct {
int sock; // Local SSH connection socket
char request_id[64];
} tunnel_t;
typedef struct { typedef struct {
char *server_ip; char *server_ip;
int port; int port;
...@@ -22,6 +31,255 @@ typedef struct { ...@@ -22,6 +31,255 @@ typedef struct {
int debug; int debug;
} wssshc_config_t; } wssshc_config_t;
tunnel_t *active_tunnel = NULL;
void *tunnel_thread(void *arg);
void handle_tunnel_request(SSL *ssl, const char *request_id, int debug) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel) {
close(active_tunnel->sock);
free(active_tunnel);
}
active_tunnel = malloc(sizeof(tunnel_t));
if (!active_tunnel) {
perror("Memory allocation failed");
pthread_mutex_unlock(&tunnel_mutex);
return;
}
active_tunnel->sock = -1; // Not connected yet
strcpy(active_tunnel->request_id, request_id);
pthread_mutex_unlock(&tunnel_mutex);
if (debug) {
printf("[DEBUG] Tunnel %s prepared (not connected yet)\n", request_id);
}
// Send tunnel_ack back to server
char ack_msg[256];
snprintf(ack_msg, sizeof(ack_msg), "{\"type\":\"tunnel_ack\",\"request_id\":\"%s\"}", request_id);
if (debug) {
printf("[DEBUG] Sending tunnel_ack: %s\n", ack_msg);
}
char frame[512];
frame[0] = 0x81; // FIN + text opcode
int msg_len = strlen(ack_msg);
frame[1] = 0x80 | msg_len; // MASK + length
char mask_key[4];
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[2 + i] = mask_key[i];
}
for (int i = 0; i < msg_len; i++) {
frame[6 + i] = ack_msg[i] ^ mask_key[i % 4];
}
int frame_len = 6 + msg_len;
SSL_write(ssl, frame, frame_len);
}
void handle_tunnel_data(SSL *ssl, const char *request_id, const char *data_hex, int debug) {
printf("handle_tunnel_data called with id: %s, data len: %zu\n", request_id, strlen(data_hex));
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel || strcmp(active_tunnel->request_id, request_id) != 0) {
printf("No active tunnel or id mismatch\n");
pthread_mutex_unlock(&tunnel_mutex);
return;
}
int sock_fd = active_tunnel->sock;
pthread_mutex_unlock(&tunnel_mutex);
// Open local SSH connection if not already open
if (sock_fd == -1) {
struct sockaddr_in addr;
int sock;
if (debug) {
printf("[DEBUG] Opening local SSH connection for tunnel %s\n", request_id);
}
// Create socket for local SSH connection
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Local socket creation failed");
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(22);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
// Connect to local SSH
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Local SSH connection failed");
close(sock);
return;
}
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && strcmp(active_tunnel->request_id, request_id) == 0) {
active_tunnel->sock = sock;
sock_fd = sock;
// Start tunnel thread
pthread_t thread;
pthread_create(&thread, NULL, tunnel_thread, ssl);
pthread_detach(thread);
if (debug) {
printf("[DEBUG] Local SSH connection established and tunnel thread started\n");
}
} else {
close(sock);
}
pthread_mutex_unlock(&tunnel_mutex);
}
if (debug) {
printf("[DEBUG] Handling tunnel_data, hex len: %zu, hex data: %.100s...\n", strlen(data_hex), data_hex);
}
// Decode hex data
size_t data_len = strlen(data_hex) / 2;
char *data = malloc(data_len);
if (!data) return;
for (size_t i = 0; i < data_len; i++) {
sscanf(data_hex + i * 2, "%2hhx", &data[i]);
}
if (debug) {
printf("[DEBUG] Decoded data len: %zu, sending to local SSH: ", data_len);
for (size_t i = 0; i < data_len && i < 50; i++) {
if (data[i] >= 32 && data[i] < 127) {
printf("%c", data[i]);
} else {
printf("\\x%02x", (unsigned char)data[i]);
}
}
printf("\n");
}
// Send to local SSH
send(sock_fd, data, data_len, 0);
free(data);
}
void handle_tunnel_close(SSL *ssl __attribute__((unused)), const char *request_id, int debug) {
pthread_mutex_lock(&tunnel_mutex);
if (active_tunnel && strcmp(active_tunnel->request_id, request_id) == 0) {
close(active_tunnel->sock);
free(active_tunnel);
active_tunnel = NULL;
if (debug) {
printf("[DEBUG] Tunnel %s closed\n", request_id);
}
}
pthread_mutex_unlock(&tunnel_mutex);
}
void *tunnel_thread(void *arg) {
SSL *ssl = (SSL *)arg;
char buffer[BUFFER_SIZE];
int bytes_read;
while (1) {
pthread_mutex_lock(&tunnel_mutex);
if (!active_tunnel) {
pthread_mutex_unlock(&tunnel_mutex);
break;
}
int sock = active_tunnel->sock;
char request_id[64];
strcpy(request_id, active_tunnel->request_id);
pthread_mutex_unlock(&tunnel_mutex);
bytes_read = recv(sock, buffer, sizeof(buffer), 0);
if (bytes_read <= 0) {
break;
}
if (global_debug) {
printf("[DEBUG] Tunnel thread: read %d bytes from local SSH: ", bytes_read);
for (int i = 0; i < bytes_read && i < 50; i++) {
if (buffer[i] >= 32 && buffer[i] < 127) {
printf("%c", buffer[i]);
} else {
printf("\\x%02x", (unsigned char)buffer[i]);
}
}
printf("\n");
}
// Send as tunnel_response
char hex_data[bytes_read * 2 + 1];
for (int i = 0; i < bytes_read; i++) {
sprintf(hex_data + i * 2, "%02x", (unsigned char)buffer[i]);
}
hex_data[bytes_read * 2] = '\0';
if (global_debug) {
printf("[DEBUG] Sending tunnel_response, hex len: %zu, hex: %.100s...\n", strlen(hex_data), hex_data);
}
char response[4096];
snprintf(response, sizeof(response),
"{\"type\":\"tunnel_response\",\"request_id\":\"%s\",\"data\":\"%s\"}",
request_id, hex_data);
if (global_debug) {
printf("[DEBUG] Sending tunnel_response, len: %zu\n", strlen(response));
}
// Send as WebSocket frame with proper length encoding
char frame[4096];
frame[0] = 0x81; // FIN + text opcode
int msg_len = strlen(response);
int header_len = 2;
char mask_key[4];
if (msg_len <= 125) {
frame[1] = 0x80 | msg_len; // MASK + length
} else if (msg_len <= 65535) {
frame[1] = 0x80 | 126; // MASK + extended length
frame[2] = (msg_len >> 8) & 0xFF;
frame[3] = msg_len & 0xFF;
header_len = 4;
} else {
frame[1] = 0x80 | 127; // MASK + extended length
// For simplicity, assume length fits in 32 bits
frame[2] = 0;
frame[3] = 0;
frame[4] = 0;
frame[5] = 0;
frame[6] = (msg_len >> 24) & 0xFF;
frame[7] = (msg_len >> 16) & 0xFF;
frame[8] = (msg_len >> 8) & 0xFF;
frame[9] = msg_len & 0xFF;
header_len = 10;
}
// Add mask key
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[header_len + i] = mask_key[i];
}
header_len += 4;
// Mask payload
for (int i = 0; i < msg_len; i++) {
frame[header_len + i] = response[i] ^ mask_key[i % 4];
}
int frame_len = header_len + msg_len;
SSL_write(ssl, frame, frame_len);
}
return NULL;
}
void print_usage(const char *program_name) { void print_usage(const char *program_name) {
fprintf(stderr, "Usage: %s [options]\n", program_name); fprintf(stderr, "Usage: %s [options]\n", program_name);
fprintf(stderr, "WebSocket SSH Client - Register with wssshd server\n\n"); fprintf(stderr, "WebSocket SSH Client - Register with wssshd server\n\n");
...@@ -91,6 +349,9 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) { ...@@ -91,6 +349,9 @@ int parse_args(int argc, char *argv[], wssshc_config_t *config) {
if (!config->server_ip || !config->client_id || !config->password) { if (!config->server_ip || !config->client_id || !config->password) {
fprintf(stderr, "Error: --server-ip, --id, and --password are required\n"); fprintf(stderr, "Error: --server-ip, --id, and --password are required\n");
print_usage(argv[0]); print_usage(argv[0]);
if (config->server_ip) free(config->server_ip);
if (config->client_id) free(config->client_id);
if (config->password) free(config->password);
return 0; return 0;
} }
...@@ -116,7 +377,7 @@ SSL_CTX* init_ssl_ctx() { ...@@ -116,7 +377,7 @@ SSL_CTX* init_ssl_ctx() {
return ctx; return ctx;
} }
int websocket_handshake(int sock, const char *host, int port, const char *path) { int websocket_handshake(SSL *ssl, const char *host, int port, const char *path) {
char request[1024]; char request[1024];
char response[BUFFER_SIZE]; char response[BUFFER_SIZE];
int bytes_read; int bytes_read;
...@@ -132,15 +393,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -132,15 +393,17 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
"\r\n", "\r\n",
path, host, port); path, host, port);
if (send(sock, request, strlen(request), 0) < 0) { if (SSL_write(ssl, request, strlen(request)) <= 0) {
perror("WebSocket handshake send failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake send failed\n");
return 0; return 0;
} }
// Read response // Read response
bytes_read = recv(sock, response, sizeof(response) - 1, 0); bytes_read = SSL_read(ssl, response, sizeof(response) - 1);
if (bytes_read <= 0) { if (bytes_read <= 0) {
perror("WebSocket handshake recv failed"); ERR_print_errors_fp(stderr);
fprintf(stderr, "WebSocket handshake recv failed\n");
return 0; return 0;
} }
...@@ -155,28 +418,70 @@ int websocket_handshake(int sock, const char *host, int port, const char *path) ...@@ -155,28 +418,70 @@ int websocket_handshake(int sock, const char *host, int port, const char *path)
return 1; return 1;
} }
int send_json_message(int sock, const char *type, const char *id, const char *password) { int send_json_message(SSL *ssl, const char *type, const char *id, const char *password) {
char message[1024]; char message[1024];
char frame[1024];
int message_len;
if (password) { if (password) {
snprintf(message, sizeof(message), message_len = snprintf(message, sizeof(message),
"{\"type\":\"%s\",\"id\":\"%s\",\"password\":\"%s\"}", "{\"type\":\"%s\",\"id\":\"%s\",\"password\":\"%s\"}",
type, id, password); type, id, password);
} else { } else {
snprintf(message, sizeof(message), message_len = snprintf(message, sizeof(message),
"{\"type\":\"%s\",\"id\":\"%s\"}", "{\"type\":\"%s\",\"id\":\"%s\"}",
type, id); type, id);
} }
if (send(sock, message, strlen(message), 0) < 0) { // Send as WebSocket frame with proper length encoding
perror("Send failed"); frame[0] = 0x81; // FIN + text opcode
int header_len = 2;
if (message_len <= 125) {
frame[1] = 0x80 | message_len; // MASK + length
} else if (message_len <= 65535) {
frame[1] = 0x80 | 126; // MASK + extended length
frame[2] = (message_len >> 8) & 0xFF;
frame[3] = message_len & 0xFF;
header_len = 4;
} else {
frame[1] = 0x80 | 127; // MASK + extended length
frame[2] = 0;
frame[3] = 0;
frame[4] = 0;
frame[5] = 0;
frame[6] = (message_len >> 24) & 0xFF;
frame[7] = (message_len >> 16) & 0xFF;
frame[8] = (message_len >> 8) & 0xFF;
frame[9] = message_len & 0xFF;
header_len = 10;
}
// Add mask key
char mask_key[4];
for (int i = 0; i < 4; i++) {
mask_key[i] = rand() % 256;
frame[header_len + i] = mask_key[i];
}
header_len += 4;
// Mask payload
for (int i = 0; i < message_len; i++) {
frame[header_len + i] = message[i] ^ mask_key[i % 4];
}
int frame_len = header_len + message_len;
if (SSL_write(ssl, frame, frame_len) <= 0) {
ERR_print_errors_fp(stderr);
fprintf(stderr, "Send failed\n");
return 0; return 0;
} }
return 1; return 1;
} }
int connect_to_server(const wssshc_config_t *config) { void connect_to_server(const wssshc_config_t *config) {
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
struct hostent *he; struct hostent *he;
int sock; int sock;
...@@ -188,13 +493,13 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -188,13 +493,13 @@ int connect_to_server(const wssshc_config_t *config) {
// Resolve hostname // Resolve hostname
if ((he = gethostbyname(config->server_ip)) == NULL) { if ((he = gethostbyname(config->server_ip)) == NULL) {
herror("gethostbyname"); herror("gethostbyname");
return 0; return;
} }
// Create socket // Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed"); perror("Socket creation failed");
return 0; return;
} }
memset(&server_addr, 0, sizeof(server_addr)); memset(&server_addr, 0, sizeof(server_addr));
...@@ -206,14 +511,14 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -206,14 +511,14 @@ int connect_to_server(const wssshc_config_t *config) {
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection failed"); perror("Connection failed");
close(sock); close(sock);
return 0; return;
} }
// Initialize SSL // Initialize SSL
ssl_ctx = init_ssl_ctx(); ssl_ctx = init_ssl_ctx();
if (!ssl_ctx) { if (!ssl_ctx) {
close(sock); close(sock);
return 0; return;
} }
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
...@@ -224,30 +529,118 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -224,30 +529,118 @@ int connect_to_server(const wssshc_config_t *config) {
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return;
} }
// Perform WebSocket handshake // Perform WebSocket handshake
if (!websocket_handshake(sock, config->server_ip, config->port, "/")) { if (!websocket_handshake(ssl, config->server_ip, config->port, "/")) {
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return;
} }
// Send registration message // Send registration message
if (!send_json_message(sock, "register", config->client_id, config->password)) { if (!send_json_message(ssl, "register", config->client_id, config->password)) {
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 0; return;
}
// Read WebSocket frame with registration response
bytes_read = SSL_read(ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) {
fprintf(stderr, "Failed to read registration response\n");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return;
}
// Parse WebSocket frame with extended length support
if (bytes_read >= 2 && ((buffer[0] & 0x8F) == 0x81 || (buffer[0] & 0x8F) == 0x82)) { // Text or binary frame
int masked = buffer[1] & 0x80;
int len_indicator = buffer[1] & 0x7F;
int header_len = 2;
int payload_len;
if (config->debug) {
printf("[DEBUG] Initial len_indicator = %d, masked = %d\n", len_indicator, masked);
fflush(stdout);
}
if (len_indicator <= 125) {
payload_len = len_indicator;
} else if (len_indicator == 126) {
if (bytes_read < 4) {
fprintf(stderr, "Incomplete extended length frame\n");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return;
}
payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3];
header_len = 4;
} else if (len_indicator == 127) {
if (bytes_read < 10) {
fprintf(stderr, "Incomplete extended length frame\n");
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return;
}
uint64_t len = 0;
for (int i = 0; i < 8; i++) {
len = (len << 8) | (unsigned char)buffer[2 + i];
}
payload_len = (int)len;
header_len = 10;
}
if (masked) {
header_len += 4;
}
if (bytes_read >= payload_len + header_len) {
char *payload = buffer + header_len;
if (masked) {
// Unmask the payload
char *mask_key = buffer + header_len - 4;
for (int i = 0; i < payload_len; i++) {
payload[i] ^= mask_key[i % 4];
}
}
// Copy payload to buffer start
memmove(buffer, payload, payload_len);
buffer[payload_len] = '\0';
} else {
fprintf(stderr, "WebSocket frame incomplete: need %d bytes, got %d\n", payload_len + header_len, bytes_read);
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return;
}
} else {
buffer[bytes_read] = '\0';
fprintf(stderr, "Unexpected frame: 0x%02x 0x%02x\n", buffer[0], buffer[1]);
}
if (strstr(buffer, "registered") == NULL) {
fprintf(stderr, "Registration failed: %s\n", buffer);
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
close(sock);
return;
} }
printf("Connected and registered as %s\n", config->client_id); printf("Connected and registered as %s\n", config->client_id);
// Keep connection alive and handle messages // Keep connection alive and handle tunnel requests
// active_tunnels = NULL; // Will implement tunnel handling
while (1) { while (1) {
bytes_read = SSL_read(ssl, buffer, sizeof(buffer) - 1); bytes_read = SSL_read(ssl, buffer, sizeof(buffer));
if (bytes_read <= 0) { if (bytes_read <= 0) {
if (config->debug) { if (config->debug) {
printf("[DEBUG] Connection lost\n"); printf("[DEBUG] Connection lost\n");
...@@ -255,22 +648,220 @@ int connect_to_server(const wssshc_config_t *config) { ...@@ -255,22 +648,220 @@ int connect_to_server(const wssshc_config_t *config) {
break; break;
} }
buffer[bytes_read] = '\0';
if (config->debug) { if (config->debug) {
printf("[DEBUG] Received: %s\n", buffer); printf("[DEBUG] Read %d bytes, frame: 0x%02x 0x%02x 0x%02x 0x%02x\n", bytes_read, buffer[0], buffer[1], buffer[2], buffer[3]);
} }
// Handle different message types if needed // Parse WebSocket frame with extended length support
// For now, just keep the connection alive if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x81) { // Text frame
int masked = buffer[1] & 0x80;
int len_indicator = buffer[1] & 0x7F;
int header_len = 2;
int payload_len;
if (len_indicator <= 125) {
payload_len = len_indicator;
} else if (len_indicator == 126) {
if (bytes_read < 4) continue;
payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3];
header_len = 4;
} else if (len_indicator == 127) {
if (bytes_read < 10) continue;
uint64_t len = 0;
for (int i = 0; i < 8; i++) {
len = (len << 8) | (unsigned char)buffer[2 + i];
}
payload_len = (int)len;
header_len = 10;
}
if (masked) {
header_len += 4;
}
if (payload_len >= 0 && (size_t)payload_len <= sizeof(buffer) - header_len && bytes_read >= payload_len + header_len) {
char *payload = buffer + header_len;
if (masked) {
char *mask_key = buffer + header_len - 4;
for (int i = 0; i < payload_len; i++) {
payload[i] ^= mask_key[i % 4];
}
}
memmove(buffer, payload, payload_len);
buffer[payload_len] = '\0';
// Handle message
if (config->debug) {
printf("[DEBUG] Received: %s\n", buffer);
}
// Parse JSON message
if (strstr(buffer, "tunnel_request")) {
// Extract request_id
char *id_start = strstr(buffer, "\"request_id\"");
if (id_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
handle_tunnel_request(ssl, id_start, config->debug);
}
}
}
}
} else if (strstr(buffer, "tunnel_data")) {
if (config->debug) {
printf("[DEBUG] Received tunnel_data\n");
}
// Extract request_id and data
char *id_start = strstr(buffer, "\"request_id\"");
char *data_start = strstr(buffer, "\"data\"");
if (id_start && data_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
char *data_colon = strchr(data_start, ':');
if (data_colon) {
char *data_quote = strchr(data_colon, '"');
if (data_quote) {
data_start = data_quote + 1;
char *data_end = strchr(data_start, '"');
if (data_end) {
*data_end = '\0';
handle_tunnel_data(ssl, id_start, data_start, config->debug);
}
}
}
}
}
}
}
} else if (strstr(buffer, "tunnel_close")) {
char *id_start = strstr(buffer, "\"request_id\"");
if (id_start) {
char *colon = strchr(id_start, ':');
if (colon) {
char *open_quote = strchr(colon, '"');
if (open_quote) {
id_start = open_quote + 1;
char *close_quote = strchr(id_start, '"');
if (close_quote) {
*close_quote = '\0';
handle_tunnel_close(ssl, id_start, config->debug);
}
}
}
}
}
} else {
if (config->debug) {
printf("[DEBUG] Incomplete frame: need %d bytes, got %d\n", payload_len + header_len, bytes_read);
}
}
} else if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x88) { // Close frame
if (config->debug) {
printf("[DEBUG] Received close frame\n");
}
return;
} else if (bytes_read >= 2 && (buffer[0] & 0x8F) == 0x89) { // Ping frame
if (config->debug) {
printf("[DEBUG] Received ping frame\n");
}
// Parse ping frame and send pong with echoed payload
int masked = buffer[1] & 0x80;
int len_indicator = buffer[1] & 0x7F;
int header_len = 2;
int payload_len;
if (len_indicator <= 125) {
payload_len = len_indicator;
} else if (len_indicator == 126) {
if (bytes_read < 4) continue;
payload_len = ((unsigned char)buffer[2] << 8) | (unsigned char)buffer[3];
header_len = 4;
} else if (len_indicator == 127) {
if (bytes_read < 10) continue;
uint64_t len = 0;
for (int i = 0; i < 8; i++) {
len = (len << 8) | (unsigned char)buffer[2 + i];
}
payload_len = (int)len;
header_len = 10;
}
if (masked) {
header_len += 4;
}
if (payload_len >= 0 && payload_len <= 1024 && bytes_read >= header_len + payload_len) {
char *payload = buffer + header_len;
if (masked) {
char *mask_key = buffer + header_len - 4;
for (int i = 0; i < payload_len; i++) {
payload[i] ^= mask_key[i % 4];
}
}
// Send pong frame with same payload, properly masked
char pong_frame[10 + payload_len]; // Max header + payload
pong_frame[0] = 0x8A; // Pong opcode
int pong_header_len = 2;
char pong_mask_key[4];
if (payload_len <= 125) {
pong_frame[1] = 0x80 | payload_len; // MASK + length
} else if (payload_len <= 65535) {
pong_frame[1] = 0x80 | 126; // MASK + extended length
pong_frame[2] = (payload_len >> 8) & 0xFF;
pong_frame[3] = payload_len & 0xFF;
pong_header_len = 4;
} else {
pong_frame[1] = 0x80 | 127; // MASK + extended length
pong_frame[2] = 0;
pong_frame[3] = 0;
pong_frame[4] = 0;
pong_frame[5] = 0;
pong_frame[6] = (payload_len >> 24) & 0xFF;
pong_frame[7] = (payload_len >> 16) & 0xFF;
pong_frame[8] = (payload_len >> 8) & 0xFF;
pong_frame[9] = payload_len & 0xFF;
pong_header_len = 10;
}
// Add mask key
for (int i = 0; i < 4; i++) {
pong_mask_key[i] = rand() % 256;
pong_frame[pong_header_len + i] = pong_mask_key[i];
}
pong_header_len += 4;
// Mask payload
for (int i = 0; i < payload_len; i++) {
pong_frame[pong_header_len + i] = payload[i] ^ pong_mask_key[i % 4];
}
int pong_frame_len = pong_header_len + payload_len;
if (config->debug) {
printf("[DEBUG] Sending pong frame, len: %d\n", pong_frame_len);
}
SSL_write(ssl, pong_frame, pong_frame_len);
}
}
} }
// Cleanup // Cleanup
SSL_free(ssl); SSL_free(ssl);
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
close(sock); close(sock);
return 1;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
...@@ -283,28 +874,27 @@ int main(int argc, char *argv[]) { ...@@ -283,28 +874,27 @@ int main(int argc, char *argv[]) {
.debug = 0 .debug = 0
}; };
pthread_mutex_init(&tunnel_mutex, NULL);
if (!parse_args(argc, argv, &config)) { if (!parse_args(argc, argv, &config)) {
pthread_mutex_destroy(&tunnel_mutex);
return 1; return 1;
} }
global_debug = config.debug;
printf("WebSocket SSH Client starting...\n"); printf("WebSocket SSH Client starting...\n");
while (1) { while (1) {
if (connect_to_server(&config)) { connect_to_server(&config);
printf("Connection successful\n"); printf("Connection lost, retrying in %d seconds...\n", config.interval);
// In a real implementation, we might want to keep the connection alive sleep(config.interval);
// For now, just exit on successful connection
break;
} else {
printf("Connection failed, retrying in %d seconds...\n", config.interval);
sleep(config.interval);
}
} }
// Cleanup // Cleanup
free(config.server_ip); free(config.server_ip);
free(config.client_id); free(config.client_id);
free(config.password); free(config.password);
pthread_mutex_destroy(&tunnel_mutex);
return 0; return 0;
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment