/* * Example of a concurrent server using TCP protocol. */ #include "tcp.h" static char *copyright= "@(#) tcpserv 2.1 Shareware Copyright (c) 1993 Sam Lantinga\n"; extern struct passwd *login_auth(); extern void outa_here(), daemonize(); void relief(); char *addr2host(); extern int errno; int childpid=0, newfd; int netdebug=0, telnet=0; int login=0, doutmp=0; int demonize=0; char *pname; char remotehost[64]; #define MAXKIDS 256 /* The absolute maximum forked processes */ int children[MAXKIDS]; /* Forked processes handling tcp connections */ static int maxkids=0; /* The maximum number of forked processes */ main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind, errno; int fd, streamfd, clilen, port; int i, j, c, found; char *ptr, *args[256]; struct passwd *pw=NULL; struct sockaddr_in cli_addr, serv_addr; static char *Usage= "Usage: %s [-f maxproc] [-d] [-t] [-u|-n] port [program [arguments]]\n"; pname = argv[0]; /* Get the run-time arguments. */ while ( (c=getopt(argc, argv, "df:ntux")) != EOF ) { switch (c) { /* Allow forking children up to a point. */ case 'f': if ( ! (maxkids=atoi(optarg)) ) { fprintf(stderr, "Maximum fork limit must be an integer.\n"); exit(1); } if ( maxkids > MAXKIDS ) { fprintf(stderr, "The maximum allowed children is %d\n", MAXKIDS); exit(1); } for ( i=0; i<maxkids; ++i ) children[i]=0; break; case 'n': login=1; doutmp=1; break; case 't': telnet=1; break; case 'u': doutmp=1; break; case 'd': demonize=1; break; case 'x': netdebug=1; break; case '?': fprintf(stderr, Usage, pname); exit(1); default: fprintf(stderr, Usage, pname); exit(1); } } argc-=optind; if ( ! login && (argc < 2 ) ) { fprintf(stderr, "Requires a program to run at a connection.\n"); fprintf(stderr, Usage, pname); exit(1); } if ( (port=atoi(argv[optind++])) < 1 ) { fprintf(stderr, "Port must be an integer.\n"); exit(1); } /* Copy the command line arguments to a local array */ for ( i=0; argv[optind]; ++i ) { if ((args[i]=(char *)malloc(strlen(argv[optind])+1)) == NULL) { perror("Malloc error"); exit(2); } strcpy(args[i], argv[optind++]); } args[i]=NULL; if ( login && (argc > 1) && args[0][0] != '/' ) { fprintf(stderr, "Login shell must be a full pathname.\n"); exit(1); } /* Erase the command line arguments */ for ( i=1; argv[i]; ++i ) { for ( j=0; argv[i][j]; ++j ) argv[i][j]=' '; argv[i]=NULL; } /* Set up tty modes for the pty */ (void) tty_getmode(0); /* * Open a TCP socket (an Internet stream socket). */ if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Can't open stream socket"); exit(3); } /* * Bind our local address so that the client can send to us. */ d_zero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { perror("Can't bind local address"); exit(3); } if ( listen(fd, 3) < 0 ) { perror("listen() error"); exit(3); } /* Run as a daemon, if desired. */ if ( demonize && !netdebug ) daemonize(); signal(SIGPIPE, SIG_IGN); /* Don't die on broken connections */ signal(SIGCLD, relief); /* We don't want zombie children */ for ( ; ; ) { /* * Wait for a connection from a client process. * This is an example of a concurrent server. */ clilen = sizeof(cli_addr); if ((newfd=accept(fd, (struct sockaddr *)&cli_addr, &clilen))<0) { if ( errno == EINTR ) continue; else { perror("accept() error"); exit(3); } } (void) addr2host((struct sockaddr *)&cli_addr, remotehost); if ( netdebug ) { fprintf(stderr, "Connection requested from %s\n", remotehost); } /* Check to see if we can fork */ if ( maxkids ) { for ( i=0, found=0; i<maxkids; ++i ) { if ( !children[i] || kill(children[i], 0 < 0) ) { found=1; break; } } if ( ! found ) { /* We had too many children already */ ptr="Server busy. Please try again later.\r\n"; write(newfd, ptr, strlen(ptr)); close(newfd); continue; } } if ( (childpid = fork()) < 0) { if ( errno == EAGAIN ) { ptr="Can't fork. Try again later.\r\n"; write(newfd, ptr, strlen(ptr)); close(newfd); continue; } else { perror("fork() error"); exit(3); } } else if (childpid == 0) { /* child process */ close(fd); if ( telnet ) init_telnet(newfd, 1); /* Do login authentication if desired */ if ( login ) { if ( (pw=login_auth(newfd)) == NULL ) { close(newfd); exit(0); } } /* Set up the pty and shell */ if ( (streamfd=setup(args, pw)) < 0 ) { perror("setup"); exit(0); } /* We now have no controlling tty */ /* redirect stderr to newfd */ close(2); dup(newfd); close(0); close(1); /* sync read/write the fd's */ sync_rw(newfd, streamfd); /* process the request */ #ifdef OLDDEBUG fprintf(stderr, "sync-rw() returned.\n"); #endif outa_here(); } close(newfd); /* parent process */ children[i]=childpid; } } /* A function to prevent zombie processes from clogging the system */ void relief(sig) int sig; { wait(NULL); signal(sig, relief); } /* A function to convert a sockaddr_in structure to its corresponding hostname (or its IP address if the hostname can't be found) */ char *addr2host(addrptr, host) struct sockaddr *addrptr; char *host; { struct in_addr peeraddr; /* The address of the connector */ struct hostent *hp; int i; char *ptr, tmp[12]; /* * The set of four bytes in the third buffer position are * the 32 bit ip address of the connector. */ d_copy((&(addrptr->sa_data[2])), (char *)(&peeraddr.s_addr), sizeof(peeraddr.s_addr)); /* Look up the address in the host tables */ if ( (hp=gethostbyaddr((char *)(&peeraddr.s_addr), sizeof(peeraddr.s_addr), AF_INET)) == NULL ) { /* Convert it to a dot-ip address */ *host='\0'; for ( i=0, ptr=(char *)&peeraddr.s_addr; i<3; ++i, ++ptr ) { sprintf(tmp, "%d.", ((int)*ptr < 0)?((int)*ptr+256):(int)*ptr); strcat(host, tmp); } sprintf(tmp, "%d", ((int)*ptr < 0)?((int)*ptr+256):(int)*ptr); strcat(host, tmp); } else strcpy(host, hp->h_name); return(host); }