home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume26 / faucet / part01 / faucet.c < prev    next >
C/C++ Source or Header  |  1993-01-29  |  9KB  |  364 lines

  1. /*
  2.  
  3.     faucet.c, part of
  4.     faucet and hose: network pipe utilities
  5.     Copyright (C) 1992 Robert Forsman
  6.  
  7.     This program is free software; you can redistribute it and/or modify
  8.     it under the terms of the GNU General Public License as published by
  9.     the Free Software Foundation; either version 2 of the License, or
  10.     (at your option) any later version.
  11.  
  12.     This program is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License
  18.     along with this program; if not, write to the Free Software
  19.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  
  21.     */
  22.  
  23. static char info[] = "faucet: a network utility for sockets\nWritten 1992 by Robert Forsman <thoth@ufl.edu>\n";
  24. #include    <stdio.h>
  25. #include    <fcntl.h>
  26. #include    <errno.h>
  27. #ifdef hpux
  28. #include    <signal.h>
  29. #include    <sgtty.h>
  30. #endif
  31. #include    <sys/wait.h>
  32. #include    <sys/param.h>
  33. #include    <sys/file.h>
  34. #include    <sys/ioctl.h>
  35. #include    <sys/socket.h>
  36. #include    <sys/un.h>
  37. #include    <netinet/in.h>
  38. #include    <netdb.h>
  39.  
  40. int    mastersocket;
  41. #define    DOSTDOUT    (1<<0)
  42. #define    DOSTDIN        (1<<1)
  43. #define    DOSTDERR    (1<<2)
  44. #define    DOONCE        (1<<3)
  45. #define    DOVERBOSE    (1<<4)
  46. #define    DOUNIX        (1<<5)
  47. long    doflags=0;
  48. int    running=1;
  49.  
  50. char    *foreignhost=NULL,*foreignport=NULL;
  51. int    foreignPORT;
  52. struct in_addr    foreignHOST;
  53.  
  54. char    *programname;
  55. extern int errno;
  56. extern char *sys_errlist[];
  57.  
  58.  
  59. int name_to_inet_port();
  60.  
  61. void nice_shutdown()
  62. /* This procedure gets called when we are killed with one of the reasonable
  63.    signals (TERM, HUP, that kind of thing).  The main while loop then
  64.    terminates and we get a chance to clean up. */
  65. {
  66.   running = 0;
  67. }
  68.  
  69.  
  70. /* print an internet host address prettily */
  71. printhost(addr)
  72.      struct in_addr    *addr;
  73. {
  74.   struct hostent    *h;
  75.   char    *s,**p;
  76.   int    i;
  77.  
  78.   h = gethostbyaddr(addr, sizeof(*addr),AF_INET);
  79.   s = (h==NULL) ? NULL : h->h_name;
  80.  
  81.   printf("%d", ((u_char*)addr)[0]);
  82.   for (i=1; i<sizeof(*addr); i++)
  83.     printf(".%d",((u_char*)addr)[i]);
  84.  
  85.   printf("(%s",s?s:"name unknown");
  86.   if (s)
  87.     for (p=h->h_aliases; *p; p++)
  88.       printf(",%s",*p);
  89.   printf(")");
  90. }
  91.  
  92. int setup_socket(name)
  93. char *name;
  94. /* This procedure creates a socket and handles retries on the inet domain.
  95.    Sockets seem to "stick" on my system (SunOS [43].x) */
  96. {
  97.   int    sock;
  98.  
  99.   sock = socket((doflags&DOUNIX)?AF_UNIX:AF_INET, SOCK_STREAM, 0);
  100.   /* I need a real value for the protocol eventually.  IPPROTO_TCP sounds
  101.      like a good value, but what about AF_UNIX sockets?  It seems to have
  102.      worked so far... */
  103.  
  104.   if (sock <0) {
  105.       perror("opening stream socket");
  106.       exit(1);
  107.     }
  108.  
  109.   if (!bindlocal(sock, name, (doflags&DOUNIX)?AF_UNIX:AF_INET)) {
  110.       fprintf(stderr,"%s: error binding stream socket %s (%s)",
  111.           programname,name,sys_errlist[errno]);
  112.       exit(1);
  113.     }
  114.  
  115.   listen(sock,NOFILE);
  116.  
  117.   return(sock);
  118. }
  119.  
  120.  
  121. void waitonchild()
  122.  
  123. {
  124.   union wait status;
  125. #if 0
  126.   unsigned char    reason,signal,rval;
  127.   char    buf[32];
  128. #endif
  129.   int    childpid;
  130.   
  131.   childpid = wait3(&status,WNOHANG,NULL);
  132.   /* What a pity I can't easily print out child statuses */
  133. #if 0
  134.   if (childpid==-1) {
  135.     fputs(stderr,programname);
  136.     fputs(stderr,": error in wait3 while handling SIGCHLD (");
  137.     fputs(stderr,sys_errlist[errno]);
  138.     fputs(stderr,")\n");
  139.     return;
  140.   }
  141.   reason = status.w_status & 0xff;
  142.   if (reason==0) {
  143.     rval = reason >> 8;
  144.     if (rval!=0) {
  145.       fputs(stderr,programname);
  146.       fputs(stderr,": Child ");
  147.       sprintf(buf,"%d",childpid);    fputs(stderr,buf);
  148.       fputs(stderr," gave abnormal exit code ");
  149.       sprintf(buf,"%d",rval);    fputs(stderr,buf);
  150.       fputs(stderr,"\n");
  151.     }
  152.   } else if (reason!=0177) {
  153.     signal = reason & 0x7f;
  154.     fputs(stderr,programname);
  155.     fputs(stderr,": Child ");
  156.     sprintf(buf,"%d",childpid);    fputs(stderr,buf);
  157.     fputs(stderr," killed by signal ");
  158.     sprintf(buf,"%d",signal);        fputs(stderr,buf);
  159.     fputs(stderr," (");
  160.     fputs(stderr,(signal<=SIGUSR2)?signames[signal]:"bogus signal number");
  161.     fputs(stderr,")\n");
  162.   }
  163. #endif
  164. }
  165.  
  166.  
  167. int
  168. authorize_address(sin)
  169.      struct sockaddr    *sin;
  170. {
  171.   if (doflags&DOUNIX) {
  172.     struct sockaddr_un     *srv = (struct sockaddr_un*)sin;
  173.     
  174.     if (foreignport != NULL && 0!=strcmp(foreignport, srv->sun_path)) {
  175.       if (doflags&DOVERBOSE) {
  176.     printf("%s: refusing connection from port %s\n",
  177.            programname, srv->sun_path);
  178.       }
  179.       return 0;
  180.     }
  181.   } else {
  182.     struct sockaddr_in    *srv = (struct sockaddr_in*)sin;
  183.     
  184.     if (foreignhost!=NULL &&
  185.     0!=bcmp(&srv->sin_addr,
  186.         &foreignHOST, sizeof(foreignHOST))) {
  187.       if (doflags&DOVERBOSE) {
  188.     printf("refusing connection from host ");
  189.     printhost(&srv->sin_addr);
  190.     printf(".\n");
  191.       }
  192.       return 0;
  193.     }
  194.     
  195.     if (foreignport!=NULL && foreignPORT != srv->sin_port) {
  196.       if (doflags&DOVERBOSE) {
  197.     printf("refusing connection from port %d.\n",
  198.            ntohs(srv->sin_port));
  199.       }
  200.       return 0;
  201.     }
  202.   }
  203.   
  204.   return 1;
  205. }
  206.  
  207.  
  208. main (argc,argv)
  209. int argc;
  210. char ** argv;
  211.  
  212. {
  213.   int    rval,length;
  214.   struct sockaddr    saddr;
  215.   struct sockaddr_in    *sinp = (struct sockaddr_in*)&saddr;
  216.   struct sockaddr_un    *sunp = (struct sockaddr_un*)&saddr;
  217.   
  218.   programname = argv[0];
  219.   
  220.   if (argc<3) {
  221.     fprintf(stderr,"Usage : %s <port> <command> (in|out|err)+ [once] [verb(|ose)] [quiet] [unix] [foreignport <port>] [foreignhost <host>]\n", programname);
  222.     exit(1);
  223.   }
  224.   
  225.   /* parse trailing args */
  226.   for (length=3; length<argc; length++) {
  227.     if (strcmp(argv[length],"in")==0)
  228.       doflags |= DOSTDIN;
  229.     else if (strcmp(argv[length],"out")==0)
  230.       doflags |= DOSTDOUT;
  231.     else if (strcmp(argv[length],"err")==0)
  232.       doflags |= DOSTDERR;
  233.     else if (strcmp(argv[length],"once")==0)
  234.       doflags |= DOONCE;
  235.     else if (strcmp(argv[length],"verbose")==0
  236.          || strcmp(argv[length],"verb")==0)
  237.       doflags |= DOVERBOSE;
  238.     else if (strcmp(argv[length],"quiet")==0)
  239.       doflags &= ~DOVERBOSE;
  240.     else if (strcmp(argv[length],"unix")==0)
  241.       doflags |= DOUNIX;
  242.     else if (strcmp(argv[length],"foreignport")==0) {
  243.       if (length+1<argc)
  244.     foreignport=argv[++length];
  245.       else
  246.     fprintf(stderr,"%s: foreignport requires port name or number.\n",
  247.         programname);
  248.     } else if (strcmp(argv[length],"foreignhost")==0) {
  249.       if (length+1<argc)
  250.     foreignhost=argv[++length];
  251.       else
  252.     fprintf(stderr,"%s: foreignhost requires host name or number.\n",
  253.         programname);
  254.     } else
  255.       fprintf(stderr,"%s: Bogus extra command line flag \"%s\".\n",
  256.           programname,argv[length]);
  257.   }
  258.   
  259.   if ( ! (doflags&(DOSTDIN|DOSTDERR|DOSTDOUT)) ) {
  260.     fprintf(stderr,"%s: Need at least one {in|out|err}.\n",programname);
  261.     exit(1);
  262.   }
  263.   
  264.   if ( (doflags&DOUNIX) && foreignhost!=NULL ) {
  265.     fprintf(stderr, "%s: foreignhost parameter makes no sense with UNIX domain sockets, ignoring.\n", programname);
  266.     foreignhost = NULL;
  267.   }
  268.   
  269.   signal(SIGCHLD,waitonchild);
  270.   
  271.   mastersocket = setup_socket(argv[1]);
  272.   
  273.   signal(SIGHUP, nice_shutdown);
  274.   signal(SIGINT, nice_shutdown);
  275.   signal(SIGPIPE, nice_shutdown);
  276.   signal(SIGALRM, nice_shutdown);
  277.   signal(SIGTERM, nice_shutdown);
  278.   
  279.   if (foreignhost != NULL && !convert_hostname(foreignhost, &foreignHOST)) {
  280.     fprintf(stderr, "%s: could not translate %s to a host address\n",
  281.         programname, foreignhost);
  282.     exit(1);
  283.   }
  284.   
  285.   if (foreignport!=NULL && !(doflags&DOUNIX) &&
  286.       0 == (foreignPORT = name_to_inet_port(foreignport)) ) {
  287.     fprintf(stderr,"%s: port %s unknown.\n",programname,foreignport);
  288.     exit(1);
  289.   }
  290.   
  291.   while (running) {
  292.     
  293.     length = sizeof(saddr);
  294.     
  295.     rval = accept(mastersocket,&saddr,&length);
  296.     
  297.     if (rval<0) {
  298.       if (errno==EWOULDBLOCK) {
  299.     printf("%s: No more connections to talk to.\n",programname);
  300.       } else if (errno!=EINTR) {
  301.     fprintf(stderr,"%s: error in accept (%s).",
  302.         programname,sys_errlist[errno]);
  303.     exit(1);
  304.       }
  305.       continue;
  306.     }
  307.     
  308.     if (!authorize_address(&saddr)) {
  309.       close(rval);
  310.       continue;
  311.     }
  312.     
  313.     if ( doflags&DOVERBOSE ) {
  314.       printf("%s: Got connection from ",programname);
  315.       if ( doflags&DOUNIX ) {
  316.     printf("%s\n", sunp->sun_path);
  317.       } else {
  318.     printhost(&sinp->sin_addr);
  319.     printf(" port %d\n",ntohs(sinp->sin_port));
  320.       }
  321.     }
  322.     
  323.     fflush(stdout);
  324.     
  325.     if ( doflags&DOONCE || fork()==0 ) {
  326.       /* child process: frob descriptors and exec */
  327.       char    *s;
  328.       
  329.       if ( (doflags&(DOONCE|DOUNIX)) == (DOONCE|DOUNIX) )
  330.     unlink(argv[1]);
  331.       /* We don't want the unix domain socket anymore */
  332.       
  333.       dup2(fileno(stderr),mastersocket);
  334.       ioctl(mastersocket,FIOCLEX,NULL);
  335.       /* We don't need old stderr hanging around after an exec.
  336.      The mastersocket has been closed by the dup2 */
  337.       
  338.       if (doflags & DOSTDIN)
  339.     dup2(rval,fileno(stdin));
  340.       if (doflags & DOSTDOUT)
  341.     dup2(rval,fileno(stdout));
  342.       if (doflags & DOSTDERR)
  343.     dup2(rval,fileno(stderr));
  344.       
  345.       close(rval); /* rval has been properly duplicated */
  346.       
  347.       execl("/bin/csh","csh","-c",argv[2],NULL);
  348.       s ="exec failed\n";
  349.       write(mastersocket,s,strlen(s));
  350.       exit(0);
  351.     } else {
  352.       /* parent: close socket.
  353.      Signal will arrive upon death of child. */
  354.       close(rval);
  355.     }
  356.   }
  357.   
  358.   /* clean up the socket when we're done */
  359.   if (doflags&DOUNIX)
  360.     unlink(argv[1]);
  361.   close(mastersocket);
  362.   
  363. }
  364.