1 /*
2 onenetd: a single-process inetd equivalent
3 Copyright 2001, 2002, 2003, 2005, 2014 Adam Sampson <ats@offog.org>
4
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8
9 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/socket.h>
22 #include <netdb.h>
23 #include <netinet/in.h>
24 #include <netinet/tcp.h>
25 #include <arpa/inet.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/wait.h>
32 #include <stdio.h>
33 #include "config.h"
34
35 int max_conns = 40;
36 int conn_count = 0;
37 int bind_family = AF_INET;
38 int use_gid = 0;
39 gid_t gid = 0;
40 int use_uid = 0;
41 uid_t uid = 0;
42 int show_port = 0;
43 int backlog = 10;
44 int no_delay = 0;
45 int verbose = 0;
46 int stderr_to_socket = 0;
47 char *response = NULL;
48 char **command;
49
50 /* This pipe is used to safely detect SIGCHLD: the SIGCHLD handler writes a
51 character to it, and the main loop can then reap children later.
52 (See http://cr.yp.to/docs/selfpipe.html for details.) */
53 int selfpipe[2];
54
55 typedef struct client {
56 int fd;
57 char *message;
58 size_t left;
59 struct client *next;
60 } client;
61 client *clients = NULL;
62
63 /* Structure big enough to contain either an IPv4 or IPv6 socket address. */
64 typedef union {
65 struct sockaddr_in v4;
66 struct sockaddr_in6 v6;
67 } either_addr_t;
68
69 /* Get the UCSPI PROTO value for an address. */
get_proto(const either_addr_t * addr)70 const char *get_proto(const either_addr_t *addr) {
71 if (addr->v4.sin_family == AF_INET6
72 && !IN6_IS_ADDR_V4MAPPED(&addr->v6.sin6_addr))
73 return "TCP6";
74 else
75 return "TCP";
76 }
77
78 /* Get the UCSPI *IP value for an address.
79 Returns a pointer to a static buffer. */
get_addr(const either_addr_t * addr)80 const char *get_addr(const either_addr_t *addr) {
81 static char buf[INET6_ADDRSTRLEN];
82 const void *src;
83 int family = addr->v4.sin_family;
84
85 if (family == AF_INET) {
86 src = &addr->v4.sin_addr;
87 } else if (IN6_IS_ADDR_V4MAPPED(&addr->v6.sin6_addr)) {
88 /* An IPv4-mapped IPv6 address; display as IPv4. */
89 family = AF_INET;
90 src = ((char *)(&addr->v6.sin6_addr)) + 12;
91 } else {
92 src = &addr->v6.sin6_addr;
93 }
94
95 const char *s = inet_ntop(family, src, buf, sizeof buf);
96 if (s == NULL)
97 return "-";
98 return s;
99 }
100
101 /* Get the UCSPI *PORT value for an address. */
get_port(const either_addr_t * addr)102 int get_port(const either_addr_t *addr) {
103 if (addr->v4.sin_family == AF_INET)
104 return ntohs(addr->v4.sin_port);
105 else
106 return ntohs(addr->v6.sin6_port);
107 }
108
109 /* Print a warning. */
warn(const char * msg)110 void warn(const char *msg) {
111 fprintf(stderr, "%s\n", msg);
112 }
113
114 /* Die with an error message. */
die(const char * msg)115 void die(const char *msg) {
116 warn(msg);
117 exit(20);
118 }
119
120 /* Handle SIGCHLD. */
handle_sigchld(int dummy)121 void handle_sigchld(int dummy) {
122 int old_errno = errno;
123 write(selfpipe[1], "c", 1);
124 errno = old_errno;
125 }
126
127 /* Change the flags on an fd. */
change_flags(int fd,int add,int remove)128 int change_flags(int fd, int add, int remove) {
129 int flags = fcntl(fd, F_GETFL);
130 int newflags;
131 if (flags == -1)
132 return -1;
133
134 newflags = (flags | add) & ~remove;
135
136 if (newflags != flags) {
137 if (fcntl(fd, F_SETFL, flags) < 0)
138 return -1;
139 }
140
141 return 0;
142 }
143
144 /* Set the FD_CLOEXEC flag on an fd. */
set_fd_cloexec(int fd)145 void set_fd_cloexec(int fd) {
146 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
147 die("unable to set FD_CLOEXEC");
148 }
149
150 /* Add an fd to an FD_SET, updating a maximum. */
fd_set_add(int fd,fd_set * fds,int * max)151 void fd_set_add(int fd, fd_set *fds, int *max) {
152 FD_SET(fd, fds);
153 if (fd > *max) *max = fd;
154 }
155
156 /* Equivalent to putenv(strdup(s)), with error checking. */
putenv_dup(const char * s)157 int putenv_dup(const char *s) {
158 char *copy = strdup(s);
159 if (copy == NULL)
160 die("strdup failed");
161
162 return putenv(copy);
163 }
164
165 /* Print the usage message. */
usage(int code)166 void usage(int code) {
167 fprintf(stderr, "onenetd version " VERSION "\n"
168 "\n"
169 "Usage: onenetd [options] address port command ...\n"
170 " address Address to bind to (0 for all local addresses)\n"
171 " port TCP port to bind to (0 for any available port)\n"
172 " command Command to execute\n"
173 "Options:\n"
174 " -c N limit to at most N children running (default 40).\n"
175 " Further connections will be deferred unless -r\n"
176 " is specified.\n"
177 " -6 bind to an IPv6 address (default IPv4)\n"
178 " -g gid setgid(gid) after binding\n"
179 " -u uid setuid(uid) after binding\n"
180 " -U setuid($UID) and setgid($GID) after binding\n"
181 " -1 print local port number to stdout after binding\n"
182 " -b N set listen() backlog to N\n"
183 " -D set TCP_NODELAY option on sockets\n"
184 " -e redirect stderr of children to socket\n"
185 " -v be verbose\n"
186 " -Q don't be verbose (default)\n"
187 " -r resp once -c limit is reached, refuse clients\n"
188 " with 'resp' rather than deferring them.\n"
189 " resp may contain \\r, \\n, \\t.\n"
190 " -h show this usage message\n"
191 "\n"
192 "Report bugs to <ats@offog.org>.\n");
193 exit(code);
194 }
195
196 /* Create and bind the listening socket. */
make_listen_socket(const char * address,const char * port)197 int make_listen_socket(const char *address, const char *port) {
198 struct addrinfo hints = {};
199 struct addrinfo *ai;
200 int rc, n, fd;
201
202 hints.ai_family = bind_family;
203 hints.ai_socktype = SOCK_STREAM;
204 hints.ai_flags = AI_PASSIVE;
205 hints.ai_protocol = 0;
206
207 rc = getaddrinfo(address, port, &hints, &ai);
208 if (rc != 0) {
209 die(gai_strerror(rc));
210 }
211
212 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
213 if (fd < 0)
214 die("unable to create socket");
215 if (change_flags(fd, O_NONBLOCK, 0) < 0)
216 die("unable to set O_NONBLOCK");
217 set_fd_cloexec(fd);
218 n = 1;
219 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n) < 0)
220 die("unable to set SO_REUSEADDR");
221 if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
222 die("unable to bind to listen address");
223 if (listen(fd, backlog) < 0)
224 die("unable to listen");
225
226 if (show_port) {
227 either_addr_t addr;
228 socklen_t size = sizeof addr;
229
230 if (getsockname(fd, (struct sockaddr *)&addr, &size) < 0)
231 die("unable to get bound address");
232
233 printf("%d\n", get_port(&addr));
234 fflush(stdout);
235 }
236
237 freeaddrinfo(ai);
238
239 return fd;
240 }
241
242 /* Try to send a chunk of the response to a client. Remove the client
243 from the list if we've sent all of it. */
try_to_send(client * prev_cl,client * cl)244 void try_to_send(client *prev_cl, client *cl) {
245 int remove = 0;
246 ssize_t count = write(cl->fd, cl->message, cl->left);
247
248 if (count >= 0) {
249 cl->message += count;
250 cl->left -= count;
251
252 if (cl->left == 0)
253 remove = 1;
254 } else if (errno == EAGAIN) {
255 /* ignorable error */
256 } else {
257 /* another error while writing */
258 remove = 1;
259 }
260
261 if (remove) {
262 close(cl->fd);
263 if (prev_cl) {
264 prev_cl->next = cl->next;
265 } else {
266 clients = cl->next;
267 }
268 free(cl);
269 }
270 }
271
272 /* Accept a new connection, and either spawn a new child process or add it to
273 the list of clients to reject. */
accept_connection(int listen_fd,int full)274 void accept_connection(int listen_fd, int full) {
275 pid_t pid;
276 either_addr_t local_addr, child_addr;
277 socklen_t len = sizeof child_addr;
278 int child_fd;
279 int n;
280
281 child_fd = accept(listen_fd, (struct sockaddr *)&child_addr, &len);
282 if (len > sizeof child_addr) {
283 warn("unable to get remote address");
284 goto no_conn;
285 }
286 if (child_fd < 0 && errno == EAGAIN)
287 goto no_conn;
288 if (child_fd < 0) {
289 warn("accept failed");
290 goto no_conn;
291 }
292 set_fd_cloexec(child_fd);
293
294 len = sizeof local_addr;
295 if (getsockname(child_fd, (struct sockaddr *)&local_addr, &len) < 0
296 || len > sizeof local_addr) {
297 warn("unable to get local address");
298 goto no_conn;
299 }
300
301 if (full) {
302 client *cl;
303
304 /* Avoid overfilling the fd_set. */
305 if (child_fd >= FD_SETSIZE && verbose) {
306 fprintf(stderr, "- dropped from %s port %d\n",
307 get_addr(&child_addr),
308 get_port(&child_addr));
309 }
310 if (child_fd >= FD_SETSIZE)
311 goto no_conn;
312
313 if (change_flags(child_fd, O_NONBLOCK, 0) < 0) {
314 warn("unable to set O_NONBLOCK");
315 goto no_conn;
316 }
317
318 cl = malloc(sizeof *cl);
319 if (!cl) {
320 warn("out of memory");
321 goto no_conn;
322 }
323
324 cl->fd = child_fd;
325 child_fd = -1;
326 cl->message = response;
327 cl->left = strlen(cl->message);
328 cl->next = clients;
329 clients = cl;
330
331 if (verbose)
332 fprintf(stderr, "- refused from %s port %d\n",
333 get_addr(&child_addr),
334 get_port(&child_addr));
335
336 /* Try to send the response now; if we send
337 all of it it'll get removed from the list
338 again. */
339 try_to_send(NULL, cl);
340
341 goto no_conn;
342 }
343
344 n = 1;
345 if (no_delay && setsockopt(child_fd, IPPROTO_TCP,
346 TCP_NODELAY, &n, sizeof n) < 0) {
347 warn("unable to set TCP_NODELAY");
348 goto no_conn;
349 }
350
351 pid = fork();
352 if (pid < 0) {
353 warn("fork failed");
354 goto no_conn;
355 }
356 if (pid == 0) {
357 char buf[80];
358
359 dup2(child_fd, 0);
360 dup2(child_fd, 1);
361 if (stderr_to_socket)
362 dup2(child_fd, 2);
363
364 snprintf(buf, sizeof buf, "PROTO=%s",
365 get_proto(&local_addr));
366 putenv_dup(buf);
367 snprintf(buf, sizeof buf, "TCPLOCALIP=%s",
368 get_addr(&local_addr));
369 putenv_dup(buf);
370 snprintf(buf, sizeof buf, "TCPLOCALPORT=%d",
371 get_port(&local_addr));
372 putenv_dup(buf);
373 snprintf(buf, sizeof buf, "TCPREMOTEIP=%s",
374 get_addr(&child_addr));
375 putenv_dup(buf);
376 snprintf(buf, sizeof buf, "TCPREMOTEPORT=%d",
377 get_port(&child_addr));
378 putenv_dup(buf);
379
380 execvp(command[0], command);
381 _exit(20);
382 }
383
384 conn_count++;
385 if (verbose)
386 fprintf(stderr, "%ld connected from %s port %d (%d/%d)\n",
387 (long) pid,
388 get_addr(&child_addr),
389 get_port(&child_addr),
390 conn_count, max_conns);
391
392 no_conn:
393 if (child_fd >= 0)
394 close(child_fd);
395 }
396
397 /* Check for child processes that have exited. */
reap_children(void)398 void reap_children(void) {
399 while (1) {
400 pid_t pid = waitpid(-1, NULL, WNOHANG);
401 if (pid <= 0)
402 break;
403
404 conn_count--;
405 if (verbose)
406 fprintf(stderr, "%ld closed (%d/%d)\n",
407 (long) pid, conn_count, max_conns);
408 }
409 }
410
main(int argc,char ** argv)411 int main(int argc, char **argv) {
412 struct sigaction sa;
413 sigset_t sig_chld;
414 int listen_fd;
415 char *s, *r;
416 int n;
417
418 while (1) {
419 int c = getopt(argc, argv, "+c:6g:u:U1b:DQvehr:");
420 if (c == -1)
421 break;
422 switch (c) {
423 case 'c':
424 max_conns = atoi(optarg);
425 break;
426 case '6':
427 bind_family = AF_INET6;
428 break;
429 case 'g':
430 use_gid = 1;
431 gid = atoi(optarg);
432 break;
433 case 'u':
434 use_uid = 1;
435 uid = atoi(optarg);
436 break;
437 case 'U':
438 s = getenv("GID");
439 if (!s)
440 die("-U specified but no $GID");
441 use_gid = 1;
442 gid = atoi(s);
443 s = getenv("UID");
444 if (!s)
445 die("-U specified but no $UID");
446 use_uid = 1;
447 uid = atoi(s);
448 break;
449 case '1':
450 show_port = 1;
451 break;
452 case 'b':
453 backlog = atoi(optarg);
454 break;
455 case 'D':
456 no_delay = 1;
457 break;
458 case 'Q':
459 verbose = 0;
460 break;
461 case 'v':
462 verbose = 1;
463 break;
464 case 'e':
465 stderr_to_socket = 1;
466 break;
467 case 'h':
468 usage(0);
469 break;
470 case 'r':
471 r = response = malloc(strlen(optarg) + 1);
472 if (!r)
473 die("out of memory");
474 for (s = optarg; *s != '\0'; s++) {
475 if (*s == '\\') {
476 s++;
477 if (*s == 'r')
478 *r++ = '\r';
479 else if (*s == 'n')
480 *r++ = '\n';
481 else if (*s == 't')
482 *r++ = '\t';
483 else
484 usage(20);
485 } else {
486 *r++ = *s;
487 }
488 }
489 *r = '\0';
490 break;
491 default:
492 usage(20);
493 }
494 }
495
496 if ((argc - optind) < 3)
497 usage(20);
498
499 listen_fd = make_listen_socket(argv[optind], argv[optind + 1]);
500 command = &argv[optind + 2];
501
502 /* Drop privileges. */
503 if (use_gid)
504 if (setgid(gid) < 0)
505 die("unable to setgid");
506 if (use_uid)
507 if (setuid(uid) < 0)
508 die("unable to setuid");
509
510 /* Create the self-pipe. */
511 if (pipe(selfpipe) < 0)
512 die("unable to create self-pipe");
513 if (change_flags(selfpipe[1], O_NONBLOCK, 0) < 0)
514 die("unable to set O_NONBLOCK");
515 set_fd_cloexec(selfpipe[0]);
516 set_fd_cloexec(selfpipe[1]);
517
518 /* Mask SIGCHLD, except when we're blocked in select(). This is because
519 many of the system calls we use are interruptable, and we'd
520 otherwise have to handle EINTR everywhere. (It would be simpler to
521 just use SA_RESTART for SIGCHLD -- but POSIX says that it's
522 implementation-defined whether select() is interrupted in that case
523 or not.) */
524 sigemptyset(&sig_chld);
525 sigaddset(&sig_chld, SIGCHLD);
526 if (sigprocmask(SIG_BLOCK, &sig_chld, NULL) < 0)
527 die("unable to block SIGCHLD");
528
529 sa.sa_handler = handle_sigchld;
530 sigemptyset(&sa.sa_mask);
531 sa.sa_flags = SA_NOCLDSTOP;
532 sigaction(SIGCHLD, &sa, NULL);
533
534 while (1) {
535 int full;
536 client *cl, *prev_cl, *next_cl;
537 fd_set read_fds, write_fds;
538
539 do {
540 sigset_t old_sigs;
541 int max = -1;
542
543 full = conn_count >= max_conns;
544 FD_ZERO(&read_fds);
545 fd_set_add(selfpipe[0], &read_fds, &max);
546 /* If we're full, and we don't have a response to send,
547 then we don't want to accept new connections -- so
548 don't check listen_fd. */
549 if (!(full && !response))
550 fd_set_add(listen_fd, &read_fds, &max);
551
552 FD_ZERO(&write_fds);
553 for (cl = clients; cl; cl = cl->next)
554 fd_set_add(cl->fd, &write_fds, &max);
555
556 if (sigprocmask(SIG_UNBLOCK, &sig_chld, &old_sigs) < 0)
557 die("unable to unblock SIGCHLD");
558
559 n = select(max + 1, &read_fds, &write_fds, NULL, NULL);
560 if (n < 0 && errno != EINTR)
561 warn("select failed");
562
563 if (sigprocmask(SIG_SETMASK, &old_sigs, NULL) < 0)
564 die("unable to restore signal mask");
565 } while (n < 0);
566
567 if (FD_ISSET(selfpipe[0], &read_fds)) {
568 char c;
569
570 /* We don't care if this fails. */
571 read(selfpipe[0], &c, 1);
572
573 reap_children();
574 }
575
576 if (FD_ISSET(listen_fd, &read_fds)) {
577 accept_connection(listen_fd, full);
578 }
579
580 prev_cl = NULL;
581 for (cl = clients; cl; cl = next_cl) {
582 next_cl = cl->next;
583
584 if (FD_ISSET(cl->fd, &write_fds))
585 try_to_send(prev_cl, cl);
586 prev_cl = cl;
587 }
588 }
589
590 return 0;
591 }
592