Initial commit

This commit is contained in:
Franck STAUFFER 2020-09-02 19:09:52 +02:00
commit e4ab475aea
Signed by: franck.stauffer
GPG Key ID: AAF5A94045CEC261
4 changed files with 318 additions and 0 deletions

13
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,13 @@
debian:
image: debian:stable-slim
before_script:
- apt-get -qq install make clang
script:
- make
alpine:
image: alpine:latest
before_script:
- apk add make clang
script:
- make

39
Makefile Normal file
View File

@ -0,0 +1,39 @@
CPPFLAGS = -D_FORTIFY_SOURCE=2 -D_DEFAULT_SOURCE
CFLAGS = $(CPPFLAGS) -std=c99 -pedantic -Wall -Wextra -Werror -Os -march=native -mtune=native -fPIE
LDFLAGS = -Wl,-z,now,-z,relro,-s,-pie
CC = clang
OBJS = obj/main.o
BIN = bin/stc
PREFIX = /usr
default: obj bin $(BIN)
obj:
@mkdir obj
bin:
@mkdir bin
$(BIN): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
obj/%.o: src/%.c
$(CC) -c -o $@ $^ $(CFLAGS)
clean:
@[ -d obj ] && rm -rf obj
@[ -d bin ] && rm -rf bin
format:
@clang-format -i -style="{BasedOnStyle: mozilla, IndentWidth: 4}" src/*.c
install: default
install -Dm755 $(BIN) $(PREFIX)/bin/stc
uninstall:
rm -f $(PREFIX)/bin/stc
.PHONY: default clean format install uninstall

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# stc
Simple Telnet Client
## Build
```
git clone https://framagit.org/franck.stauffer/stc
cd stc
make
```
## Install
```
sudo make install
```
## Uninstall
```
sudo make uninstall
```
## Usage
```
stc address|hostname [port]
```

237
src/main.c Normal file
View File

@ -0,0 +1,237 @@
#include <arpa/inet.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");
}
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;
}
for (uint_fast8_t i = 0; i < 3; ++i) {
if (buf[i] == DO)
buf[i] = WONT;
else if (buf[i] == WILL)
buf[i] = 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)
{
fprintf(stderr, "Received %s", (sig == SIGTERM) ? "SIGTERM" : "SIGINT");
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 [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("epoll_create1");
return 4;
}
atexit(cleanup);
if (signal(SIGINT, sighandler) == SIG_ERR) {
perror("signal");
return 5;
}
if (signal(SIGTERM, sighandler) == SIG_ERR) {
perror("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("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("epoll_ctl");
return 7;
}
uint_fast8_t run = 1;
do {
struct epoll_event events[2];
int_fast8_t count = epoll_wait(epoll_fd, events, 2, -1);
if (count == -1) {
perror("epoll_wait");
return 8;
}
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("recv");
return 9;
}
if (!ret)
return 0;
if (buf[0] == IAC) {
ret = recv(sock, buf + 1, 2, 0);
if (ret < 0) {
perror("recv");
return 10;
}
if (!ret)
return 0;
negotiate(buf);
} else {
putchar(buf[0]);
fflush(stdin);
}
} else if (events[i].data.fd == 0) {
const uint8_t chr = getchar();
if (chr == 3) {
fputs("\n\rLeaving...\n\r", stdout);
return 0;
}
if (send(sock, &chr, 1, 0) != 1) {
perror("send");
return 11;
}
if (chr == '\n')
putchar('\r');
}
}
} while (run);
}