bnetkit

simple networking utilities
git clone git://bvnf.space/bnetkit.git
Log | Files | Refs

commit 856e8ca93dd7a096a3cc917aeb121253b7c07dc3
parent d62e836f685a1d6e65b6c9e2d633a955e397d05e
Author: phoebos <ben@bvnf.space>
Date:   Wed, 11 May 2022 01:54:03 +0100

add TLS-enabled nc

haven't used libtls before, so might be buggy.

Diffstat:
M.gitignore | 1+
MMakefile | 10+++++++++-
Atlsnc.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 214 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1,2 @@ nc +tlsnc diff --git a/Makefile b/Makefile @@ -1,9 +1,17 @@ .POSIX: XCFLAGS = $(CFLAGS) -Wall -Wextra -Wpedantic -g -Og -D_XOPEN_SOURCE=700 +TLSFLAGS = $$(pkg-config --libs --cflags libtls || echo -ltls) + +all: nc tlsnc nc: nc.c $(CC) $(XCFLAGS) $(LDFLAGS) nc.c -o $@ +tlsnc: tlsnc.c + $(CC) $(XCFLAGS) $(LDFLAGS) $(TLSFLAGS) tlsnc.c -o $@ + clean: - rm -f nc + rm -f nc tlsnc + +.PHONY: clean diff --git a/tlsnc.c b/tlsnc.c @@ -0,0 +1,204 @@ +/* + * This file is in the public domain. If this is not possible in your region, + * please refer to the UNLICENSE which should be included with the software. + * Author: Ben Fuller + * Upstream: https://git.bvnf.space/bnetkit/ + * + * Basic TLS-enabled netcat-like utility to create a connection on the specified + * address and port, using stdin and stdout. + * + * Needs libtls. + * + */ +#include <stdio.h> +#include <errno.h> +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <tls.h> + +#define BUF_LEN 1024 + +int sock_write(struct tls *ctx, char *msg, size_t len); +int sock_read(struct tls *ctx); + +int server_connect(char *argv0, char *nodename, char *servname) { + int sfd = -1; + struct addrinfo hints, *result, *rp; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + + int s = getaddrinfo(nodename, servname, &hints, &result); + if (s != 0) { + fprintf(stderr, "%s: getaddrinfo: %s\n", argv0, gai_strerror(s)); + return -1; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; + + if (close(sfd) == -1) { + perror("close"); + freeaddrinfo(result); + return -1; + } + } + + freeaddrinfo(result); + + if (rp == NULL) { + fprintf(stderr, "%s: %s:%s: connection refused\n", argv0, nodename, servname); + return -1; + } + + return sfd; +} + +int main(int argc, char **argv) { + int timeout = 5 * 1000; /* 5 seconds */ + if (argc < 3) { + fprintf(stderr, "usage: %s host port\n", argv[0]); + return 1; + } + + struct tls_config *cfg = tls_config_new(); + const char *def_ca_file = tls_default_ca_cert_file(); + tls_config_set_ca_file(cfg, def_ca_file); + tls_config_insecure_noverifycert(cfg); + struct tls *ctx = tls_client(); + if (ctx == NULL) { + fprintf(stderr, "tls client creation failed\n"); + tls_config_free(cfg); + return 1; + } + if (tls_configure(ctx, cfg) == -1) { + fprintf(stderr, "tls_configure: %s\n", tls_error(ctx)); + tls_config_free(cfg); + tls_free(ctx); + return 1; + } + tls_config_free(cfg); + + int sfd = server_connect(argv[0], argv[1], argv[2]); + if (sfd == -1) + return 1; + if (tls_connect_socket(ctx, sfd, argv[1]) == -1) { + fprintf(stderr, "tls_connect_socket: %s\n", tls_error(ctx)); + return 1; + } + + int pollret; + struct pollfd fds[2]; + fds[0].fd = 0; + fds[0].events = POLLIN; + fds[1].fd = sfd; + fds[1].events = POLLIN; + + char *line = NULL; + size_t linelen = 0; + + while (1) { + fds[0].revents = 0; + fds[1].revents = 0; + errno = 0; + pollret = poll(fds, 2, timeout); + if (pollret < 0) { + if (errno == EAGAIN) + continue; + perror("poll"); + free(line); + return 1; + } else if (pollret == 0) { + /* timeout */ + fprintf(stderr, "%s: %s:%s: timeout\r\n", argv[0], argv[1], argv[2]); + free(line); + return 1; + } + if (fds[0].revents) { + ssize_t n = getline(&line, &linelen, stdin); + if (n == -1) { + /* send EOF to server */ + //if (shutdown(sfd, SHUT_WR) == -1) + //perror("shutdown"); + + /* prevent poll from checking stdin all the time */ + fds[0].fd = -1; + continue; + } + ssize_t ret = tls_write(ctx, line, (size_t)n); + if (ret == -1) { + fprintf(stderr, "sock_write: %s\n", tls_error(ctx)); + tls_free(ctx); + free(line); + return 1; + } else if (ret == TLS_WANT_POLLIN) + fds[1].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + fds[1].events = POLLOUT; + } + if (fds[1].revents) { + char buff[BUF_LEN] = {'\0'}; + ssize_t ret = tls_read(ctx, buff, BUF_LEN); + + if (ret == -1) { + fprintf(stderr, "tls_read: %s\n", tls_error(ctx)); + free(line); + return 1; + } else if (ret == TLS_WANT_POLLIN) + fds[1].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + fds[1].events = POLLOUT; + else if (ret == 0) /* received EOF */ + break; + else { + write(1, buff, ret); + } + } + } + + free(line); + + if (tls_close(ctx) == -1) + fprintf(stderr, "tls_close: %s\n", tls_error(ctx)); + tls_free(ctx); + if (close(sfd) == -1) { + perror("close"); + return 1; + } + return 0; +} + +int sock_write(struct tls *ctx, char *msg, size_t len) { + ssize_t ret = tls_write(ctx, msg, len); + if (ret != (ssize_t) len) { + fprintf(stderr, "sock_write: %s\n", tls_error(ctx)); + return 1; + } + return 0; +} + +int sock_read(struct tls *ctx) { + char buff[BUF_LEN] = {'\0'}; + ssize_t ret = tls_read(ctx, buff, BUF_LEN); + + if (ret < 0) { + fprintf(stderr, "sock_read: %s\n", tls_error(ctx)); + return -1; + } + if (ret == 0) + return 0; + write(1, buff, ret); + return 1; +}