summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-01-27 14:55:37 +0100
committerTom Smeding <tom.smeding@gmail.com>2020-01-27 14:55:37 +0100
commit8d7eff7835405ed10b7659cc831a6727d0c02daf (patch)
treec18862a612caaa4acb4774f0cbde451d4667313c
Initial
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--Makefile18
-rw-r--r--netcatchat.cpp171
m---------termio0
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