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 }