⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 proxy.c

📁 快速开发
💻 C
字号:
/* * 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#endifstatic 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	0x10static 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;  short port;  strcpy(host, str);  if ((p = strchr(host, ':')) == NULL) {    fprintf(stderr, "%s: invalid address: %s\n", prog, host);    exit(1);  }  *p++ = '\0';  port = (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 DEBUGstatic 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%x(%d)\n",     i, iov[i].iov_base, iov[i].iov_len, iov[i].iov_len);    total += iov[i].iov_len;  }  printf("total 0x%x(%d)\n", total, 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;}#endifstatic 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));}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -