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