home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume26
/
faucet
/
part01
/
faucet.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-29
|
9KB
|
364 lines
/*
faucet.c, part of
faucet and hose: network pipe utilities
Copyright (C) 1992 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 by Robert Forsman <thoth@ufl.edu>\n";
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#ifdef hpux
#include <signal.h>
#include <sgtty.h>
#endif
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
int mastersocket;
#define DOSTDOUT (1<<0)
#define DOSTDIN (1<<1)
#define DOSTDERR (1<<2)
#define DOONCE (1<<3)
#define DOVERBOSE (1<<4)
#define DOUNIX (1<<5)
long doflags=0;
int running=1;
char *foreignhost=NULL,*foreignport=NULL;
int foreignPORT;
struct in_addr foreignHOST;
char *programname;
extern int errno;
extern char *sys_errlist[];
int name_to_inet_port();
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;
}
/* print an internet host address prettily */
printhost(addr)
struct in_addr *addr;
{
struct hostent *h;
char *s,**p;
int i;
h = gethostbyaddr(addr, sizeof(*addr),AF_INET);
s = (h==NULL) ? NULL : h->h_name;
printf("%d", ((u_char*)addr)[0]);
for (i=1; i<sizeof(*addr); i++)
printf(".%d",((u_char*)addr)[i]);
printf("(%s",s?s:"name unknown");
if (s)
for (p=h->h_aliases; *p; p++)
printf(",%s",*p);
printf(")");
}
int setup_socket(name)
char *name;
/* This procedure creates a socket and handles retries on the inet domain.
Sockets seem to "stick" on my system (SunOS [43].x) */
{
int sock;
sock = socket((doflags&DOUNIX)?AF_UNIX:AF_INET, SOCK_STREAM, 0);
/* 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(1);
}
if (!bindlocal(sock, name, (doflags&DOUNIX)?AF_UNIX:AF_INET)) {
fprintf(stderr,"%s: error binding stream socket %s (%s)",
programname,name,sys_errlist[errno]);
exit(1);
}
listen(sock,NOFILE);
return(sock);
}
void waitonchild()
{
union wait status;
#if 0
unsigned char reason,signal,rval;
char buf[32];
#endif
int childpid;
childpid = wait3(&status,WNOHANG,NULL);
/* What a pity I can't easily print out child statuses */
#if 0
if (childpid==-1) {
fputs(stderr,programname);
fputs(stderr,": error in wait3 while handling SIGCHLD (");
fputs(stderr,sys_errlist[errno]);
fputs(stderr,")\n");
return;
}
reason = status.w_status & 0xff;
if (reason==0) {
rval = reason >> 8;
if (rval!=0) {
fputs(stderr,programname);
fputs(stderr,": Child ");
sprintf(buf,"%d",childpid); fputs(stderr,buf);
fputs(stderr," gave abnormal exit code ");
sprintf(buf,"%d",rval); fputs(stderr,buf);
fputs(stderr,"\n");
}
} else if (reason!=0177) {
signal = reason & 0x7f;
fputs(stderr,programname);
fputs(stderr,": Child ");
sprintf(buf,"%d",childpid); fputs(stderr,buf);
fputs(stderr," killed by signal ");
sprintf(buf,"%d",signal); fputs(stderr,buf);
fputs(stderr," (");
fputs(stderr,(signal<=SIGUSR2)?signames[signal]:"bogus signal number");
fputs(stderr,")\n");
}
#endif
}
int
authorize_address(sin)
struct sockaddr *sin;
{
if (doflags&DOUNIX) {
struct sockaddr_un *srv = (struct sockaddr_un*)sin;
if (foreignport != NULL && 0!=strcmp(foreignport, srv->sun_path)) {
if (doflags&DOVERBOSE) {
printf("%s: refusing connection from port %s\n",
programname, srv->sun_path);
}
return 0;
}
} else {
struct sockaddr_in *srv = (struct sockaddr_in*)sin;
if (foreignhost!=NULL &&
0!=bcmp(&srv->sin_addr,
&foreignHOST, sizeof(foreignHOST))) {
if (doflags&DOVERBOSE) {
printf("refusing connection from host ");
printhost(&srv->sin_addr);
printf(".\n");
}
return 0;
}
if (foreignport!=NULL && foreignPORT != srv->sin_port) {
if (doflags&DOVERBOSE) {
printf("refusing connection from port %d.\n",
ntohs(srv->sin_port));
}
return 0;
}
}
return 1;
}
main (argc,argv)
int argc;
char ** argv;
{
int rval,length;
struct sockaddr saddr;
struct sockaddr_in *sinp = (struct sockaddr_in*)&saddr;
struct sockaddr_un *sunp = (struct sockaddr_un*)&saddr;
programname = argv[0];
if (argc<3) {
fprintf(stderr,"Usage : %s <port> <command> (in|out|err)+ [once] [verb(|ose)] [quiet] [unix] [foreignport <port>] [foreignhost <host>]\n", programname);
exit(1);
}
/* parse trailing args */
for (length=3; length<argc; length++) {
if (strcmp(argv[length],"in")==0)
doflags |= DOSTDIN;
else if (strcmp(argv[length],"out")==0)
doflags |= DOSTDOUT;
else if (strcmp(argv[length],"err")==0)
doflags |= DOSTDERR;
else if (strcmp(argv[length],"once")==0)
doflags |= DOONCE;
else if (strcmp(argv[length],"verbose")==0
|| strcmp(argv[length],"verb")==0)
doflags |= DOVERBOSE;
else if (strcmp(argv[length],"quiet")==0)
doflags &= ~DOVERBOSE;
else if (strcmp(argv[length],"unix")==0)
doflags |= DOUNIX;
else if (strcmp(argv[length],"foreignport")==0) {
if (length+1<argc)
foreignport=argv[++length];
else
fprintf(stderr,"%s: foreignport requires port name or number.\n",
programname);
} else if (strcmp(argv[length],"foreignhost")==0) {
if (length+1<argc)
foreignhost=argv[++length];
else
fprintf(stderr,"%s: foreignhost requires host name or number.\n",
programname);
} else
fprintf(stderr,"%s: Bogus extra command line flag \"%s\".\n",
programname,argv[length]);
}
if ( ! (doflags&(DOSTDIN|DOSTDERR|DOSTDOUT)) ) {
fprintf(stderr,"%s: Need at least one {in|out|err}.\n",programname);
exit(1);
}
if ( (doflags&DOUNIX) && foreignhost!=NULL ) {
fprintf(stderr, "%s: foreignhost parameter makes no sense with UNIX domain sockets, ignoring.\n", programname);
foreignhost = NULL;
}
signal(SIGCHLD,waitonchild);
mastersocket = setup_socket(argv[1]);
signal(SIGHUP, nice_shutdown);
signal(SIGINT, nice_shutdown);
signal(SIGPIPE, nice_shutdown);
signal(SIGALRM, nice_shutdown);
signal(SIGTERM, nice_shutdown);
if (foreignhost != NULL && !convert_hostname(foreignhost, &foreignHOST)) {
fprintf(stderr, "%s: could not translate %s to a host address\n",
programname, foreignhost);
exit(1);
}
if (foreignport!=NULL && !(doflags&DOUNIX) &&
0 == (foreignPORT = name_to_inet_port(foreignport)) ) {
fprintf(stderr,"%s: port %s unknown.\n",programname,foreignport);
exit(1);
}
while (running) {
length = sizeof(saddr);
rval = accept(mastersocket,&saddr,&length);
if (rval<0) {
if (errno==EWOULDBLOCK) {
printf("%s: No more connections to talk to.\n",programname);
} else if (errno!=EINTR) {
fprintf(stderr,"%s: error in accept (%s).",
programname,sys_errlist[errno]);
exit(1);
}
continue;
}
if (!authorize_address(&saddr)) {
close(rval);
continue;
}
if ( doflags&DOVERBOSE ) {
printf("%s: Got connection from ",programname);
if ( doflags&DOUNIX ) {
printf("%s\n", sunp->sun_path);
} else {
printhost(&sinp->sin_addr);
printf(" port %d\n",ntohs(sinp->sin_port));
}
}
fflush(stdout);
if ( doflags&DOONCE || fork()==0 ) {
/* child process: frob descriptors and exec */
char *s;
if ( (doflags&(DOONCE|DOUNIX)) == (DOONCE|DOUNIX) )
unlink(argv[1]);
/* We don't want the unix domain socket anymore */
dup2(fileno(stderr),mastersocket);
ioctl(mastersocket,FIOCLEX,NULL);
/* We don't need old stderr hanging around after an exec.
The mastersocket has been closed by the dup2 */
if (doflags & DOSTDIN)
dup2(rval,fileno(stdin));
if (doflags & DOSTDOUT)
dup2(rval,fileno(stdout));
if (doflags & DOSTDERR)
dup2(rval,fileno(stderr));
close(rval); /* rval has been properly duplicated */
execl("/bin/csh","csh","-c",argv[2],NULL);
s ="exec failed\n";
write(mastersocket,s,strlen(s));
exit(0);
} else {
/* parent: close socket.
Signal will arrive upon death of child. */
close(rval);
}
}
/* clean up the socket when we're done */
if (doflags&DOUNIX)
unlink(argv[1]);
close(mastersocket);
}