123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- /*
- * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Silicon Graphics, Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include "st.h"
- #define IOBUFSIZE (16*1024)
- #define IOV_LEN 256
- #define IOV_COUNT (IOBUFSIZE / IOV_LEN)
- #ifndef INADDR_NONE
- #define INADDR_NONE 0xffffffff
- #endif
- static char *prog; /* Program name */
- static struct sockaddr_in rmt_addr; /* Remote address */
- static unsigned long testing;
- #define TESTING_VERBOSE 0x1
- #define TESTING_READV 0x2
- #define TESTING_READ_RESID 0x4
- #define TESTING_WRITEV 0x8
- #define TESTING_WRITE_RESID 0x10
- static void read_address(const char *str, struct sockaddr_in *sin);
- static void start_daemon(void);
- static int cpu_count(void);
- static void set_concurrency(int nproc);
- static void *handle_request(void *arg);
- static void print_sys_error(const char *msg);
- /*
- * This program acts as a generic gateway. It listens for connections
- * to a local address ('-l' option). Upon accepting a client connection,
- * it connects to the specified remote address ('-r' option) and then
- * just pumps the data through without any modification.
- */
- int main(int argc, char *argv[])
- {
- extern char *optarg;
- int opt, sock, n;
- int laddr, raddr, num_procs, alt_ev, one_process;
- int serialize_accept = 0;
- struct sockaddr_in lcl_addr, cli_addr;
- st_netfd_t cli_nfd, srv_nfd;
- prog = argv[0];
- num_procs = laddr = raddr = alt_ev = one_process = 0;
- /* Parse arguments */
- while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) {
- switch (opt) {
- case 'a':
- alt_ev = 1;
- break;
- case 'l':
- read_address(optarg, &lcl_addr);
- laddr = 1;
- break;
- case 'r':
- read_address(optarg, &rmt_addr);
- if (rmt_addr.sin_addr.s_addr == INADDR_ANY) {
- fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);
- exit(1);
- }
- raddr = 1;
- break;
- case 'p':
- num_procs = atoi(optarg);
- if (num_procs < 1) {
- fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg);
- exit(1);
- }
- break;
- case 'S':
- /*
- * Serialization decision is tricky on some platforms. For example,
- * Solaris 2.6 and above has kernel sockets implementation, so supposedly
- * there is no need for serialization. The ST library may be compiled
- * on one OS version, but used on another, so the need for serialization
- * should be determined at run time by the application. Since it's just
- * an example, the serialization decision is left up to user.
- * Only on platforms where the serialization is never needed on any OS
- * version st_netfd_serialize_accept() is a no-op.
- */
- serialize_accept = 1;
- break;
- case 't':
- testing = strtoul(optarg, NULL, 0);
- break;
- case 'X':
- one_process = 1;
- break;
- case 'h':
- case '?':
- fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r <host:port>\n",
- prog);
- fprintf(stderr, "options are:\n");
- fprintf(stderr, " -p <num_processes> number of parallel processes\n");
- fprintf(stderr, " -S serialize accepts\n");
- fprintf(stderr, " -a use alternate event system\n");
- #ifdef DEBUG
- fprintf(stderr, " -t mask testing/debugging mode\n");
- fprintf(stderr, " -X one process, don't daemonize\n");
- #endif
- exit(1);
- }
- }
- if (!laddr) {
- fprintf(stderr, "%s: local address required\n", prog);
- exit(1);
- }
- if (!raddr) {
- fprintf(stderr, "%s: remote address required\n", prog);
- exit(1);
- }
- if (num_procs == 0)
- num_procs = cpu_count();
- fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog,
- inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port));
- /* Start the daemon */
- if (one_process)
- num_procs = 1;
- else
- start_daemon();
- if (alt_ev)
- st_set_eventsys(ST_EVENTSYS_ALT);
- /* Initialize the ST library */
- if (st_init() < 0) {
- print_sys_error("st_init");
- exit(1);
- }
- /* Create and bind listening socket */
- if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
- print_sys_error("socket");
- exit(1);
- }
- n = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) {
- print_sys_error("setsockopt");
- exit(1);
- }
- if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) {
- print_sys_error("bind");
- exit(1);
- }
- listen(sock, 128);
- if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) {
- print_sys_error("st_netfd_open");
- exit(1);
- }
- /* See the comment regarding serialization decision above */
- if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd)
- < 0) {
- print_sys_error("st_netfd_serialize_accept");
- exit(1);
- }
- /* Start server processes */
- if (!one_process)
- set_concurrency(num_procs);
- for ( ; ; ) {
- n = sizeof(cli_addr);
- cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n,
- ST_UTIME_NO_TIMEOUT);
- if (cli_nfd == NULL) {
- print_sys_error("st_accept");
- exit(1);
- }
- if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) {
- print_sys_error("st_thread_create");
- exit(1);
- }
- }
- /* NOTREACHED */
- return 1;
- }
- static void read_address(const char *str, struct sockaddr_in *sin)
- {
- char host[128], *p;
- struct hostent *hp;
- unsigned short port;
- strcpy(host, str);
- if ((p = strchr(host, ':')) == NULL) {
- fprintf(stderr, "%s: invalid address: %s\n", prog, host);
- exit(1);
- }
- *p++ = '\0';
- port = (unsigned short) atoi(p);
- if (port < 1) {
- fprintf(stderr, "%s: invalid port: %s\n", prog, p);
- exit(1);
- }
- memset(sin, 0, sizeof(struct sockaddr_in));
- sin->sin_family = AF_INET;
- sin->sin_port = htons(port);
- if (host[0] == '\0') {
- sin->sin_addr.s_addr = INADDR_ANY;
- return;
- }
- sin->sin_addr.s_addr = inet_addr(host);
- if (sin->sin_addr.s_addr == INADDR_NONE) {
- /* not dotted-decimal */
- if ((hp = gethostbyname(host)) == NULL) {
- fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);
- exit(1);
- }
- memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
- }
- }
- #ifdef DEBUG
- static void show_iov(const struct iovec *iov, int niov)
- {
- int i;
- size_t total;
- printf("iov %p has %d entries:\n", iov, niov);
- total = 0;
- for (i = 0; i < niov; i++) {
- printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n",
- i, iov[i].iov_base, (unsigned long) iov[i].iov_len,
- (unsigned long) iov[i].iov_len);
- total += iov[i].iov_len;
- }
- printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total);
- }
- /*
- * This version is tricked out to test all the
- * st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for
- * anything serious. st_(read|write) are all this function really
- * needs.
- */
- static int pass(st_netfd_t in, st_netfd_t out)
- {
- char buf[IOBUFSIZE];
- struct iovec iov[IOV_COUNT];
- int ioviter, nw, nr;
- if (testing & TESTING_READV) {
- for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) {
- iov[ioviter].iov_base = &buf[ioviter * IOV_LEN];
- iov[ioviter].iov_len = IOV_LEN;
- }
- if (testing & TESTING_VERBOSE) {
- printf("readv(%p)...\n", in);
- show_iov(iov, IOV_COUNT);
- }
- if (testing & TESTING_READ_RESID) {
- struct iovec *riov = iov;
- int riov_cnt = IOV_COUNT;
- if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
- if (testing & TESTING_VERBOSE) {
- printf("resid\n");
- show_iov(riov, riov_cnt);
- printf("full\n");
- show_iov(iov, IOV_COUNT);
- }
- nr = 0;
- for (ioviter = 0; ioviter < IOV_COUNT; ioviter++)
- nr += iov[ioviter].iov_len;
- nr = IOBUFSIZE - nr;
- } else
- nr = -1;
- } else
- nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT);
- } else {
- if (testing & TESTING_READ_RESID) {
- size_t resid = IOBUFSIZE;
- if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
- nr = IOBUFSIZE - resid;
- else
- nr = -1;
- } else
- nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
- }
- if (testing & TESTING_VERBOSE)
- printf("got 0x%x(%d) E=%d\n", nr, nr, errno);
- if (nr <= 0)
- return 0;
- if (testing & TESTING_WRITEV) {
- for (nw = 0, ioviter = 0; nw < nr;
- nw += iov[ioviter].iov_len, ioviter++) {
- iov[ioviter].iov_base = &buf[nw];
- iov[ioviter].iov_len = nr - nw;
- if (iov[ioviter].iov_len > IOV_LEN)
- iov[ioviter].iov_len = IOV_LEN;
- }
- if (testing & TESTING_VERBOSE) {
- printf("writev(%p)...\n", out);
- show_iov(iov, ioviter);
- }
- if (testing & TESTING_WRITE_RESID) {
- struct iovec *riov = iov;
- int riov_cnt = ioviter;
- if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
- if (testing & TESTING_VERBOSE) {
- printf("resid\n");
- show_iov(riov, riov_cnt);
- printf("full\n");
- show_iov(iov, ioviter);
- }
- nw = 0;
- while (--ioviter >= 0)
- nw += iov[ioviter].iov_len;
- nw = nr - nw;
- } else
- nw = -1;
- } else
- nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT);
- } else {
- if (testing & TESTING_WRITE_RESID) {
- size_t resid = nr;
- if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
- nw = nr - resid;
- else
- nw = -1;
- } else
- nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
- }
- if (testing & TESTING_VERBOSE)
- printf("put 0x%x(%d) E=%d\n", nw, nw, errno);
- if (nw != nr)
- return 0;
- return 1;
- }
- #else /* DEBUG */
- /*
- * This version is the simple one suitable for serious use.
- */
- static int pass(st_netfd_t in, st_netfd_t out)
- {
- char buf[IOBUFSIZE];
- int nw, nr;
- nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
- if (nr <= 0)
- return 0;
- nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
- if (nw != nr)
- return 0;
- return 1;
- }
- #endif
- static void *handle_request(void *arg)
- {
- struct pollfd pds[2];
- st_netfd_t cli_nfd, rmt_nfd;
- int sock;
- cli_nfd = (st_netfd_t) arg;
- pds[0].fd = st_netfd_fileno(cli_nfd);
- pds[0].events = POLLIN;
- /* Connect to remote host */
- if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
- print_sys_error("socket");
- goto done;
- }
- if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {
- print_sys_error("st_netfd_open_socket");
- close(sock);
- goto done;
- }
- if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
- sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) {
- print_sys_error("st_connect");
- st_netfd_close(rmt_nfd);
- goto done;
- }
- pds[1].fd = sock;
- pds[1].events = POLLIN;
- /*
- * Now just pump the data through.
- * XXX This should use one thread for each direction for true full-duplex.
- */
- for ( ; ; ) {
- pds[0].revents = 0;
- pds[1].revents = 0;
- if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) {
- print_sys_error("st_poll");
- break;
- }
- if (pds[0].revents & POLLIN) {
- if (!pass(cli_nfd, rmt_nfd))
- break;
- }
- if (pds[1].revents & POLLIN) {
- if (!pass(rmt_nfd, cli_nfd))
- break;
- }
- }
- st_netfd_close(rmt_nfd);
- done:
- st_netfd_close(cli_nfd);
- return NULL;
- }
- static void start_daemon(void)
- {
- pid_t pid;
- /* Start forking */
- if ((pid = fork()) < 0) {
- print_sys_error("fork");
- exit(1);
- }
- if (pid > 0)
- exit(0); /* parent */
- /* First child process */
- setsid(); /* become session leader */
- if ((pid = fork()) < 0) {
- print_sys_error("fork");
- exit(1);
- }
- if (pid > 0) /* first child */
- exit(0);
- chdir("/");
- umask(022);
- }
- /*
- * Create separate processes ("virtual processors"). Since it's just an
- * example, there is no watchdog - the parent just exits leaving children
- * on their own.
- */
- static void set_concurrency(int nproc)
- {
- pid_t pid;
- int i;
- if (nproc < 1)
- nproc = 1;
- for (i = 0; i < nproc; i++) {
- if ((pid = fork()) < 0) {
- print_sys_error("fork");
- exit(1);
- }
- /* Child returns */
- if (pid == 0)
- return;
- }
- /* Parent just exits */
- exit(0);
- }
- static int cpu_count(void)
- {
- int n;
- #if defined (_SC_NPROCESSORS_ONLN)
- n = (int) sysconf(_SC_NPROCESSORS_ONLN);
- #elif defined (_SC_NPROC_ONLN)
- n = (int) sysconf(_SC_NPROC_ONLN);
- #elif defined (HPUX)
- #include <sys/mpctl.h>
- n = mpctl(MPC_GETNUMSPUS, 0, 0);
- #else
- n = -1;
- errno = ENOSYS;
- #endif
- return n;
- }
- static void print_sys_error(const char *msg)
- {
- fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
- }
|