mirror of
https://github.com/m1ngsama/TNT.git
synced 2025-12-24 10:51:41 +00:00
Replace telnet with SSH and fix full-screen display
- Implement SSH server using libssh for secure connections - Replace insecure telnet with encrypted SSH protocol - Add automatic terminal size detection via PTY requests - Support dynamic window resize (SIGWINCH handling) - Fix UI display bug by using SSH channel instead of fd - Update tui_clear_screen to work with SSH connections - Add RSA host key auto-generation on first run - Update README with SSH instructions and security notes - Add libssh dependency to Makefile with auto-detection - Remove all telnet-related code Security improvements: - All traffic now encrypted - Host key authentication - No more plaintext transmission
This commit is contained in:
parent
82cfb5795b
commit
a4d67be103
6 changed files with 311 additions and 66 deletions
11
Makefile
11
Makefile
|
|
@ -3,9 +3,18 @@
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -O2 -std=c11 -D_XOPEN_SOURCE=700
|
CFLAGS = -Wall -Wextra -O2 -std=c11 -D_XOPEN_SOURCE=700
|
||||||
LDFLAGS = -pthread
|
LDFLAGS = -pthread -lssh
|
||||||
INCLUDES = -Iinclude
|
INCLUDES = -Iinclude
|
||||||
|
|
||||||
|
# Detect libssh location (homebrew on macOS)
|
||||||
|
ifeq ($(shell uname), Darwin)
|
||||||
|
LIBSSH_PREFIX := $(shell brew --prefix libssh 2>/dev/null)
|
||||||
|
ifneq ($(LIBSSH_PREFIX),)
|
||||||
|
INCLUDES += -I$(LIBSSH_PREFIX)/include
|
||||||
|
LDFLAGS += -L$(LIBSSH_PREFIX)/lib
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
SRC_DIR = src
|
SRC_DIR = src
|
||||||
INC_DIR = include
|
INC_DIR = include
|
||||||
OBJ_DIR = obj
|
OBJ_DIR = obj
|
||||||
|
|
|
||||||
40
README.md
40
README.md
|
|
@ -12,13 +12,30 @@
|
||||||
- 🕐 **Full timestamps** - Year-month-day hour:minute with timezone
|
- 🕐 **Full timestamps** - Year-month-day hour:minute with timezone
|
||||||
- 📖 **Bilingual help** - Press ? for Chinese/English help
|
- 📖 **Bilingual help** - Press ? for Chinese/English help
|
||||||
- 🌏 **UTF-8 support** - Full support for Chinese, Japanese, Korean
|
- 🌏 **UTF-8 support** - Full support for Chinese, Japanese, Korean
|
||||||
- 📦 **Single binary** - Lightweight ~50KB executable
|
- 📦 **Single binary** - Lightweight executable
|
||||||
- 🚀 **Telnet access** - No client installation needed
|
- 🔒 **SSH access** - Secure encrypted connections
|
||||||
|
- 🖥️ **Auto terminal detection** - Adapts to your terminal size
|
||||||
- 💾 **Message persistence** - All messages saved to log file
|
- 💾 **Message persistence** - All messages saved to log file
|
||||||
- ⚡ **Low resource usage** - Minimal memory and CPU
|
- ⚡ **Low resource usage** - Minimal memory and CPU
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
- libssh (required for SSH support)
|
||||||
|
|
||||||
|
**Install dependencies:**
|
||||||
|
```bash
|
||||||
|
# On macOS
|
||||||
|
brew install libssh
|
||||||
|
|
||||||
|
# On Ubuntu/Debian
|
||||||
|
sudo apt-get install libssh-dev
|
||||||
|
|
||||||
|
# On Fedora/RHEL
|
||||||
|
sudo dnf install libssh-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
**Build:**
|
||||||
```bash
|
```bash
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
@ -36,9 +53,11 @@ make debug
|
||||||
|
|
||||||
Connect from another terminal:
|
Connect from another terminal:
|
||||||
```bash
|
```bash
|
||||||
telnet localhost 2222
|
ssh -p 2222 localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The server will prompt for a password (any password is accepted) and then ask for your username.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Operating Modes
|
### Operating Modes
|
||||||
|
|
@ -78,10 +97,11 @@ telnet localhost 2222
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **Network**: Multi-threaded TCP server
|
- **Network**: Multi-threaded SSH server using libssh
|
||||||
- **TUI**: ANSI escape sequences
|
- **TUI**: ANSI escape sequences with automatic terminal size detection
|
||||||
- **Storage**: Append-only log file
|
- **Storage**: Append-only log file
|
||||||
- **Concurrency**: pthread + rwlock
|
- **Concurrency**: pthread + rwlock
|
||||||
|
- **Security**: Encrypted SSH connections with host key authentication
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
@ -98,6 +118,16 @@ PORT=3333 ./tnt
|
||||||
- Thread-safe operations
|
- Thread-safe operations
|
||||||
- Proper UTF-8 handling for CJK characters
|
- Proper UTF-8 handling for CJK characters
|
||||||
- Box-drawing characters for UI
|
- Box-drawing characters for UI
|
||||||
|
- SSH protocol with PTY support for terminal size detection
|
||||||
|
- Dynamic window resize handling
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- **Encrypted connections**: All traffic is encrypted via SSH
|
||||||
|
- **Host key authentication**: RSA host key generated on first run
|
||||||
|
- **Password authentication**: Currently accepts any password (customize for production)
|
||||||
|
- **Host key persistence**: Stored in `host_key` file
|
||||||
|
- **No plaintext**: Unlike telnet, all data is encrypted in transit
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,14 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "chat_room.h"
|
#include "chat_room.h"
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/server.h>
|
||||||
|
|
||||||
/* Client connection structure */
|
/* Client connection structure */
|
||||||
typedef struct client {
|
typedef struct client {
|
||||||
int fd; /* Socket file descriptor */
|
int fd; /* Socket file descriptor (not used with SSH) */
|
||||||
|
ssh_session session; /* SSH session */
|
||||||
|
ssh_channel channel; /* SSH channel */
|
||||||
char username[MAX_USERNAME_LEN];
|
char username[MAX_USERNAME_LEN];
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ void tui_render_command_output(struct client *client);
|
||||||
void tui_render_input(struct client *client, const char *input);
|
void tui_render_input(struct client *client, const char *input);
|
||||||
|
|
||||||
/* Clear the screen */
|
/* Clear the screen */
|
||||||
void tui_clear_screen(int fd);
|
void tui_clear_screen(struct client *client);
|
||||||
|
|
||||||
/* Get help text based on language */
|
/* Get help text based on language */
|
||||||
const char* tui_get_help_text(help_lang_t lang);
|
const char* tui_get_help_text(help_lang_t lang);
|
||||||
|
|
|
||||||
301
src/ssh_server.c
301
src/ssh_server.c
|
|
@ -1,6 +1,9 @@
|
||||||
#include "ssh_server.h"
|
#include "ssh_server.h"
|
||||||
#include "tui.h"
|
#include "tui.h"
|
||||||
#include "utf8.h"
|
#include "utf8.h"
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/server.h>
|
||||||
|
#include <libssh/callbacks.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
@ -9,11 +12,59 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
/* Send data to client */
|
/* Global SSH bind instance */
|
||||||
|
static ssh_bind g_sshbind = NULL;
|
||||||
|
|
||||||
|
/* Generate or load SSH host key */
|
||||||
|
static int setup_host_key(ssh_bind sshbind) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
/* Check if host key exists */
|
||||||
|
if (stat(HOST_KEY_FILE, &st) == 0) {
|
||||||
|
/* Load existing key */
|
||||||
|
if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, HOST_KEY_FILE) < 0) {
|
||||||
|
fprintf(stderr, "Failed to load host key: %s\n", ssh_get_error(sshbind));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate new key */
|
||||||
|
printf("Generating new RSA host key...\n");
|
||||||
|
ssh_key key;
|
||||||
|
if (ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key) < 0) {
|
||||||
|
fprintf(stderr, "Failed to generate RSA key\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Export key to file */
|
||||||
|
if (ssh_pki_export_privkey_file(key, NULL, NULL, NULL, HOST_KEY_FILE) < 0) {
|
||||||
|
fprintf(stderr, "Failed to export host key\n");
|
||||||
|
ssh_key_free(key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_key_free(key);
|
||||||
|
|
||||||
|
/* Set restrictive permissions */
|
||||||
|
chmod(HOST_KEY_FILE, 0600);
|
||||||
|
|
||||||
|
/* Load the newly created key */
|
||||||
|
if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, HOST_KEY_FILE) < 0) {
|
||||||
|
fprintf(stderr, "Failed to load host key: %s\n", ssh_get_error(sshbind));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send data to client via SSH channel */
|
||||||
int client_send(client_t *client, const char *data, size_t len) {
|
int client_send(client_t *client, const char *data, size_t len) {
|
||||||
if (!client || !client->connected) return -1;
|
if (!client || !client->connected || !client->channel) return -1;
|
||||||
ssize_t sent = write(client->fd, data, len);
|
|
||||||
|
int sent = ssh_channel_write(client->channel, data, len);
|
||||||
return (sent < 0) ? -1 : 0;
|
return (sent < 0) ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,13 +82,13 @@ int client_printf(client_t *client, const char *fmt, ...) {
|
||||||
static int read_username(client_t *client) {
|
static int read_username(client_t *client) {
|
||||||
char username[MAX_USERNAME_LEN] = {0};
|
char username[MAX_USERNAME_LEN] = {0};
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
unsigned char buf[4];
|
char buf[4];
|
||||||
|
|
||||||
tui_clear_screen(client->fd);
|
tui_clear_screen(client);
|
||||||
client_printf(client, "请输入用户名: ");
|
client_printf(client, "请输入用户名: ");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ssize_t n = read(client->fd, buf, 1);
|
int n = ssh_channel_read(client->channel, buf, 1, 0);
|
||||||
if (n <= 0) return -1;
|
if (n <= 0) return -1;
|
||||||
|
|
||||||
unsigned char b = buf[0];
|
unsigned char b = buf[0];
|
||||||
|
|
@ -57,20 +108,20 @@ static int read_username(client_t *client) {
|
||||||
if (pos < MAX_USERNAME_LEN - 1) {
|
if (pos < MAX_USERNAME_LEN - 1) {
|
||||||
username[pos++] = b;
|
username[pos++] = b;
|
||||||
username[pos] = '\0';
|
username[pos] = '\0';
|
||||||
write(client->fd, &b, 1);
|
client_send(client, &b, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* UTF-8 multi-byte */
|
/* UTF-8 multi-byte */
|
||||||
int len = utf8_byte_length(b);
|
int len = utf8_byte_length(b);
|
||||||
buf[0] = b;
|
buf[0] = b;
|
||||||
if (len > 1) {
|
if (len > 1) {
|
||||||
read(client->fd, &buf[1], len - 1);
|
ssh_channel_read(client->channel, &buf[1], len - 1, 0);
|
||||||
}
|
}
|
||||||
if (pos + len < MAX_USERNAME_LEN - 1) {
|
if (pos + len < MAX_USERNAME_LEN - 1) {
|
||||||
memcpy(username + pos, buf, len);
|
memcpy(username + pos, buf, len);
|
||||||
pos += len;
|
pos += len;
|
||||||
username[pos] = '\0';
|
username[pos] = '\0';
|
||||||
write(client->fd, buf, len);
|
client_send(client, buf, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,11 +337,9 @@ static void handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
void* client_handle_session(void *arg) {
|
void* client_handle_session(void *arg) {
|
||||||
client_t *client = (client_t*)arg;
|
client_t *client = (client_t*)arg;
|
||||||
char input[MAX_MESSAGE_LEN] = {0};
|
char input[MAX_MESSAGE_LEN] = {0};
|
||||||
unsigned char buf[4];
|
char buf[4];
|
||||||
|
|
||||||
/* Get terminal size (assume 80x24 for telnet) */
|
/* Terminal size already set from PTY request */
|
||||||
client->width = 80;
|
|
||||||
client->height = 24;
|
|
||||||
client->mode = MODE_INSERT;
|
client->mode = MODE_INSERT;
|
||||||
client->help_lang = LANG_ZH;
|
client->help_lang = LANG_ZH;
|
||||||
client->connected = true;
|
client->connected = true;
|
||||||
|
|
@ -318,8 +367,8 @@ void* client_handle_session(void *arg) {
|
||||||
tui_render_screen(client);
|
tui_render_screen(client);
|
||||||
|
|
||||||
/* Main input loop */
|
/* Main input loop */
|
||||||
while (client->connected) {
|
while (client->connected && ssh_channel_is_open(client->channel)) {
|
||||||
ssize_t n = read(client->fd, buf, 1);
|
int n = ssh_channel_read(client->channel, buf, 1, 0);
|
||||||
if (n <= 0) break;
|
if (n <= 0) break;
|
||||||
|
|
||||||
unsigned char b = buf[0];
|
unsigned char b = buf[0];
|
||||||
|
|
@ -344,7 +393,7 @@ void* client_handle_session(void *arg) {
|
||||||
int char_len = utf8_byte_length(b);
|
int char_len = utf8_byte_length(b);
|
||||||
buf[0] = b;
|
buf[0] = b;
|
||||||
if (char_len > 1) {
|
if (char_len > 1) {
|
||||||
read(client->fd, &buf[1], char_len - 1);
|
ssh_channel_read(client->channel, &buf[1], char_len - 1, 0);
|
||||||
}
|
}
|
||||||
int len = strlen(input);
|
int len = strlen(input);
|
||||||
if (len + char_len < MAX_MESSAGE_LEN - 1) {
|
if (len + char_len < MAX_MESSAGE_LEN - 1) {
|
||||||
|
|
@ -380,73 +429,225 @@ cleanup:
|
||||||
room_broadcast(g_room, &leave_msg);
|
room_broadcast(g_room, &leave_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(client->fd);
|
if (client->channel) {
|
||||||
|
ssh_channel_close(client->channel);
|
||||||
|
ssh_channel_free(client->channel);
|
||||||
|
}
|
||||||
|
if (client->session) {
|
||||||
|
ssh_disconnect(client->session);
|
||||||
|
ssh_free(client->session);
|
||||||
|
}
|
||||||
free(client);
|
free(client);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize server socket */
|
/* Handle SSH authentication */
|
||||||
|
static int handle_auth(ssh_session session) {
|
||||||
|
ssh_message message;
|
||||||
|
|
||||||
|
do {
|
||||||
|
message = ssh_message_get(session);
|
||||||
|
if (!message) break;
|
||||||
|
|
||||||
|
if (ssh_message_type(message) == SSH_REQUEST_AUTH) {
|
||||||
|
if (ssh_message_subtype(message) == SSH_AUTH_METHOD_PASSWORD) {
|
||||||
|
/* Accept any password for simplicity */
|
||||||
|
/* In production, you'd want to verify against a user database */
|
||||||
|
ssh_message_auth_reply_success(message, 0);
|
||||||
|
ssh_message_free(message);
|
||||||
|
return 0;
|
||||||
|
} else if (ssh_message_subtype(message) == SSH_AUTH_METHOD_NONE) {
|
||||||
|
/* Deny and ask for password */
|
||||||
|
ssh_message_auth_set_methods(message, SSH_AUTH_METHOD_PASSWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_message_reply_default(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle SSH channel requests */
|
||||||
|
static ssh_channel handle_channel_open(ssh_session session) {
|
||||||
|
ssh_message message;
|
||||||
|
ssh_channel channel = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
message = ssh_message_get(session);
|
||||||
|
if (!message) break;
|
||||||
|
|
||||||
|
if (ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN &&
|
||||||
|
ssh_message_subtype(message) == SSH_CHANNEL_SESSION) {
|
||||||
|
channel = ssh_message_channel_request_open_reply_accept(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_message_reply_default(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle PTY request and get terminal size */
|
||||||
|
static int handle_pty_request(ssh_channel channel, client_t *client) {
|
||||||
|
ssh_message message;
|
||||||
|
|
||||||
|
do {
|
||||||
|
message = ssh_message_get(ssh_channel_get_session(channel));
|
||||||
|
if (!message) break;
|
||||||
|
|
||||||
|
if (ssh_message_type(message) == SSH_REQUEST_CHANNEL) {
|
||||||
|
if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_PTY) {
|
||||||
|
/* Get terminal dimensions from PTY request */
|
||||||
|
client->width = ssh_message_channel_request_pty_width(message);
|
||||||
|
client->height = ssh_message_channel_request_pty_height(message);
|
||||||
|
|
||||||
|
/* Default to 80x24 if invalid */
|
||||||
|
if (client->width <= 0 || client->width > 500) client->width = 80;
|
||||||
|
if (client->height <= 0 || client->height > 200) client->height = 24;
|
||||||
|
|
||||||
|
ssh_message_channel_request_reply_success(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
return 0;
|
||||||
|
} else if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) {
|
||||||
|
ssh_message_channel_request_reply_success(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
return 0;
|
||||||
|
} else if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) {
|
||||||
|
/* Handle terminal resize */
|
||||||
|
client->width = ssh_message_channel_request_pty_width(message);
|
||||||
|
client->height = ssh_message_channel_request_pty_height(message);
|
||||||
|
|
||||||
|
if (client->width <= 0 || client->width > 500) client->width = 80;
|
||||||
|
if (client->height <= 0 || client->height > 200) client->height = 24;
|
||||||
|
|
||||||
|
/* Re-render screen with new dimensions */
|
||||||
|
if (client->connected) {
|
||||||
|
tui_render_screen(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_message_free(message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_message_reply_default(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize SSH server */
|
||||||
int ssh_server_init(int port) {
|
int ssh_server_init(int port) {
|
||||||
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
g_sshbind = ssh_bind_new();
|
||||||
if (listen_fd < 0) {
|
if (!g_sshbind) {
|
||||||
perror("socket");
|
fprintf(stderr, "Failed to create SSH bind\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set socket options */
|
/* Set up host key */
|
||||||
int opt = 1;
|
if (setup_host_key(g_sshbind) < 0) {
|
||||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
ssh_bind_free(g_sshbind);
|
||||||
|
|
||||||
struct sockaddr_in addr = {0};
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
addr.sin_port = htons(port);
|
|
||||||
|
|
||||||
if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
|
||||||
perror("bind");
|
|
||||||
close(listen_fd);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(listen_fd, 10) < 0) {
|
/* Bind to port */
|
||||||
perror("listen");
|
ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_BINDPORT, &port);
|
||||||
close(listen_fd);
|
ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_BINDADDR, "0.0.0.0");
|
||||||
|
|
||||||
|
/* Set verbose level for debugging */
|
||||||
|
int verbosity = SSH_LOG_WARNING;
|
||||||
|
ssh_bind_options_set(g_sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||||
|
|
||||||
|
if (ssh_bind_listen(g_sshbind) < 0) {
|
||||||
|
fprintf(stderr, "Failed to bind to port %d: %s\n", port, ssh_get_error(g_sshbind));
|
||||||
|
ssh_bind_free(g_sshbind);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return listen_fd;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start server (blocking) */
|
/* Start SSH server (blocking) */
|
||||||
int ssh_server_start(int listen_fd) {
|
int ssh_server_start(int unused) {
|
||||||
printf("TNT chat server listening on port %d\n", DEFAULT_PORT);
|
(void)unused;
|
||||||
printf("Connect with: telnet localhost %d\n", DEFAULT_PORT);
|
|
||||||
|
printf("TNT chat server listening on port %d (SSH)\n", DEFAULT_PORT);
|
||||||
|
printf("Connect with: ssh -p %d localhost\n", DEFAULT_PORT);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct sockaddr_in client_addr;
|
ssh_session session = ssh_new();
|
||||||
socklen_t client_len = sizeof(client_addr);
|
if (!session) {
|
||||||
|
fprintf(stderr, "Failed to create SSH session\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
|
/* Accept connection */
|
||||||
if (client_fd < 0) {
|
if (ssh_bind_accept(g_sshbind, session) != SSH_OK) {
|
||||||
if (errno == EINTR) continue;
|
fprintf(stderr, "Error accepting connection: %s\n", ssh_get_error(g_sshbind));
|
||||||
perror("accept");
|
ssh_free(session);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform key exchange */
|
||||||
|
if (ssh_handle_key_exchange(session) != SSH_OK) {
|
||||||
|
fprintf(stderr, "Key exchange failed: %s\n", ssh_get_error(session));
|
||||||
|
ssh_disconnect(session);
|
||||||
|
ssh_free(session);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle authentication */
|
||||||
|
if (handle_auth(session) < 0) {
|
||||||
|
fprintf(stderr, "Authentication failed\n");
|
||||||
|
ssh_disconnect(session);
|
||||||
|
ssh_free(session);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open channel */
|
||||||
|
ssh_channel channel = handle_channel_open(session);
|
||||||
|
if (!channel) {
|
||||||
|
fprintf(stderr, "Failed to open channel\n");
|
||||||
|
ssh_disconnect(session);
|
||||||
|
ssh_free(session);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create client structure */
|
/* Create client structure */
|
||||||
client_t *client = calloc(1, sizeof(client_t));
|
client_t *client = calloc(1, sizeof(client_t));
|
||||||
if (!client) {
|
if (!client) {
|
||||||
close(client_fd);
|
ssh_channel_close(channel);
|
||||||
|
ssh_channel_free(channel);
|
||||||
|
ssh_disconnect(session);
|
||||||
|
ssh_free(session);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->fd = client_fd;
|
client->session = session;
|
||||||
|
client->channel = channel;
|
||||||
|
client->fd = -1; /* Not used with SSH */
|
||||||
|
|
||||||
|
/* Handle PTY request and get terminal size */
|
||||||
|
if (handle_pty_request(channel, client) < 0) {
|
||||||
|
/* Set defaults if PTY request fails */
|
||||||
|
client->width = 80;
|
||||||
|
client->height = 24;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create thread for client */
|
/* Create thread for client */
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
if (pthread_create(&thread, NULL, client_handle_session, client) != 0) {
|
if (pthread_create(&thread, NULL, client_handle_session, client) != 0) {
|
||||||
close(client_fd);
|
ssh_channel_close(channel);
|
||||||
|
ssh_channel_free(channel);
|
||||||
|
ssh_disconnect(session);
|
||||||
|
ssh_free(session);
|
||||||
free(client);
|
free(client);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Clear the screen */
|
/* Clear the screen */
|
||||||
void tui_clear_screen(int fd) {
|
void tui_clear_screen(client_t *client) {
|
||||||
|
if (!client || !client->connected) return;
|
||||||
const char *clear = ANSI_CLEAR ANSI_HOME;
|
const char *clear = ANSI_CLEAR ANSI_HOME;
|
||||||
write(fd, clear, strlen(clear));
|
client_send(client, clear, strlen(clear));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render the main screen */
|
/* Render the main screen */
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue