diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | netcatchat.cpp | 171 | ||||
m--------- | termio | 0 |
5 files changed, 193 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a14bc2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +netcatchat diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..034d1af --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "termio"] + path = termio + url = https://github.com/tomsmeding/termio diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f410982 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CXX = g++ +CXXFLAGS = -Wall -Wextra -std=c++11 -O2 -Itermio +TARGET = netcatchat + +.PHONY: all clean + +all: $(TARGET) + +clean: + rm -f $(TARGET) + + +$(TARGET): netcatchat.cpp termio/libtermio.a + $(CXX) -o $@ $^ + +termio/libtermio.a: + git submodule update --init --recursive + $(MAKE) -C termio diff --git a/netcatchat.cpp b/netcatchat.cpp new file mode 100644 index 0000000..8eebda5 --- /dev/null +++ b/netcatchat.cpp @@ -0,0 +1,171 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#include "termio/termio.h" + +#include <vector> +#include <string> + +using namespace std; + +#define PORT 1234 + +#define STR_(x) #x +#define STR(x) STR_(x) + +__attribute__((noreturn)) +static void die_perror(const char *msg) { + perror(msg); + exit(1); +} + +static int create_server_socket(void) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) die_perror("socket"); + + int one = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); + + struct sockaddr_in name; + memset(&name, 0, sizeof name); + name.sin_family = AF_INET; + name.sin_addr.s_addr = htonl(INADDR_ANY); + name.sin_port = htons(PORT); + if (bind(sock, (struct sockaddr*)&name, sizeof name) < 0) die_perror("bind"); + + if (listen(sock, 16) < 0) die_perror("listen"); + + return sock; +} + +static void writeall(int sock, const char *data, size_t len) { + size_t cursor = 0; + while (cursor < len) { + ssize_t nw = write(sock, data + cursor, len - cursor); + if (nw <= 0) die_perror("write"); + cursor += nw; + } +} + +static void print_buffer(const char *buffer, size_t len) { + Size tsize = querytermsize(); + if (len + 1 >= tsize.w) { + printf("\x1B[2K\x1B[G%s", buffer + len - tsize.w + 1); + } else { + printf("\x1B[2K\x1B[G%s", buffer); + } + fflush(stdout); +} + +static void communicate(int sock) { + string buffer; + string inbuffer; + + initkeyboard(false); + atexit(endkeyboard); + + while (true) { + Size tsize = querytermsize(); + + fd_set inset; + FD_ZERO(&inset); + FD_SET(0, &inset); + FD_SET(sock, &inset); + int ret = select(sock + 1, &inset, NULL, NULL, NULL); + if (ret < 0) { + if (errno == EINTR) continue; + die_perror("select"); + } + + if (FD_ISSET(0, &inset)) { + int key = tgetkey(); + if (key == -1) { // EOF + return; + } else if (key == KEY_LF || key == KEY_CR) { + buffer.push_back('\n'); + writeall(sock, buffer.data(), buffer.size()); + buffer.pop_back(); + printf("\x1B[2K\x1B[G%s\n", buffer.data()); + fflush(stdout); + buffer.clear(); + } else if (key == KEY_BACKSPACE) { + if (buffer.empty()) { + bel(); + } else { + buffer.pop_back(); + print_buffer(buffer.data(), buffer.size()); + } + } else if (32 <= key) { + buffer.push_back(key); + print_buffer(buffer.data(), buffer.size()); + } else { + bel(); + } + } + + if (FD_ISSET(sock, &inset)) { + size_t prevsize = inbuffer.size(); + inbuffer.resize(prevsize + 1024); + + ssize_t nr = read(sock, (void*)(inbuffer.data() + prevsize), 1024); + if (nr < 0) die_perror("read"); + if (nr == 0) return; // eof + + inbuffer.resize(prevsize + nr); + for (size_t idx; (idx = inbuffer.find('\n')) != string::npos; ) { + inbuffer[idx] = '\0'; + printf("\x1B[2K\x1B[G%s\n", inbuffer.data()); + inbuffer.erase(inbuffer.begin(), inbuffer.begin() + idx + 1); + } + + print_buffer(buffer.data(), buffer.size()); + } + } +} + +int main(int argc, char **argv) { + if (argc == 2 && strcmp(argv[1], "-s") == 0) { + int lsock = create_server_socket(); + + printf("Listening on port %d\n", PORT); + int sock = accept(lsock, NULL, NULL); + if (sock == -1) die_perror("accept"); + + communicate(sock); + close(sock); + } else if (argc == 2) { + struct addrinfo *addrs; + struct addrinfo hints; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (getaddrinfo(argv[1], STR(PORT), &hints, &addrs) < 0) die_perror("getaddrinfo"); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) die_perror("socket"); + + bool success = false; + for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) { + if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) continue; + success = true; + break; + } + + if (!success) { + perror("connect"); + freeaddrinfo(addrs); + exit(1); + } + + communicate(sock); + close(sock); + } +} diff --git a/termio b/termio new file mode 160000 +Subproject 1ea284945f6e0e3b3a58458b366f87129f8aed9 |