stc/src/main.c

255 lines
5.8 KiB
C

#include <arpa/inet.h>
#include <ctype.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>
#define SE 0xF0
#define SB 0xFA
#define WILL 0xFB
#define WONT 0xFC
#define DO 0xFD
#define DONT 0xFE
#define IAC 0xFF
#define CMD_WINDOW_SIZE 0x1F
int epoll_fd;
int sock;
struct termios tin;
void
cleanup(void)
{
if (tcsetattr(STDIN_FILENO, TCSANOW, &tin))
perror("ERROR: tcsetattr");
if (close(epoll_fd) || close(sock))
perror("ERROR: close");
putchar('\r');
}
uint_fast8_t
negotiate(unsigned char* buf)
{
if (buf[1] == DO && buf[2] == CMD_WINDOW_SIZE) {
uint8_t msg0[3] = { IAC, WILL, CMD_WINDOW_SIZE };
if (send(sock, msg0, 3, 0) != 3) {
perror("ERROR: send");
return 0;
}
uint8_t msg1[9] = { IAC, SB, CMD_WINDOW_SIZE, 0, 80, 0, 24, IAC, SE };
if (send(sock, msg1, 9, 0) != 9) {
perror("ERROR: send");
return 0;
}
return 1;
}
if (buf[1] == DO)
buf[1] = WONT;
else if (buf[1] == WILL)
buf[1] = DO;
if (send(sock, buf, 3, 0) != 3) {
perror("ERROR: send");
return 0;
}
return 1;
}
uint_fast8_t
net_init(const char* host, const char* port)
{
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("ERROR: socket");
return 0;
}
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* res;
int_fast8_t err = getaddrinfo(host, port, &hints, &res);
if (err) {
if (err == EAI_SYSTEM)
perror("ERROR: getaddrinfo");
else
fprintf(stderr, "ERROR: getaddrinfo: %s\n", gai_strerror(err));
return 0;
}
if (connect(sock, res->ai_addr, res->ai_addrlen)) {
perror("ERROR: connect");
if (close(sock))
perror("ERROR: close");
return 0;
}
freeaddrinfo(res);
return 1;
}
void
sighandler(int sig)
{
fputs("\n\rINFO: Received SIGTERM\n", stderr);
exit(sig);
}
uint_fast8_t
terminal_set(void)
{
if (tcgetattr(STDIN_FILENO, &tin)) {
perror("ERROR: tcgetattr");
return 0;
}
static struct termios tlocal;
memcpy(&tlocal, &tin, sizeof(struct termios));
cfmakeraw(&tlocal);
if (tcsetattr(STDIN_FILENO, TCSANOW, &tlocal)) {
perror("ERROR: tcsetattr");
return 0;
}
return 1;
}
int
main(int argc, char** argv)
{
if (argc < 2 || argc > 3) {
printf("USAGE: %s, address|domainname [port]\n", argv[0]);
return 1;
}
if (!net_init(argv[1], (argc == 3) ? argv[2] : "23"))
return 2;
if (!terminal_set())
return 3;
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("ERROR: epoll_create1");
return 4;
}
atexit(cleanup);
if (signal(SIGTERM, sighandler) == SIG_ERR) {
perror("ERROR: signal");
return 5;
}
struct epoll_event ev[2];
memset(&ev, 0, 2 * sizeof(struct epoll_event));
ev[0].events = EPOLLIN;
ev[0].data.fd = 0;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &ev[0])) {
perror("ERROR: epoll_ctl");
return 6;
}
ev[1].events = EPOLLIN;
ev[1].data.fd = sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev[1])) {
perror("ERROR: epoll_ctl");
return 6;
}
uintmax_t typed_in = 0;
for (;;) {
struct epoll_event events[2];
int_fast8_t count = epoll_wait(epoll_fd, events, 2, -1);
if (count == -1) {
perror("ERROR: epoll_wait");
return 7;
}
for (int_fast8_t i = 0; i < count; ++i) {
if (events[i].data.fd == sock) {
uint8_t buf[3];
memset(buf, 0, 3);
int_fast8_t ret = recv(sock, buf, 1, 0);
if (ret == -1) {
perror("ERROR: recv");
return 8;
}
if (!ret)
return 0;
if (buf[0] == IAC) {
ret = recv(sock, buf + 1, 2, 0);
if (ret < 0) {
perror("ERROR: recv");
return 8;
}
if (!ret)
return 0;
negotiate(buf);
} else {
putchar(buf[0]);
if (buf[0] == '\n')
putchar('\r');
fflush(stdin);
}
} else if (events[i].data.fd == 0) {
const uint8_t chr = getchar();
if (send(sock, &chr, 1, 0) != 1) {
perror("ERROR: send");
return 9;
}
switch (chr) {
case '\b':
if (!typed_in)
continue;
--typed_in;
putchar('\b');
putchar(' ');
putchar('\b');
break;
case '\n':
typed_in = 0;
putchar('\n');
putchar('\r');
break;
default:
if (isprint(chr)) {
++typed_in;
putchar(chr);
}
}
} else {
fputs("ERROR: Event on an untracked file "
"descriptor\n\r",
stderr);
return 10;
}
fflush(stdout);
}
}
}