/* 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]); }