home *** CD-ROM | disk | FTP | other *** search
- /* Public domain. */
-
- #include <stdio.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <sys/file.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <arpa/inet.h>
- #include <sys/time.h>
- #include <sys/param.h>
- #include "dupdup.h"
- #include "malloc.h"
- #include "errno.h"
- #include "username.h"
- #include "hostname.h"
- #include "portname.h"
- #include "env.h"
- #include "getopt.h"
- #include "authuser.h"
- #include "numfiles.h"
-
- #ifndef MAXHOSTNAMELEN
- #define MAXHOSTNAMELEN 64
- #endif
-
- static int flagdo931 = 1;
- static int flagprintlocal = 0;
- static int flagverbose = 1;
- static struct sockaddr_in sabind;
-
- static char *fnrecord;
-
- int record(uid,host,port,pid)
- int uid;
- char *host;
- int port;
- int pid;
- {
- char *home;
- char pidstr[50];
- int fd;
- /* The idea is to record our pid in $HOME/.tcpserver.host.uid.port. */
- /* Then tcpkiller can read the pid from there, and send us a signal. */
- /* TCPHOME overrides HOME if it is set. */
- /* XXX: should we choose a signal that won't hurt ordinary processes? */
- /* XXX: anything good to do if $HOME is undefined or unwritable? */
-
- home = env_get("TCPHOME");
- if (!home)
- {
- home = env_get("HOME");
- if (!home)
- return -1;
- }
- fnrecord = malloc(strlen(home) + strlen(host) + 100);
- if (!fnrecord)
- return -1;
- sprintf(fnrecord,"%s/.tcpserver.%s.%d.%d",home,host,uid,port);
-
- unlink(fnrecord); /* XXX */
-
- fd = open(fnrecord,O_WRONLY | O_CREAT | O_EXCL,0400);
- if (fd == -1)
- {
- /* XXX */
- return -1;
- }
-
- sprintf(pidstr,"%d",pid);
-
- write(fd,pidstr,strlen(pidstr) + 1); /* XXX: check for partial writes */
-
- close(fd);
- return 0;
- }
-
- SIGRET sigterm()
- {
- if (fnrecord)
- unlink(fnrecord); /*XXX*/
- exit(0);
- }
-
- SIGRET sigchld()
- {
- wait((union wait *) 0); /*XXX*/
- }
-
- static char errbuf[500];
- void die(n,s) int n; char *s;
- { if (flagverbose) fprintf(stderr,"tcpserver: fatal: %s\n",s); exit(n); }
- void dies(n,s,t) int n; char *s; char *t;
- { if (flagverbose) fprintf(stderr,"tcpserver: fatal: %s%s\n",s,t); exit(n); }
- void diep(n,s) int n; char *s;
- { if (flagverbose) sprintf(errbuf,"tcpserver: fatal: %s",s); perror(errbuf); exit(n); }
- void warnp(s) char *s;
- { if (flagverbose) sprintf(errbuf,"tcpserver: warning: %s",s); perror(errbuf); }
- void warnsp(s,t) char *s; char *t;
- { if (flagverbose) sprintf(errbuf,"tcpserver: warning: %s%s",s,t); perror(errbuf); }
-
- main(argc,argv,envp)
- int argc;
- char *argv[];
- char *envp[];
- {
- int s;
- int dummy;
- int t;
- int *d;
- int tdup;
- int numfiles;
- static struct sockaddr_in sa;
- int uid;
- int i;
- char lochost[MAXHOSTNAMELEN];
- struct in_addr locinaddr;
- char tcplocal[300]; /*XXX*/
- char tcpremote[300]; /*XXX*/
- int locport;
- int remport;
- char *remhost;
- char *ruser;
- char *uname;
- int opt;
-
- while ((opt = getopt(argc,argv,"qQ4rR")) != EOF)
- switch(opt)
- {
- case 'q':
- flagverbose = 0;
- break;
- case 'Q':
- flagverbose = 1;
- break;
- case '4':
- flagprintlocal = 1;
- break;
- case 'r':
- flagdo931 = 1;
- break;
- case 'R':
- flagdo931 = 0;
- break;
- case '?':
- break; /*XXX*/
- default:
- break; /*XXX*/
- }
- argc -= optind; argv += optind;
-
- if (argc < 3)
- die(1,"need at least three arguments");
-
- uid = getuid();
- if (uid2username(uid,&uname) == -1) /* will never happen */
- die(2,"cannot figure out my own username");
-
- gethostname(lochost,sizeof(lochost));
- if (hostname2inaddr(lochost,&locinaddr) == -1)
- die(2,"cannot figure out my own hostname");
-
- if ((argv[0][0] == '0') && (argv[0][1] == 0))
- sabind.sin_addr.s_addr = INADDR_ANY; /* special case "0" */
- else
- {
- if (hostname2inaddr(argv[0],&sabind.sin_addr) == -1)
- dies(2,"cannot figure out hostname ",argv[0]);
- /* XXX: check whether sabind.sin_addr matches locinaddr? */
- }
-
- if (portname2port(argv[1],&locport,"tcp") == -1)
- dies(2,"cannot figure out portname ",argv[1]);
- locport = locport & 65535;
- sabind.sin_port = htons((short) locport);
-
- close(6);
- close(7); /* XXX: these have implications that should be documented */
- signal(SIGTERM,sigterm);
- signal(SIGINT,sigterm);
- signal(SIGCHLD,sigchld);
- signal(SIGPIPE,SIG_IGN);
-
- numfiles = NUMFILES;
- d = (int *) malloc(numfiles * sizeof(int));
- if (!d)
- die(6,"cannot malloc");
- for (i = 0;i < numfiles;++i)
- d[i] = -1;
-
- s = socket(AF_INET,SOCK_STREAM,0);
- if (s == -1)
- diep(7,"cannot create server socket");
- sabind.sin_family = AF_INET;
- if (bind(s,(struct sockaddr *) &sabind,sizeof(sabind)) == -1)
- diep(9,"cannot bind server socket");
- if (listen(s,5) == -1)
- diep(10,"cannot listen on server socket");
- dummy = sizeof(sabind);
- if (getsockname(s,&sabind,&dummy) == -1)
- diep(9,"cannot figure out local connection address");
- locport = ntohs(sabind.sin_port);
-
- if (env_init() == -1)
- die(11,"cannot init environment");
- if (env_put("PROTO=TCP") == -1)
- die(11,"out of memory putting PROTO into environment");
- sprintf(tcplocal,"TCPLOCAL=%s@%s(%s):%d"
- ,uname
- ,inet_ntoa(locinaddr) /* could use sabind.sin_addr */
- ,lochost
- ,locport
- );
- if (env_put(tcplocal) == -1)
- die(11,"out of memory putting TCPLOCAL into environment");
-
- if (flagprintlocal)
- {
- FILE *fd4;
- fd4 = fdopen(4,"w");
- if (!fd4)
- die(1,"cannot print TCPLOCAL on fd 4");
- fprintf(fd4,"%s@%s(%s):%d\n"
- ,uname
- ,inet_ntoa(locinaddr) /* could use sabind.sin_addr */
- ,lochost
- ,locport
- );
- fclose(fd4);
- close(4); /* just in case */
- }
-
- if (record(uid,lochost,locport,getpid()) == -1)
- die(1,"cannot record process id for tcpkiller");
- /* XXX: could use argv[0] instead of lochost here... hmmm */
-
- for (;;)
- {
- /* XXX: don't accept too many requests at once? */
- dummy = sizeof(sa);
- t = accept(s,(struct sockaddr *) &sa,&dummy);
- if (t == -1) /* so when can this happen? */
- {
- switch(errno)
- {
- case SIGCHLD: break; /*XXX*/
- case EINTR: break; /*XXX*/
- case EMFILE: sleep(1); break; /*XXX*/
- case ENFILE: sleep(1); break; /*XXX*/
- default: ; /*XXX*/
- }
- continue;
- }
-
- switch(fork())
- {
- case -1:
- warnp("cannot fork");
- break;
-
- case 0:
-
- remport = ntohs(sa.sin_port);
- ruser = "";
- if (flagdo931)
- {
- ruser = auth_tcpuser((unsigned long) sa.sin_addr.s_addr
- ,(unsigned short) locport
- ,(unsigned short) remport);
- if (!ruser)
- ruser = ""; /* XXX: any other suggestions? */
- }
- if (inaddr2hostname(sa.sin_addr,&remhost) == -1) /* will never happen */
- die(1,"cannot find hostname");
- sprintf(tcpremote,"TCPREMOTE=%s@%s(%s):%d"
- ,ruser
- ,inet_ntoa(sa.sin_addr)
- ,remhost
- ,remport
- );
- if (env_put(tcpremote) == -1)
- die(1,"out of memory putting TCPREMOTE into environment");
-
- tdup = dup(t);
- if (tdup == -1)
- diep(1,"cannot dup connection to client");
-
- /* Now put t into descriptor 6 and tdup into descriptor 7. */
- /* XXX: should check that t and tdup are small enough */
- d[t] = 6;
- d[tdup] = 7;
- if (dupdup(d,numfiles) == -1)
- diep(1,"cannot dup connection to client");
-
- signal(SIGPIPE,SIG_DFL);
- execvp(argv[2],argv + 2);
- warnsp("cannot exec ",argv[2]);
- exit(1);
-
- default:
- close(t);
- }
- }
- }
-