/* $Id: hose.c,v 1.22 1998/06/12 19:59:18 thoth Exp thoth $, 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[] = "hose: a network utility for sockets\nWritten 1992-98 by Robert Forsman \n$Id: hose.c,v 1.22 1998/06/12 19:59:18 thoth Exp thoth $\n"; #include #include #include #ifdef hpux #include #endif #include #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 #endif /* defined(USE_IOCTL) */ #include #include #include #include #include #include #include #include #ifdef AIX #include #endif #include "memmove.h" #include "common.h" #define DOUNIX (1<<0) #define DOVERBOSE (1<<1) #define DOJAM (1<<2) #define DOSLAVE (1<<3) #define EXITCODE_CONNECTION 127 #define EXITCODE_ARGS 126 #define EXITCODE_FAILED_SYSCALL 125 #define EXITCODE_PIPE 124 struct in_addr ** /* addr_array */ convert_hostname(); long doflags=0; int retry=0; /* how many times to retry after ECONNREFUSED */ unsigned delay=5; /* how long to wait between each retry */ int shutdn=0; /* should we fork, wait and shutdown? */ char *localport=NULL; /* local port name */ char *localaddr=NULL; /* local internet address */ extern int errno; int name_to_inet_port(); void usage () { fprintf(stderr,"Usage : %s (--in|--out|--err|--fd N|--slave)+ [--verb(|ose)] [--unix] [--localport ] [--localhost ] [--retry n] [--delay n] [--shutdown [r|w][a]] [--noreuseaddr] -[i][o][e][#3[,4[,5...]]][s][v][q][u] [-p ] [-h ] [ args ... ]\n",progname); } int setup_socket(hostname,portname, reuseaddr) char *hostname; char *portname; int reuseaddr; { int sock = -1; struct in_addr ** addresses=0; struct sockaddr_un unix_addr; struct sockaddr_in inet_addr; int num_addresses; int length; int tries; int cstat; if (doflags&DOUNIX) { unix_addr.sun_family = AF_UNIX; strncpy( unix_addr.sun_path, portname, sizeof(unix_addr.sun_path)); unix_addr.sun_path[sizeof(unix_addr.sun_path) - 1] = 0; length = sizeof(struct sockaddr_un); num_addresses = 1; } else { inet_addr.sin_family = AF_INET; if (0==(addresses = convert_hostname(hostname, &num_addresses))) { fprintf(stderr, "%s: could not translate %s to a host address\n", progname, hostname); exit(EXITCODE_CONNECTION); } inet_addr.sin_port = name_to_inet_port(portname); if (inet_addr.sin_port==0) { fprintf(stderr,"%s: bogus port number %s\n",progname,portname); exit(EXITCODE_CONNECTION); } length = sizeof(struct sockaddr_in); } for (tries = 0; retry<0 || tries <= retry; tries++) { int j; /* multi-homed hosts are a little tricky */ for ( j=0; j=0 || fromsocklen>=0) { /********************/ FD_ZERO(&readfds); FD_ZERO(&writefds); if (tosocklen>=0) { if (tosocklen==0) { FD_SET(0, &readfds); } else { FD_SET(sock, &writefds); } } if (fromsocklen>=0) { if (fromsocklen==0) { FD_SET(sock, &readfds); } else { FD_SET(1, &writefds); } } /********************/ rval=select(sock+1, &readfds, &writefds, (fd_set*)0, (struct timeval*)0); /********************/ if (rval<0) { if (errno != EINTR) { perror("during copyio() select(2)"); exit(EXITCODE_PIPE); } } else if (rval==0) { break; } /********************/ if (FD_ISSET(1, &writefds)) { rval = write(1, fromsockbuf, fromsocklen); if (rval<0) { perror("during copyio() write(2)(1)"); exitval = EXITCODE_PIPE; fromsocklen = -1; shutdown(sock, 0); } else { memmove(fromsockbuf, fromsockbuf+rval, fromsocklen-rval); fromsocklen -= rval; } } if (FD_ISSET(sock, &writefds)) { rval = write(sock, tosockbuf, tosocklen); if (rval<0) { perror("during copyio() write(2)(sock)"); exitval = EXITCODE_PIPE; tosocklen = -1; shutdown(sock, 1); } else { memmove(tosockbuf, tosockbuf+rval, tosocklen-rval); tosocklen -= rval; } } if (FD_ISSET(0, &readfds)) { tosocklen = read(0, tosockbuf, BSIZE); if (tosocklen<0) { perror("during copyio() read(2)(0)"); exitval = EXITCODE_PIPE; tosocklen = -1; } else if (tosocklen==0) { tosocklen = -1; shutdown(sock, 1); } } if (FD_ISSET(sock, &readfds)) { fromsocklen = read(sock, fromsockbuf, BSIZE); if (fromsocklen<0) { perror("during copyio() read(2)(0)"); exitval = EXITCODE_PIPE; fromsocklen = -1; } else if (fromsocklen==0) { fromsocklen = -1; shutdown(sock, 0); } } } exit(exitval); } void endjam() { doflags &= ~DOJAM; } /**********************************************************************/ /* 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); 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 sock,i; int jampipe[2]; char **cmd; int reuseaddr =1; set_progname(argv[0]); if (argc<4) { usage(); exit(EXITCODE_ARGS); } if (strcmp(argv[1],"-unix-")==0 || strcmp(progname,"uhose")==0 ) doflags |= DOUNIX; for (i=3; ipipe(jampipe)) { perror("opening jampipe"); exit(EXITCODE_ARGS); } } fflush(stdout); fflush(stderr); while ( (doflags & DOJAM) && fork() ) { char ch; close (jampipe[1]); while (1==read(jampipe[0], &ch, 1)) ; close (jampipe[0]); jampipe[0] = -1; if (0>pipe(jampipe)) { perror("opening jampipe"); exit(EXITCODE_FAILED_SYSCALL); } } if (doflags&DOJAM) close (jampipe[0]); reserve_fds(0); sock = setup_socket(argv[1],argv[2], reuseaddr); if (doflags&DOUNIX && localport!=NULL) unlink(localport); if (doflags &DOSLAVE) { copyio(sock); } fflush(stdout); fflush(stderr); /* if we're to shutdown(2) the socket when the subprocess exits we need to fork */ i = shutdn ? fork() : 0; if (i) { /* we are supposed to shutdown(2) the socket and we are the parent */ int status; int pid; pid = wait(&status); if (pid != -1 && i!=pid) fprintf(stderr, "Strange, wait returned a child I don't know about. I'm an unwed father!\n"); shutdown(sock, 2); /* shut the socket down nicely? */ close(sock); exit( (status&0xff) ? EXITCODE_FAILED_SYSCALL : ((status>>8)&0xff)); } else { int sparefd; char *s; sparefd = dup(fileno(stderr)); #ifdef USE_IOCTL ioctl(sparefd,FIOCLEX,NULL); #else fcntl(sparefd,F_SETFD,FD_CLOEXEC); #endif dup_n(sock); /* dup the socket onto all the chosen file descriptors */ close(sock); if (doflags&DOJAM) close (jampipe[1]); execvp(cmd[0], cmd); s ="exec failed for "; write(sparefd,s,strlen(s)); write(sparefd,cmd[0],strlen(cmd[0])); write(sparefd,"\n",1); exit(EXITCODE_FAILED_SYSCALL); } /* NOTREACHED */ }