main.c (7281B)
1 #ifdef USE_CHROOT 2 #define _DEFAULT_SOURCE 3 #define _BSD_SOURCE 4 #endif 5 #define _POSIX_C_SOURCE 200809L 6 7 #include <netinet/in.h> 8 #include <sys/socket.h> 9 10 #include <fcntl.h> 11 #include <grp.h> 12 #include <limits.h> 13 #include <netdb.h> 14 #include <pwd.h> 15 #include <stdarg.h> 16 #include <stdbool.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <time.h> 21 #include <unistd.h> 22 23 #include <ev.h> 24 25 #ifdef USE_TLS 26 #include <tls.h> 27 #endif 28 29 #include "arg.h" 30 #include "client.h" 31 #include "common.h" 32 33 char dfl_hostname[128]; 34 const char dfl_port[] = "70"; 35 const char dfl_gopherroot[] = "/var/gopher"; 36 37 const char *hostname = dfl_hostname; 38 const char *gopherroot = dfl_gopherroot; 39 const char *oport = NULL; 40 41 char *argv0; 42 int logfd = -1; 43 44 struct listener { 45 ev_io watcher; 46 int fd; 47 #ifdef USE_TLS 48 struct tls *tlsctx; 49 #endif 50 }; 51 52 struct listener listen_watcher; 53 ev_timer timeout_watcher; 54 55 static void logprintf(const char *fmt, ...) 56 { 57 va_list args; 58 59 if (logfd < 0) 60 return; 61 62 va_start(args, fmt); 63 vdprintf(logfd, fmt, args); 64 va_end(args); 65 } 66 67 void accesslog(struct client *c, const char *resource, const char *qs, const char *ss) 68 { 69 char tbuf[64] = ""; 70 char abuf[INET6_ADDRSTRLEN]; 71 72 if (logfd < 0) 73 return; 74 75 getnameinfo((struct sockaddr *)&c->addr, c->addrlen, abuf, sizeof(abuf), NULL, 0, NI_NUMERICHOST); 76 strftime(tbuf, sizeof(tbuf), "%d/%b/%Y:%H:%M:%S %z", localtime(&(time_t){ time(NULL) })); 77 78 logprintf("%s - - [%s] \"%s\" \"%s\" \"%s\"\n", 79 abuf, tbuf, resource, qs ? qs : "", ss ? ss : ""); 80 } 81 82 char *cleanup_path(char *path, char **basename, size_t *pathlen) 83 { 84 size_t parts[512]; 85 size_t np = 0; 86 size_t w = 0; 87 size_t r = 0; 88 89 while (path[r] == '/' && r < *pathlen) 90 r++; 91 92 if (r == *pathlen) { 93 *pathlen = 0; 94 if (basename) 95 *basename = path; 96 return path; 97 } 98 99 while (r < *pathlen) { 100 if (!path[r]) 101 return NULL; 102 if (path[r] == '/') { 103 if (w) 104 path[w++] = '/'; 105 do { 106 r++; 107 } while (path[r] == '/'); 108 continue; 109 } else if (path[r] == '.') { 110 if (r + 1 == *pathlen || path[r + 1] == '/') { 111 for (r++; r < *pathlen && path[r] == '/'; r++); 112 continue; 113 } else if (path[r + 1] == '.') { 114 if (r + 2 == *pathlen || path[r + 2] == '/') { 115 if (!np) 116 return NULL; 117 w = parts[--np]; 118 if (r + 2 == *pathlen && w) 119 w--; 120 for (r += 2; r < *pathlen && path[r] == '/'; r++); 121 continue; 122 } 123 } 124 } 125 parts[np++] = w; 126 while (r < *pathlen && path[r] && path[r] != '/') 127 path[w++] = path[r++]; 128 } 129 130 if (basename) { 131 if (np) 132 *basename = path + parts[np - 1]; 133 else 134 *basename = path; 135 } 136 137 if (w && path[w - 1] == '/') 138 w--; 139 *pathlen = w; 140 return path; 141 } 142 143 static void listen_cb(EV_P_ ev_io *w, int revents) 144 { 145 struct sockaddr_storage addr; 146 socklen_t addrlen = sizeof(addr); 147 struct listener *l = (struct listener *)w; 148 int fd; 149 struct client *c; 150 151 (void)revents; 152 153 fd = accept(l->fd, (struct sockaddr *)&addr, &addrlen); 154 155 if (fd < 0) 156 return; 157 158 c = client_new(EV_A_ fd, (struct sockaddr *)&addr, addrlen 159 #ifdef USE_TLS 160 , listen_watcher.tlsctx 161 #endif 162 ); 163 if (!c) { 164 close(fd); 165 return; 166 } 167 } 168 169 static void usage(void) 170 { 171 fprintf(stderr, 172 "usage: %s [-46d] " 173 #ifdef USE_TLS 174 "[-t key cert] " 175 #endif 176 "[-l logfile] [-b rootdir] [-p port] [-o outport] " 177 "[-u user] [-g group] [-h host] [-i listen address]\n", 178 argv0); 179 exit(1); 180 } 181 182 static void croak(const char *s) 183 { 184 perror(s); 185 exit(1); 186 } 187 188 int main (int argc, char *argv[]) 189 { 190 #ifdef USE_TLS 191 struct tls_config *tlscfg; 192 const char *keyfile = NULL; 193 const char *certfile = NULL; 194 #endif 195 char gopherrootbuf[PATH_MAX]; 196 struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_flags = AI_PASSIVE, .ai_socktype = SOCK_STREAM }; 197 struct addrinfo *addrs; 198 struct addrinfo *ai; 199 #if EV_MULTIPLICITY 200 EV_P = EV_DEFAULT; 201 #endif 202 const char *bindto = NULL; 203 const char *port = dfl_port; 204 const char *user = NULL; 205 const char *group = NULL; 206 const char *logfile = NULL; 207 int lfd = -1; 208 bool dofork = true; 209 #ifdef USE_CHROOT 210 bool dochroot = false; 211 #endif 212 uid_t uid; 213 gid_t gid; 214 215 signal(SIGPIPE, SIG_IGN); 216 217 ARGBEGIN { 218 case '4': 219 hints.ai_family = AF_INET; 220 break; 221 case '6': 222 hints.ai_family = AF_INET6; 223 break; 224 #ifdef USE_CHROOT 225 case 'c': 226 dochroot = true; 227 break; 228 #endif 229 case 'd': 230 dofork = false; 231 break; 232 #ifdef USE_TLS 233 case 't': 234 keyfile = EARGF(usage()); 235 certfile = EARGF(usage()); 236 break; 237 #endif 238 case 'h': 239 hostname = EARGF(usage()); 240 break; 241 case 'i': 242 bindto = EARGF(usage()); 243 break; 244 case 'b': 245 gopherroot = EARGF(usage()); 246 break; 247 case 'p': 248 port = EARGF(usage()); 249 break; 250 case 'P': 251 oport = EARGF(usage()); 252 break; 253 case 'u': 254 user = EARGF(usage()); 255 break; 256 case 'g': 257 group = EARGF(usage()); 258 break; 259 case 'l': 260 logfile = EARGF(usage()); 261 break; 262 default: 263 usage(); 264 break; 265 } ARGEND; 266 267 if (!oport) 268 oport = port; 269 270 if (hostname == dfl_hostname) { 271 if (bindto) 272 hostname = bindto; 273 else 274 gethostname(dfl_hostname, sizeof(dfl_hostname)); 275 } 276 277 if (getaddrinfo(bindto, port, &hints, &addrs)) 278 croak("Resolving bind address failed"); 279 280 for (ai = addrs; ai; ai = ai->ai_next) { 281 lfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 282 283 if (lfd < 0) 284 continue; 285 286 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); 287 288 if (!bind(lfd, ai->ai_addr, ai->ai_addrlen)) 289 break; 290 291 close(lfd); 292 } 293 294 if (logfile && (logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) < 0) 295 croak("Log opening failed"); 296 297 if (!ai) 298 croak("Bind failed"); 299 300 freeaddrinfo(addrs); 301 302 if (listen(lfd, 10)) 303 croak("Listen failed"); 304 305 #ifdef USE_TLS 306 if (keyfile && certfile) { 307 tls_init(); 308 listen_watcher.tlsctx = tls_server(); 309 if (!(tlscfg = tls_config_new()) || 310 tls_config_set_key_file(tlscfg, keyfile) || 311 tls_config_set_cert_file(tlscfg, certfile) || 312 tls_configure(listen_watcher.tlsctx, tlscfg)) 313 croak("TLS configuration error"); 314 tls_config_free(tlscfg); 315 } else 316 listen_watcher.tlsctx = NULL; 317 #endif 318 319 if (*gopherroot != '/' && getcwd(gopherrootbuf, sizeof(gopherrootbuf))) { 320 size_t l = strlen(gopherrootbuf); 321 int ll = snprintf(gopherrootbuf + l, sizeof(gopherrootbuf) - l, "/%s", gopherroot); 322 if ((l += ll - 1) < sizeof(gopherrootbuf) - 1 && cleanup_path(gopherrootbuf + 1, NULL, &l)) { 323 gopherrootbuf[l + 1] = '\0'; 324 gopherroot = gopherrootbuf; 325 } 326 } 327 328 if (user) { 329 struct passwd *u = getpwnam(user); 330 if (!u) 331 croak("No such user"); 332 uid = u->pw_uid; 333 } 334 335 if (group) { 336 struct group *g = getgrnam(group); 337 if (!g) 338 croak("No such group"); 339 gid = g->gr_gid; 340 } 341 342 #ifdef USE_CHROOT 343 if (dochroot) { 344 if (chroot(gopherroot)) 345 croak("chroot failed"); 346 gopherroot = strcpy(gopherrootbuf, "/"); 347 } 348 #endif 349 350 if (group && setgid(gid)) 351 croak("setgid failed"); 352 if (user && setuid(uid)) 353 croak("setuid failed"); 354 355 if (dofork) { 356 if (fork()) { 357 close(lfd); 358 return 0; 359 } 360 setsid(); 361 if (fork()) { 362 close(lfd); 363 return 0; 364 } 365 close(STDIN_FILENO); 366 close(STDOUT_FILENO); 367 close(STDERR_FILENO); 368 } 369 370 listen_watcher.fd = lfd; 371 ev_io_init(&listen_watcher.watcher, listen_cb, lfd, EV_READ); 372 ev_io_start(EV_A_ &listen_watcher.watcher); 373 374 ev_run(EV_A_ 0); 375 376 #ifdef USE_TLS 377 if (listen_watcher.tlsctx) 378 tls_close(listen_watcher.tlsctx); 379 #endif 380 close(listen_watcher.fd); 381 382 return 0; 383 }