/* faucet.c, part of faucet and hose: network pipe utilities Copyright (C) 1992-98 Robert Forsman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static char info[] = "faucet: a network utility for sockets\nWritten 1992-98 by Robert Forsman \n$Id: faucet.c,v 1.22 1998/08/13 15:01:06 thoth Exp $\n"; #include #include extern int errno; /* I hate the errno header file */ #include #include #include #include #ifdef hpux #include #endif /* defined(hpux) */ #include #include #include #ifdef USE_IOCTL #include /* find the FIOCLEX ioctl */ #ifdef linux #include #else /* defined(linux) */ #ifdef sco #include #else /* defined(sco) */ #include #endif /* defined(sco) */ #endif /* defined(linux) */ #else /* defined(USE_IOCTL) */ #include #include #include #endif /* defined(USE_IOCTL) */ #include #ifndef NOUNIXSOCKETS #include #endif #include #include /* for getrlimit with -daemon option */ #include #include #include "common.h" int mastersocket; #define DOONCE (1<<0) #define DOVERBOSE (1<<1) #ifndef NOUNIXSOCKETS #define DOUNIX (1<<2) #endif #define DODAEMON (1<<3) #define EXITCODE_CONNECTION 127 #define EXITCODE_ARGS 126 #define EXITCODE_FAILED_SYSCALL 125 #define EXITCODE_PIPE 124 int doflags=0; int running=1; struct in_addr ** /* addr_array */ convert_hostname(); char *localhost=NULL; char *foreignhost=NULL,*foreignport=NULL; int foreignPORT; int foreignCOUNT=0; struct in_addr **foreignHOST; int name_to_inet_port(); void usage () { fprintf(stderr,"Usage : %s (--in|--out|--err|--fd N)+ [--once] [--verb(|ose)] [--quiet] [--unix] [--foreignport ] [--foreignhost ] [--localhost ] [--daemon] [--serial] [--shutdown (r|w)] [--pidfile fname] [--noreuseaddr] [--backlog n] -[i][o][e][#3[,4[,5...]]][v][1][q][u][d][s] [-p ] [-h ] [-H ] command args\n", progname); } void nice_shutdown() /* This procedure gets called when we are killed with one of the reasonable signals (TERM, HUP, that kind of thing). The main while loop then terminates and we get a chance to clean up. */ { running = 0; } int setup_socket(name, backlog, reuseaddr) char *name; int backlog; int reuseaddr; /* This procedure creates a socket and handles retries on the inet domain. Sockets seem to "stick" on my system (SunOS [43].x) */ { int sock; int family; #ifdef DOUNIX if (doflags&DOUNIX) family = AF_UNIX; else #endif family = AF_INET; sock = socket(family, SOCK_STREAM, #ifdef DOUNIX (doflags&DOUNIX)?0: #endif IPPROTO_TCP); /* I need a real value for the protocol eventually. IPPROTO_TCP sounds like a good value, but what about AF_UNIX sockets? It seems to have worked so far... */ if (sock <0) { perror("opening stream socket"); exit(EXITCODE_CONNECTION); } if (!bindlocal(sock, name, localhost, family, reuseaddr)) { fprintf(stderr,"%s: error binding stream socket %s (%s)\n", progname,name,strerror(errno)); exit(EXITCODE_CONNECTION); } /* We used to ask for NOFILE (max number of open files) for the size of the connect queue. Linux didn't like it (NOFILE=256) so we hardcoded a smaller value. */ listen(sock, (backlog>0 ? backlog : 5) ); return(sock); } void waitonchild() { int status; int childpid; childpid = wait(&status); } int authorize_address(sin) struct sockaddr *sin; { #ifdef DOUNIX if (doflags&DOUNIX) { struct sockaddr_un *srv = (struct sockaddr_un*)sin; if (foreignport != NULL && 0!=strcmp(foreignport, srv->sun_path)) { if (doflags&DOVERBOSE) { fprintf(stderr, "%s: refusing connection from port %s\n", progname, srv->sun_path); } return 0; } } else #endif { struct sockaddr_in *srv = (struct sockaddr_in*)sin; int i; if (foreignhost) { for (i=0; isin_addr, foreignHOST[i], sizeof(struct in_addr))) break; } if (i>=foreignCOUNT) { if (doflags&DOVERBOSE) { fprintf(stderr, "refusing connection from host "); printhost(stderr, &srv->sin_addr); fprintf(stderr, ".\n"); } return 0; } } if (foreignport!=NULL && foreignPORT != srv->sin_port) { if (doflags&DOVERBOSE) { fprintf(stderr, "refusing connection from port %d.\n", ntohs(srv->sin_port)); } return 0; } } return 1; } /**********************************************************************/ /* since we have flag processing for long and short, we do the same thing in two separate pieces of code. The non-trivial ones we encapsulate in a small function */ void flag_in() { add_fd(0); if (how_shutdown == 0) /* make sure we can read from the socket */ how_shutdown = -1; else if (how_shutdown==-2) how_shutdown = 1; } void flag_out() { add_fd(1); if (how_shutdown == 1) /* make sure we can write to the socket */ how_shutdown = -1; else if (how_shutdown==-2) how_shutdown = 0; } void flag_err() { add_fd(2); if (how_shutdown == 1) /* make sure we can write to the socket */ how_shutdown = -1; else if (how_shutdown==-2) how_shutdown = 0; } int flag_scan_comma_fds(s) char *s; { int rval=0; while (1) { int fd; int n; if (1 != sscanf(s, "%i%n", &fd, &n)) { fprintf(stderr, "%s: parse error in file descriptor list at '%s'\n", progname, s); usage(); exit(EXITCODE_ARGS); } add_fd(fd); rval +=n; s += n; if (*s == ',') { rval++; s++; } else { break; } } return rval; } /**********************************************************************/ int main (argc,argv) int argc; char ** argv; { int rval, i; union { struct sockaddr_in in; #ifdef DOUNIX struct sockaddr_un un; #endif } saddr; struct sockaddr_in *sinp = &saddr.in; #ifdef DOUNIX struct sockaddr_un *sunp = &saddr.un; #endif char **cmd; char *pidfilename=0; /* we'll write our PID in decimal into this file */ FILE *pidfp=0; int serialize=0; int backlog=0; /* parameter to pass to listen(2) */ int reuseaddr=1; /* Shall we set SO_REUSEADDR? */ /* * */ set_progname(argv[0]); if (argc<3) { usage(); exit(EXITCODE_ARGS); } /* parse trailing args */ for (i=2; i0) argument.\n", progname, arg); usage(); exit(EXITCODE_ARGS); } } else { int j; for (j=0; arg[j]; j++) { switch (arg[j]) { case 'i': flag_in(); break; case 'o': flag_out(); break; case 'e': flag_err(); break; case '#': j += flag_scan_comma_fds(arg+j+1); break; case '1': doflags |= DOONCE; break; case 'v': doflags |= DOVERBOSE; break; case 'q': doflags &= ~DOVERBOSE; break; case 'u': #ifdef DOUNIX doflags |= DOUNIX; #else fprintf(stderr, "%s: unix-domain sockets are not supported in this binary.\n", progname); exit(EXITCODE_ARGS); #endif break; case 'p': if (i+1=0) { ioctl(rval, TIOCNOTTY, &rval); close(rval); } } /* it seems printing to a closed FP will kill a process in some OSs. */ freopen("/dev/null", "w", stderr); freopen("/dev/null", "w", stdout); freopen("/dev/null", "r", stdin); #endif { int childpid = fork(); if (childpid<0) { fprintf(stderr, "%s: WAUGH! fork failed while trying to enter -daemon mode. This bodes ill.\n", progname); } else if (childpid>0) exit(0); } #ifndef NO_SETSID setsid(); #endif } if (pidfp) { fprintf(pidfp, "%ld\n", (long) getpid()); fclose(pidfp); } while (running) { { int length; length = sizeof(saddr); rval = accept(mastersocket,(struct sockaddr*)&saddr,&length); } if (rval<0) { if (errno==EWOULDBLOCK) { /* this can't happen, but why take chances? */ fprintf(stderr, "%s: No more connections to talk to.\n",progname); } else if (errno!=EINTR) { fprintf(stderr,"%s: error in accept (%s).", progname,strerror(errno)); exit(EXITCODE_FAILED_SYSCALL); } continue; } if (!authorize_address(&saddr)) { close(rval); continue; } if ( doflags&DOVERBOSE ) { fprintf(stderr, "%s: Got connection from ",progname); #ifdef DOUNIX if ( doflags&DOUNIX ) { puts(sunp->sun_path); } else #endif { printhost(stderr, &sinp->sin_addr); fprintf(stderr, " port %d\n",ntohs(sinp->sin_port)); } } fflush(stdout); if ( doflags&DOONCE || fork()==0 ) { /* XXX should check error return */ /* child process: frob descriptors and exec */ char *s; int duped_stderr; #ifdef DOUNIX if ( (doflags&(DOONCE|DOUNIX)) == (DOONCE|DOUNIX) ) unlink(argv[1]); /* We don't want the unix domain socket anymore */ #endif /* the child doesn't need the master socket fd */ close(mastersocket); /* put stderr somewhere safe temporarily */ duped_stderr = dup(fileno(stderr)); /* but we don't want it to hang around after we exec... */ #ifdef USE_IOCTL ioctl(duped_stderr,FIOCLEX,NULL); #else fcntl(duped_stderr,F_SETFD,FD_CLOEXEC); #endif /* We don't need old stderr hanging around after an exec. The mastersocket has been closed by the dup2 */ dup_n(rval); /* dup the socket onto all the chosen file descriptors */ close(rval); /* rval has been properly duplicated */ execvp(cmd[0], cmd); s ="exec failed\n"; write(duped_stderr,s,strlen(s)); exit(0); } else { /* parent: close socket. Signal will arrive upon death of child. */ close(rval); if (serialize) { int status; pid_t pid; pid = wait(&status); /* child has exited */ if (pid == -1) { fprintf(stderr, "%s: error serializing (waiting on child) ", progname); perror(""); } } } } if (pidfilename != 0) { if (doflags&DOVERBOSE) fprintf(stderr, "%s: removing pid file %s\n", progname, pidfilename); unlink(pidfilename); /* if it fails, we just don't care */ } #ifdef DOUNIX /* clean up the socket when we're done */ if (doflags&DOUNIX) unlink(argv[1]); #endif close(mastersocket); return 0; }