First commit
This commit is contained in:
commit
772f1ef657
4 changed files with 261 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.swp
|
||||||
|
*.o
|
||||||
|
lewdfingerd
|
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -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
|
24
UNLICENSE
Normal file
24
UNLICENSE
Normal file
|
@ -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]
|
211
lewdfingerd.c
Normal file
211
lewdfingerd.c
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue