diff --git a/ipsvd/udpsvd.c b/ipsvd/udpsvd.c new file mode 100644 index 000000000..b3f60823f --- /dev/null +++ b/ipsvd/udpsvd.c @@ -0,0 +1,221 @@ +/* Based on ipsvd utilities written by Gerrit Pape + * which are released into public domain by the author. + * Homepage: http://smarden.sunsite.dk/ipsvd/ + * + * Copyright (C) 2007 Denis Vlasenko. + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +/* Based on ipsvd ipsvd-0.12.1. This tcpsvd accepts all options + * which are supported by one from ipsvd-0.12.1, but not all are + * functional. See help text at the end of this file for details. + * + * Code inside #if 0" is parts of original tcpsvd which are not implemented + * for busyboxed version. + * + * Output of verbose mode matches original (modulo bugs and + * unimplemented stuff). Unnatural splitting of IP and PORT + * is retained (personally I prefer one-value "IP:PORT" notation - + * it is a natural string representation of struct sockaddr_XX). + */ + +#include "busybox.h" + +unsigned verbose; + +static void sig_term_handler(int sig) +{ + if (verbose) + printf("udpsvd: info: sigterm received, exit\n"); + exit(0); +} + +int udpsvd_main(int argc, char **argv); +int udpsvd_main(int argc, char **argv) +{ + const char *instructs; + char *str_t, *user; + unsigned opt; +// unsigned lookuphost = 0; +// unsigned paranoid = 0; +// unsigned long timeout = 0; + + char *remote_hostname; + char *local_hostname; + char *remote_ip; + char *local_ip; + uint16_t local_port, remote_port; + union { + struct sockaddr sa; + struct sockaddr_in sin; + USE_FEATURE_IPV6(struct sockaddr_in6 sin6;) + } sock_adr; + socklen_t sockadr_size; + int sock; + int wstat; + unsigned pid; + struct bb_uidgid_t ugid; + + enum { + OPT_v = (1 << 0), + OPT_u = (1 << 1), + OPT_l = (1 << 2), + OPT_h = (1 << 3), + OPT_p = (1 << 4), + OPT_i = (1 << 5), + OPT_x = (1 << 6), + OPT_t = (1 << 7), + }; + + opt_complementary = "ph:vv"; + opt = getopt32(argc, argv, "vu:l:hpi:x:t:", + &user, &local_hostname, &instructs, &instructs, &str_t, &verbose); + //if (opt & OPT_x) iscdb =1; + //if (opt & OPT_t) timeout = xatou(str_t); + if (!(opt & OPT_h)) + remote_hostname = (char *)""; + if (opt & OPT_u) { + if (!get_uidgid(&ugid, user, 1)) + bb_error_msg_and_die("unknown user/group: %s", user); + } + argv += optind; + if (!argv[0][0] || LONE_CHAR(argv[0], '0')) + argv[0] = (char*)"0.0.0.0"; + + setlinebuf(stdout); + + signal(SIGTERM, sig_term_handler); + signal(SIGPIPE, SIG_IGN); + + local_port = bb_lookup_port(argv[1], "udp", 0); + sock = create_and_bind_dgram_or_die(argv[0], local_port); + + if (opt & OPT_u) { /* drop permissions */ + xsetgid(ugid.gid); + xsetuid(ugid.uid); + } + bb_sanitize_stdio(); /* fd# 1,2 must be opened */ + close(0); + + if (verbose) { + /* we do it only for ":port" cosmetics... oh well */ + len_and_sockaddr *lsa = xhost2sockaddr(argv[0], local_port); + char *addr = xmalloc_sockaddr2dotted(&lsa->sa, lsa->len); + printf("udpsvd: info: listening on %s", addr); + free(addr); + if (option_mask32 & OPT_u) + printf(", uid %u, gid %u", + (unsigned)ugid.uid, (unsigned)ugid.gid); + puts(", starting"); + } + + again: +/* io[0].fd = s; + io[0].events = IOPAUSE_READ; + io[0].revents = 0; + taia_now(&now); + taia_uint(&deadline, 3600); + taia_add(&deadline, &now, &deadline); + iopause(io, 1, &deadline, &now); + if (!(io[0].revents | IOPAUSE_READ)) + goto again; + io[0].revents = 0; +*/ + sockadr_size = sizeof(sock_adr); + if (recvfrom(sock, NULL, 0, MSG_PEEK, &sock_adr.sa, &sockadr_size) == -1) { + bb_perror_msg("recvfrom"); + goto again; + } + + while ((pid = fork()) < 0) { + bb_perror_msg("fork failed, sleeping"); + sleep(5); + } + if (pid > 0) { /* parent */ + while (wait_pid(&wstat, pid) == -1) + bb_perror_msg("error waiting for child"); + if (verbose) + printf("udpsvd: info: end %u\n", pid); + goto again; + } + + /* Child */ + +/* if (recvfrom(sock, 0, 0, MSG_PEEK, (struct sockaddr *)&sock_adr, &sockadr_size) == -1) + drop("unable to read from socket"); +*/ + remote_ip = xmalloc_sockaddr2dotted_noport(&sock_adr.sa, sockadr_size); + remote_port = get_nport(&sock_adr.sa); + remote_port = ntohs(remote_port); + if (verbose) { + printf("udpsvd: info: pid %u from %s\n", pid, remote_ip); + } + if (opt & OPT_h) { + remote_hostname = xmalloc_sockaddr2host(&sock_adr.sa, sizeof(sock_adr)); + if (!remote_hostname) { + bb_error_msg("warning: cannot look up hostname for %s", remote_ip); + remote_hostname = (char*)""; + } + } + +#if 0 + if (instructs) { + ac = ipsvd_check(iscdb, &inst, &match, (char*)instructs, + remote_ip, remote_hostname.s, timeout); + if (ac == -1) discard("unable to check inst", remote_ip); + if (ac == IPSVD_ERR) discard("unable to read", (char*)instructs); + } else + ac = IPSVD_DEFAULT; +#endif + + if (verbose) { +#if 0 + out("udpsvd: info: "); + switch(ac) { + case IPSVD_DENY: out("deny "); break; + case IPSVD_DEFAULT: case IPSVD_INSTRUCT: out("start "); break; + case IPSVD_EXEC: out("exec "); break; + } +#endif + printf("udpsvd: info: %u %s:%s :%s:%s:%u\n", + pid, local_hostname, local_ip, + remote_hostname, remote_ip, remote_port); +#if 0 + if (instructs) { + out(" "); + if (iscdb) { + out((char*)instructs); out("/"); + } + outfix(match.s); + if(inst.s && inst.len && (verbose > 1)) { + out(": "); outinst(&inst); + } + } +#endif + } + +#if 0 + if (ac == IPSVD_DENY) { + recv(s, 0, 0, 0); + _exit(100); + } + if (ac == IPSVD_EXEC) { + args[0] = "/bin/sh"; + args[1] = "-c"; + args[2] = inst.s; + args[3] = NULL; + run = args; + } else run = prog; +#endif + + xmove_fd(sock, 0); + dup2(0, 1); + + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + argv += 2; + + BB_EXECVP(argv[0], argv); + bb_perror_msg_and_die("exec '%s'", argv[0]); +}