Initial commit
This commit is contained in:
commit
e4ab475aea
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal 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
39
Makefile
Normal 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
29
README.md
Normal 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
237
src/main.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user