Last active
May 2, 2020 02:26
-
-
Save Low-power/94d2b33fc3350ef5945489b63a74651f to your computer and use it in GitHub Desktop.
Chatroom proxy for SSHOUT and ssh-chat revision 24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Chatroom relay between SSHOUT and ssh-chat | |
* Copyright 2015-2020 Rivoreo | |
* | |
* This program is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License as published by the | |
* Free Software Foundation, either version 3 of the License, or (at your | |
* option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
* more details. | |
*/ | |
/* Required files syncrw.c, syncrw.h and sshout/api.h can be retrieved from | |
* https://sourceforge.net/p/sshout/daemon-code/ci/master/tree/ | |
*/ | |
#define _GNU_SOURCE | |
#include <sys/select.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
#include "syncrw.h" | |
#include <signal.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <time.h> | |
#include <arpa/inet.h> | |
#include "sshout/api.h" | |
#define ntoh64(a) (ntohs(1) == 1 ? (a) : ((uint64_t)htonl((a) & 0xFFFFFFFF) << 32) | ntohl((a) >> 32)) | |
#ifndef MAX | |
#define MAX(A,B) ((A)>(B)?(A):(B)) | |
#endif | |
#define SSH_CHAT_DEFAULT_USER_NAME "relay" | |
#define SSHOUT_MAX_API_VERSION 1 | |
static char *sshout_server_name = NULL; | |
static uint16_t sshout_server_port = 22; | |
static char *ssh_chat_server_name = NULL; | |
static uint16_t ssh_chat_server_port = 22; | |
static int is_sshout_server_address_public = 0; | |
static int is_ssh_chat_server_address_public = 0; | |
static pid_t sshout_ssh_pid = -1; | |
static pid_t ssh_chat_ssh_pid = -1; | |
static void signal_handler(int sig) { | |
if(sig != SIGCHLD) return; | |
while(1) { | |
pid_t pid = waitpid(-1, NULL, WNOHANG); | |
if(pid == -1) { | |
if(errno == EINTR) continue; | |
if(errno == ECHILD) return; | |
perror("waitpid"); | |
return; | |
} | |
if(!pid) return; | |
if(pid == sshout_ssh_pid) sshout_ssh_pid = -1; | |
else if(pid == ssh_chat_ssh_pid) ssh_chat_ssh_pid = -1; | |
} | |
} | |
static void print_usage(const char *name) { | |
fprintf(stderr, "Usage: %s [<options-for-sshout>] <sshout-server-url> [<options-for-ssh-chat>] <ssh-chat-server-url>\n\n" | |
"Options:\n" | |
" -i <file> Use this private key file for public key authentication\n" | |
" -o <ssh-option> Set ssh option, see ssh_config(5) for details\n" | |
" -v Be verbose\n" | |
" -P Allow users from another server getting this server address\n\n" | |
"URL format:\n" | |
" ssh://[<user-name>@]<server-name>[:<port>]/\n" | |
" The default user name is 'sshout' for SSHOUT, '%s' for ssh-chat;\n" | |
" default port number is 22.\n\n", name, SSH_CHAT_DEFAULT_USER_NAME); | |
} | |
static void add_arg(char ***argv, char *a) { | |
unsigned int count = 1; | |
char **p = *argv; | |
if(p) while(*p++) count++; | |
p = realloc(*argv, sizeof(char *) * (count + 1)); | |
if(!p) { | |
perror("realloc"); | |
exit(1); | |
} | |
p[count - 1] = a; | |
p[count] = NULL; | |
*argv = p; | |
} | |
static int prase_ssh_url(char **host_name, uint16_t *port, char **user_name, const char *url) { | |
if(strncmp(url, "ssh:", 4)) { | |
fputs("URL scheme is not 'ssh'\n", stderr); | |
return -1; | |
} | |
const char *p = url + 4; | |
if(*p == '/' && *++p == '/' && *++p == '/') { | |
fputs("Too many '/'\n", stderr); | |
return -1; | |
} | |
const char *end = strchr(p, '/'); | |
if(end && end[1]) fputs("Ignoring extra path in URL\n", stderr); | |
const char *at = strchr(p, '@'); | |
if(at && (!end || at < end)) { | |
size_t len = at - p; | |
*user_name = malloc(len + 1); | |
if(!*user_name) { | |
perror("malloc"); | |
return -1; | |
} | |
memcpy(*user_name, p, len); | |
(*user_name)[len] = 0; | |
p = at + 1; | |
} | |
size_t host_name_len; | |
char *colon = strchr(p, ':'); | |
if(colon && (!end || colon < end)) { | |
//char *port_p = colon + 1; | |
//size_t len = end ? end - port_p : strlen(port_p); | |
char *port_end_p; | |
*port = strtoul(colon + 1, &port_end_p, 10); | |
if((end && port_end_p < end) || (!end && *port_end_p)) { | |
fputs("Invalid port number in URL", stderr); | |
return -1; | |
} | |
host_name_len = colon - p; | |
} else { | |
host_name_len = end ? end - p : strlen(p); | |
} | |
*host_name = malloc(host_name_len + 1); | |
if(!*host_name) { | |
perror("malloc"); | |
return -1; | |
} | |
memcpy(*host_name, p, host_name_len); | |
(*host_name)[host_name_len] = 0; | |
return 0; | |
} | |
static pid_t start_ssh_process(const char *host, uint16_t port, const char *user, const char *command, char **extra_argv, int *pipe_write_fd, int *pipe_read_fd) { | |
int pipe_fds_0[2]; | |
int pipe_fds_1[2]; | |
if(pipe(pipe_fds_0) < 0) { | |
perror("pipe"); | |
goto failed; | |
} | |
if(pipe(pipe_fds_1) < 0) { | |
perror("pipe"); | |
close(pipe_fds_0[0]); | |
close(pipe_fds_0[1]); | |
goto failed; | |
} | |
pid_t pid = fork(); | |
if(pid < 0) { | |
perror("fork"); | |
close(pipe_fds_0[0]); | |
close(pipe_fds_0[1]); | |
close(pipe_fds_1[0]); | |
close(pipe_fds_1[1]); | |
goto failed; | |
} | |
if(pid) { | |
close(pipe_fds_0[0]); | |
close(pipe_fds_1[1]); | |
*pipe_write_fd = pipe_fds_0[1]; | |
*pipe_read_fd = pipe_fds_1[0]; | |
return pid; | |
} else { | |
unsigned int count = 11; | |
if(extra_argv) { | |
char **v = extra_argv; | |
while(*v++) count++; | |
} | |
unsigned int count_without_command = count; | |
if(command) count += 2; | |
char port_number_s[6]; | |
snprintf(port_number_s, sizeof port_number_s, "%hu", (unsigned short int)port); | |
char *full_argv[count + 1]; | |
full_argv[0] = "ssh"; | |
full_argv[1] = "-o"; | |
full_argv[2] = "ServerAliveInterval=120"; | |
full_argv[3] = "-o"; | |
full_argv[4] = "PasswordAuthentication=no"; | |
full_argv[5] = (char *)host; | |
full_argv[6] = "-p"; | |
full_argv[7] = port_number_s; | |
full_argv[8] = "-l"; | |
full_argv[9] = (char *)user; | |
full_argv[10] = "-T"; | |
if(extra_argv && count_without_command > 11) memcpy(full_argv + 11, extra_argv, sizeof(char *) * (count_without_command - 11)); | |
if(command) { | |
full_argv[count - 2] = "--"; | |
full_argv[count - 1] = (char *)command; | |
} | |
full_argv[count] = NULL; | |
close(pipe_fds_0[1]); | |
close(pipe_fds_1[0]); | |
close(0); | |
close(1); | |
if(dup2(pipe_fds_0[0], 0) == -1) { | |
perror("dup2"); | |
_exit(1); | |
} | |
if(dup2(pipe_fds_1[1], 1) == -1) { | |
perror("dup2"); | |
_exit(1); | |
} | |
execvp("ssh", full_argv); | |
perror("ssh"); | |
_exit(1); | |
} | |
failed: | |
*pipe_write_fd = -1; | |
*pipe_read_fd = -1; | |
return -1; | |
} | |
static unsigned int sshout_api_version; | |
static char sshout_canonical_user_name[32]; | |
static unsigned int sshout_canonical_user_name_length; | |
#define GET_PACKET_EOF -1 | |
#define GET_PACKET_ERROR -2 | |
#define GET_PACKET_SHORT_READ -3 | |
#define GET_PACKET_TOO_SMALL -4 | |
#define GET_PACKET_TOO_LARGE -5 | |
#define GET_PACKET_OUT_OF_MEMORY -6 | |
#define GET_PACKET_INCOMPLETE -7 | |
static int sshout_get_api_packet(int fd, struct sshout_api_packet **packet, uint32_t *length, int block_read) { | |
uint32_t orig_length; | |
int s; | |
if(block_read) s = sync_read(fd, &orig_length, sizeof orig_length); | |
else do { | |
s = read(fd, &orig_length, sizeof orig_length); | |
} while(s < 0 && errno == EINTR); | |
if(s < 0) return GET_PACKET_ERROR; | |
if(!s) return GET_PACKET_EOF; | |
if(s < sizeof orig_length) return block_read ? GET_PACKET_EOF : GET_PACKET_SHORT_READ; | |
*length = ntohl(orig_length); | |
if(*length < 1) return GET_PACKET_TOO_SMALL; | |
if(*length > SSHOUT_API_PACKET_MAX_LENGTH) return GET_PACKET_TOO_LARGE; | |
*packet = malloc(sizeof orig_length + *length); | |
if(!*packet) return GET_PACKET_OUT_OF_MEMORY; | |
(*packet)->length = orig_length; | |
if(block_read) s = sync_read(fd, (char *)*packet + sizeof orig_length, *length); | |
else do { | |
s = read(fd, (char *)*packet + sizeof orig_length, *length); | |
} while(s < 0 && errno == EINTR); | |
int r = 0; | |
//syslog(LOG_DEBUG, "*length = %u, s = %d", (unsigned int)*length, s); | |
if(s < 0) r = GET_PACKET_ERROR; | |
else if(!s) r = GET_PACKET_EOF; | |
else if(s < *length) r = block_read ? GET_PACKET_EOF : GET_PACKET_SHORT_READ; | |
if(r) free(*packet); | |
return r; | |
} | |
static void sshout_send_hello(int fd) { | |
uint32_t length = 1 + 6 + 2; | |
struct sshout_api_packet *packet = malloc(4 + length); | |
if(!packet) { | |
fprintf(stderr, "sshout_send_hello: out of memory\n"); | |
return; | |
} | |
packet->length = htonl(length); | |
packet->type = SSHOUT_API_HELLO; | |
uint8_t *p = packet->data; | |
memcpy(p, "SSHOUT", 6); | |
p += 6; | |
*(uint16_t *)p = htons(SSHOUT_MAX_API_VERSION); | |
if(sync_write(fd, packet, 4 + length) < 0) { | |
perror("sshout_send_hello: write"); | |
} | |
free(packet); | |
} | |
static void sshout_send_request_online_users(int fd) { | |
uint32_t length = 1; | |
struct sshout_api_packet *packet = malloc(4 + length); | |
if(!packet) { | |
fprintf(stderr, "sshout_send_request_online_users: out of memory\n"); | |
return; | |
} | |
packet->length = htonl(length); | |
packet->type = SSHOUT_API_GET_ONLINE_USER; | |
if(sync_write(fd, packet, 4 + length) < 0) { | |
perror("sshout_send_request_online_users: write"); | |
} | |
free(packet); | |
} | |
static void write_backspaces(int fd, size_t count) { | |
char *chunk = NULL;; | |
size_t chunk_len; | |
while(count > 0) { | |
chunk_len = count > 1024 ? 1024 : count; | |
if(!chunk) { | |
chunk = malloc(chunk_len); | |
if(!chunk) { | |
char bs = '\b'; | |
int s; | |
do { | |
s = write(fd, &bs, 1); | |
} while((s < 0 && errno == EINTR) || --count > 0); | |
return; | |
} | |
memset(chunk, '\b', chunk_len); | |
} | |
sync_write(fd, chunk, chunk_len); | |
count -= chunk_len; | |
} | |
free(chunk); | |
} | |
static int ssh_chat_send_sshout_online_user_list(int fd, uint8_t *p, uint32_t data_length) { | |
if(2 + 2 > data_length) return -1; | |
uint16_t my_id = ntohs(*(uint16_t *)p); | |
p += 2; | |
uint16_t count = ntohs(*(uint16_t *)p); | |
if(!count) return -1; // WTF? | |
p += 2; | |
char reply_prefix[28]; | |
snprintf(reply_prefix, sizeof reply_prefix, "/reply %u online user%s: ", | |
(unsigned int)(count - 1), count == 2 ? "" : "s"); | |
size_t written_len = strlen(reply_prefix); | |
sync_write(fd, reply_prefix, written_len); | |
uint16_t i = 0; | |
size_t v_size = 0; | |
int name_written = 0; | |
while(i++ < count) { | |
if(2 + 2 + v_size + 2 + 1 > data_length) { | |
write_backspaces(fd, written_len); | |
return -1; | |
} | |
uint16_t id = ntohs(*(uint16_t *)p); | |
p += 2; | |
uint8_t user_name_len = *p++; | |
if(2 + 2 + v_size + 2 + 1 + user_name_len + 1 > data_length) { | |
write_backspaces(fd, written_len); | |
return -1; | |
} | |
if(id != my_id) { | |
if(name_written) sync_write(fd, ", ", 2); | |
else name_written = 1; | |
sync_write(fd, p, user_name_len); | |
written_len += user_name_len; | |
} | |
p += user_name_len; | |
uint8_t host_name_len = *p++; | |
if(2 + 2 + v_size + 2 + 1 + user_name_len + 1 + host_name_len > data_length) { | |
write_backspaces(fd, written_len); | |
return -1; | |
} | |
p += host_name_len; // Skip host_name | |
v_size += 2 + 1 + user_name_len + 1 + host_name_len; | |
} | |
sync_write(fd, "\r\n", 2); | |
return 0; | |
} | |
static void sshout_send_plain_text_message_fixed_length(int fd, const char *to_user, const char *message, size_t len) { | |
size_t user_name_len = strlen(to_user); | |
if(user_name_len > 255) user_name_len = 255; | |
size_t length = 1 + 1 + user_name_len + 1 + 4 + len; | |
struct sshout_api_packet *packet = malloc(4 + length); | |
if(!packet) { | |
fprintf(stderr, "sshout_send_plain_text_message_fixed_length: out of memory\n"); | |
return; | |
} | |
packet->length = htonl(length); | |
packet->type = SSHOUT_API_SEND_MESSAGE; | |
uint8_t *p = packet->data; | |
*p++ = user_name_len; | |
memcpy(p, to_user, user_name_len); | |
p += user_name_len; | |
*p++ = SSHOUT_API_MESSAGE_TYPE_PLAIN; | |
*(uint32_t *)p = htonl(len); | |
p += 4; | |
memcpy(p, message, len); | |
if(sync_write(fd, packet, 4 + length) < 0) { | |
perror("sshout_send_hello: write"); | |
} | |
free(packet); | |
} | |
static char *ssh_chat_user_name = NULL; | |
static char last_requesting_sshout_user[64]; | |
static int do_sshout_message(int sshout_write_fd, int ssh_chat_write_fd, uint8_t *p, uint32_t data_length) { | |
if(8 + 1 > data_length) return -1; | |
uint64_t t = ntoh64(*(uint64_t *)p); | |
p += 8; | |
uint8_t from_user_len = *p++; | |
if(8 + 1 + from_user_len > data_length) return -1; | |
char from_user[from_user_len + 1]; | |
memcpy(from_user, p, from_user_len); | |
from_user[from_user_len] = 0; | |
p += from_user_len; | |
if(strcmp(from_user, sshout_canonical_user_name) == 0) return 0; | |
uint8_t to_user_len = *p++; | |
if(8 + 1 + from_user_len + 1 + to_user_len > data_length) return -1; | |
char to_user[to_user_len + 1]; | |
memcpy(to_user, p, to_user_len); | |
to_user[to_user_len] = 0; | |
p += to_user_len; | |
uint8_t message_type = *p++; | |
uint32_t message_len = ntohl(*(uint32_t *)p); | |
if(8 + 1 + from_user_len + 1 + to_user_len + 1 + 4 + message_len < message_len) return -1; | |
if(8 + 1 + from_user_len + 1 + to_user_len + 1 + 4 + message_len > data_length) return -1; | |
p += 4; | |
if(strcmp(to_user, sshout_canonical_user_name) == 0) { | |
if(from_user_len + 1 > sizeof last_requesting_sshout_user) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, "User name too long?", 19); | |
return 0; | |
} | |
if(message_type != SSHOUT_API_MESSAGE_TYPE_PLAIN) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, "Only plain text message is supported in PM", 42); | |
return 0; | |
} | |
const char *command = (const char *)p; | |
size_t len = message_len; | |
if(*command == '/') { | |
command++; | |
len--; | |
} | |
if(len == 4 && memcmp(command, "list", 4) == 0) { | |
if(*last_requesting_sshout_user) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, last_requesting_sshout_user, "Failed to process your request from ssh-chat server", 51); | |
} | |
memcpy(last_requesting_sshout_user, from_user, from_user_len + 1); | |
sync_write(ssh_chat_write_fd, "/list\r\n", 7); | |
} else if(len == 3 && memcmp(command, "msg", 3) == 0) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, "Missing user\nUsage: msg <user> <message> [<message>] [...]", 58); | |
} else if(len > 3 && memcmp(command, "msg ", 4) == 0) { | |
const char *to_user_p = command + 4; | |
const char *p = memchr(to_user_p, ' ', len - 4); | |
if(p) { | |
size_t to_user_name_len = p - to_user_p; | |
if(strncmp(to_user_p, ssh_chat_user_name, to_user_name_len) == 0 && !ssh_chat_user_name[to_user_name_len]) { | |
fprintf(stderr, "do_sshout_message: igroning private messaging request from '%s' to myself '%s'\n", | |
from_user, ssh_chat_user_name); | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, | |
"Cannot PM to myself", 19); | |
} else { | |
if(*last_requesting_sshout_user) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, | |
last_requesting_sshout_user, | |
"Failed to process your request from ssh-chat server", 51); | |
} | |
memcpy(last_requesting_sshout_user, from_user, from_user_len + 1); | |
p++; | |
len -= 4 + to_user_name_len + 1; | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, to_user_p, to_user_name_len); | |
sync_write(ssh_chat_write_fd, " ", 1); | |
sync_write(ssh_chat_write_fd, from_user, from_user_len); | |
sync_write(ssh_chat_write_fd, " to ", 4); | |
sync_write(ssh_chat_write_fd, to_user_p, to_user_name_len); | |
sync_write(ssh_chat_write_fd, ": ", 2); | |
sync_write(ssh_chat_write_fd, p, len); | |
sync_write(ssh_chat_write_fd, "\r\n", 2); | |
} | |
} else { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, | |
"Missing message\nUsage: msg <user> <message> [<message>] [...]", 61); | |
} | |
} else if(len == 4 && memcmp(command, "peer", 4) == 0) { | |
if(is_ssh_chat_server_address_public) { | |
size_t server_name_len = strlen(ssh_chat_server_name); | |
// Need space for ending zero that would be written by sprintf(3). | |
char reply[37 + server_name_len + (ssh_chat_server_port == 22 ? 0 : 6) + 1 + 1]; | |
size_t len; | |
if(ssh_chat_server_port == 22) { | |
memcpy(reply, "The peer is a ssh-chat server: ssh://", 37); | |
memcpy(reply + 37, ssh_chat_server_name, server_name_len); | |
reply[37 + server_name_len] = '/'; | |
len = 37 + server_name_len + 1; | |
} else { | |
len = sprintf(reply, "The peer is a ssh-chat server: ssh://%s:%hu/", | |
ssh_chat_server_name, (unsigned short int)ssh_chat_server_port); | |
} | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, reply, len); | |
} else { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, | |
"The peer is a ssh-chat server: [private address]", 48); | |
} | |
} else if(len == 4 && memcmp(command, "help", 4) == 0) { | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, | |
"Supported commands: list, msg, peer", 35); | |
} else { | |
char reply[17 + len + 1]; | |
memcpy(reply, "Unknown command '", 17); | |
memcpy(reply + 17, command, len); | |
reply[17 + len] = '\''; | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, from_user, reply, 17 + len + 1); | |
} | |
return 0; | |
} | |
if(strcmp(to_user, "GLOBAL")) { | |
// This shouldn't happen | |
fprintf(stderr, "Received a private message to '%s'\n", to_user); | |
return 0; | |
} | |
const char *message; | |
switch(message_type) { | |
case SSHOUT_API_MESSAGE_TYPE_PLAIN: | |
message = (char *)p; | |
break; | |
case SSHOUT_API_MESSAGE_TYPE_RICH: | |
message_len = 6; | |
message = "[HTML]"; | |
break; | |
case SSHOUT_API_MESSAGE_TYPE_IMAGE: | |
message_len = 7; | |
message = "[Image]"; | |
break; | |
default: | |
fprintf(stderr, "unknown message type %hhu\n", message_type); | |
return 0; | |
} | |
sync_write(ssh_chat_write_fd, from_user, from_user_len); | |
sync_write(ssh_chat_write_fd, ": ", 2); | |
sync_write(ssh_chat_write_fd, message, message_len); | |
sync_write(ssh_chat_write_fd, "\r\n", 2); | |
return 0; | |
} | |
static struct user_join_time { | |
char *user_name; | |
size_t user_name_len; | |
int state; | |
time_t last_change; | |
struct user_join_time *next; | |
} *recent_joined_users = NULL; | |
// Return boolean | |
static int check_recent_join(const char *user_name, size_t user_name_len, int current_state) { | |
time_t current_time = time(NULL); | |
struct user_join_time *p = recent_joined_users, **pp = &recent_joined_users; | |
while(p) { | |
if(current_time - p->last_change > (p->state > 2 ? 1800 : 1200)) { | |
*pp = p->next; | |
free(p->user_name); | |
free(p); | |
p = *pp; | |
continue; | |
} | |
//if(strcmp(p->user_name, user_name) == 0) { | |
if(p->user_name_len == user_name_len && memcmp(p->user_name, user_name, user_name_len) == 0) { | |
p->last_change = current_time; | |
if(p->state > 2) return 1; | |
if(p->state < 2 && p->state == current_state) { | |
// Shouldn't happen! | |
char u[user_name_len + 1]; | |
memcpy(u, user_name, user_name_len); | |
u[user_name_len] = 0; | |
fprintf(stderr, "check_recent_join: user '%s' has duplicated state %d!\n", | |
u, current_state); | |
p->state = 3; | |
return 1; | |
} | |
p->state = p->state > 1 ? 3 : 2; | |
return 0; | |
} | |
pp = &p->next; | |
p = p->next; | |
} | |
p = malloc(sizeof(struct user_join_time)); | |
if(!p) { | |
fprintf(stderr, "check_recent_join: out of memory\n"); | |
return 0; | |
} | |
//p->user_name = strdup(user_name); | |
p->user_name = malloc(user_name_len); | |
if(!p->user_name) { | |
fprintf(stderr, "check_recent_join: out of memory\n"); | |
free(p); | |
return 0; | |
} | |
memcpy(p->user_name, user_name, user_name_len); | |
p->user_name_len = user_name_len; | |
p->state = current_state; | |
p->last_change = current_time; | |
p->next = recent_joined_users; | |
recent_joined_users = p; | |
return 0; | |
} | |
static void do_sshout_packet(int sshout_read_fd, int sshout_write_fd, int ssh_chat_write_fd) { | |
struct sshout_api_packet *packet; | |
uint32_t length; | |
int e = sshout_get_api_packet(sshout_read_fd, &packet, &length, 1); | |
switch(e) { | |
case GET_PACKET_EOF: | |
return; | |
case GET_PACKET_ERROR: | |
fprintf(stderr, "fd %d: %s\n", sshout_read_fd, strerror(errno)); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
return; | |
case GET_PACKET_SHORT_READ: | |
fprintf(stderr, "fd %d short read\n", sshout_read_fd); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
return; | |
case GET_PACKET_TOO_SMALL: | |
fprintf(stderr, "Received API packet too small\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
return; | |
case GET_PACKET_TOO_LARGE: | |
fprintf(stderr, "Received API packet too large (%u bytes)\n", length); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
return; | |
case GET_PACKET_OUT_OF_MEMORY: | |
fprintf(stderr, "Out of memory\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
return; | |
case 0: | |
break; | |
default: | |
fprintf(stderr, "Unknown error %d from sshout_get_api_packet\n", e); | |
abort(); | |
} | |
switch(packet->type) { | |
case SSHOUT_API_PASS: | |
if(length < 1 + 6 + 2 + 1) { | |
fprintf(stderr, "SSHOUT_API_PASS: malformed packet: too short (%u bytes)\n", (unsigned int)length); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
if(memcmp(packet->data, "SSHOUT", 6)) { | |
fprintf(stderr, "SSHOUT_API_PASS: handshake failed, magic mismatch\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
if(sshout_api_version) { | |
fprintf(stderr, "SSHOUT_API_PASS: handshake is already done in this session\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
//sshout_api_version = 0; | |
break; | |
} | |
sshout_api_version = ntohs(*(uint16_t *)(packet->data + 6)); | |
if(sshout_api_version > SSHOUT_MAX_API_VERSION) { | |
// Server shouldn't reply version higher than what we passed in SSHOUT_API_HELLO, but... | |
fprintf(stderr, "SSHOUT_API_PASS: invalid API version %u from server\n", sshout_api_version); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
//sshout_api_version = 0; | |
break; | |
} | |
{ | |
uint8_t user_name_len = *(uint8_t *)(packet->data + 6 + 2); | |
if(user_name_len > sizeof sshout_canonical_user_name - 1) { | |
fprintf(stderr, "SSHOUT_API_PASS: user name too long (%hhu)\n", user_name_len); | |
user_name_len = sizeof sshout_canonical_user_name - 1; | |
} | |
if(1 + 6 + 2 + 1 + user_name_len > length) { | |
fprintf(stderr, "SSHOUT_API_PASS: malformed packet: user_name is longer than packet (user_name_len=%hhu)\n", user_name_len); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
sshout_canonical_user_name_length = user_name_len; | |
memcpy(sshout_canonical_user_name, packet->data + 6 + 2 + 1, user_name_len); | |
sshout_canonical_user_name[user_name_len] = 0; | |
} | |
break; | |
case SSHOUT_API_ONLINE_USERS_INFO: | |
if(ssh_chat_send_sshout_online_user_list(ssh_chat_write_fd, packet->data, length - 1) < 0) { | |
fprintf(stderr, "SSHOUT_API_ONLINE_USERS_INFO: failed\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
} | |
break; | |
case SSHOUT_API_RECEIVE_MESSAGE: | |
if(do_sshout_message(sshout_write_fd, ssh_chat_write_fd, packet->data, length - 1) < 0) { | |
fprintf(stderr, "SSHOUT_API_RECEIVE_MESSAGE: failed\n"); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
} | |
break; | |
case SSHOUT_API_USER_STATE_CHANGE: | |
if(length < 1 + 1 + 1) { | |
fprintf(stderr, "SSHOUT_API_USER_STATE_CHANGE: malformed packet: too short (%u bytes)\n", (unsigned int)length); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
{ | |
uint8_t state = *packet->data; | |
uint8_t user_name_len = packet->data[1]; | |
if(1 + 1 + 1 + user_name_len > length) { | |
fprintf(stderr, "SSHOUT_API_USER_STATE_CHANGE: malformed packet: user_name is longer than packet (user_name_len=%hhu)\n", user_name_len); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
if(user_name_len == sshout_canonical_user_name_length && memcmp(packet->data + 2, sshout_canonical_user_name, user_name_len) == 0) break; | |
if(check_recent_join((const char *)(packet->data + 2), user_name_len, state)) { | |
fprintf(stderr, "SSHOUT_API_USER_STATE_CHANGE: user join/leave too often, skipping announcement to ssh-chat\n"); | |
break; | |
} | |
sync_write(ssh_chat_write_fd, "* ", 2); | |
sync_write(ssh_chat_write_fd, packet->data + 2, user_name_len); | |
sync_write(ssh_chat_write_fd, " has ", 5); | |
if(state) { | |
sync_write(ssh_chat_write_fd, "joined", 6); | |
} else { | |
sync_write(ssh_chat_write_fd, "left", 4); | |
} | |
} | |
sync_write(ssh_chat_write_fd, "\r\n", 2); | |
break; | |
case SSHOUT_API_ERROR: | |
if(1 + 4 + 4 > length) { | |
fprintf(stderr, "SSHOUT_API_ERROR: malformed packet: too short (%u bytes)\n", (unsigned int)length); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
{ | |
// gcc(1) says: dereferencing type-punned pointer will break strict-aliasing rules | |
//uint32_t error_code = ntohl(*(uint32_t *)packet->data); | |
uint32_t error_code; | |
memcpy(&error_code, packet->data, sizeof error_code); | |
error_code = ntohl(error_code); | |
fprintf(stderr, "SSHOUT error %u\n", (unsigned int)error_code); | |
if(error_code == SSHOUT_API_ERROR_USER_NOT_FOUND) { | |
#if 0 | |
uint32_t len = ntohl(*(uint32_t *)(packet->data + 4)); | |
if(1 + 4 + 4 + len < len || 1 + 4 + 4 + len > length) { | |
fprintf(stderr, "SSHOUT_API_ERROR: malformed packet: error_message is longer than packet (len=%u)\n", (unsigned int)len); | |
if(sshout_ssh_pid > 0) kill(sshout_ssh_pid, SIGTERM); | |
break; | |
} | |
#endif | |
sync_write(ssh_chat_write_fd, "/reply ", 7); | |
#if 0 | |
sync_write(ssh_chat_write_fd, packet->data + 4 + 4, len); | |
#else | |
sync_write(ssh_chat_write_fd, "User not found", 14); | |
#endif | |
sync_write(ssh_chat_write_fd, "\r\n", 2); | |
} | |
} | |
break; | |
case SSHOUT_API_MOTD: | |
fprintf(stderr, "SSHOUT_API_MOTD: ignored\n"); | |
break; | |
} | |
free(packet); | |
} | |
static char ssh_chat_read_buffer[4096]; | |
static size_t ssh_chat_read_buffer_read; | |
static char ssh_chat_prompt[256]; | |
static size_t ssh_chat_prompt_length; | |
static int need_check_ssh_chat_nick_name; | |
static int ssh_chat_nick_check_ok; | |
static void do_ssh_chat_line(const char *line, size_t line_len, int ssh_chat_write_fd, int sshout_write_fd) { | |
//fprintf(stderr, "function: do_ssh_chat_line(%p, %zu, %d)\n", line, line_len, sshout_write_fd); | |
#ifdef NEED_WORKAROUND_BROKEN_SSH_CHAT_TIMESTAMPS_IMPLEMENTATION | |
if(line_len > 7 && memcmp(line, "\x1b[A\x1b[2K", 7) == 0) { | |
// https://github.com/shazow/ssh-chat/pull/297/files#diff-7e8766c37bfedc72cfb3260489e0bf8dR165 | |
// XXX | |
line += 7; | |
line_len -= 7; | |
} | |
#endif | |
if(need_check_ssh_chat_nick_name && !ssh_chat_nick_check_ok && line_len > 6 && memcmp(line, "[Guest", 6) == 0) { | |
if(ssh_chat_ssh_pid <= 0) { | |
fprintf(stderr, "do_ssh_chat_line: ssh_chat_ssh_pid = %d\n", (int)ssh_chat_ssh_pid); | |
return; | |
} | |
const char *sb = memchr(line + 6, ']', line_len - 6); | |
if(sb) { | |
size_t len = sb - line - 1; | |
char name[len + 1]; | |
memcpy(name, line + 1, len); | |
name[len] = 0; | |
fprintf(stderr, "My nick name at ssh chat server was '%s', killing connection process %d ...\n", | |
name, (int)ssh_chat_ssh_pid); | |
} else { | |
fprintf(stderr, "My nick name at ssh chat server must be piao, killing connection process %d ...\n", | |
(int)ssh_chat_ssh_pid); | |
} | |
kill(ssh_chat_ssh_pid, SIGTERM); | |
sleep(1); | |
sleep(1); | |
return; | |
} | |
if(line_len * 3 < ssh_chat_prompt_length) return; | |
if(strncmp(ssh_chat_prompt, line, ssh_chat_prompt_length)) return; | |
ssh_chat_nick_check_ok = 1; | |
//assert(strlen(ssh_chat_prompt) <= ssh_chat_prompt_length); | |
const char *p = line + strlen(ssh_chat_prompt); | |
if(p[0] != '\x1b' || p[1] != '[') return; | |
if(p[2] == 'D') { | |
do { | |
p += 3; | |
if(p - line > line_len) return; | |
} while(memcmp(p, "\x1b[D", 3) == 0); | |
} else { | |
// https://github.com/shazow/ssh-chat/commit/418c991677ede73a5ff3259600c0c67f12bf43ea | |
char *end; | |
unsigned int n = strtoul(p + 2, &end, 10); | |
if(!end || end - p < 3 || end - p > 4) return; | |
if(*end != 'D') return; | |
if(n != ssh_chat_prompt_length) return; | |
p = end + 1; | |
} | |
if(p - line + 3 < line_len && memcmp(p, "\x1b[K", 3) == 0) p += 3; | |
if(p - line + ssh_chat_prompt_length < line_len && strncmp(ssh_chat_prompt, p, ssh_chat_prompt_length) == 0) { | |
// This is my own input line that being redisplayed by newer ssh-chat | |
// Needed after https://github.com/shazow/ssh-chat/pull/306 | |
return; | |
} | |
if(p - line + 12 < line_len && memcmp(p, "[PM from ", 9) == 0) { | |
p += 9; | |
const char *sb = memchr(p, ']', line_len - (p - line)); | |
if(!sb || sb - line + 2 >= line_len || sb[1] != ' ') return; | |
size_t user_name_len = sb - p; | |
char from_user_name[user_name_len + 1]; | |
memcpy(from_user_name, p, user_name_len); | |
from_user_name[user_name_len] = 0; | |
p = sb + 2; | |
if(*p == '/') p++; | |
size_t len = line_len - (p - line); | |
if(len < 2) return; | |
if(p[len - 1] == '\r') len--; | |
if(p[len - 1] == '\a') len--; | |
if((len == 4 && memcmp(p, "list", 4) == 0) || (len == 5 && memcmp(p, "names", 5) == 0)) { | |
sshout_send_request_online_users(sshout_write_fd); | |
} else if(len == 3 && memcmp(p, "msg", 3) == 0) { | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " Missing user\r\n", 15); | |
} else if(len > 3 && memcmp(p, "msg ", 4) == 0) { | |
const char *to_user_p = p + 4; | |
p = memchr(to_user_p, ' ', len - 4); | |
if(!p) { | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " Missing message\r\n", 18); | |
return; | |
} | |
size_t to_user_name_len = p - to_user_p; | |
char to_user_name[to_user_name_len]; | |
memcpy(to_user_name, to_user_p, to_user_name_len); | |
to_user_name[to_user_name_len] = 0; | |
if(strcmp(to_user_name, sshout_canonical_user_name) == 0) { | |
fprintf(stderr, "do_ssh_chat_line: igroning private messaging request from '%s' to myself '%s'\n", | |
from_user_name, sshout_canonical_user_name); | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " Cannot PM to myself\r\n", 22); | |
return; | |
} | |
p++; | |
len -= 4 + to_user_name_len + 1; | |
size_t new_message_len = user_name_len + 4 + to_user_name_len + 2 + len; | |
char message[new_message_len]; | |
memcpy(message, from_user_name, user_name_len); | |
memcpy(message + user_name_len, " to ", 4); | |
memcpy(message + user_name_len + 4, to_user_name, to_user_name_len); | |
memcpy(message + user_name_len + 4 + to_user_name_len, ": ", 2); | |
memcpy(message + user_name_len + 4 + to_user_name_len + 2, p, len); | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, to_user_name, message, new_message_len); | |
} else if(len == 4 && memcmp(p, "peer", 4) == 0) { | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " The peer is a SSHOUT server: ", 30); | |
if(is_sshout_server_address_public) { | |
sync_write(ssh_chat_write_fd, "ssh://", 6); | |
size_t server_name_len = strlen(sshout_server_name); | |
sync_write(ssh_chat_write_fd, sshout_server_name, server_name_len); | |
if(sshout_server_port != 22) { | |
char buffer[1 + 5 + 1]; | |
size_t len = sprintf(buffer, ":%hu", (unsigned short int)sshout_server_port); | |
sync_write(ssh_chat_write_fd, buffer, len); | |
} | |
sync_write(ssh_chat_write_fd, "/\r\n", 3); | |
} else { | |
sync_write(ssh_chat_write_fd, "[private address]\r\n", 19); | |
} | |
} else if(len == 4 && memcmp(p, "help", 4) == 0) { | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " Supported commands: list, msg, peer\r\n", 38); | |
} else { | |
char message[len + 1]; | |
memcpy(message, p, len); | |
message[len] = 0; | |
fprintf(stderr, "do_ssh_chat_line: igroning unrecognized command '%s' from '%s'\n", | |
message, from_user_name); | |
sync_write(ssh_chat_write_fd, "/msg ", 5); | |
sync_write(ssh_chat_write_fd, from_user_name, user_name_len); | |
sync_write(ssh_chat_write_fd, " Unknown command '", 18); | |
sync_write(ssh_chat_write_fd, p, len); | |
sync_write(ssh_chat_write_fd, "'\r\n", 3); | |
} | |
return; | |
} | |
size_t remain_len = line_len - (p - line); | |
if(remain_len >= 2 && (p[0] == '-' || p[0] == ' ') && p[1] == '>') { | |
if(!*last_requesting_sshout_user || p[0] == ' ') return; | |
if(remain_len > 8) { | |
const char *reply = p + 3; | |
size_t len = remain_len - 3; | |
if(memcmp(reply, "Err: ", 5)) { | |
if(len < 13) return; | |
if(len > 13 && memcmp(reply, "[Sent PM to ", 12) == 0 && memchr(reply + 13, ']', len - 13)) { | |
*last_requesting_sshout_user = 0; | |
return; | |
} | |
const char *colon = memchr(reply, ':', len); | |
if(!colon || colon - reply < 11) return; | |
if(memcmp(colon - 10, " connected", 10)) return; | |
} | |
if(reply[len - 1] == '\r') len--; | |
if(reply[len - 1] == '\a') len--; | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, last_requesting_sshout_user, reply, len); | |
*last_requesting_sshout_user = 0; | |
} | |
return; | |
} | |
if(remain_len >= 3 && (p[0] == '*' || p[0] == ' ') && p[1] == '*' && p[2] == ' ') { | |
p++; | |
remain_len--; | |
} else { | |
const char *colon = memchr(p, ':', remain_len); | |
if(!colon || colon - p + 1 >= remain_len || colon[1] != ' ') return; | |
} | |
if(p[remain_len - 1] == '\r') remain_len--; | |
if(p[remain_len - 1] == '\a') remain_len--; | |
#if 0 | |
sync_write(STDERR_FILENO, p, remain_len); | |
sync_write(STDERR_FILENO, "\n", 1); | |
#endif | |
sshout_send_plain_text_message_fixed_length(sshout_write_fd, "GLOBAL", p, remain_len); | |
} | |
static void do_ssh_chat_data(int ssh_chat_read_fd, int ssh_chat_write_fd, int sshout_write_fd) { | |
int s; | |
if(ssh_chat_read_buffer_read >= sizeof ssh_chat_read_buffer) { | |
fputs("do_ssh_chat_message: no buffer space, discarding old contains\n", stderr); | |
ssh_chat_read_buffer_read = 0; | |
} | |
do { | |
s = read(ssh_chat_read_fd, ssh_chat_read_buffer + ssh_chat_read_buffer_read, sizeof ssh_chat_read_buffer - ssh_chat_read_buffer_read); | |
} while(s < 0 && errno == EINTR); | |
if(s < 0) { | |
perror("do_ssh_chat_message: read"); | |
return; | |
} | |
if(!s) return; | |
int ss = ssh_chat_read_buffer_read < 2 ? 0 : ssh_chat_read_buffer_read - 1; | |
int ss_diff = ssh_chat_read_buffer_read - ss; | |
char *br = memmem(ssh_chat_read_buffer + ss, s + ss_diff, "\r\n", 2); | |
if(br) { | |
int skip_len = 0; | |
char *last_br_end; | |
do { | |
if(*br) *br = 0; | |
int line_len = br - ssh_chat_read_buffer - skip_len; | |
do_ssh_chat_line(ssh_chat_read_buffer + skip_len, line_len, ssh_chat_write_fd, sshout_write_fd); | |
br += 2; | |
last_br_end = br; | |
br = memmem(br, s + ss_diff - (br - (ssh_chat_read_buffer + ss)), "\r\n", 2); | |
skip_len += line_len + 2; | |
} while(br); | |
ss += s - skip_len; | |
ssh_chat_read_buffer_read = ss + ss_diff; | |
memmove(ssh_chat_read_buffer, last_br_end, ssh_chat_read_buffer_read); | |
} else { | |
ssh_chat_read_buffer_read += s; | |
} | |
} | |
int main(int argc, char **argv) { | |
char **sshout_ssh_options = NULL; | |
char *sshout_user_name = NULL; | |
char **ssh_chat_ssh_options = NULL; | |
int i = 1; | |
while(argv[i]) { | |
if(argv[i][0] == '-') { | |
char *o = argv[i] + 1; | |
if(o[1]) { | |
fprintf(stderr, "%s: Invalid option syntax; this program requiring arguments separated from its option\n", | |
argv[0]); | |
return -1; | |
} | |
if(sshout_server_name && ssh_chat_server_name) { | |
fprintf(stderr, "%s: Extra option after last server URL\n", argv[0]); | |
return -1; | |
} | |
char ***ssh_argv = sshout_server_name ? &ssh_chat_ssh_options : &sshout_ssh_options; | |
switch(*o) { | |
case 'i': | |
if(++i >= argc) { | |
fprintf(stderr, "%s: Option '-i' requires an argument\n", argv[0]); | |
return -1; | |
} | |
add_arg(ssh_argv, "-i"); | |
add_arg(ssh_argv, argv[i]); | |
break; | |
case 'o': | |
if(++i >= argc) { | |
fprintf(stderr, "%s: Option '-o' requires an argument\n", argv[0]); | |
return -1; | |
} | |
add_arg(ssh_argv, "-o"); | |
add_arg(ssh_argv, argv[i]); | |
break; | |
case 'v': | |
add_arg(ssh_argv, "-v"); | |
break; | |
case 'P': | |
//*(sshout_server_name ? &is_ssh_chat_server_address_public : &is_sshout_server_address_public) = 1; | |
if(sshout_server_name) is_ssh_chat_server_address_public = 1; | |
else is_sshout_server_address_public = 1; | |
break; | |
default: | |
fprintf(stderr, "%s: Invalid option '-%c'\n", argv[0], *o); | |
return -1; | |
} | |
} else if(!sshout_server_name) { | |
if(prase_ssh_url(&sshout_server_name, &sshout_server_port, &sshout_user_name, argv[i]) < 0) { | |
fprintf(stderr, "%s: Failed to prase '%s' as a SSH URL\n", argv[0], argv[i]); | |
return -1; | |
} | |
} else if(!ssh_chat_server_name) { | |
if(prase_ssh_url(&ssh_chat_server_name, &ssh_chat_server_port, &ssh_chat_user_name, argv[i]) < 0) { | |
fprintf(stderr, "%s: Failed to prase '%s' as a SSH URL\n", argv[0], argv[i]); | |
return -1; | |
} | |
} else { | |
fprintf(stderr, "%s: Extra string after SSH chat server URL\n", argv[0]); | |
return -1; | |
} | |
i++; | |
} | |
if(!sshout_server_name || !ssh_chat_server_name) { | |
fprintf(stderr, "%s: need 2 URLs\n", argv[0]); | |
print_usage(argv[0]); | |
return -1; | |
} | |
if(!sshout_user_name) { | |
sshout_user_name = strdup("sshout"); | |
if(!sshout_user_name) { | |
fprintf(stderr, "%s: Out of memory\n", argv[0]); | |
return 1; | |
} | |
} | |
if(!ssh_chat_user_name) { | |
ssh_chat_user_name = strdup(SSH_CHAT_DEFAULT_USER_NAME); | |
if(!ssh_chat_user_name) { | |
fprintf(stderr, "%s: Out of memory\n", argv[0]); | |
return 1; | |
} | |
} | |
need_check_ssh_chat_nick_name = strncmp(ssh_chat_user_name, "Guest", 5); | |
ssh_chat_prompt_length = snprintf(ssh_chat_prompt, sizeof ssh_chat_prompt, "[%s] ", ssh_chat_user_name); | |
struct sigaction act = { .sa_handler = SIG_IGN }; | |
if(sigaction(SIGPIPE, &act, NULL) < 0) { | |
perror("sigaction: SIGPIPE"); | |
return 1; | |
} | |
act.sa_handler = signal_handler; | |
if(sigaction(SIGCHLD, &act, NULL) < 0) { | |
perror("sigaction: SIGCHLD"); | |
return 1; | |
} | |
int sshout_write_fd = -1, sshout_read_fd = -1; | |
int ssh_chat_write_fd = -1, ssh_chat_read_fd = -1; | |
fd_set rfdset; | |
while(1) { | |
if(sshout_ssh_pid == -1 || kill(sshout_ssh_pid, 0) < 0) { | |
if(sshout_write_fd != -1) close(sshout_write_fd); | |
if(sshout_read_fd != -1) close(sshout_read_fd); | |
sshout_api_version = 0; | |
sshout_ssh_pid = start_ssh_process(sshout_server_name, sshout_server_port, sshout_user_name, "api", sshout_ssh_options, &sshout_write_fd, &sshout_read_fd); | |
if(sshout_ssh_pid == -1) { | |
sleep(1); | |
continue; | |
} | |
sshout_send_hello(sshout_write_fd); | |
} | |
if(ssh_chat_ssh_pid == -1 || kill(ssh_chat_ssh_pid, 0) < 0) { | |
if(ssh_chat_write_fd != -1) close(ssh_chat_write_fd); | |
if(ssh_chat_read_fd != -1) close(ssh_chat_read_fd); | |
ssh_chat_nick_check_ok = 0; | |
ssh_chat_read_buffer_read = 0; | |
ssh_chat_ssh_pid = start_ssh_process(ssh_chat_server_name, ssh_chat_server_port, ssh_chat_user_name, NULL, ssh_chat_ssh_options, &ssh_chat_write_fd, &ssh_chat_read_fd); | |
if(ssh_chat_ssh_pid == -1) { | |
sleep(1); | |
continue; | |
} | |
sync_write(ssh_chat_write_fd, "/theme mono\r\n", 13); | |
} | |
FD_ZERO(&rfdset); | |
FD_SET(sshout_read_fd, &rfdset); | |
FD_SET(ssh_chat_read_fd, &rfdset); | |
int max_fd = MAX(sshout_read_fd, ssh_chat_read_fd); | |
if(select(max_fd + 1, &rfdset, NULL, NULL, NULL) < 0) { | |
perror("select"); | |
sleep(1); | |
continue; | |
} | |
if(FD_ISSET(sshout_read_fd, &rfdset)) { | |
do_sshout_packet(sshout_read_fd, sshout_write_fd, ssh_chat_write_fd); | |
} | |
if(FD_ISSET(ssh_chat_read_fd, &rfdset)) { | |
do_ssh_chat_data(ssh_chat_read_fd, ssh_chat_write_fd, sshout_write_fd); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment