hfingerd

[archived] hhvn.uk:79
git clone https://hhvn.uk/hfingerd
git clone git://hhvn.uk/hfingerd
Log | Files | Refs | LICENSE

main.c (4214B)


      1 /* Copyright (c) 2021 hhvn <dev@hhvn.uk> */
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <unistd.h>
      7 #include <libgen.h>
      8 #include <stdarg.h>
      9 #include <netdb.h>
     10 #include <errno.h>
     11 #include <signal.h>
     12 #include <pwd.h>
     13 #include <sys/types.h>
     14 #include <sys/socket.h>
     15 #include <sys/wait.h>
     16 #include "arg.h"
     17 #include "main.h"
     18 #include "handler.h"
     19 
     20 /* defaults, must be strings to replace args */
     21 char *host = "localhost",
     22      *port = "79",
     23      *planfile = ".plan";
     24 /* except bools */
     25 int usecgi = 1,
     26     verbosebool = 0;
     27 
     28 char *argv0;
     29 
     30 void
     31 usage(void) {
     32 	fprintf(stderr, "usage: %s [-vCc] [-i interface] [-p port] [-f planfile]\n", basename(argv0));
     33 	exit(EXIT_USAGE);
     34 }
     35 
     36 int 
     37 verbose(const char *format, ...) {
     38 	va_list ap;
     39 
     40 	if (!verbosebool)
     41 		return 0;
     42 
     43 	va_start(ap, format);
     44 	vfprintf(stderr, format, ap);
     45 	va_end(ap);
     46 
     47 	return 1;
     48 }
     49 
     50 int 
     51 error(const char *format, ...) {
     52 	va_list ap;
     53 
     54 	va_start(ap, format);
     55 	fprintf(stderr, "Error: ");
     56 	vfprintf(stderr, format, ap);
     57 	va_end(ap);
     58 
     59 	return 1;
     60 }
     61 
     62 void 
     63 die(const int exitc, const char *format, ...) {
     64 	va_list ap;
     65 
     66 	va_start(ap, format);
     67 	fprintf(stderr, "Fatal: ");
     68 	vfprintf(stderr, format, ap);
     69 	va_end(ap);
     70 
     71 	exit(exitc);
     72 }
     73 
     74 int
     75 getsock(struct addrinfo *hints, char *host, char *port) {
     76 	struct addrinfo *ai;
     77 	int sret, sockopt;
     78 	int fd;
     79 
     80 	if ((sret = getaddrinfo(host, port, hints, &ai)) != 0 || ai == NULL)
     81 		die(1, "getaddrinfo() for %s:%s, %s\n", host, port, gai_strerror(sret));
     82 
     83 	fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
     84 	sockopt = 1;
     85 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (int*)&sockopt, sizeof(int)) == -1)
     86 		die(1, "setsockopt(): %s\n", strerror(errno));
     87 	if (bind(fd, ai->ai_addr, ai->ai_addrlen) == -1)
     88 		die(1, "bind(): %s\n", strerror(errno));
     89 	if (listen(fd, CQUEUE) == -1)
     90 		die(1, "listen(): %s\n", strerror(errno));
     91 	verbose("Listening on port %s with fd %d\n", port, fd);
     92 
     93 	freeaddrinfo(ai);
     94 
     95 	return fd;
     96 }
     97 
     98 int
     99 read_line(int fd, char *dest, size_t len) {
    100 	size_t	i = 0;
    101 	char	c = 0;
    102 
    103 	do {
    104 		if (read(fd, &c, sizeof(char)) != sizeof(char))
    105 			return 0;
    106 		if (c != '\r')
    107 			dest[i++] = c;
    108 	} while (c != '\n' && i < len);
    109 
    110 	dest[i-1] = '\0';
    111 	return 1;
    112 }
    113 
    114 void 
    115 handoff(int fd) {
    116 	char user[1024];
    117 
    118 	read_line(fd, user, sizeof(user));
    119 	if (strncmp(user, "/W", strlen("/W")) == 0) {
    120 		verbose("Stripping user of /W prefix\n");
    121 		strlcpy(user, &user[3], sizeof(user));
    122 		/* 3 elems are guaranted: /W<null> */
    123 	}
    124 
    125 	if (user[0] == '\0')
    126 		get_userlist(fd);
    127 	else
    128 		get_plan(fd, user);
    129 }
    130 
    131 void
    132 sighandler(int signal) {
    133 	switch (signal) {
    134 	case SIGCHLD:
    135 		while (waitpid(-1, NULL, WNOHANG) == 0);
    136 		break;
    137 	default:
    138 		exit(EXIT_SUCCESS);
    139 	}
    140 }
    141 
    142 int 
    143 main(int argc, char *argv[]) {
    144 	struct sockaddr_storage addr;
    145 	struct addrinfo hints;
    146 	char promises[512];
    147 	socklen_t addrlen;
    148 	int sock, handle;
    149 	int serrno;
    150 	pid_t pid;
    151 
    152 	ARGBEGIN {
    153 	case 'i':
    154 		host = EARGF(usage());
    155 		break;
    156 	case 'p':
    157 		port = EARGF(usage());
    158 		break;
    159 	case 'f':
    160 		planfile = EARGF(usage());
    161 		break;
    162 	case 'c':
    163 		usecgi = 1;
    164 		break;
    165 	case 'C':
    166 		usecgi = 0;
    167 		break;
    168 	case 'v':
    169 		verbosebool = 1;
    170 		break;
    171 	default:
    172 		usage();
    173 	} ARGEND;
    174 
    175 	snprintf(promises, sizeof(promises),
    176 			"stdio rpath inet getpw dns proc id unveil %s",
    177 			usecgi ? "exec" : "");
    178 	if (pledge(promises, NULL) == -1)
    179 		die(1, "pledge: %s\n", promises);
    180 
    181 	if (argc != 0)
    182 		usage();
    183 
    184 	memset(&hints, 0, sizeof(hints));
    185 	hints.ai_family = AF_UNSPEC;
    186 	hints.ai_flags = AI_PASSIVE;
    187 	hints.ai_socktype = SOCK_STREAM;
    188 	sock = getsock(&hints, host, port);
    189 
    190 	/* reap children */
    191 	signal(SIGCHLD, sighandler);
    192 
    193 	for (;;) {
    194 		addrlen = sizeof(addr);
    195 		if ((handle = accept(sock, (struct sockaddr *)&addr, &addrlen)) == -1)
    196 			die(1, "accept(): %s\n", strerror(errno));
    197 		verbose("Accepted client with handle %d\n", handle);
    198 
    199 		switch (pid = fork()) {
    200 		case -1:
    201 			error("fork(): %s\n", strerror(errno));
    202 			shutdown(handle, SHUT_RDWR);
    203 			close(handle);
    204 			break;
    205 		case 0:
    206 			handoff(handle);
    207 			/* use getpid, since pid var is 0 */
    208 			verbose("handle %d, PID %d finished\n", handle, getpid());
    209 			shutdown(handle, SHUT_RDWR);
    210 			close(handle);
    211 			exit(EXIT_SUCCESS);
    212 		default:
    213 			verbose("Forking handle %d to PID %d\n", handle, pid);
    214 		}
    215 	}
    216 	return 0;
    217 }