commit 772f1ef657e0610e4eacb3ccbf7c6a39b5120bac Author: Juhani Krekelä Date: Tue Jul 3 17:26:06 2018 +0000 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5253903 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +*.o +lewdfingerd diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..49fd579 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +DESTDIR ?= +PREFIX ?= /usr/local +EXEC_PREFIX ?= $(PREFIX) +BINDIR ?= $(DESTDIR)$(EXEC_PREFIX)/bin + +CFLAGS += -std=c11 -Os -g -Wall -Wextra -pedantic +CPPFLAGS += +LDFLAGS += + +all: lewdfingerd + +install: all + install lewdfingerd $(BINDIR) + +lewdfingerd: lewdfingerd.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< + +.PHONY: all install clean distclean + +clean: + rm lewdfingerd + +distclean: clean diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..69843e4 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to [http://unlicense.org] diff --git a/lewdfingerd.c b/lewdfingerd.c new file mode 100644 index 0000000..92ed741 --- /dev/null +++ b/lewdfingerd.c @@ -0,0 +1,211 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pollfd *listens = NULL; +size_t num_listens = 0; + +void log_error(const char * restrict format, ...) { + va_list args; + + va_start(args, format); + + vfprintf(stderr, format, args); + + va_end(args); +} + +void setup_listen(void) { + struct addrinfo hints; + struct addrinfo *getaddrinfo_result; + + // AF_UNSPEC: either IPv4 or IPv6 + // SOCK_STREAM: TCP + // AI_PASSIVE: fill out my IP for me + // AI_ADDRCONFIG: only return addresses I have a configured interface for + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + + int status = getaddrinfo(NULL, "79", &hints, &getaddrinfo_result); + + if(status != 0) { + log_error("getaddrinfo failed: %s\n", gai_strerror(status)); + exit(1); + } + + for(struct addrinfo *res = getaddrinfo_result; res != NULL; res = res->ai_next) { + int yes = 1; + + // Add corresponding interface to table of sockets + // Create socket + int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if(sock == -1) { + perror("socket"); + exit(1); + } + + // Disable the IPv4 over IPv6, as that results in IPv4 and IPv6 sockets conflicting and one of them not being able to be set up + if(res->ai_family == AF_INET6) { + if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) { + perror("setsockopt"); + exit(1); + } + } + + // Set reuseaddr + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { + perror("setsockopt"); + exit(1); + } + + // Bind onto given address + if(bind(sock, res->ai_addr, res->ai_addrlen) == -1) { + perror("bind"); + exit(1); + } + + // Listen for incoming connections + if(listen(sock, 1) == -1) { + perror("listen"); + exit(1); + } + + // Grow the table of sockets + size_t index = num_listens++; + if(SIZE_MAX / sizeof(struct pollfd) < num_listens){ + log_error("Too many interfaces to listen on\n"); + exit(1); + } + + listens = realloc(listens, num_listens * sizeof(struct pollfd)); + + if(listens == NULL) { + perror("realloc"); + exit(1); + } + + // Add socket to the table + struct pollfd new_pollobj = {.fd = sock, .events = POLLIN}; + listens[index] = new_pollobj; + } + + freeaddrinfo(getaddrinfo_result); +} + +void drop_privileges(void) { + uid_t uid = getuid(); + gid_t gid = getgid(); + + if(setresgid(gid, gid, gid) != 0) { + perror("setresgid"); + exit(1); + } + if(setresuid(uid, uid, uid) != 0) { + perror("setresuid"); + exit(1); + } +} + +ssize_t writeall(int fd, const char *buf, size_t amount) { + size_t written = 0; + while(written < amount) { + ssize_t r = write(fd, buf + written, amount - written); + if(r < 0) { + return r; + } + + written += r; + } + + return written; +} + +void handle_connection(int sock) { + size_t request_size = 0; + char *request = NULL; + + for(;;) { + ssize_t amount_read; + char iobuf[1024]; + + amount_read = read(sock, &iobuf, sizeof(iobuf)); + + if(amount_read == 0) { + log_error("Client hung up\n"); + shutdown(sock, SHUT_RDWR); + close(sock); + return; + } else if(amount_read < 0) { + perror("read"); + shutdown(sock, SHUT_RDWR); + close(sock); + return; + } + + size_t index = request_size; + request_size += amount_read; + if((request = realloc(request, request_size)) == NULL) { + perror("realloc"); + exit(1); + } + memmove(request + index, iobuf, amount_read); + + if(request_size >= 2 && request[request_size-2] == '\r' && request[request_size-1] == '\n') { + // Request read + break; + } + } + + request[request_size-2] = '\0'; + + if(!strcmp(request, "")) { + const char *response = "Who do you want to finger?\r\n"; + if(writeall(sock, response, strlen(response)) < 0) { + perror("write"); + } + } else { + const char *response = "Lewd.\r\n"; + if(writeall(sock, response, strlen(response)) < 0) { + perror("write"); + } + } + + shutdown(sock, SHUT_RDWR); + close(sock); +} + +int main(void) { + drop_privileges(); + setup_listen(); + + for(;;) { + int amount_ready = poll(listens, num_listens, -1); + if(amount_ready < 0) { + perror("poll"); + exit(1); + } + + for(size_t i = 0; i < num_listens && amount_ready > 0; i++) { + if(listens[i].revents & POLLIN) { + struct sockaddr_storage client_addr; + socklen_t addr_size = sizeof(client_addr); + + int sock = accept(listens[i].fd, (struct sockaddr *)&client_addr, &addr_size); + + handle_connection(sock); + + amount_ready--; + } + } + } +}